Несколько незначительных ошибок в вашем коде:
отсутствует ваше определение COPYDATASTRUCT
[StructLayout]
.
ваше определениеSendMessage()
немного неверно (wParam
должен быть UIntPtr
вместо Int32
).
вы не освобождаете память, выделенную с помощью IntPtrAlloc()
.
Теперь по основной проблеме:
Вам необходимо использовать UnmanagedType.ByValTStr
вместо UnmanagedType.LPTStr
при маршалинге строк в качестве символьных массивов фиксированной длины (см. Строки, используемые в структурах ).
Но, что более важно (основываясь на деталях, которые вы указали в комментариях, а не в своем основном вопросе), сторона Delphi ожидает, что строки в полученной структуре будутбыть закодирован в формате Short String , который немного отличается от необработанных массивов символов:
A ShortString имеет длину от 0 до 255 однобайтовых символов.Хотя длина ShortString может динамически изменяться, его память статически выделяется 256 байтов;первый байт хранит длину строки, а оставшиеся 255 байтов доступны для символов.Если S
является переменной ShortString , Ord(S[0])
, как и Length(S)
, возвращает длину S
;присвоение значения S[0]
, как и вызов SetLength
, изменяет длину S
. ShortString поддерживается только для обратной совместимости.
Язык Delphi поддерживает типы коротких строк - по сути, подтипы ShortString - максимальная длина которых составляет от 0 до255 символов.Они обозначены цифрой в скобках, добавленной к зарезервированному слову строка .Например:
var MyString: string[100];
создает переменную с именем MyString
, максимальная длина которой составляет 100 символов.Это эквивалентно объявлениям:
type CString = string[100];
var MyString: CString;
Переменные, объявленные таким образом, выделяют столько памяти, сколько требует тип - то есть указанная максимальная длина плюс один байт.В нашем примере MyString
использует 101 байт по сравнению с 256 байтами для переменной предопределенного типа ShortString .
Когда вы присваиваете значение переменной короткой строки,Строка усекается, если она превышает максимальную длину для типа.
Стандартные функции High
и Low
работают с идентификаторами и переменными коротких строк.High
возвращает максимальную длину типа короткой строки, а Low
возвращает ноль.
Итак, попробуйте что-то более похожее на это:
[StructLayout(LayoutKind.Sequential)]
public struct COPYDATASTRUCT
{
public IntPtr dwData;
public int cbData;
public IntPtr lpData;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public struct ReturnStruct
{
public int i;
public byte card_len;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)]
public string card;
public byte name_len;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)]
public string name;
public byte responsecode_len;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 5)]
public string responsecode;
public byte responsetext_len;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)]
public string responsetext;
public byte approval_len;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 15)]
public string approval;
public byte tranid_len;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)]
public string tranid;
public byte reference_len;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
public string reference;
public double d;
public byte transactionType_len;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 24)]
public string transactionType;
public byte creditCardType_len;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)]
public string creditCardType;
public int EMVContact;
public byte applicationName_len;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)]
public string applicationName;
public byte applicationIdentifier_len;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 15)]
public string applicationIdentifier;
public byte reserved_len;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)]
public string reserved;
}
public ReturnStruct GetReturnStruct()
{
var ret = new ReturnStruct();
ret.i = ...;
ret.card = ...;
ret.card_len = (byte) Math.Min(System.Text.Encoding.Default.GetByteCount(ret.card), 50);
ret.name = ...;
ret.name_len = (byte) Math.Min(System.Text.Encoding.Default.GetByteCount(ret.name), 100);
ret.responsecode = ...;
ret.responsecode_len = (byte) Math.Min(System.Text.Encoding.Default.GetByteCount(ret.responsecode), 5);
ret.responsetext = ...;
ret.responsetext_len = (byte) Math.Min(System.Text.Encoding.Default.GetByteCount(ret.responsetext), 100);
ret.approval = ...;
ret.approval_len = (byte) Math.Min(System.Text.Encoding.Default.GetByteCount(ret.approval), 15);
ret.tranid = ...;
ret.tranid_len = (byte) Math.Min(System.Text.Encoding.Default.GetByteCount(ret.tranid), 50);
ret.reference = ...;
ret.reference_len = (byte) Math.Min(System.Text.Encoding.Default.GetByteCount(ret.reference), 16);
ret.d = ...;
ret.transactionType = ...;
ret.transactionType_len = (byte) Math.Min(System.Text.Encoding.Default.GetByteCount(ret.transactionType), 24);
ret.creditCardType = ...;
ret.creditCardType_len = (byte) Math.Min(System.Text.Encoding.Default.GetByteCount(ret.creditCardType), 10);
ret.EMVContact = ...;
ret.applicationName = ...;
ret.applicationName_len = (byte) Math.Min(System.Text.Encoding.Default.GetByteCount(ret.applicationName), 50);
ret.applicationIdentifier = ...;
ret.applicationIdentifier_len = (byte) Math.Min(System.Text.Encoding.Default.GetByteCount(ret.applicationIdentifier), 15);
ret.reserved = ...;
ret.reserved_len = (byte) Math.Min(System.Text.Encoding.Default.GetByteCount(ret.reserved), 10);
return ret;
}
public static IntPtr IntPtrAlloc<T>(T param)
{
IntPtr retval = Marshal.AllocHGlobal(Marshal.SizeOf(param));
Marshal.StructureToPtr(param, retval, false);
return retval;
}
public static void IntPtrFree(ref IntPtr preAllocated)
{
Marshal.FreeHGlobal(preAllocated);
preAllocated = IntPtr.Zero;
}
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
public static extern int SendMessage(IntPtr hWnd, int uMsg, UIntPtr wParam, IntPtr lParam);
public void SendFingerPrintResult(string msg)
{
// get the window to send struct
IntPtr receiverHandle = GetWindow();
if (receiverHandle == IntPtr.Zero) return;
// Get the struct
ReturnStruct ret = GetReturnStruct();
IntPtr ptr = IntPtrAlloc(ret);
try
{
var cds = new COPYDATASTRUCT
{
dwData = IntPtr.Zero,
cbData = Marshal.SizeOf(ret),
lpData = ptr
};
IntPtr iPtr = IntPtrAlloc(cds);
try
{
SendMessage(receiverHandle, WM_COPYDATA, senderID, iPtr);
}
finally
{
IntPtrFree(ref iPtr);
}
}
finally
{
IntPtrFree(ref ptr);
}
}
При желании вы можете удалить внутренний вызов IntPtrAlloc()
, настроив определение SendMessage()
(см. C #использование SendMessage, проблема с WM_COPYDATA ):
[DllImport("user32.dll", CharSet = CharSet.Auto, EntryPoint = "SendMessage", SetLastError = false)]
public static extern int SendMessageCopyData(IntPtr hWnd, int uMsg, UIntPtr wParam, ref COPYDATASTRUCT lParam);
public void SendFingerPrintResult(string msg)
{
// get the window to send struct
IntPtr receiverHandle = GetWindow();
if (receiverHandle == IntPtr.Zero) return;
// Get the struct
ReturnStruct ret = GetReturnStruct();
IntPtr ptr = IntPtrAlloc(ret);
try
{
var cds = new COPYDATASTRUCT
{
dwData = IntPtr.Zero,
cbData = Marshal.SizeOf(ret),
lpData = ptr
};
SendMessageCopyData(receiverHandle, WM_COPYDATA, senderID, ref cds);
}
finally
{
IntPtrFree(ref ptr);
}
}
Вы также можете рассмотреть возможность создания пользовательской оболочки, чтобы помочь с маршалингом значений ShortString:
class DelphiShortStringHelper
{
public static void WriteToPtr(string s, ref IntPtr ptr, byte maxChars = 255)
{
byte[] bytes = System.Text.Encoding.Default.GetBytes(s);
int strLen = Math.Min(bytes.Length, (int)maxChars);
Marshal.WriteByte(ptr, (byte)strLen);
ptr = IntPtr.Add(ptr, 1);
Marshal.Copy(bytes, 0, ptr, strLen);
ptr = IntPtr.Add(ptr, (int)maxChars);
}
}
public struct ReturnStruct
{
public int i;
public string card;
public string name;
public string responsecode;
public string responsetext;
public string approval;
public string tranid;
public string reference;
public double d;
public string transactionType;
public string creditCardType;
public int EMVContact;
public string applicationName;
public string applicationIdentifier;
public string reserved;
public IntPtr ToPtr()
{
IntPtr ret = Marshal.AllocHGlobal(473);
IntPtr ptr = ret;
Marshal.WriteInt32(ptr, i); ptr = IntPtr.Add(ptr, 4);
DelphiShortStringHelper.WriteToPtr(card, ref ptr, 50);
DelphiShortStringHelper.WriteToPtr(name, ref ptr, 100);
DelphiShortStringHelper.WriteToPtr(responsecode, ref ptr, 5);
DelphiShortStringHelper.WriteToPtr(responsetext, ref ptr, 100);
DelphiShortStringHelper.WriteToPtr(approval, ref ptr, 15);
DelphiShortStringHelper.WriteToPtr(tranid, ref ptr, 50);
DelphiShortStringHelper.WriteToPtr(reference, ref ptr, 16);
Marshal.Copy(new double[]{d}, 0, ptr, 1); ptr = IntPtr.Add(ptr, 8);
DelphiShortStringHelper.WriteToPtr(transactionType, ref ptr, 24);
DelphiShortStringHelper.WriteToPtr(creditCardType, ref ptr, 10);
Marshal.WriteInt32(ptr, EMVContact); ptr = IntPtr.Add(ptr, 4);
DelphiShortStringHelper.WriteToPtr(applicationName, ref ptr, 50);
DelphiShortStringHelper.WriteToPtr(applicationIdentifier, ref ptr, 15);
DelphiShortStringHelper.WriteToPtr(reserved, ref ptr, 10);
return ret;
}
}
public ReturnStruct GetReturnStruct()
{
var ret = new ReturnStruct();
ret.i = ...;
ret.card = ...;
ret.name = ...;
ret.responsecode = ...;
ret.responsetext = ...;
ret.approval = ...;
ret.tranid = ...;
ret.reference = ...;
ret.d = ...;
ret.transactionType = ...;
ret.creditCardType = ...;
ret.EMVContact = ...;
ret.applicationName = ...;
ret.applicationIdentifier = ...;
ret.reserved = ...;
return ret;
}
public void SendFingerPrintResult(string msg)
{
// get the window to send struct
IntPtr receiverHandle = GetWindow();
if (receiverHandle == IntPtr.Zero) return;
// Get the struct
ReturnStruct ret = GetReturnStruct();
IntPtr ptr = ret.ToPtr();
try
{
...
}
finally
{
Marshal.FreeHGlobal(ptr);
}
}