Структура Marshall для передачи ее в запись delphi через sendmessage
Я пытаюсь передать структуру в delphi через C#, я сделал следующее, чтобы передать сообщение, я следовал формату от pinvoke, чтобы скопировать структуру данных из https://www.pinvoke.net/default.aspx/Structures.COPYDATASTRUCT, но на delphi я не получаю сообщений. В некотором смысле, я верю, что это потому, что я неправильно закодировал структуру. Когда я передаю только строковое сообщение, я получаю его, но когда я пытаюсь передать структуру, ничего не происходит
Это то, что я сделал до сих пор.
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace ccTestForm2
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
SendFingerPrintResult();
}
const int WM_COPYDATA = 0x004A;
//include SendMessage
[DllImport("user32.dll")]
public static extern IntPtr FindWindow(string lpszClass, string
lpszWindow);
[DllImport("user32.dll", CharSet = CharSet.Ansi, EntryPoint = "SendMessage", SetLastError = false)]
public static extern int SendMessageCopyData(IntPtr hWnd, int uMsg, UIntPtr wParam, ref COPYDATASTRUCT lParam);
[StructLayout(LayoutKind.Sequential)]
public struct COPYDATASTRUCT
{
public IntPtr dwData;
public int cbData;
public IntPtr lpData;
}
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 = 2;
ret.card = "1234";
ret.name = "test";
ret.responsecode = "mese";
ret.responsetext = "dork";
ret.approval = "Plerk";
ret.tranid = "pse";
ret.reference = "Ig";
ret.d = DateTime.UtcNow.ToOADate();
ret.transactionType = "cit";
ret.creditCardType = "t2";
ret.EMVContact = 0;
ret.applicationName = "mpp";
ret.applicationIdentifier = "nne";
ret.reserved = "12";
return ret;
}
public void SendFingerPrintResult()
{
// get the window to send struct
IntPtr receiverHandle = FindWindow("TReceiverMainForm", "ReceiverMainForm");
if (receiverHandle == IntPtr.Zero) return;
// Get the struct
ReturnStruct ret = GetReturnStruct();
IntPtr ptr = ret.ToPtr();
try
{
var cds = new COPYDATASTRUCT
{
dwData = IntPtr(2), // to identify the message contents
cbData = Marshal.SizeOf(ret),
lpData = ptr
};
SendMessageCopyData(receiverHandle, WM_COPYDATA, UIntPtr.Zero, ref cds);
}
finally
{
Marshal.FreeHGlobal(ptr);
}
}
}
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);
}
}
}
1 ответ
Несколько мелких ошибок в вашем коде:
ваше определение
COPYDATASTRUCT
пропал, отсутствует[StructLayout]
,ваше определение
SendMessage()
немного не так (wParam
должен бытьUIntPtr
вместоInt32
).Вы не освобождаете память, которую выделяете
IntPtrAlloc()
,
Теперь по основному вопросу:
Вам нужно использовать UnmanagedType.ByValTStr
вместо UnmanagedType.LPTStr
при маршалинге строк в виде символьных массивов фиксированной длины (см. Строки, используемые в структурах).
Но, что более важно (основываясь на деталях, которые вы указали в комментариях, а не в своем основном вопросе), сторона Delphi ожидает, что строки в полученной структуре будут закодированы в формате Short String, который немного отличается от необработанных массивов символов:
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);
}
}