[英]How to return value from DLL using parameter of function as a pointer in C++?
我有一個簡單的 DLL:
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
#include "framework.h"
#ifdef MYDLLDIR
#define DLLDIR __declspec(dllexport) __stdcall
#else
#define DLLDIR __declspec(dllimport)
#endif
extern "C" {
DLLDIR void callByPtr(int *i);
};
客戶:
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;
}
}
結果:
---- 致電前----
我 = 10
ptri = 0025FB40
---- 來電后----
我 = 11286192
ptri = 0025FB3C
ptri 的地址已更改,值不是 11。
如何正確實現這一點,以便我可以使用上述方法從 DLL 中獲取值?
謝謝!
您的導出定義也不正確。 應該是這樣的:
#ifdef MYDLL_EXPORT
#define MYDLLDIR __declspec(dllexport)
#else
#define MYDLLDIR __declspec(dllimport)
#endif
並為導出(dll,#MYDLL_EXPORT 定義)和導入(客戶端,#MYDLL_EXPORT 未定義)使用相同的宏(MYDLLDIR)
您必須在所有地方對 callByPtr 使用相同的調用約定,在您的情況下為 __stdcall(默認為 __cstdcall)。
在你的 pch.h 然后:
MYDLLDIR void __stdcall callByPtr(int *i);
因為您在導出的函數 DLLDIR void callByPtr(int *i); 中返回 void; 您應該使用 C 和 C++ 程序 __cdecl 的默認調用約定。
更改后:
重建沒有錯誤和警告,輸出如下:
我 10
ptri 0113FCA4
---- 來電后----
我 11
ptri 0113FCA4
句法
返回類型
__stdcall
函數名[(參數列表)]
在調用約定符自帶函數的返回類型之后。 你定義它的方式是之前,所以(可能)編譯器忽略了它??? ,最終在.dll中將函數導出為__cdecl (默認),當.exe將其稱為__stdcall 時,砰! -> Stack Corruption ,你認為你的指針實際上是完全不同的東西,因此你的輸出很奇怪。
有趣的是,在我的最后,編譯器( VS2017 )吐出error C2062: type 'void' unexpected
當我嘗試使用您的表單( #define DLL00_EXPORT_API __declspec(dllexport) __stdcall
)構建.dll時, error C2062: type 'void' unexpected
。
下面是一個工作示例(我修改了文件名和內容)。
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 :
#define DLL00_EXPORTS
#include "dll00.h"
void CALL_CONV callByPtr(int *pI) {
if (pI) {
(*pI)++;
}
}
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;
}
輸出:
[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
注意事項:
顯然,提供的代碼不是生成輸出的代碼:
“次要”代碼問題:
__declspec(dllexport) __stdcall
與__declspec(dllimport)
,但由於您是動態加載.dll而不是鏈接到它,它與錯誤無關為什么需要使用__stdcall ? (答案在評論中提供:“與其他語言兼容”):
僅在32 位上很重要(在64 位上它被忽略)
它引入了很多額外的問題(其中一些你甚至沒有經歷過),比如函數名稱修改(檢查轉儲箱輸出),只能使用.def文件來避免
總而言之,這似乎是一個XY 問題。 您應該使用默認值(完全擺脫__stdcall ):
所有列出的語言( Delphi , Python , C# ,...)都支持__cdecl (畢竟,我認為那里運行的大多數機器代碼仍然是用C編寫的)
有關整個區域的更多詳細信息,您可以檢查(包括(遞歸)引用的URL s):
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.