PInvoke 签名与非托管目标签名不匹配

PInvoke signature does not match the unmanaged target signature

我正在尝试为 modbusm.dll (win-tech.com/html/mbusocx.htm) 创建一个包装器 C# 文件,为此我使用 dumpbin 输出。

文件转储 modbusm.dll

File Type: DLL

Section contains the following exports for modbusm.dll

00000000 characteristics
41128817 time date stamp Fri Aug 06 00:48:47 2004
    0.00 version
       1 ordinal base
      27 number of functions
      27 number of names

ordinal hint RVA      name

      1    0 000085BA _AbortTheCall@4
      2    1 00003441 _CloseConnection@4
      3    2 000033A7 _ConnectASCII@12
      4    3 000033E1 _ConnectDanielsASCII@12
      5    4 000033C4 _ConnectDanielsRTU@12
      6    5 0000338A _ConnectRTU@12
      7    6 00001019 _ConnectTCP2@12
      8    7 00001000 _ConnectTCP@8
      9    8 0000829A _DialCall@8
     10    9 00003376 _EnableConnectionCallback@4
     11    A 00003342 _EnableModbusCallback@8
     12    B 00008123 _GetCallState@8
     13    C 00007FD2 _GetLineDeviceName@12
     14    D 00003320 _GetPollDelay@0
     15    E 00003339 _Get_Modbus_DLL_Revision@0
     16    F 000033FE _HookRspNotification@16
     17   10 000032ED _InitializeWinSock@0
     18   11 0000277C _MBAPWndProc@16
     19   12 0000393F _MODBUSResponse@16
     20   13 00007EAA _NumberOfLineDevices@0
     21   14 00003521 _PollMODBUS@8
     22   15 000039F2 _ReadDebugData@16
     23   16 00003BA4 _ReadTransparentResponse@16
     24   17 0000332A _SetPollDelay@4
     25   18 00003313 _UnInitializeWinSock@0
     26   19 00003712 _WriteMODBUS@12
     27   1A 00003AB3 _WriteTransparentString@12

Summary

    5000 .data
    2000 .rdata
    2000 .reloc
    1000 .rsrc
    C000 .text

我的 C# 包装器是

class MbMasterV7
    {
        [DllImport("modbusm.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode, EntryPoint = "_ConnectTCP2@12")]
        public static extern int ConnectModbusTCP(short Port);


        public static string TCPDevice { set; get; }
    }

当我运行代码

MbMasterV7.TCPDevice = "127.0.0.1";  // from  demo version of .ocx file converted using tlbimp.exe
MbMasterV7.ConnectModbusTCP(502);

在 visual studio 中,我遇到了异常

A call to PInvoke function 'TestApp!TestApp.MbMasterV7::ConnectModbusTCP' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the calling convention and parameters of the PInvoke signature match the target unmanaged signature.

我已经尝试了所有调用约定并得到了同样的错误。 可用于 modbus 协议的 .Net 库对于我正在使用的新 plc 类型来说不够好。

FILE HEADER VALUES
             14C machine (x86)
               5 number of sections
        41128817 time date stamp Fri Aug 06 00:48:47 2004
               0 file pointer to symbol table
               0 number of symbols
              E0 size of optional header
            210E characteristics
                   Executable
                   Line numbers stripped
                   Symbols stripped
                   32 bit word machine
                   DLL

OPTIONAL HEADER VALUES
             10B magic # (PE32)
            6.00 linker version
            C000 size of code
            A000 size of initialized data
               0 size of uninitialized data
            94E4 entry point (100094E4)
            1000 base of code
            D000 base of data
        10000000 image base (10000000 to 10016FFF)
            1000 section alignment
            1000 file alignment
            4.00 operating system version
            0.00 image version
            4.00 subsystem version
               0 Win32 version
           17000 size of image
            1000 size of headers
               0 checksum
               2 subsystem (Windows GUI)
               0 DLL characteristics
          100000 size of stack reserve
            1000 size of stack commit
          100000 size of heap reserve
            1000 size of heap commit
               0 loader flags
              10 number of directories
            DF90 [     357] RVA [size] of Export Directory
            D7C8 [      50] RVA [size] of Import Directory
           14000 [     3E8] RVA [size] of Resource Directory
               0 [       0] RVA [size] of Exception Directory
               0 [       0] RVA [size] of Certificates Directory
           15000 [     BC0] RVA [size] of Base Relocation Directory
               0 [       0] RVA [size] of Debug Directory
               0 [       0] RVA [size] of Architecture Directory
               0 [       0] RVA [size] of Global Pointer Directory
               0 [       0] RVA [size] of Thread Storage Directory
               0 [       0] RVA [size] of Load Configuration Directory
               0 [       0] RVA [size] of Bound Import Directory
            D000 [     198] RVA [size] of Import Address Table Directory
               0 [       0] RVA [size] of Delay Import Directory
               0 [       0] RVA [size] of COM Descriptor Directory
               0 [       0] RVA [size] of Reserved Directory

首先,_ConnectTCP2@12表示将12个字节作为参数传递给函数,这意味着short(长度为2个字节)显然不兼容。你需要传递12个字节作为参数,大概是3DWORDs.

为了冒险,让我们实际反汇编二进制文件,看看那里发生了什么。

所以:ConnectTCP@8 正在接收 2 DWORD 作为参数,并调用 ConnectTCP2 并使用 0x1F6 作为第二个参数(实际上是一个短参数)。此外,调用约定为 stdcall.

这些信息足以让我们弄清楚如何调用该函数:

    [DllImport("modbusm.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, EntryPoint = "_ConnectTCP2@12")]
    public static extern int ConnectModbusTCP(int a, short Port, int b);

会工作,但会抛出

 "Unhandled Exception: System.AccessViolationException: 
    Attempted to read or write protected memory.
    This is often an indication that other memory is corrupt."

这是因为第二个整数(我称之为b)实际上是一个指向结构的指针(它的值我只能根据代码猜测)。所以让我们重建结构。根据代码,该结构被访问了五次,如下所示:

Offset  Type
0x00 -> INT32
0x04 -> INT32
0x08 -> INT32
0x0C -> INT16
0x10 -> INT32

因此,通过创建以下结构:

struct MbMasterStruct
{
    int a;
    int b;
    int c;
    short d;
    int e;
}

并将函数重新定义为:

unsafe class MbMasterV7
{
    [DllImport("modbusm.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, EntryPoint = "_ConnectTCP2@12")]
    public static extern int ConnectModbusTCP(int a, short Port, MbMasterStruct * b);
}

并如下调用它:

static void Main(string[] args)
{
    var structure = new MbMasterStruct();
    unsafe
    {
        MbMasterV7.ConnectModbusTCP(1, 2, &structure);
    }
}

它确实有效,而且不抛出。在我的电脑上,它返回了 51(当结构和参数都为零时)。

现在由您来理解每个参数并了解如何正确调用函数。