Using Delphi's stuct arrays and strings in C# - c#

I've been trying to invoke a method that have been created in Delphi in the following way:
function _Func1(arrParams: array of TParams): Integer;stdcall;
type
TParams = record
Type: int;
Name: string;
Amount : Real;
end;
My code is:
[DllImport("some.dll", EntryPoint = "_Func1", CallingConvention = CallingConvention.StdCall)]
public static extern int Func(
[MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.Struct)] TParams[] arrParams)
And the struct is:
[StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct TParams
{
public int Type;
[MarshalAs(UnmanagedType.AnsiBStr)]
public string Name;
public double Amount;
}
When I am calling this method I'm getting the error:
Cannot marshal field 'Name' of type 'TParams': Invalid managed/unmanaged type combination (String fields must be paired with LPStr, LPWStr, BStr or ByValTStr).
However none of those combinations works, as Delphi's strings are prefixed with its length and it is Ansi for sure (I've tried it with other string parameters). Does anyone have a clue how to solve this?

There are two main problems with this, use of open arrays and use of Delphi string.
Open arrays
Delphi open arrays are implemented by passing a pointer to the first element of the array and an extra parameter specifying the index of the last item, high in Delphi terminology. For more information see this answer.
Delphi strings
The C# marshaller cannot interop with a Delphi string. Delphi strings are private types, only to be used internally to a Delphi module. Instead you should use a null-terminated string, PAnsiChar.
Putting it all together you can write it like this:
Delphi
type
TParams = record
_Type: Integer;//Type is a reserved word in Delphi
Name: PAnsiChar;
Amount: Double;
end;
function Func(const arrParams: array of TParams): Integer; stdcall;
C#
[StructLayoutAttribute(LayoutKind.Sequential)]
public struct TParams
{
public int Type;
public string Name;
public double Amount;
}
[DllImport("some.dll")]
public static extern int Func(TParams[] arrParams, int high);
TParams[] params = new TParams[len];
...populate params
int retval = Func(params, params.Length-1);

To compliment David's answer, you can marshal to a Delphi string, but it's ugly. In C#, you have to replace all of your strings in the struct with IntPtr.
private static IntPtr AllocDelphiString(string str)
{
byte[] unicodeData = Encoding.Unicode.GetBytes(str);
int bufferSize = unicodeData.Length + 6;
IntPtr hMem = Marshal.AllocHGlobal(bufferSize);
Marshal.WriteInt32(hMem, 0, unicodeData.Length); // prepended length value
for (int i = 0; i < unicodeData.Length; i++)
Marshal.WriteByte(hMem, i + 4, unicodeData[i]);
Marshal.WriteInt16(hMem, bufferSize - 2, 0); // null-terminate
return new IntPtr(hMem.ToInt64() + 4);
}
This can directly be sent to Delphi, where it'll properly be read as a string.
Remember that you must free this string when you're done with it. However, GlobalFree() can't be called directly on the pointer to the string, because it doesn't point to the start of the allocation. You'll have to cast that pointer to a long, then subtract 4, then cast it back to a pointer. This compensates for the length prefix.

Related

Pass struct* from c# to c++ dll

The struct in c++ dll is defined like this:
struct WAVE_INFO {
int channel_num;
int audio_type;
char *wave_data;
int wave_length;
};
And the calling method like this:
extern "C" STRUCTDLL_API int processStruct(WAVE_INFO *pIn, WAVE_INFO *pOut);
The wave_data in my c# struct has to be byte array (byte[])****, not char[] or string. How should I defind the struct and the method in c# where the dll is called? And the length of wave_date is fixed, let's say like 100.
First of all, I would say that the C++ struct is declared incorrectly. The payload is binary data so the array should be unsigned char* rather than char*.
Leaving that aside, the struct is a little fiddly to marshal because of the array. It goes something like this:
[StructLayout(LayoutKind.Sequential)]
struct WAVE_INFO
{
public int channel_num;
public int audio_type;
public IntPtr wave_data;
public int wave_length;
}
We can't use byte[] in the struct to be marshalled. Instead we have to declare the array as IntPtr and handle the marshalling ourselves. The cleanest way is to declare byte[] arrays and pin them with GCHandle.
The imported function looks like this:
[DllImport(dllfilename, CallingConvention = CallingConvention.Cdecl)]
static extern int processStruct(ref WAVE_INFO infoIn, ref WAVE_INFO infoOut);
And the rather messy call to the function goes like this:
var dataIn = new byte[256];
// populate the input data array
var dataOut = new byte[256];
GCHandle dataInHandle = GCHandle.Alloc(dataIn, GCHandleType.Pinned);
try
{
GCHandle dataOutHandle = GCHandle.Alloc(dataOut, GCHandleType.Pinned);
try
{
WAVE_INFO infoIn;
infoIn.audio_type = 1;
infoIn.channel_num = 2;
infoIn.wave_data = dataInHandle.AddrOfPinnedObject();
infoIn.wave_length = dataIn.Length;
WAVE_INFO infoOut = new WAVE_INFO();
infoOut.wave_data = dataOutHandle.AddrOfPinnedObject();
infoOut.wave_length = dataOut.Length;
int retval = processStruct(ref infoIn, ref infoOut);
// dataOut should have been populated by processStruct
}
finally
{
dataOutHandle.Free();
}
}
finally
{
dataInHandle.Free();
}
My assumption here is that the first parameter is used for input, and the second parameter for output. But that the onus is on the caller to allocate the wave data array for the output struct.
I've also assumed a calling convention, but you'd have to inspect the C++ macro STRUCTDLL_API to determine what the true calling convention is.

DllExport: Passing an array of arrays from Delphi to C#

I am using RGiesecke's "Unmanaged Exports" package to create a dll from C# that can be called from a Delphi application.
Specifically, I am looking to pass an array of arrays of a struct.
What I have made work in C# is
public struct MyVector
{
public float X;
public float Y;
}
[DllExport]
public static void DoStuff([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)]
MyVector[] vectors, int count)
{
// Do stuff
}
Which can then be called from Delphi, doing something like this:
unit MyUnit
interface
type
TVector = array[X..Y] of single;
TVectorCollection = array of TVector;
procedure TDoExternalStuff(const vectors : TVectorCollection; count : integer; stdcall;
procedure DoSomeWork;
implementation
procedure DoSomeWork;
var
vectors : array of TVector;
fDoExternalStuff : TDoExternalStuff;
Handle: THandle;
begin
// omitted: create and fill vectors
Handle := LoadLibrary('MyExport.dll');
#fDoExternalStuff := GetProcAddress(Handle, 'DoStuff');
fDoExternalStuff(vectors, Length(vectors));
end;
end.
However, what I really need to do is to pass an array of array of TVector. An array of structs that hold an array of TVector would also do. But writing
[DllExport]
public static void DoStuff([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)]
MyVector[][] vectors, int count)
{
// Do stuff
}
Does not work with Delphi
...
TVectorCollection = array of array of TVector;
...
procedure DoSomeWork;
var
vectors : array of array of TVector;
fDoExternalStuff : TDoExternalStuff;
Handle: THandle;
begin
// omitted: create and fill vectors
Handle := LoadLibrary('MyExport.dll');
#fDoExternalStuff := GetProcAddress(Handle, 'DoStuff');
fDoExternalStuff(vectors, Length(vectors)); //external error
end;
And I would also be a bit surprised if it did, since I am not specifying the length of the individual elements of the jagged array anywhere.
Is there a way for me to setup my DllExport function to be able to marshal this type of element?
Is there a way for me to setup my DllExport function to be able to marshal this type of element?
No, p/invoke marshalling never descends into sub-arrays. You will have to marshal this manually.
Personally I'd pass an array of pointers to the first elements of the sub-arrays, and an array of the lengths of the sub-arrays.
On the C# side it will look like this:
[DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = false)]
public static extern void CopyMemory(IntPtr dest, IntPtr src, uint count);
[DllExport]
public static void DoStuff(
[In]
int arrayCount,
[In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)]
IntPtr[] arrays,
[In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)]
int[] subArrayCount,
)
{
MyVector[][] input = new MyVector[arrayCount];
for (int i = 0; i < arrayCount; i++)
{
input[i] = new MyVector[subArrayCount[i]];
GCHandle gch = GCHandle.Alloc(input[i], GCHandleType.Pinned);
try
{
CopyMemory(
gch.AddrOfPinnedObject(),
arrays[i],
(uint)(subArrayCount[i]*Marshal.SizeOf(typeof(MyVector))
);
}
finally
{
gch.Free();
}
}
}
It's a little messy since we can't use Marshal.Copy because it doesn't know about your struct. And there is []no simple built in way to copy from IntPtr to IntPtr](https://github.com/dotnet/corefx/issues/493). Hence the p/invoke of CopyMemory. Anyway, there's many ways to skin this one, this is just my choice. Do note that I am relying on your type being blittable. If you changed the type so that it was not blittable then you'd need to use Marshal.PtrToStructure.
On the Delphi side you can cheat a little and take advantage of the fact that a dynamic array of dynamic arrays is actually a pointer to an array of pointers to the sub-arrays. It will look like this:
type
TVectorDoubleArray = array of array of TVector;
TIntegerArray = array of Integer;
procedure DoStuff(
arrays: TVectorDoubleArray;
arrayCount: Integer;
subArrayCount: TIntegerArray
); stdcall; external dllname;
....
procedure CallDoStuff(const arrays: TVectorDoubleArray);
var
i: Integer;
subArrayCount: TIntegerArray;
begin
SetLength(subArrayCount, Length(arrays));
for i := 0 to high(subArrayCount) do
subArrayCount[i] := Length(arrays[i]);
DoStuff(Length(Arrays), arrays, subArrayCount);
end;

How to pass a C# double[] to a C++ function that requires constant double* pArr? C++, C#

I have a function in C++ written as:
MyFunc(const double* pArray, int length);
I need to pass a non-constant array into it:
//C#
double[] myDoubleArray = new double[] { 1, 2, 3, 4, 5 };
MyFunc(myDoubleArray, 5);
The program is breaking when I do this.
edit:
//C# declaration
[DllImport(#"RTATMATHLIB.dll", EntryPoint = "?MyFunc##YANPBNHHHH#Z")]
public static extern double MyFunc(double[] data, int length);
//C# usage
public static double MyFunc(double[] data)
{
return MyFunc(data, data.Length);
}
//C++ export
__declspec(dllexport) double MyFunc(const double* data, int length);
//C++ signature
double MyFunc(const double* data, int length)
{
return 0; //does not quite matter what it returns...
}
You can always pass a non-constant array to a function requiring a constant array. The const qualifier to the array implies that the function will not modify the contents of the array. The array does not need to be constant before passing to the function; the function will not modify the contents, due to the const in the declaration
Constness can be trivially added. Your program is not wrong. You must have missed some other detail.

Best Way To Marshal A Pointer of Array of Struct

I'm calling functions from C++ that returns a pointer to an array of struct and I'm having problems since I'm new to this operation/implementation.
My C++ codes:
// My C++ Structs
typedef struct _MainData {
double dCount;
DataS1 *DS1;
int iCount1;
DataS2 *DS2;
int iCount2;
}MainData;
typedef struct _DataS1 {
unsigned int uiCount1;
unsigned int uiCount2;
int iCount;
void *pA;
void *pB;
} DataS1;
typedef struct _DataS2 {
unsigned int uiCount1;
unsigned int uiCount2;
unsigned int uiCount3;
unsigned int uiCount4;
double dCount;
int iCount1;
char strLbl[64];
} DataS2;
// My C++ Function
MainData* GetData(const int ID)
{
MainData* mData;
int iLength = Get_Count();
mData = new MainData[iLength];
for(int x = 0;x < VarCounter; x++)
{
// Codes here assign data to mData[x]
}
return mData;
}
Question:
How can I call the C++ function GetData to C#?
My current codes in C# are:
[DllImport(".\\sdata.dll")]
[return: MarshalAs(UnmanagedType.LPArray)]
private static unsafe extern MainData[] GetData(int ID);
// The struct MainData in my C# side is already "Marshalled"...
//My function call is here:
MainData[] SmpMapData = GetData(ID);
When I compiled it, there's an exception:
"Cannot marshal 'return value': Invalid managed/unmanaged type combination."
Sorry for the poor coding... Please help...
I tried to do exactly the same thing but didn't succeed due to lack of time but I learned something in process:
Allocate memory in C#
To pass array of structs, struct must be blittable.
Good luck with that, I couldn't make it to work.
One problem is that the marshaller doesn't know how many items are in the array returned by the C++ code. An alternative approach could be to have two C++ methods - one which returns the number of items, and one which returns a single MainData given an index.
What do your structures look like on the C# side?
Since you are coding both the C++ and C# side, it may be easier to use C++/CLI to interface them instead of PInvoke.

Converting Delphi code for unmanaged dll to C#

I hope someone can assist me with the problem I'm currently experiencing. We have a lot of Delphi legacy code, and need to convert some of our Delphi applications to C#.
The legacy code I'm currently struggling with is that of calling a function from a 3rd party application's non-COM DLL.
Here is the C-style header and struct used for the specific function:
/*** C Function AwdApiLookup ***/
extern BOOL APIENTRY AwdApiLookup( HWND hwndNotify, ULONG ulMsg,
BOOL fContainer, CHAR cObjectType,
SEARCH_CRITERIA* searchCriteria,
USHORT usCount, USHORT usSearchType,
VOID pReserved );
/*** C Struct SEARCH_CRITERIA ***/
typedef struct _search_criteria
{
UCHAR dataname[4];
UCHAR wildcard;
UCHAR comparator[2];
UCHAR datavalue[75];
} SEARCH_CRITERIA;
In our Delphi code, we have converted the above function and structure as:
(*** Delphi implementation of C Function AwdApiLookup ***)
function AwdApiLookup(hwndNotify: HWND; ulMsg: ULONG; fContainer: Boolean;
cObjectType: Char; pSearchCriteria: Pointer; usCount: USHORT;
usSearchType: USHORT; pReserved: Pointer): Boolean; stdcall;
external 'AWDAPI.dll';
(*** Delphi implementation of C Struct SEARCH_CRITERIA ***)
TSearch_Criteria = record
dataname: array [0..3] of char;
wildcard: char;
comparator: array [0..1] of char;
datavalue: array [0..74] of char;
end;
PSearch_Criteria = ^TSearch_Criteria;
and the way we call the above mentioned code in Delphi is:
AwdApiLookup(0, 0, true, searchType, #criteriaList_[0],
criteriaCount, AWD_USE_SQL, nil);
where criteriaList is defined as
criteriaList_: array of TSearch_Criteria;
After all that is said and done we can now look at the C# code, which I cannot get to work. I'm sure I'm doing something wrong here, or my C header is not translated correctly. My project does compile correctly, but when the function is called, I get a "FALSE" value back, which indicates that the function did not execute correctly in the DLL.
My C# code thus far:
/*** C# implementation of C Function AwdApiLookup ***/
DllImport("awdapi.dll", CharSet = CharSet.Auto)]
public static extern bool AwdApiLookup(IntPtr handle, ulong ulMsg,
bool fContainer, char cObjectType,
ref SearchCriteria pSearchCriteria,
ushort usCount, ushort usSearchType,
Pointer pReserverd);
/*** C# implementation of C Struct SEARCH_CRITERIA ***/
[StructLayout(LayoutKind.Sequential)]
public struct SearchCriteria
{
private readonly byte[] m_DataName;
private readonly byte[] m_Wildcard;
private readonly byte[] m_Comparator;
private readonly byte[] m_DataValue;
public SearchCriteria(string dataName, string comparator, string dataValue)
{
m_DataName = Encoding.Unicode.GetBytes(
dataName.PadRight(4, ' ').Substring(0, 4));
m_Wildcard = Encoding.Unicode.GetBytes("0");
m_Comparator = Encoding.Unicode.GetBytes(
comparator.PadRight(2, ' ').Substring(0, 2));
m_DataValue = Encoding.Unicode.GetBytes(
dataValue.PadRight(75, ' ').Substring(0, 75));
}
public byte[] dataname { get { return m_DataName; } }
public byte[] wildcard { get { return m_Wildcard; } }
public byte[] comparator { get { return m_Comparator; } }
public byte[] datavalue { get { return m_DataValue; } }
}
My C# call to the C# function looks like this
var callResult = UnsafeAwdApi.CallAwdApiLookup(IntPtr.Zero, 0, true, 'W',
ref searchCriteria[0], criteriaCount,
66, null);
where searchCriteria and criteriaCount is defined as
List<SearchCriteria> criteriaList = new List<SearchCriteria>();
var searchCriteria = criteriaList.ToArray();
var criteriaCount = (ushort)searchCriteria.Length;
and adding data to searchCriteria:
public void AddSearchCriteria(string dataName, string comparator, string dataValue)
{
var criteria = new SearchCriteria();
criteria.DataName = dataName;
criteria.Wildcard = "0";
criteria.Comparator = comparator;
criteria.DataValue = dataValue;
criteriaList.Add(criteria);
}
Like I said, my code compiles correctly, but when the function executes, it returns "FALSE", which should not be the case as the Delphi function does return data with the exact same input.
I know I'm definitely doing something wrong here, and I've tried a couple of things, but nothing seems to be working.
Any assistance or nudge in the right direction would be greatly appreciated.
Thanks, Riaan
Several things here.
First of all C++ ULONG is a 32-bit integer, and becomes uint in C# - ulong is 64-bit.
For the struct, you don't need to mess with byte arrays. Use strings, and ByValTStr. Also, it's not really worth bothering with readonly and properties for interop structs. Yes, mutable value types are generally bad in a pure .NET API, but in this case it's the existing API, there's no point in masking it. So:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct SearchCriteria
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 4]
public string m_DataName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1]
public string m_Wildcard;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 2]
public string m_Comparator;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 75]
public string m_DataValue;
}
If you really want to do all the string conversions yourself, it may be easier to just use unsafe and fixed-size arrays:
public unsafe struct SearchCriteria
{
public fixed byte m_DataName[4];
public byte m_Wildcard;
public fixed byte m_Comparator[2];
public fixed byte m_DataValue[75];
}
[EDIT] Two more things.
CHAR cObjectType should become byte cObjectType, and not char cObjectType that you currently use.
Also, yes, there is a problem with array marshaling in your example. Since your P/Invoke declaration is ref SearchCriteria pSearchCriteria - i.e. a single value passed by reference - that's precisely what P/Invoke mashaler will do. Keep in mind that, unless your struct only has fields of unmanaged types in it (the one with fixed arrays above is that, the one with string is not), the marshaler will have to copy the structs. It won't just pass address to the first element of the array directly. And in this case, since you didn't tell it it's an array there, it will only copy the single element you reference.
So, if you use the version of the struct with string, fields, you need to change the P/Invoke declaration. If you only need to pass SEARCH_CRITERIA objects into the function, but won't need to read data from them after it returns, just use an array:
public static extern bool AwdApiLookup(IntPtr handle, uint ulMsg,
bool fContainer, byte cObjectType,
SearchCriteria[] pSearchCriteria,
ushort usCount, ushort usSearchType,
Pointer pReserverd);
And call it like this:
var callResult = UnsafeAwdApi.CallAwdApiLookup(
IntPtr.Zero, 0, true, (byte)'W',
searchCriteria, criteriaCount,
66, null);
If function writes data into that array, and you need to read it, use [In, Out]:
[In, Out] SearchCriteria[] pSearchCriteria,
If you use the version with fixed byte[] arrays, you can also change the P/Invoke declaration to read SearchCriteria* pSearchCriteria, and then use:
fixed (SearchCriteria* p = &searchCriteria[0])
{
AwdApiLookup(..., p, ...);
}
This will require unsafe as well, though.

Categories

Resources