我正在研究一个DLL,它将使用_stdcall调用对话从另一种语言(因此没有导入库和包括dll的头文件)中使用.问题是VC++似乎总是对其导出的符号进行一些名称修饰.我见过的所有引用都说使用extern"C",但这似乎仍然留下了一个前导下划线,并且在导出的名称之后加了一个@加号.
最糟糕的一点是在目标语言中加载扩展dll的自动方式基本上是"func_name = GetProcAddress(dll,"func_name")"所以使用未修饰的名称GetProcAddress失败,并使用装饰名称它抱怨非法变量名称( @ 不被允许) :(
如何让VC++导出一些没有名字装饰的东西呢?
extern "C" __declspec(dllexport) int __stdcall test(int x, const char *str);
Run Code Online (Sandbox Code Playgroud)
DUMPBIN.EXE
00011366 _test @ 8 = @ ILT + 865(_test @ 8)
我知道__stdcall函数不能有省略号,但我想确保没有支持stdarg.h函数的平台来调用除__cdecl或__stdcall之外的约定.
对于曾为负责定义这些宏的 Microsoft 团队工作过、认识为其工作过的人或以其他方式与 Microsoft 团队有任何联系的任何人来说,这是一个好奇的问题。
我明白__stdcall是什么,我知道为什么使用它,我只是不明白为什么微软会为同一件事制作三个单独的宏。我能看到的唯一好处是在源文件中提供一些语义含义,但除此之外,据我所知,它没有提供其他好处。显然这样做是有道理的,我只是想知道它是什么!:)
结构如何作为参数传递?
由于结构的大小比正常大,各个字段是否顺序通过?
如果是这样,它们是否与正常参数相反?
他们在cdecl和stdcall之间有什么区别吗?
我有一个出口的DLL
extern "C" __declspec(dllexport) int __stdcall Foo( void );
Run Code Online (Sandbox Code Playgroud)
dll的转储显示
******************************************************************************
Section: Exports
File Offset: 00001400 (5120)
Flags: 00000000
Time Stamp: 00000000
Major Version: 0000
Minor Version: 0000
Exports from simple.dll
3 exported name(s), 3 export addresse(s). Ordinal base is 1.
Sorted by Name:
RVA Ord. Hint Name
-------- ---- ---- ----
00002104 3 0000 std::nothrow
00001258 2 0001 Foo
000020F8 1 0002 ___CPPdebugHook
******************************************************************************
Run Code Online (Sandbox Code Playgroud)
我从以下def文件开始:
LIBRARY simple.dll
EXPORTS
Foo
Run Code Online (Sandbox Code Playgroud)
这创建了一个包含以下导出的lib文件:
Exports
ordinal name
_Foo
Run Code Online (Sandbox Code Playgroud)
当我链接到这个库时,msvc链接器抱怨它找不到_Foo @ 0.为了解决这个问题,我在def文件中添加了一个别名.
LIBRARY simple.dll …Run Code Online (Sandbox Code Playgroud) 我的目标是使用__cdecl和__stdcall调用约定轻松提取任意函数的原型.它在32位工作正常.唯一改变的是模板函数参数中的调用约定.
根据维基百科的说法: "在Windows环境中编译x64架构时(无论是使用Microsoft还是非Microsoft工具),只有一个调用约定 - 这里描述的那个,所以stdcall,thiscall,cdecl,fastcall等. ,现在都是一样的."
这打破了64位的代码.即使调用约定相同,将函数作为参数传递仍然需要使用正确的命名法.IE如果函数定义为__stdcall,则必须将其传递给接受__stdcall的包装器.即使__cdecl相同,您仍然必须将定义为__cdecl的函数传递给接受__cdecl的包装器.
以32位工作的示例:
template<typename T, typename... Args>
struct WrapperSTD { typedef T(__stdcall *Functor)(Args...); };
template<typename T, typename... Args>
struct WrapperC { typedef T(*Functor)(Args...); };
template<typename T, typename... Args>
WrapperSTD<T, Args...> wrap(T(__stdcall *func)(Args...)) {
return WrapperSTD<T, Args...>{};
}
template<typename T, typename... Args>
WrapperC<T, Args...> wrap(T(*func)(Args...)) {
return WrapperC<T, Args...>{};
}
Run Code Online (Sandbox Code Playgroud)
我的目标是能够运行,例如:
using MsgBoxProto = decltype(wrap(MessageBoxA))::Functor;
Run Code Online (Sandbox Code Playgroud)
这适用于32位.但是,由于__stdcall和__cdecl在x64中显然是相同的,因此它不能在64位中工作并引发错误,表示调用是不明确的.它还告诉我模板已经定义.直观地说,似乎我能够将带有__cdecl的函数传递给这个__stdcall函数,因为编译器认为它们是相同的.但是,这不起作用:
template<typename T, typename... Args>
struct WrapperFC { typedef T(__stdcall *Functor)(Args...); };
template<typename T, typename... Args>
WrapperFC<T, Args...> wrap(T(__stdcall …Run Code Online (Sandbox Code Playgroud) 有充分的理由吗?
他们的内部函数(未导出)是否也是stdcall约定?
我需要编写调用外部函数的代码,该函数可以是32位Windows应用程序中的stdcall调用或cdecl。
我的代码(调用者)无法事先知道将要使用的代码。现在,如果我尝试从定义为stdcall的调用站点中调用cdecl函数,则会出现一个checkEsp异常对话框,并且我猜那是有充分原因的。
有什么办法吗?
哎呀,当我做出这个答案时,有一件事我忘记了,而且我对自己并不是很确定,而且我似乎无法在MSDN和谷歌以及Stack Overflow搜索中找到相关信息.
Windows API中有许多地方使用负数,或者数字太大而无法使用有符号整数; 例如CW_USEDEFAULT,INVALID_HANDLE_VALUE,GWLP_USERDATA,等等.在C的世界里,一切都很好,花花公子:语言的整数提升规则得到了拯救.
但在Go中,我必须将所有参数传递给函数uintptr(相当于C的uintptr_t).函数的返回值也以这种方式返回,然后我需要进行比较.Go不允许整数提升,并且它不允许您在编译时将有符号常量表达式转换为无符号表达式.
现在,我在我的UI库中设置了一些用于处理这些常量的jerry-rig.(这是这个解决方案在行动中的样子.)但是,我对这个解决方案并不满意; 我觉得它喜欢ABI的事情,我想要完全确定我在做什么.
所以我的问题是:将签名值传递给Windows API函数时如何处理?返回时如何处理?
我的所有常量都是自动生成的(示例输出).自动生成器使用C ffi,我宁愿不用于主项目,因为我可以直接调用DLL(这也使得交叉编译至少在今年剩余时间内更容易).如果我可以以某种方式利用它,例如通过将所有内容都变成表单的C端变量
uintptr_t x_CONST_NAME = (uintptr_t) (CONST_NAME);
Run Code Online (Sandbox Code Playgroud)
这会有所帮助.但如果没有这个答案我就不能这样做.
谢谢!
IRC上的某个人将其改为不同(重新格式化以避免水平滚动):
[19:13] <FraGag> basically, you're asking whether an int with a value of -1
will be returned as 0x00000000FFFFFFFF or as 0xFFFFFFFFFFFFFFFF
if an int is 4 bytes and an uintptr is 8 bytes
Run Code Online (Sandbox Code Playgroud)
基本上这个,但专门用于Windows API互操作,传入的参数,无论uintptr大小.
处理从DLL创建LIB的一般方法在如何在有.dll文件和头文件的情况下创建.lib文件中描述- 仍然是为具有未修饰stdcall函数的DLL创建导入库(例如核心WinAPI DLL kernel32.dll等) .),一个要经过一个相当漫长和复杂的过程(如例如描述这里).对于具有许多功能的DLL,此过程非常耗时且容易出错 - 当原始DLL发生更改时(例如,由于供应商更新),它在自动渲染情况下也很容易失败.
有没有一种有效的自动化方法?