I’m making a wrapper to call a Delphi dll in a C# app, and I’m struggling to make it work. It doesn’t crash but the returned result is random data from memory.
I don’tknow if the problem is coming from my structures, or if it’s a problem of marshaling/unmarshaling data, or something else.
NB : I’m not the owner of the Delphi DLL, I don’t have access to the source code. All I have is these structure definition and procedure signature :
{ ////////////////////////////////////////////////////////////////////////////// }
{ ****************************************************************************** }
{ ////////////////////////////////////////////////////////////////////////////// }
TSVADParameterInOut = record
// Grundeinstellungen
Sprache: Byte; { 0:deutsch 1: }
Daten_Path: shortstring { wo die textDatei ist };
BlendrahmenSpaltmas: integer; { Intern festgelegt auf 12,0mm. Wird nicht beachtet }
// Kosten einstellungen
Rabattstufensaetze: array [0 .. 8] of integer; { 0:ohne 1 Rabatt 29 /2:34 / 3:37 / 4:38 5:47 6:49 7:51 8:93 }
// Ausgabe Daten
Error: integer;
Systemgrenze: integer;
end;
PTSVADParameterInOut = ^TSVADParameterInOut;
{ ////////////////////////////////////////////////////////////////////////////// }
{ ****************************************************************************** }
{ ////////////////////////////////////////////////////////////////////////////// }
TSVABauteil = record
B_Anzahl: integer;
B_VE_Anzahl: integer;
B_Art: integer;
B_Preis: real;
B_Artnr: longint;
B_Bezeichnung: shortstring;
B_Bezeichnung_nr: shortstring;
B_Rabattstuffe: integer;
B_VE_einheit: Byte;
end;
PTSVABauteil = ^TSVABauteil;
TSVABauteile = array [0 .. 255] of TSVABauteil;
PTSVABauteile = ^TSVABauteile;
TSVABlendBauteile = array [0 .. 255] of TSVABauteil;
PTSVABlendBauteile = ^TSVABlendBauteile;
procedure GetBauteilPreisListe(
paramerterString: PCHAR;
var infoParameter: TSVADParameterInOut;
var aPGesamtBauteile: PTSVABauteile); stdcall; external 'something.dll';
And here is the C# code :
internal class Program
{
static void Main(string[] args)
{
IDataProvider provider = new DataProvider();
var qrCode = "1001214350062700643000000143501943119471030000000400010101121001010010100000010000000210001011000000010110001000000000000000000000";
provider.GetComponentsPriceList(qrCode);
}
}
namespace Example
{
public interface IDataProvider
{
void GetComponentsPriceList(string qrCode);
}
}
namespace Example
{
public class DataProvider : IDataProvider
{
private const string DLL_PATH = "something.dll";
[DllImport(DLL_PATH, CallingConvention = CallingConvention.StdCall)]
private static extern void VAD_GetBauteilPreisListe(string parameterString, ref TSVADParameterInOut infoParameter, ref IntPtr aPGesamtBauteile);
public void GetComponentsPriceList(string qrCode)
{
var infoParameter = new TSVADParameterInOut();
infoParameter.Sprache = 1;
infoParameter.Rabattstufensaetze = new int[9];
// Allocate unmanaged memory for the array of TSVABauteile.
IntPtr ptrBauteile = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(TSVABauteile)));
try
{
// Call the Delphi procedure.
VAD_GetBauteilPreisListe(qrCode, ref infoParameter, ref ptrBauteile);
// Marshal the unmanaged memory to managed structure.
TSVABauteile bauteile = (TSVABauteile)Marshal.PtrToStructure(ptrBauteile, typeof(TSVABauteile));
// Access the bauteile array.
var results = new List<TSVABauteil>();
foreach (var item in bauteile.Bauteile)
{
if (item.B_Artnr != 0)
{
results.Add(item);
Console.WriteLine($"Art.: {item.B_Artnr} | Price: {item.B_Preis} | Desc.: {item.B_Bezeichnung}");
}
}
Console.WriteLine($"n> Count: {results.Count}"); // should return 14 elements.
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
// Free the unmanaged memory.
Marshal.FreeHGlobal(ptrBauteile);
}
Console.ReadKey();
}
}
}
My C# structures (
[StructLayout(LayoutKind.Sequential, Pack = 0)]
public struct TSVADParameterInOut
{
public byte Sprache;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 255)]
public string Daten_Path;
public int BlendrahmenSpaltmas;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 9)]
public int[] Rabattstufensaetze;
public int Error;
public int Systemgrenze;
}
[StructLayout(LayoutKind.Sequential, Pack = 0)]
public struct TSVABauteil
{
public int B_Anzahl;
public int B_VE_Anzahl;
public int B_Art;
public double B_Preis;
public int B_Artnr;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string B_Bezeichnung;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string B_Bezeichnung_nr;
public int B_Rabattstuffe;
public byte B_VE_einheit;
}
[StructLayout(LayoutKind.Sequential, Pack = 0)]
public struct TSVABauteile
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
public TSVABauteil[] Bauteile;
}
I have been through a lot of error (memory violation, stack overflow, unmanaged memory errors and so on) to finally come to this code that doesn’t crash, but now I can’t make any progress.
Does anybody can figure out what is wrong in this code ?
I’ve tried :
- all structure packing sizes combinations possible from 0 to 128 for each
- define charset as charset.Ansi
- map Delphi shortstring into IntPtr
- use unsafe blocks
- don’t use ref for the aPGesamtBauteile parameter
- Marshal the parameterString
- an a lot of code variants like:
IntPtr ptrBauteile = Marshal.AllocHGlobal(sizeOfBauteileArray);
try
{
// Initialize the memory with zeroes
for (int i = 0; i < sizeOfBauteileArray; i++)
{
Marshal.WriteByte(ptrBauteile, i, 0);
}
// Call the Delphi procedure
GetBauteilPreisListe(paramerterString, ref infoParameter, ref ptrBauteile);
// Marshal the unmanaged memory to managed structure
TSVABauteil[] managedBauteileArray = new TSVABauteil[256];
IntPtr current = ptrBauteile;
for (int i = 0; i < 256; i++)
{
managedBauteileArray[i] = (TSVABauteil)Marshal.PtrToStructure(current, typeof(TSVABauteil));
current = IntPtr.Add(current, sizeOfBauteil);
}
// access the bauteile array
foreach (var item in managedBauteileArray)
{
if (item.B_Artnr != 0)
{
Console.WriteLine($"B_Anzahl: {item.B_Anzahl}, B_Preis: {item.B_Preis}, B_Bezeichnung: {item.B_Bezeichnung}");
}
}
}
finally
{
// Free the unmanaged memory
Marshal.FreeHGlobal(ptrBauteile);
}
ADM is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.