我有一个商业案例,我需要能够在使用 P/Invoke 时指定我自己的调用约定。具体来说,我有一个使用非标准 ABI 的旧版 dll,我需要能够为每个函数指定调用约定。
例如,此 dll 中的一个函数通过 EAX 和 EBX 接受其前两个参数,其余通过堆栈接受。另一个函数通过 ECX 接受一个参数,其余的在堆栈中。我有几百个这样的函数,并且想避免编写我自己的中间桥 DLL 来访问这些函数。
我的另一个选择是手动滚动我自己的自定义 P/Invoke,这显然是不可取的。
任何帮助表示赞赏,谢谢,
我有一个商业案例,我需要能够在使用 P/Invoke 时指定我自己的调用约定。具体来说,我有一个使用非标准 ABI 的旧版 dll,我需要能够为每个函数指定调用约定。
例如,此 dll 中的一个函数通过 EAX 和 EBX 接受其前两个参数,其余通过堆栈接受。另一个函数通过 ECX 接受一个参数,其余的在堆栈中。我有几百个这样的函数,并且想避免编写我自己的中间桥 DLL 来访问这些函数。
我的另一个选择是手动滚动我自己的自定义 P/Invoke,这显然是不可取的。
任何帮助表示赞赏,谢谢,
我不明白您对自定义 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
}
}
我相当确定没有单独的 dll 就没有内置的方式来完成你想要的。除了运行时系统支持的内容之外,我还没有看到指定调用约定的方法。
不久前我正在学习调用约定,并编写了一些代码来转换调用约定。代码从一个特殊的 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;
}
}