Reputation: 525
I am forced to work with unmanaged delphi dll. I dont have an access to the source code. Only vague documentation:
type
TServiceData = packed record
DBAlias: PChar;
LicKey: PChar;
Pass: PChar;
end;
PServiceData = ^TServiceData;
function CreateRole(SrvData: PServiceData; var UserName: PChar): byte; stdcall;
UserName
is supposed to be an out
param.
My C# code:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct SERVICE_DATA
{
public string DBALias;
public string LicKey;
public string Pass;
}
[DllImport(dllname, CallingConvention = CallingConvention.StdCall)]
public static extern byte CreateRole(SERVICE_DATA data, out string str);
I have no idea what can cause the stack imbalance (except calling convention which seems to be correct). I dont know if strings in my structure are marshalled correctly but according to other threads this would not cause PStackImbalanceException. Any help will be much appreciated:)
EDIT. I have implemented suggestions from David and now I am getting access violation exception:
"Unhandled Exception: System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt”
My structure and method declaration is just copy-pasted from the answer, there is nothing fancy in the way I am calling it:
string str;
var data = new SERVICE_DATA();
data.DBALias = "test";
data.LicKey = "test";
data.Pass = "test";
var result = CreateRole(ref data, out str);
Upvotes: 1
Views: 239
Reputation: 613441
There are a couple of things wrong with the translation:
CoTaskMemAlloc
. I'd guess that you aren't doing that and so you'll hit problems when the marshaller attempts to deallocate the pointer with a call to CoTaskMemFree
.I'd probably use the COM string type for the strings. I would also avoid packing records because that is bad practise as a general rule.
I'd write it like this:
Delphi
type
TServiceData = record
DBAlias: WideString;
LicKey: WideString;
Pass: WideString;
end;
function CreateRole(const SrvData: TServiceData; out UserName: WideString): Byte;
stdcall;
C#
[StructLayout(LayoutKind.Sequential)]
public struct SERVICE_DATA
{
[MarshalAs(UnmanagedType.BStr)]
public string DBALias;
[MarshalAs(UnmanagedType.BStr)]
public string LicKey;
[MarshalAs(UnmanagedType.BStr)]
public string Pass;
}
[DllImport(dllname, CallingConvention = CallingConvention.StdCall)]
public static extern byte CreateRole(
[In] ref SERVICE_DATA data,
[MarshalAs(UnmanagedType.BStr)] out string str
);
Here is a complete test project to show that this works as expected:
Delphi
library Project1;
type
TServiceData = record
DBAlias: WideString;
LicKey: WideString;
Pass: WideString;
end;
function CreateRole(const SrvData: TServiceData; out UserName: WideString): Byte;
stdcall;
begin
UserName := SrvData.DBAlias + SrvData.LicKey + SrvData.Pass;
Result := Length(UserName);
end;
exports
CreateRole;
begin
end.
C#
using System;
using System.Runtime.InteropServices;
namespace ConsoleApplication1
{
class Program
{
const string dllname = @"...";
[StructLayout(LayoutKind.Sequential)]
public struct SERVICE_DATA
{
[MarshalAs(UnmanagedType.BStr)]
public string DBALias;
[MarshalAs(UnmanagedType.BStr)]
public string LicKey;
[MarshalAs(UnmanagedType.BStr)]
public string Pass;
}
[DllImport(dllname, CallingConvention = CallingConvention.StdCall)]
public static extern byte CreateRole(
[In] ref SERVICE_DATA data,
[MarshalAs(UnmanagedType.BStr)] out string str
);
static void Main(string[] args)
{
SERVICE_DATA data;
data.DBALias = "DBALias";
data.LicKey = "LicKey";
data.Pass = "Pass";
string str;
var result = CreateRole(ref data, out str);
Console.WriteLine(result);
Console.WriteLine(str);
}
}
}
Output
17 DBALiasLicKeyPass
Upvotes: 2