[英]How to return value from DLL using parameter of function as a pointer in C++?
I have a simple DLL:我有一个简单的 DLL:
dllmain.cpp: dllmain.cpp:
#define MYDLLDIR
#include "pch.h"
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
...
}
void callByPtr(int *i) {
(*i)++;
}
pch.h pch.h
#include "framework.h"
#ifdef MYDLLDIR
#define DLLDIR __declspec(dllexport) __stdcall
#else
#define DLLDIR __declspec(dllimport)
#endif
extern "C" {
DLLDIR void callByPtr(int *i);
};
Client:客户:
typedef void(__stdcall* callByPtr)(int*);
int main()
{
HINSTANCE hDLL;
hDLL = LoadLibrary(_T("MyDll.dll"));
if (NULL != hDLL)
{
callByPtr myCall = (callByPtr)GetProcAddress(hDLL, "callByPtr");
if (!myCall) {
return EXIT_FAILURE;
}
int i = 10;
int* ptri = &i;
std::cout << "i " << i << std::endl;
std::cout << "ptri " << ptri << std::endl;
myCall(ptri);
std::cout << "---- After Call ----\n";
std::cout << "i " << i << std::endl;
std::cout << "ptri " << ptri << std::endl;
}
}
Result:结果:
---- Before Call ---- ---- 致电前----
i = 10我 = 10
ptri = 0025FB40 ptri = 0025FB40
---- After Call ---- ---- 来电后----
i = 11286192我 = 11286192
ptri = 0025FB3C ptri = 0025FB3C
The adress of ptri has changed and value was not 11. ptri 的地址已更改,值不是 11。
How to implement this properly that I can get a value from DLL using method above?如何正确实现这一点,以便我可以使用上述方法从 DLL 中获取值?
Thank you!谢谢!
Your exporting definitions are also not correct.您的导出定义也不正确。 Should be something like:应该是这样的:
#ifdef MYDLL_EXPORT
#define MYDLLDIR __declspec(dllexport)
#else
#define MYDLLDIR __declspec(dllimport)
#endif
and use the same macro (MYDLLDIR) for both export (dll, #MYDLL_EXPORT defined) and import(clients, #MYDLL_EXPORT NOT defined)并为导出(dll,#MYDLL_EXPORT 定义)和导入(客户端,#MYDLL_EXPORT 未定义)使用相同的宏(MYDLLDIR)
You have to use the same calling convention for callByPtr in all places, in your case __stdcall (the default one is __cstdcall).您必须在所有地方对 callByPtr 使用相同的调用约定,在您的情况下为 __stdcall(默认为 __cstdcall)。
In your pch.h then:在你的 pch.h 然后:
MYDLLDIR void __stdcall callByPtr(int *i);
since you return void in your exported function DLLDIR void callByPtr(int *i);因为您在导出的函数 DLLDIR void callByPtr(int *i); 中返回 void; you should use the default calling convention for C and C++ programs __cdecl.您应该使用 C 和 C++ 程序 __cdecl 的默认调用约定。
After changing:更改后:
The rebuild is without errors and warnings and the output is like that:重建没有错误和警告,输出如下:
i 10我 10
ptri 0113FCA4 ptri 0113FCA4
---- After Call ---- ---- 来电后----
i 11我 11
ptri 0113FCA4 ptri 0113FCA4
According to [MS.Docs]: __stdcall根据[MS.Docs]:__stdcall
Syntax句法
return-type
__stdcall
function-name [( argument-list )]返回类型__stdcall
函数名[(参数列表)]
the calling convention specifier comes after the function return type.在调用约定符自带函数的返回类型之后。 The way you defined it is before , so (probably) the compiler ignored it ???你定义它的方式是之前,所以(可能)编译器忽略了它??? , ending up in the .dll exporting the function as __cdecl (default), and when the .exe called it as __stdcall , Bang! ,最终在.dll中将函数导出为__cdecl (默认),当.exe将其称为__stdcall 时,砰! -> Stack Corruption , and what you think is your pointer is actually something completely different, hence your weird outputs. -> Stack Corruption ,你认为你的指针实际上是完全不同的东西,因此你的输出很奇怪。
The thing that is interesting is that on my end, the compiler ( VS2017 ) spits error C2062: type 'void' unexpected
when I try to build the .dll using your form ( #define DLL00_EXPORT_API __declspec(dllexport) __stdcall
).有趣的是,在我的最后,编译器( VS2017 )吐出error C2062: type 'void' unexpected
当我尝试使用您的表单( #define DLL00_EXPORT_API __declspec(dllexport) __stdcall
)构建.dll时, error C2062: type 'void' unexpected
。
Below is a working example (I modified the file names and contents).下面是一个工作示例(我修改了文件名和内容)。
dll00.h : dll00.h :
#pragma once
#if defined(_WIN32)
# if defined(DLL00_EXPORTS)
# define DLL00_EXPORT_API __declspec(dllexport)
# else
# define DLL00_EXPORT_API __declspec(dllimport)
# endif
#else
# define DLL00_EXPORT_API
#endif
#if defined(CALL_CONV_STDCALL)
# define CALL_CONV __stdcall
#else
# define CALL_CONV
#endif
#if defined(__cplusplus)
extern "C" {
#endif
DLL00_EXPORT_API void CALL_CONV callByPtr(int *pI);
#if defined(__cplusplus)
}
#endif
dll00.cpp : dll00.cpp :
#define DLL00_EXPORTS
#include "dll00.h"
void CALL_CONV callByPtr(int *pI) {
if (pI) {
(*pI)++;
}
}
main00.cpp : main00.cpp :
#include <iostream>
#include <Windows.h>
#include "dll00.h"
#if defined(CALL_CONV_STDCALL)
# define FUNC_NAME "_callByPtr@4"
#else
# define FUNC_NAME "callByPtr"
#endif
using std::cout;
using std::endl;
typedef void(CALL_CONV *CallByPtrFunc)(int*);
int main() {
HMODULE hDLL;
hDLL = LoadLibrary("dll00.dll");
if (!hDLL) {
std::cout << "LoadLibrary failed" << std::endl;
return -1;
}
CallByPtrFunc callByPtr = (CallByPtrFunc)GetProcAddress(hDLL, FUNC_NAME);
if (!callByPtr) {
std::cout << "GetProcAddress failed" << std::endl;
CloseHandle(hDLL);
return EXIT_FAILURE;
}
int i = 10;
int *ptri = &i;
std::cout << "i " << i << std::endl;
std::cout << "ptri " << ptri << std::endl;
callByPtr(ptri);
std::cout << "---- After Call ----\n";
std::cout << "i " << i << std::endl;
std::cout << "ptri " << ptri << std::endl;
CloseHandle(hDLL);
return 0;
}
Output :输出:
[cfati@CFATI-5510-0:e:\\Work\\Dev\\StackOverflow\\q063951075]> sopr.bat *** Set shorter prompt to better fit when pasted in StackOverflow (or other) pages *** [prompt]> "c:\\Install\\pc032\\Microsoft\\VisualStudioCommunity\\2017\\VC\\Auxiliary\\Build\\vcvarsall.bat" x86 ********************************************************************** ** Visual Studio 2017 Developer Command Prompt v15.9.27 ** Copyright (c) 2017 Microsoft Corporation ********************************************************************** [vcvarsall.bat] Environment initialized for: 'x86' [prompt]> [prompt]> dir /b dll00.cpp dll00.h main00.cpp [prompt]> :: Build the .dll (passing /DCALL_CONV_STDCALL) [prompt]> cl /nologo /MD /DDLL /DCALL_CONV_STDCALL dll00.cpp /link /NOLOGO /DLL /OUT:dll00.dll dll00.cpp Creating library dll00.lib and object dll00.exp [prompt]> [prompt]> :: Build the .exe (also passing /DCALL_CONV_STDCALL) [prompt]> cl /nologo /MD /W0 /EHsc /DCALL_CONV_STDCALL main00.cpp /link /NOLOGO /OUT:main00.exe main00.cpp [prompt]> [prompt]> dir /b dll00.cpp dll00.dll dll00.exp dll00.h dll00.lib dll00.obj main00.cpp main00.exe main00.obj [prompt]> [prompt]> main00.exe i 10 ptri 00F5FCC8 ---- After Call ---- i 11 ptri 00F5FCC8 [prompt]> :: It worked !!! [prompt]> [prompt]> dumpbin /EXPORTS dll00.dll Microsoft (R) COFF/PE Dumper Version 14.16.27043.0 Copyright (C) Microsoft Corporation. All rights reserved. Dump of file dll00.dll File Type: DLL Section contains the following exports for dll00.dll 00000000 characteristics FFFFFFFF time date stamp 0.00 version 1 ordinal base 1 number of functions 1 number of names ordinal hint RVA name 1 0 00001000 _callByPtr@4 Summary 1000 .data 1000 .rdata 1000 .reloc 1000 .text
Notes :注意事项:
Obviously, the code presented is not the one generating your output :显然,提供的代码不是生成输出的代码:
"Minor" code problems: “次要”代码问题:
__declspec(dllexport) __stdcall
vs. __declspec(dllimport)
, but since you're dynamically loading the .dll instead of linking to it, it has nothing to do with the error DLLDIR定义之间的差异( @Petr_Dokoupil也提到): __declspec(dllexport) __stdcall
与__declspec(dllimport)
,但由于您是动态加载.dll而不是链接到它,它与错误无关Why the need to use __stdcall ?为什么需要使用__stdcall ? (answer provided in comment: " to be compatible with other languages "): (答案在评论中提供:“与其他语言兼容”):
It only matters on 32bit (on 64bit it's ignored)仅在32 位上很重要(在64 位上它被忽略)
It introduces a lot of additional issues (some of them you didn't even experienced), like the function name mangling (check dumpbin output), which can only be avoided using a .def file它引入了很多额外的问题(其中一些你甚至没有经历过),比如函数名称修改(检查转储箱输出),只能使用.def文件来避免
All in all, this seems like an XY Problem .总而言之,这似乎是一个XY 问题。 You should use defaults (get rid of __stdcall completely):您应该使用默认值(完全摆脱__stdcall ):
All the listed languages ( Delphi , Python , C# , ...) have support for __cdecl (after all, I think that most of machine code running out there, is still written in C )所有列出的语言( Delphi , Python , C# ,...)都支持__cdecl (毕竟,我认为那里运行的大多数机器代码仍然是用C编写的)
For more details regarding this whole area, you could check (including (recursively) referenced URL s):有关整个区域的更多详细信息,您可以检查(包括(递归)引用的URL s):
[SO]: Python Ctypes - loading dll throws OSError: [WinError 193] %1 is not a valid Win32 application (@CristiFati's answer) [SO]:Python Ctypes - 加载 dll 抛出 OSError:[WinError 193] %1 不是有效的 Win32 应用程序(@CristiFati 的回答)
[SO]: When using fstream in a library I get linker errors in the executable (@CristiFati's answer) [SO]:在库中使用 fstream 时,我在可执行文件中收到链接器错误(@CristiFati 的回答)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.