我正在一个 dll 中编写一些插件代码,该代码由我无法控制的主机调用。
主机假定插件导出为 __stdcall 函数。主机被告知函数的名称和它期望的参数的详细信息,并通过 LoadLibrary、GetProcAddress 动态地调用它并手动将参数推送到堆栈上。
通常插件 dll 公开一个常量接口。我的插件公开了一个在 dll 加载时配置的接口。为了实现这一点,我的插件公开了一组在编译 dll 时定义的标准入口点,并根据需要将它们分配给正在公开的内部功能。
每个内部函数可能采用不同的参数,但这会与物理入口点名称一起传达给主机。我所有的物理 dll 入口点都定义为采用单个 void * 指针,并且我通过从第一个参数和已与主机通信的已知参数列表的偏移量工作,自己从堆栈中编组后续参数。
主机可以使用正确的参数成功调用我的插件中的函数并且一切正常......但是,我知道 a) 我的函数没有按照定义的那样清理堆栈作为采用 4 字节指针的 __stdcall 函数,因此即使调用者已将更多参数压入堆栈,它们也总是在最后执行“ret 4”。b) 我无法处理不带参数的函数,因为 ret 4 会在我返回时从堆栈中弹出 4 个字节太多。
将我的插件追踪到主机的调用代码后,我可以看到实际上 a) 没什么大不了的;主机会丢失一些堆栈空间,直到它从调度调用返回,此时它会清理它的堆栈帧,从而清理我的垃圾;然而...
我可以通过切换到 __cdecl 而根本不清理来解决 b)。我假设我可以通过切换到裸函数并编写我自己的通用参数清理代码来解决 a)。
由于我知道刚刚调用的函数使用的参数空间量,因此我希望它像以下那样简单:
extern "C" __declspec(naked) __declspec(dllexport) void * __stdcall EntryPoint(void *pArg1)
{
size_t argumentSpaceUsed;
{
void *pX = RealEntryPoint(
reinterpret_cast<ULONG_PTR>(&pArg1),
argumentSpaceUsed);
__asm
{
mov eax, dword ptr pX
}
}
__asm
{
ret argumentSpaceUsed
}
}
Run Code Online (Sandbox Code Playgroud)
但这不起作用,因为 ret …
我在学习使用C++进行游戏制作的状态时遇到了调用约定.
在前一个问题中有人说MSDN没有很好地解释_stdcall - 我同意.
调用_stdcall等约定的主要目的是什么?参数放在堆栈上的顺序是否重要?它如何减少X86中的代码大小(正如其他人所说)?
我有一个DLL,它导出一个函数...
extern "C"
int __stdcall
MP_GetFactory( gmpi::IMpUnknown** returnInterface )
{
}
Run Code Online (Sandbox Code Playgroud)
我用Code :: Blocks GCC编译器(V3.4.5)编译它.问题:导致dll导出装饰函数名称...
MP_GetFactory@4
Run Code Online (Sandbox Code Playgroud)
这无法加载,应该是老了......
MP_GetFactory
Run Code Online (Sandbox Code Playgroud)
我研究了大约4个小时.我认为--add-stdcall-alias是修复此问题的选项.我的代码::块日志显示...
mingw32-g ++.exe -shared -Wl, - out-implib = bin\Debug\libGainGCC.a -Wl, - dll obj\Debug\se_sdk3\mp_sdk_audio.o obj\Debug\se_sdk3\mp_sdk_common.o obj\Debug\Gain\Gain.o obj\Debug\Gain\gain.res -o bin\Debug\GainGCC.sem --add -stdcall-alias -luser32
..所以我认为这是正确的选择吗?但没有运气.Dependancy Walker仅显示正在导出的装饰名称.我通过使用__cdecl而不是__stdcall得到了它的工作,然后将该名称导出为ok,但该函数在被调用时会破坏堆栈(因为调用者期望其他调用约定).
我的解决方案有一个非托管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.
我一直在使用 PInvoke 让我的 C# 应用程序调用我编写的 C++ 函数。
现在,我到处都听到我需要用__stdcall约定定义那些外部可访问的功能。我的问题是:为什么?
到目前为止,我不小心忽略了这个建议,一切都像魅力一样。当我添加__stdcall到我的函数中时,一切都以相同的方式工作(或者至少看起来如此)。
这篇文章说__stdcall是用于Win32位函数,但我是针对x64平台编译的。这是否意味着我不应该使用__stdcall它,还是意味着我错过了其他东西?
并且请在回复时使用简单的英语。;-) 这样的行(引自我链接的文章):
被调用者清理堆栈,因此编译器使可变参数函数 __cdecl。
让我的大脑感觉有风滚草从里面吹过。
我在创建COM DLL时遇到了一些麻烦.我的IClassFactory实现如下所示:
include <windows.h>
#include <ObjBase.h>
#include "AddObj.h"
#include "AddObjFactory.h"
HRESULT __stdcall CAddFactory::CreateInstance(IUnknown* pUnknownOuter,
const IID& iid, void** ppv)
{
if (pUnknownOuter) { return CLASS_E_NOAGGREGATION; }
CAddObj* pObject = new CAddObj();
if (pObject == NULL)
{
return E_OUTOFMEMORY;
}
return pObject->QueryInterface(iid, ppv);
}
HRESULT __stdcall CAddFactory::LockServer(BOOL bLock)
{
return E_NOTIMPL;
}
Run Code Online (Sandbox Code Playgroud)
我的问题是Visual Studio总是说"错误C2143:语法错误:缺少';' 在第6行的"__stdcall"之前(以及更多行).我已经google了,我发现我要包含windows.h.但这并不能解决我的问题...任何人都知道我的代码有什么问题或者我要包含什么来解决问题?我通过编译头文件得到了同样的错误:
#include <Windows.h>
#include <ObjBase.h>
class CAddFactory : public IClassFactory
{
public:
HRESULT __stdcall QueryInterface(REFIID riid, void **ppObj);
ULONG __stdcall AddRef();
ULONG __stdcall Release();
HRESULT __stdcall CreateInstance(IUnknown* …Run Code Online (Sandbox Code Playgroud) 我正在尝试加深对汇编语言的理解。我知道,当函数创建堆栈帧时,它将推送当前EBP值,而不是将堆栈指针值复制到EBP。第一个(也是唯一一个)功能参数由访问EBP + 8。但是为什么是8?推送后的下一个值在EBP逻辑上是偏移量4。我阅读了许多网页,但似乎我不明白这部分。
我有一个旧的c ++库,并且所有方法都使用pascal调用约定导出,因为我知道c#marshaller不支持pascal调用约定,我可以在c#侧使用stdcall并以相反的顺序传递参数吗?
据我所知,只有caller-clean-stack约定可以使用变量参数.
顺便说一句,WinApi StringCchPrintfW就是这样声明的.(我删除了SAL)
__inline HRESULT __stdcall StringCchPrintfW
(
STRSAFE_LPWSTR pszDest,size_t cchDest,STRSAFE_LPCWSTR pszFormat,...
);
stdcall可以有变量参数吗?
windows winapi variadic-functions calling-convention stdcall
我正在开发一个项目来生成用于离线测试的虚拟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相同?