2

I'm having trouble translating from some C declarations to Delphi XE2 for calling functions in a DLL. I translated all the function declarations from a Visual Basic source file, but in testing them I ran in to problems. Some functions returned Long values, but debugging my code I observed that the functions involved returned values that weren't right. Then I turned to the original code in C, and there I found the root of my troubles: At some point in the original C code there is this declaration:

typedef struct { } __RSI_CHANNEL;       
typedef __RSI_CHANNEL FAR* RSI_CHANNEL;

Now, some functions return RSI_CHANNEL; those functions return values like this:

return (RSI_CHANNEL)ws;

And ws is declared as:

rsiChannel FAR* ws = new FAR rsiChannel;

rsiChannel is a typedef struct. So far, so good...by now I guess some of you may have recognized this as PIMPL idiom. Ok, according to the source code comments, I should have to save that return value (RSI_CHANNEL) and test against NULL, and pass it through function calls untouched...nothing more...so my take is it should be implemented in Delphi as Pointer. But it doesn't work. Something like this:

Type 
  RSI_CHANNEL = Pointer;

...{ later in implementation block }...

Function rsiInitWsock(HostName : PAnsiChar; port : Long) : RSI_CHANNEL; stdcall; external 'rsidll32';

No compile errors, no runtime errors. If I call this function, I get Nil.

¿Any idea how this could be implemented in Delphi XE2? and, ¿what I'm doing wrong? thanks in advance.

Additional details:

  • Delphi XE2 (target: Win32)
  • Windows 7 x64

I found the problem; it didn't had to do with my code, it was right since the beginning; it has to do with a ping function in the DLL, it worked on a laptop but it doesn't want to work with a desktop PC (both Win7), and when it doesn't work it breaks subsequent function calls to the DLL (why, I don't know...yet). Anyway, it wasn't a full solution, but @DavidHeffernan was the first to come up with the idea that the problem was someplace else, so I'm accepting his answer mainly because it pointed me in the right direction. Thanks to everyone!

ogalonzo
  • 37
  • 7
  • What does the C declaration of `rsiInitWsock` look like? – J... Aug 14 '14 at 20:21
  • It is declared as: RSI_DLL RSI_CHANNEL RSI_API rsiInitWsock(char* hostname, WORD port); RSI_DLL is a macro used for the exported functions in the DLL, RSI_API = stdcall. – ogalonzo Aug 14 '14 at 20:23
  • I think `WORD` -> `UInt16`, No? You're declaring `long`, which is a signed 64-bit integer. – J... Aug 14 '14 at 20:28
  • 1
    @J... Only in C# is `long` 64 bits wide – David Heffernan Aug 14 '14 at 20:33
  • @DavidHeffernan I was actually just wondering if I'd crossed wires, that it was maybe 32-bit... I always use `Int64` in delphi - the myriad of aliases trip me up! In any case, it's not unsigned and it's not a 16-bit `word`. – J... Aug 14 '14 at 20:35
  • You're correct, that's a minor mistake but it doesn't have an impact on the problem. As David Hefferman said, it lies elsewhere. – ogalonzo Aug 14 '14 at 20:38
  • Your translation is correct. But I read somewhere that you must put the DLL in the data directory for it to work. I assume that that data directory must be on the PATH then, too. – Rudy Velthuis Aug 15 '14 at 06:55
  • @RudyVelthuis , I will try moving the DLL around. Thanks. – ogalonzo Aug 15 '14 at 14:13

2 Answers2

3

As described, your handling of RSI_CHANNEL is correct. Declaring it as Pointer is the appropriate action. To make type safety stronger you could define a distinct type rather than an alias:

Type 
  RSI_CHANNEL = type Pointer;

If the port parameter is really WORD then that maps to Word in Delphi.

As to your problem, it lies elsewhere. The translation of RSI_CHANNEL is accurate.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • I should add that I tried several options...all that were pointer-based returned Nil. Since the VB implementation used Long as return type, I used integer-based types until I saw the C declaration. Interestingly, when I use Int64 (and UInt64) as return type I get something (a big number), everything else gets 0 or Nil. – ogalonzo Aug 14 '14 at 20:28
  • Int64 is wrong because your pointers are 32 bit. Your problem lies elsewhere. – David Heffernan Aug 14 '14 at 20:30
  • 2
    If you use an `Int64`, the compiler will expect the function to return a 64bit value using the EDX and EAX registers, and then will combine them together to create the `Int64` value. But a pointer in a 32bit environment would be returned using the EAX register only, leaving the EDX register unused or random at worse. That would account for large `Int64` values being "returned". – Remy Lebeau Aug 14 '14 at 21:41
2

Since RSI_CHANNEL is a typed pointer in the C code, I would declare a similar typed pointer in Delphi to match, instead of using an untyped Pointer (this is also inline with modern Delphi versions using STRICT to avoid untyped pointers in Win32 API handle types like HWND, etc):

type
  RSI_CHANNEL = ^__RSI_CHANNEL;
  __RSI_CHANNEL = record
  end;       

Function rsiInitWsock(HostName : PAnsiChar; port : WORD) : RSI_CHANNEL; stdcall; external 'rsidll32';
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • 1
    Then your problem lies elsewhere, not related to the pointer itself. You are going to have to debug into the DLL function call and find out EXACTLY what it is really returning and how it is returning it. Something fishy is going on. – Remy Lebeau Aug 14 '14 at 21:37
  • The code you have shown should not be behaving the way you have described. – Remy Lebeau Aug 14 '14 at 21:42
  • @Remy those comments are the focus of the question and should appear in the answer. – David Heffernan Aug 15 '14 at 03:01