There are many problems that I see. First of all, I would declare the structure as follows:
[StructLayoutAttribute(LayoutKind.Sequential, CharSet=CharSet.Unicode)] public struct TItem { public Int32 Id; [MarshalAs(UnmanagedType.BStr)] public string Description; }
You will need to use UnmanagedType.BStr so that the string can be allocated on the unmanaged side and freed on the managed side. An alternative would be marshaling like LPWStr , but then you have to allocate CoTaskMemAlloc on the unmanaged side.
The Delphi entry becomes:
type TItem = record Id : Int32; Description : WideString; end;
You can clearly see that your code is incorrect if you look at this line:
result[index].Description := PWideChar(Query.FieldByName(ADataField).AsString);
Here you point result[index].Description to the memory that will be freed when the function returns.
Trying to use an open Delphi array is risky at best. I would not do that. If you insist on this, you should at least consider the value passed to high , and not write at the end of the array. What more, you have to pass the correct value for maximum. This is projectItems.Length-1 .
Now you are using pass by value for the array, so nothing you write in Delphi code will return to the C # code. Moreover, C # code has [In] sorting by default, so even when you switch to pass by var, the marshaller will not march elements back to projectItems on the managed side.
Personally, I stop using the open array and will be explicit:
function GetTableData( ATableName: PWideChar; AIdField: PWideChar; ADataField: PWideChar; Items: PItem; ItemsLen: Integer ): Integer; stdcall;
Here Items points to the first element in the array, and ItemsLen length of the provided array. The return value of the function must be the number of elements copied to the array.
To implement this, use either pointer arithmetic or ($POINTERMATH ON} . I prefer the latter option. I don't think I need to demonstrate this.
On the C # side, you have:
[DllImport(dllname, CharSet=CharSet.Unicode)] public static extern int GetTableData( string tableName, string idField, string dataField, [In,Out] TItem[] items, int itemsLen );
Name it as follows:
int len = GetTableData("Project", "ProjectId", "ProjectName", projectItems, projectItems.Length);
Having said all of the above, I have doubts as to whether the marshaller will marshal an array of unresponsive types. I have a feeling that this will not happen. In this case, your main options are:
- Switch to passing the string as an
IntPtr to the record. Highlight using CoTaskMemAlloc . Destroy the Marshal.FreeCoTaskMem on the managed side. - Use an open request, get the following recording interface, close the request, which will lead to several calls of its own code, each of which returns one element.
Personally, I would choose the latter approach.