Структура Marshall для передачи ее в запись delphi через sendmessage

2

Я пытаюсь передать структуру в Delphi через С#, я сделал следующее, чтобы передать сообщение, я следовал формату от 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);
    }
}

}

  • 0
    «Там нет ничего» Действительно, что это может означать. Сообщение не приходит? Сообщение приходит, но не содержит то, что вы ожидаете? Конечно, было бы проще попробовать на более простом примере, возможно, структуре, содержащей одну строку? Тогда пример будет проще. Затем вы можете сделать полное минимальное воспроизведение. UnmanagedType.LPTStr совершенно не так. Это указатель. Вам нужен встроенный массив UnmanagedType.ByValTStr . Но тогда у вас есть фиксированная длина строк. Что противно. Так почему бы вам не передать закодированную карту JSON в виде одной строки?
  • 0
    когда lpData является строкой, а не структурой, которая [MarshalAs(UnmanagedType.LPTStr, SizeConst = 24)] как [MarshalAs(UnmanagedType.LPTStr, SizeConst = 24)] я получаю строку. когда я передаю его как UnmanagedType.ByValTStr есть только тарабарщина, поэтому я придерживаюсь UnmanagedType.LPTStr .
Показать ещё 7 комментариев
Теги:
windows-messages
sendmessage

1 ответ

4
Лучший ответ

Несколько мелких ошибок в вашем коде:

  • ваше определение 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() (см. С# с использованием 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);
    }
}
  • 0
    Я попробовал это, и я получил String type Gibberish . Единственная проблема в том, что мне предоставляется только тестовое приложение Delphi, которое я не могу контролировать. Когда я изменил int i на строку, я смог получить только значение "i" из структуры, которую я отправляю.
  • 0
    Если у вас нет контроля над кодом Delphi, как вы узнаете, какую структуру он ожидает получить? Кроме того, для отправителей WM_COPYDATA является обычной практикой устанавливать для dwData уникальное значение, а не 0, например из RegisterWindowMessage() . Откуда вы знаете, что приложение Delphi этого не ожидает? Даже VCL использует WM_COPYDATA внутреннего использования, поэтому сообщения должны быть однозначно идентифицированы, чтобы их нельзя было неправильно интерпретировать.
Показать ещё 13 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню