P / Invoke和C#的自定义调用约定

时间:2008-12-24 05:09:15

标签: c# pinvoke calling-convention

我有一个商业案例,我需要能够在使用P / Invoke时指定我自己的调用约定。具体来说,我有一个使用非标准ABI的遗留DLL,我需要能够为每个函数指定调用约定。

例如,此dll中的一个函数通过EAX和EBX接受其前两个参数,其余函数通过堆栈接受。另一个函数通过ECX接受一个参数,其余函数在堆栈上。我有几百个这样的函数,并且希望避免编写我自己的中间桥DLL来访问这些函数。

我的另一个选择是手动滚动我自己的自定义P / Invoke,这是不可取的,原因很明显。

感谢任何帮助,谢谢,

3 个答案:

答案 0 :(得分:3)

我不明白你对自定义P / Invoke的意思,但我看不出你如何在没有内联汇编的非托管C ++的情况下逃脱。但是,由于几乎所有内容都以32位值的形式传递,因此您可能只为每个函数签名编写一个代理,每个函数对应一个代理。或者你可以编写一个代码生成器,它从XML生成代理。我不能看到这个版本太不受欢迎,因为所有代理功能都非常简单:

int RealFunction(int param1, const char * param2, char param 3);

int MyFunction(int param1, int param2, int param3) { // argument types do not matter as long as they are not doubles or structures
   __asm {
      mov eax, param1
      mov ebx, param2
      push param3
      call RealFunction
      ; depending on calling convention, you might need to do add esp, 12 here
      ; if RealFunction does not return its result in eax, you will need to do mov eax, <wherever the return value is> here
   }
}

答案 1 :(得分:0)

我相当确定没有单独的dll没有内置的方法来完成你想要的东西。除了运行时系统支持的内容之外,我还没有看到指定调用约定的方法。

答案 2 :(得分:0)

我正在学习一段时间调用约定并编写一些代码来转换调用约定。代码从一个特殊的C#包装器调用,包装器库使用反射发射(无法使Marshal.getdelegateforfunctionpointer工作)为特殊的裸存根方法发出新的p / invoke方法。它修复了参数,然后调用实际方法。

这是c代码。我没有C#部分方便:(当时我也在学习汇编程序,所以代码可能很糟糕:)

typedef struct
{
    USHORT ParameterOneOffset;  // The offset of the first parameter in dwords starting at one
    USHORT ParameterTwoOffset;  // The offset of the second parmaeter in dwords starting at one
} FastCallParameterInfo;



    __declspec( naked,dllexport ) void __stdcall InvokeFast()
{
    FastCallParameterInfo paramInfo;
    int functionAddress;
    int retAddress;
    int paramOne, paramTwo;
    __asm
    {
        // Pop the return address and parameter info.  Store in memory.
        pop retAddress;
        pop paramInfo;
        pop functionAddress;

        // Check if any parameters should be stored in edx                          
        movzx ecx, paramInfo.ParameterOneOffset;     
        cmp ecx,0;
        je NoRegister;  

        // Calculate the offset for parameter one.
        movzx ecx, paramInfo.ParameterOneOffset;    // Move the parameter one offset to ecx
        dec ecx;                                    // Decrement by 1
        mov eax, 4;                                 // Put 4 in eax
        mul ecx;                                    // Multiple offset by 4

        // Copy the value from the stack on to the register.
        mov ecx, esp;                               // Move the stack pointer to ecx
        add ecx, eax;                               // Subtract the offset.
        mov eax, ecx;                               // Store in eax for later.
        mov ecx, [ecx];                             // Derefernce the value
        mov paramOne, ecx;                          // Store the value in memory.

        // Fix up stack
        add esp,4;                                  // Decrement the stack pointer
        movzx edx, paramInfo.ParameterOneOffset;    // Move the parameter one offset to edx
        dec edx;                                    // Decrement by 1
        cmp edx,0;                                  // Compare offset with zero
        je ParamOneNoShift;                         // If first parameter then no shift.

    ParamOneShiftLoop:
        mov ecx, eax;
        sub ecx, 4;
        mov ecx, [ecx]
        mov [eax], ecx;                             // Copy value over
        sub eax, 4;                                 // Go to next 
        dec edx;                                    // decrement edx
        jnz ParamOneShiftLoop;                      // Loop
    ParamOneNoShift:
        // Check if any parameters should be stored in edx                          
        movzx ecx, paramInfo.ParameterTwoOffset;     
        cmp ecx,0;
        je NoRegister;  

        movzx ecx, paramInfo.ParameterTwoOffset;    // Move the parameter two offset to ecx
        sub ecx, 2;                                 // Increment the offset by two.  One extra for since we already shifted for ecx
        mov eax, 4;                                 // Put 4 in eax
        mul ecx;                                    // Multiple by 4

        // Copy the value from the stack on to the register.
        mov ecx, esp;                               // Move the stack pointer to ecx
        add ecx, eax;                               // Subtract the offset.
        mov eax, ecx;                               // Store in eax for later.
        mov ecx, [ecx];                             // Derefernce the value
        mov paramTwo, ecx;                          // Store the value in memory.           

        // Fix up stack
        add esp,4;                                  // Decrement the stack pointer
        movzx edx, paramInfo.ParameterTwoOffset;    // Move the parameter two offset to ecx
        dec edx;                                    // Decrement by 1
        cmp edx,0;                                  // Compare offset with zero
        je NoRegister;                              // If first parameter then no shift.
    ParamTwoShiftLoop:
        mov ecx, eax;
        sub ecx, 4;
        mov ecx, [ecx]
        mov [eax], ecx;                             // Copy value over
        sub eax, 4;                                 // Go to next 
        dec edx;                                    // decrement edx
        jnz ParamTwoShiftLoop;                      // Loop


    NoRegister:
        mov ecx, paramOne;                          // Copy value from memory to ecx register
        mov edx, paramTwo;                          // 
        push retAddress;
        jmp functionAddress;
    }
}
相关问题