我的解决方案有一个非托管C++ DLL,它导出一个函数,一个托管应用程序PInvokes这个函数.
我刚刚将解决方案从.NET 3.5转换为.NET 4.0并得到了这个PInvokeStackImbalance "对PInvoke函数的调用[...]已经使堆栈失衡"异常.事实证明,我正在调用__cdecl'ed函数,因为它是__stdcall:
C++部分(被调用者):
__declspec(dllexport) double TestFunction(int param1, int param2); // by default is __cdecl
Run Code Online (Sandbox Code Playgroud)
C#部分(来电者):
[DllImport("TestLib.dll")] // by default is CallingConvention.StdCall
private static extern double TestFunction(int param1, int param2);
Run Code Online (Sandbox Code Playgroud)
所以,我已经修复了这个bug,但现在我对.NET 3.5中的工作原理感兴趣吗?当没有人(既不是被叫者也不是调用者)清理堆栈时,为什么(多次重复)情况没有引起堆栈溢出或其他一些不当行为,但只是工作正常?Pnvoke中是否有某种检查,就像Raymond Chen在他的文章中提到的那样?这也很有趣,为什么相反类型的破坏约定(让__stdcall被调用者像被__cdecl一样被PInvoked)根本不起作用,导致只有EntryPointNotFoundException.
我正在开发一个项目来生成用于离线测试的虚拟DLL.我们有真正的DLL及其头文件,虽然它们似乎不兼容.DLL中的名称是未解码的,但函数转发声明声明被称为__stdcall
:
example.h文件
DWORD __stdcall DoSomething(byte aByte);
Run Code Online (Sandbox Code Playgroud)
Dependency Walker中的example.dll:
2 (0x0002) 2 (0x0002) DoSomething 0x000831C0
Run Code Online (Sandbox Code Playgroud)
据我所知,如果可以在__stdcall
约定中导出函数,其名称应该在依赖walker中读取:
2 (0x0002) 2 (0x0002) _DoSomething@1 0x000831C0
Run Code Online (Sandbox Code Playgroud)
这是否意味着我们的头文件与编译的DLL不对应或者我错过了什么?
最终,我如何形成虚拟函数的导出,其行为方式与我正在模拟的真实DLL相同?
如何将参数从C main函数传递给汇编函数?我知道我的自定义函数必须看起来像:
void function(char *somedata) __attribute__((cdecl));
Run Code Online (Sandbox Code Playgroud)
现在我将如何somedata
在程序集文件中使用.我的操作系统是Linux Ubuntu,我的处理器是x86.
我在VS中创建了新的Win32项目,并为此目的选择了动态库(*.dll).
我在主文件中定义了一些导出函数:
__declspec(dllexport)
int TestCall(void)
{
int value = 4 / 2;
std::cout << typeid(value).name() << std::endl;
return value;
}
__declspec(dllexport)
void SwapMe(int *first, int *second)
{
int tmp = *first;
*first = *second;
*second = tmp;
}
Run Code Online (Sandbox Code Playgroud)
当我看到dumpin/exports时,我得到了:
ordinal hint RVA name
1 0 00001010 ?SwapMe@@YAXPEAH0@Z
2 1 00001270 ?TestCall@@YAHXZ
Run Code Online (Sandbox Code Playgroud)
我正在调用C#这样的版本:
[DllImport(@"lib1.dll", EntryPoint = "?TestCall@@YAHXZ",
CallingConvention = CallingConvention.Cdecl)]
static extern int TestCall();
Run Code Online (Sandbox Code Playgroud)
这不是使用导出方法的正确形式.我在C++ DLL项目中为导出方法生成这样的名称失败了吗?
为什么在 x86 上没有伴随指令离开?那样,
pushl %ebp
movl %esp,%ebp
pushl $3
popl %eax
leave
ret
Run Code Online (Sandbox Code Playgroud)
可以变成:
enter #or something
pushl $3
popl %eax
leave
ret
Run Code Online (Sandbox Code Playgroud)
一般来说,这不是更快吗?
我目前正在使用 WSA 对 tcp/ip 服务器进行编程。经过一些故障排除后,我的一个朋友说我应该使用bool __cdecl winsock_server ( void )
而不是bool winsock_server()
.
但他没有向我解释什么__cdecl
和(void)
正在做什么。我已经知道这__cdecl
改变了在汇编程序级别上将参数放入堆栈的方式,但这是什么(void)
意思?
我应该指出我是 C++ 的新手。我之前只用 C# 和 VB.NET 编程。
提前致谢!