Подпись PInvoke не соответствует неуправляемой целевой подписи

Я пытаюсь создать файл-оболочку C# для modbusm.dll (win-tech.com/html/mbusocx.htm), для этого я использую вывод dumpbin.

Дамп файла modbusm.dll

Тип файла: DLL

Раздел содержит следующие экспорты для 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

Резюме

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

моя оболочка С#

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);

в визуальной студии я получаю исключение

Вызов функции PInvoke «TestApp!TestApp.MbMasterV7::ConnectModbusTCP» привел к разбалансировке стека. Вероятно, это связано с тем, что управляемая подпись PInvoke не соответствует неуправляемой целевой подписи. Убедитесь, что соглашение о вызовах и параметры подписи PInvoke соответствуют целевой неуправляемой подписи.

Я пробовал все соглашения о вызовах и получаю ту же ошибку. Библиотеки .Net, доступные для протокола Modbus, недостаточно хороши для нового типа ПЛК, с которым я работаю.

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

person jobinelv    schedule 08.05.2015    source источник
comment
Откуда вы знаете, что такое тип параметра + возвращаемого значения?   -  person Peter Wishart    schedule 08.05.2015
comment
win-tech.com/html/mbusocx.htm , у меня есть Файл ActiveX (ocx) той же dll (демо), и я преобразовал его в сборку .net из этой сборки, и они предоставляют файл .doc, объясняющий код.   -  person jobinelv    schedule 08.05.2015
comment
Экспорт в вопросе не относится к OCX (должен иметь методы DllRegisterServer и т. д.). Итак, я предполагаю, что вы запустили какой-то конвертер, который создал стандартную DLL из информации о типе OCX, но у вас есть неправильная сигнатура метода при импорте этой DLL. Можете ли вы опубликовать документы/заголовки из этого преобразования? Почему вы не можете использовать OCX напрямую?   -  person Peter Wishart    schedule 08.05.2015
comment
codeproject.com/Articles/14180/ , я следую методу, указанному в URL-адресе. я не смог найти ни одной хорошей статьи о написании оболочки C#.   -  person jobinelv    schedule 08.05.2015
comment
экспорт в вопросе не из ocx , а из dll, найденного с пакетом из win-tech.com/html/mbusocx.htm . я предполагаю, что методы dll используют ту же подпись, что и в ocx, поскольку имена методов похожи. OCX демо работает только 30 минут   -  person jobinelv    schedule 08.05.2015
comment
doc dropbox.com/s/m6lx0h3my2b9xnr/manl.doc?dl=0   -  person jobinelv    schedule 08.05.2015
comment
Вы уверены, что это правильная подпись?   -  person Mark Segal    schedule 08.05.2015


Ответы (1)


Во-первых, _ConnectTCP2@12 означает, что 12 байт передаются функции в качестве параметров, а это означает, что short (длиной 2 байта) явно несовместимо. Вам нужно передать 12 байтов в качестве параметров, предположительно, как 3 DWORDs.

Давайте, ради приключений, действительно разберем бинарник, чтобы посмотреть, что там происходит. введите здесь описание изображения

Итак: 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 (когда структура и параметры были равны нулю).

Теперь вам нужно понять каждый параметр и увидеть, как правильно вызывать функцию.

person Mark Segal    schedule 08.05.2015
comment
Вы можете прокомментировать, какие инструменты используются для дизассемблирования и какой файл используется в качестве входных данных? - person jobinelv; 09.05.2015
comment
Я использовал IDA (интерактивный дизассемблер) и modbusm.dll в качестве входных данных, @jobinelv - person Mark Segal; 09.05.2015