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!
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.
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';
Related
I am trying to implement low-level code that passes variadic arguments to a function pointer (with the function defined in NASM). This pointer can change at any time, so I need to declare a function pointer with the following signature:
typedef int (__fastcall* callback)(...);
However, unless I declare it with a non-variadic parameter (which wont work), the compiler outputs the error "error : expected a type specifier". Is there any way to fix this just using C? Is there a different implementation that would work short of adjusting all of the other parameters based on calling convention in assembly? Any ideas or solutions would be appreciated.
Edit: I'm aware that this is a very hacky implementation, but I am working with a legacy codebase. So as much as I'd like to, I can't completely change how everything works as I need a solution which will work in the current codebase.
I'm attempting to build a test framework in TCL that will allow me to perform scripted tests on a remote device over a TCP socket. There already exists a Visual Basic interface and with SWIG in Ubuntu I'm reusing the C functions that it calls to build a shared library that will work as an extension to TCL. I have had success at incorporating basic functions, such as opening/closing sockets, and basic read/writes to single memory addresses on the device using SWIG's typemaps.i to provide pointers (*OUTPUT) to the readAddress function to return address values to TCL.
The problem is that for this to be useful I am going to have to incorporate a large number of Remote Procedure Calls which pass complicated data types into (and back out of!) the device. As a proof of concept I'm attempting to get a relatively simple function working. This attempts to read default test parameters via an RPC; a pointer to a struct is provided for the function to use for the results: rpc_testDefaults ( testDefaults_t *testDefaults ).
The typedef for testDefaults_t is in testDefaults.h, which is in the style of the following:
// testDefaults.h
#include <stdint.h>
typedef uint32_t customType_t;
typedef struct
{
customType_t varName1; // Description
uint32_t varName2; // Description
// 13 more uint32_t elements
} testDefaults_t;
// Two more struct type definitions
testDefaults.c is along the lines of this:
// testDefaults.c
#include "testDefaults.h"
// #ifdefs to compile as 'client' OR 'server' (defaults to 'client')
rpc_testDefaults ( testDefaults_t, *testDefaults )
{
// function
}
My SWIG interface file looks like this:
// rpcTest.i
%module rpcTest
%include <cpointer.i>
%include "testDefaults.h"
%pointer_functions(testDefaults_t, testDefaults);
//%apply int *OUTPUT {testDefaults_t, *testDefaults};
%{
#include "testDefaults.h"
extern int rpc_testDefaults ( testDefaults_t, *testDefaults )
}%
extern int rpc_testDefaults ( testDefaults_t, *testDefaults )
There are many other .c and header files in the same folder which support this function and the others which I mentioned I got working.
I run swig -tcl -debug-typedef rpcTest.i which gives me rpcTest_wrap.c, I can see that the testDefaults_t has been recognised as a type/scope as is has a section in the debug output (it's also included in the unnamed scope section: testDefaults_t -> testDefaults_t).
I run gcc -fPIC -DCLIENT_FLAG -c *.c -I/usr/include/tcl8.5 and I get an error from a line in the SWIG output file: rpcTest_wrap.c:1803:3: error: unknown type name 'testDefaults_t' (plus a lot more errors derived from this). The line in question is the first line in this function:
static testDefaults_t *new_testDefaults() {
return (testDefaults_t *)malloc(sizeof(testDefaults_t));
}
Which I believe is cpointers.i creating a function for TCL to 'create' a pointer to that struct.
I have a feeling this is something to do with gcc including files in the wrong order, but I'm at a loss as to what to do next. I've tried many combinations of defining the header in various places in the interface file and this is the combination that gives the least errors :). You can see my commented-out partial attempt at using typemaps instead of cpointers but I'm even more clueless with these, I managed it for a pointer to a single value but it didn't seem to be working for a struct with it's own type. It did compile without error though.
So is it possible to get what I'm trying to achieve working using cpointers.i? Any suggestions on how to overcome the compiler issue? Would I be better off learning how to use typemaps? Let me know where I can provide more detail if it would help, I may be leaving out crucial information as I've had to summarize and change all the names as this is company stuff.
Any help/criticism would be greatly appreciated!
Looking into the rpcTest_wrap.c file I noticed that the include for testDefaults.h was right after the group of functions that were attempting to use it. I replaced "int" in the interface file with testDefaults_t (I think this is correct), ran SWIG, edited the output (dangerous I know!) so that the include happened just before these functions, and it compiles fine. I can load the shared library into TCL and run the new functions.
However, and this is perhaps a new question, [in TCL] using the new functions to create a pointer, feed it into rpc_testDefaults, and attempting to dereference the resulting pointer using testDefaults_value just returns another pointer. I realize that I can't just dereference a struct, but I have no idea how to dereference individual elements. Any tutorials I can find only refer to dereferencing non-structs (and none of these tutorials are for TCL). Something somewhere mentioned that there are similarities between structs and TK widgets so I'll have a look into this, but I'm still not sure is what I'm attempting to do even possible, and if it is is this the right way to do it.
Again, what I am attempting to do is in TCL, access individual elements of a struct which has been returned from (or fed into) a C function via a pointer.
UPDATE: I got this working in the end by using the functions SWIG generates which I discovered in the end of the wrapper file. cpointer.i wasn't needed at all. In TCL I first create a pointer using the new function new_testDefaults which returns a string with in the form of a TCL style pointer. I pass this pointer into rpc_testDefaults, which returns nothing as it was actually a void function. I can then access individual elements from the struct referenced by the above pointer by setting it as an argument to the elementName_get and elementName_set functions generated by SWIG. The next task is to get more complicated functions working, structs in structs etc, but now that I'm familiar with the methodology it shouldn't be too hard.
I'm trying to convert a C-Header to pascal, but I'm struggling with the following line:
typedef struct GLFWwindow GLFWwindow;
Since I'm not very good at C I can't even figure out what this statement means. Therefor I'm also unable to translate it.
Is it some kind of anonymous structure or maybe a handle? In the subsequent code it's usually referred to as follows:
typedef void (* GLFWwindowposfun)(GLFWwindow*,int,int);
The thing which confuses me the most is, that the structure is not defined anywhere (it has no member?!). I assume the answer is incredibly simple but nevertheless I hope someone will help me :)
Afaik yes, it is a forward define, making the struct opague, but the final struct should be declared before use in the implementation.
This construct has no direct equivalent in Pascal, best replaced by a single "pointer" or a pointer to an empty record.
The second construct is a procedure type declaration
type
PGLFWWindow = ^GLFWWindow; // pointer types need explicit declaration
// in most modern pascals
TGLFWwindowposfun = Procedure (param1:PGLFFwindowposfun;
param2,param3:integer);cdecl;
Note
prefix "P", "T", delphi style, since all identifiers share one namespace base classes of identifiers are separate by hungarian prefix notation (P=pointer, T=type etc).
The GLFWindow type is better also prefix with T, for consistency.
The integer type is usually the same as C, but e.g. in the default mode FreePascal integer is 16-bit (for TP compatibility). If you use FPC, ctypes.cint is the best fit for C's integer.
Since the default Pascal calling convention is usually not the same as C's on x86, I applied a calling convention modifier cdecl which means "C calling convention".
addendum to be clear, GLFWINDOW would be an empty record (GLWINDOW= record end;), not a pointer to an empty record.
Is it possible to compile C headers with the Rad Studio XE C++ compiler and link them with Delphi code?
Thus eliminating the need to convert header files to pascal ?
The reason for the question is..
C - Header definition:
DLLEXPORT int url_engine_version(char *version, size_t length);
Attempt at Delphi Definition
function url_engine_version(version: PByte; var length: cardinal): integer;
cdecl; external 'corplib.dll';
Main app tried to call it using:
engVer: Pointer;
engLen: cardinal;
engLen := 64;
GetMem(engVer,engLen);
url_engine_version(engVer,engLen);
But Delphi AV's when it tries to call the routine.
Working C# definition - Chick works if I pass a StringBuilder predefinded as length 64
[DllImport("corplib.dll", CharSet = CharSet.Ansi,
CallingConvention = CallingConvention.Cdecl)]
public static extern int url_engine_version(StringBuilder version, [Out] int length);
The answer to your actual question is: No. Sorry.
However, I think in this case the problem is quite simple...
Either the C Header you have reproduced here is wrong or the C# declaration is or you have just been very lucky to not have the C# code crash and burn as badly as the Delphi code.
The problem I think is that the C Header declares the length parameter as a SIZE_T, NOT a pointer to a SIZE_T. i.e. it is an input parameter, not an output or in/out parameter.
You presumably use length to specify the size of buffer allocated for the pointer you pass in version. I further presume that the function returns the number of actual bytes used for the data placed in the version buffer.
The Delphi version crashes, I believe, because by specifying length as var, you are passing length by reference, i.e. the function receives not "64" but a pointer to the value "64", but it is using this pointer value, not the 64 value.
The C# code may be dodging the bullet by (also incorrectly, if the C Header itself is correct) declaring the parameter as out. This may translate into something that, if not correct, is at least not as "damagingly incorrect" at runtime.
I think simply removing the "var" from the length parameter declaration should solve your problem:
function url_engine_version(aVersion: PByte; aLength: cardinal): integer; cdecl; external 'corplib.dll';
The main problem with converting are differences in the type system. A good delphi header can't be derived from c.
For example c doesn't distinguish pointers to one element and pointers to arrays. It doesn't distinguish bools and ints. A char* can mean a zero terminated string, a pointer to bytes, a pointer to a single char, a char passed by reference,...
And in your example the delphi code passes the last parameter by reference(i.e. as pointer to UInt32) and the c code doesn't. But I don't understand why the C# code works.
Project JEDI have done a lot of C header conversions for Delphi. They have an excellent set of resources, tutorials etc. on their website.
They also have a tool that can automate this which was actually derived from original code by Bob Swart.
I'm using Delphi to make an XLL add-in for Excel, which involves making a lot of calls to the Excel4v function of xlcall32.dll. However, as I'm guessing very few Delphi experts here have worked with that specific API, I'm hoping that the problem might have been observed in other APIs too.
In C, specifically in the xlcall.h file that comes with the Microsoft Excel 2007 XLL SDK, Excel4v is defined as:
int pascal Excel4v(int xlfn, LPXLOPER operRes, int count, LPXLOPER opers[]);
In Delphi I'm using:
function Excel4v(xlfn: Integer; operRes: LPXLOPER; count: Integer;
opers: array of LPXLOPER): Integer; stdcall; external 'xlcall32.dll';
LPXLOPER is a pointer to a struct (in C) or record (in Delphi).
I've been doing my homework on declaring C functions in Delphi (this excellent article was a great help), and I think I'm declaring Excel4v properly. However, calls from Delphi code into that function cause exceptions ("access violation..." is what I keep seeing) unless they are followed by the following line:
asm pop sink; end;
Where "sink" is defined somewhere as an integer.
I have no clue about assembly... So there's no way would I have thought to try fixing the exceptions with "asm pop sink; end;". But "asm pop sink; end;" does indeed fix the exceptions. I first saw it used in this useful article on making XLLs using Delphi. Here's the most relevant quote:
"From Delphi the big stumbling block
with add-ins is the extra parameter
after the return address on the stack.
This comes free with every call to
Excel. I’ve never found out what it
holds, but so long as you throw it
away, your add-in will work fine. Add
the line asm pop variable, end; after
every call where variable can be any
global, local or object variable that
is at least 4 bytes long- integer is
fine. To repeat- THIS MUST BE INCLUDED
after every Excel4v call. Otherwise
you are constructing a time-bomb."
Basically I want to understand what's actually happening, and why. What could be causing a Win32 function to return an "extra parameter after the return address on the stack", and what does that actually mean?
Might there be another way to fix this, e.g. with a different compiler option or a different way of declaring the function?
And is there anything risky about calling "asm pop sink; end;" after every call to Excel4v...? It seems to work fine, but, as I don't understand what's going on, it feels a little dangerous...
I don't believe it's pascal vs stdcall - they are very similar calling conventions and should not result in a mismatched stack on function exit.
From the referenced article,
This would indeed be a very nice
syntax, but it is not the same as the
above array definition. Array-of
parameters are open array parameters.
They may look like any array, and they
do accept any array, but they get an
extra (hidden) parameter, which holds
the highest index in the array (the
High value). Since this is only so in
Delphi, and not in C or C++, you'd
have a real problem. (See also my
article on open arrays), since the
real number of parameters wouldn't
match.
You're getting the extra "highest array index" parameter being passed to the function. This is an int and has to be cleaned up when the function exits so that you don't wind up with a corrupted stack and crash. The article indicates how to pass arrays to C functions.
Something like:
type
PLPXLOPER = ^LPXLOPER;
And pass PLPXLOPER as the last parameter.
Your calling convention is wrong, specifically the "stdcall". The C declaration is specified as "pascal"
Stdcall passes parameters in right to left order, expects the routine to clean up, and does not use registers. Pascal, OTOH passes parameters in left to right order. Therefore, things are not happening the way the other half of the code expects in either case.
Change your Delphi declaration to also be "pascal" instead of "stdcall".
Most Windows functions use __stdcall for their calling conventions.