Calling a Delphi method in a dll from c# - c#

I am trying to call a method in a Delphi DLL with the following signature:
function SMap4Ovr(const OverFileName : ShortString ;
const Aclay : Integer ;
const Acarbon : Double ;
out errstr : ShortString): WordBool;
I am using the following import in C#:
[DllImport("SMap.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public static extern bool SMap4Ovr(
string OverFileName,
int Aclay,
double Acarbon,
out string errstr
);
But I am getting a AccessViolationException.
I seem to be able to call into a couple of simpler methods in the DLL that have string parameters but not ints or doubles.
I have also tried with the CallingConvention = CallingConvention.Cdecl but this gives me the same error.

When writing interop code it is critical that both sides of the interface match in every way. Here are the main issues that you must make agree on both sides:
Calling conventions.
Parameters lists.
Parameter types and semantics.
The first observation is that your calling conventions do not match. You have register on the Delphi side and stdcall on the C# side. The Delphi register convention is private to Delphi and so you should use stdcall.
Secondly, your string parameter types do not match. The Delphi shortstring is a data type that became legacy when Delphi 2 was released and should be considered a relic from the previous century. It was never a valid interop type, and there's nothing in the p/invoke framework that can be used to match it. Whilst you could attempt to do the marshalling by hand, this is a lot of work that is simply not needed when there are simple solutions available. You should try to forget all about shortstring.
You need to use a string type that both sides of the interface can work with. You could use null-terminated C strings, but a better and simpler choice is the COM BSTR which is WideString in Delphi.
So, the final result is as follows.
Delphi
function SMap4Ovr(
OverFileName: WideString;
Aclay: Integer;
Acarbon: Double;
out errstr: WideString
): WordBool; stdcall;
C#
[DllImport("SMap.dll")]
public static extern bool SMap4Ovr(
[MarshalAs(UnmanagedType.BStr)]
string OverFileName,
int Aclay,
double Acarbon,
[MarshalAs(UnmanagedType.BStr)]
out string errstr
);
I did not bother specifying the calling convention on the DllImport since the default is stdcall. If you prefer you can be explicit about this.
Be careful when using WideString that you don't attempt to use it as a return value. Because Delphi uses non-standard semantics for return values, you can only use simple types that fit into a register as return values.

Default calling convention in Delphi is register, not stdcall. It seems calling conventions details show us that Microsoft fastcall is not the same as Borland fastcall (register)
And C# string type differs from Delphi ShortString (it contains internally one byte length + string body)

Related

Understanding Piece of C# code

I have been coding in c# for a bit but I came across some piece of code which does not make any sense to me at all. It looks something like below:
[DllImport(DllName, SetLastError = true,
CallingConvention=CallingConvention.Cdecl)]
static extern byte QLIB_UploadCEFS_File(UInt32 handle, string sFileName,
string sPartitionFileName);
I have no idea how to interpret this code. Can anyone explain me what are they trying to achieve here?
The is a P/Invoke declaration. It declares a function, external to this module, implemented in an unmanaged DLL.
The DllImport attribute specifies the name of the unmanaged DLL, DllName in this instance. The other properties of the DllImport attribute specify, in this instance, the calling convention, and that the function sets the Win32 last error variable.
The function declaration itself specifies the signature of the unmanaged function. In this case the parameters mean that the function has the following unmanaged declaration:
unsigned char QLIB_UploadCEFS_File(
unsigned int handle,
const char *sFileName,
const char *sPartitionFileName
);
From the perspective of the managed code that calls the function, it's just like any other function. You call it passing parameters as specified in the code in your question.
For what it is worth, I do suspect that the declaration is incorrect. The first parameter is a handle and these are almost always pointer sized. So, whilst the code is probably just fine under 32 bit, it is likely to break under 64 bit. I would expect to see that first parameter declared as IntPtr. Of course, this is speculation because I cannot see the actual unmanaged function declaration.

PInvoke & Delphi

How can i use this dll function in c#? I tried the following but i get error.
"External component has thrown an exception."
First time i am doing this PInvoke stuff with C# and Delphi.
function HTTPGET(location:string):string; stdcall;
var
HTTP:TIdHttp;
begin
HTTP := TidHttp.Create(nil);
try
result := HTTP.Get(location);
finally
FreeAndNil(HTTP);
end;
end;
exports
HTTPGET;
begin
end.
namespace Test
{
class Program
{
[DllImport("project1.dll")]
public static extern string HTTPGET(string location);
static void Main(string[] args)
{
Console.WriteLine(HTTPGET("http://www.reuters.com/"));
}
}
}
You cannot call that function from C#. That's because you cannot use Delphi string for interop. You can use PAnsiChar for strings passed from managed to unmanaged, but in the other direction it's more complex. You'd need to allocate the memory at the caller, or use a shared heap. I prefer the latter approach which is easiest done with the COM BSTR. This is WideString in Delphi.
As has been discussed before, you cannot use WideString as a return value for interop, since Delphi uses a different ABI from MS tools for return values.
The Delphi code needs to look like this:
procedure HTTPGET(URL: PAnsiChar; out result: WideString); stdcall;
On the C# side you write it like this:
[DllImport("project1.dll")]
public static extern void HTTPGET(
string URL,
[MarshalAs(UnmanagedType.BStr)]
out string result
);
If you want Unicode for the URL then use PWideChar and CharSet.Unicode.
procedure HTTPGET(URL: PWideChar; out result: WideString); stdcall;
....
[DllImport("project1.dll", CharSet=CharSet.Unicode)]
public static extern void HTTPGET(
string URL,
[MarshalAs(UnmanagedType.BStr)]
out string result
);
Do not use string type: strings require memory management, and C# and Delphi modules obviously use different memory managers (leave alone that C# passes char* and Delphi expects String). Try changing location type to PChar in your DLL, and also change your result type so it's either PChar (the buffer should be allocated explicitly) or something else, but not string.
As i remember, you can't marshall delphi strings with C#... you have to use a workaround with PChar and manage the memory yourself, or use something like the workaround provided in the last answer here:
Using Delphi's stuct arrays and strings in C#
Try Unmanaged Exports for c# by Robert Giesecke
https://sites.google.com/site/robertgiesecke/Home/uploads/unmanagedexports
we are using encrypting engine from c# in Delphi App due compatibility with php and works well (so, the mentioned strings problem do not exists).
our earlier solution (worse) : register c# dll as com component and use it. with above solution output library have to be placed in exe directory and without registration works well :)

Calling a delphi DLL function from C# Code

I have a DLL compiled in Delphi 2007 and an example using it in other Delphi project. Here is a part of code:
TErrorCallback = function(Msg:PChar):byte of object;
TSaveEventCallback = function (Line:PChar; HiCode:PChar; LoCode:PChar; MobileNo:PChar):byte of object;
function InitModule(ErrorCallback:TErrorCallback; SaveEventCallback :TSaveEventCallback; MainWindowHandle:THandle; Sock_Event:integer):byte; stdcall; external 'My.dll' name 'InitModule';
function DLLSocketEvent(var msg: TMessage): byte; stdcall; external 'My.dll' name 'DLLSocketEvent';
function InitObjList(Objs: array of PChar; NumObjs: byte; Name: PChar):byte; stdcall; external 'My.dll' name 'InitObjList';
And here is my C# analog:
class Message
{
unsigned int msg;
int wParam;
int lParam;
int result;
};
delegate byte ErrorCallbackDelegate(string msg);
delegate byte SaveEventCallbackDelegate(string line, string hiCode, string loCode, string mobileNo);
[DllImport("My.dll")]
static extern byte InitModule(ErrorCallbackDelegate errorCallback, SaveEventCallbackDelegate saveEventCallback, IntPtr mainWindowsHandle, Int32 sockEvent);
[DllImport("My.dll")]
static extern byte DllSocketEvent(Message msg);
[DllImport("My.dll")]
static extern byte InitObjList(string[] objs, byte numObjs, string name);
The point is I've tried only InitModule method and it throwed an exception:
A call to PInvoke function 'ProjTest!ProjTest.MyClass::InitModule' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the calling convention and parameters of the PInvoke signature match the target unmanaged signature.
Please, help me with this. How should I describe these DLL functions in C#?
You can't call that DLL from C#. The main problem are the two of object callbacks. There's no way in C# to match that. You will need to modify the existing DLL or add an intermediate adapter DLL. As it stands your DLL is only accessible from Delphi or C++ Builder.
If you can modify the DLL then the modification you need to make is to remove the of object. If you need the callback to act on an instance then you will need to pass the instance as a parameter. However, C# delegates can wrap all that up transparently so you would only need to pass the instance as a parameter if you needed the DLL to be accessible from other languages, e.g. Delphi.
The other issue is the open array parameter. That is also not easily accessed from other languages. Although there are tricks, I would recommend passing a reference to the first element rather than an open array. Open arrays are unique to Delphi.
I also do not understand why you are using the byte type to hold array length. You should use Integer for this. There's nothing to gain from using byte and you simply invite overflow. Also, MainWindowHandle should not be THandle in Delphi. It should be HWND.
My recommendation to you would be to modify the DLL to have a C compatible interface and thus be accessible from all languages that support that. In practice this would make it accessible from all mainstream programming languages.

Delphi method signature conversion to c#

I have this signature in a delphi 2007 function I'm calling (the SomeOtherFile is another DLL that it in turn is calling):
function MyFunction(Place, Name: PChar):_Recordset; stdcall; far; external 'SomeOtherFile.DLL';
I'm trying to call it from C# code like this:
[DllImport("MyFile.dll", CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Ansi, EntryPoint="MyFunction")]
public static extern DataSet MyFunction(string Place, [MarshalAs(UnmanagedType.LPStr)]string Name);
Whenever I run this and store it into a variable, I get a runtime error about type mismatches. I guess I'm reading the signature wrong, but I can't figure out what it should be.
edit
The actual error is: A call to PInvoke function [...] has unbalanced the stack...I've also tried both params using the MarshalAs attribute, and it throws the same thing.
I've done a bit of digging around and I think you need to marshal the return value as a Recordset interface. I'm sure the P/Invoke marshaller won't magically convert your Delphi _Recordset into a .net DataSet class instance.
So I think you can write it something like this:
[DllImport("MyFile.dll")]
[return: MarshalAs(UnmanagedType.Interface)]
public static extern object MyFunction(string Place, string Name);
Call it like this
Recordset rs = (Recordset) MyFunction(Place, Name);
I'm assuming that the Place and Name parameters are input parameters, in which case the default marshalling for string is just fine.
You don't need to specify ANSI character set because that is the default too. You don't need to name the entry point if it has the same name as the C# function. You don't need to specify the calling convention because stdcall is the default.
The Recordset interface resides in the ADODB namespace.
As an aside, the use of far in your Delphi function import is spurious. The far keyword stopped having any effect once we left the 16 bit world behind.

Importing Pchar Delphi DLL to C#?

I have a procedure in delphi:
procedure PasswordDLL(month integer; password pchar);
export;
The procedure should output the password to "password" pchar that I passed in..
From what I google..and reading....
ref: HERE and HERE
I come up with:
[DllImport(
"DelphiPassword.dll",
CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Ansi,
EntryPoint = "PasswordDLL")]
public static extern void PasswordDLL(
int month,
[MarshalAs(UnmanagedType.LPStr)] string password
);
Then when I call:
string pass = "";
PasswordDLL(2, pass);
So the password to output to the "pass" string.
But I will get BadImageFormatException was unhandled: An attempt was made to load a program with an incorrect format. (Exception from HRESULT: 0x8007000B)
Sounds the function format I used is wrong?
I wonder if I used incorrect UnmanagedType for PChar, but from the reading, it is either LPWStr and LPStr.. Did I missed something?
Thanks in advance...
First off Since you have not stated which Delphi version your using I will answer assuming Delphi 6 for no other reason than I am familiar with it.
Your Delphi procedure does not specify a calling convention in its declaration, so it won't be using stdcall as per your import. It will use the default Delphi register convention which places the first few parameters in registers rather than on the stack. If you can change your Delhpi DLL add stdcall; after the declaration and rebuild and your calling conventions will match.
The table below summarizes calling conventions.
Directive Parameter order Clean-up Passes parameters in registers?
--------- --------------- -------- -------------------------------
register Left-to-right Routine Yes
pascal Left-to-right Routine No
cdecl Right-to-left Caller No
stdcall Right-to-left Routine No
safecall Right-to-left Routine No
Looking at the .NET documentation there does not seem to be a calling convention that matches Delphi's register convention (see table below) so I think your only option may be to change convention in the Delphi DLL.
Member name Description
----------- ------------------------
Cdecl The caller cleans the stack. This enables calling functions with varargs, which makes it appropriate to use for methods that accept a variable number of parameters, such as Printf.
FastCall This calling convention is not supported.
StdCall The callee cleans the stack. This is the default convention for calling unmanaged functions with platform invoke.
ThisCall The first parameter is the this pointer and is stored in register ECX. Other parameters are pushed on the stack. This calling convention is used to call methods on classes exported from an unmanaged DLL.
Winapi Supported by the .NET Compact Framework. This member is not actually a calling convention, but instead uses the default platform calling convention. For example, on Windows the default is StdCall and on Windows CE .NET it is Cdecl.
Your Delphi (6) Pchar (pointer to a null terminated ANSI string) marshalling looks correct.

Categories