I have a Delphi DLL that I did not write, but need to call from a C# ASP.NET 3.5 app. Here is the function definition I got from the developers:
function Cr
The return value might be another problem. It is probably either a memory leak(They allocate a buffer on the heap and never free it) or an access to already free memory(They return a local string variable cast to PChar).
Returning strings(or variable sized data in general) from a function to another module is problematic in general.
One solution(used by winapi) is to require the caller to pass in a buffer and its size. The disadvantage of that is that if the buffer is too small the function fails, and the caller must call it again with a larger buffer.
Another possible solution is to allocate the buffer from the heap in the function and return it. Then you need to export another function which the caller must use to free the allocated memory again. This ensures that the memory is freed by the same runtime which allocated it.
Passing a (Delphi)string parameter between different(not borland) languages is probably impossible. And even between Delphi modules you to ensure both modules use the same instance of the memory manager. Usually this means adding "uses ShareMem" as the first uses to all modules. Another difference is the calling convention "register" which is a fastcall convention, but not identical with the fastcall MS compilers use.
A completely different solution could be recompiling the Delphi dll with one of the Delphi.net compilers. How much work that is depends on their code.
I've never done this but try changing your code to:
function CreateCode(SerialID : String;
StartDateOfYear, YearOfStartDate, YearOfEndDate, DatePeriod : Word;
CodeType,RecordNumber,StartHour,EndHour : Byte) : PChar; stdcall;
external 'CreateCodeDLL.dll';
Note the extra stdcall.
Edit2: As you can see from the other replies you either have to do the change above or write a wrapper dll that does the same thing.
While you are asking them to change the calling convention, you should also ask them to change the first parameter so that it is not a "string". Get them to use a pointer to a (null-terminated) char or widechar array instead. Using Delphi strings as DLL parameters is a bad idea even without the added complexity of trying to achieve cross-language compatibility. In addition the string variable will either contain ASCII or Unicode content depending on which version of Delphi they are using.
Create a COM wrapper in Delphi and call that in your C# code via interop. Voila.. easy to use from C# or any other future platform.
Delphi uses the so called fastcall calling convention by default. This means that the compiler tries to pass parameters to a function in the CPU registers and only uses the stack if there are more parameters than free registers. For example Delphi uses (EAX, EDX, ECX) for the first three parameters to a function.
In your C# code you're actually using the stdcall calling convention, which instructs the compiler to pass parameters via the stack (in reverse order, i.e. last param is pushed first) and to let the callee cleanup the stack.
In contrast, the cdecl calling used by C/C++ compilers forces the caller to cleanup the stack.
Just make sure you're using the same calling convention on both sides. Stdcall is mostly used because it can be used nearly everywhere and is supported by every compiler (Win32 APIs also use this convention).
Note that fastcall isn't supported by .NET anyway.
jn is right. The function prototype, as given, cannot be easily called directly from C# as long as it is in Delphi's register
calling convention. You either need to write a stdcall
wrapper function for it - perhaps in another DLL if you don't have source - or you need to get the people who maintain the function to change its calling convention to stdcall
.
Update: I also see that the first argument is a Delphi string. This isn't something that C# can supply either. It should be a PChar instead. Also, it's important to be clear about whether the function is Ansi or Unicode; if the DLL is written with Delphi 2009 (or later), then it is Unicode, otherwise it is Ansi.