C++ Dll创建
一、添加 DllMain(必要)
#include <fstream>
void Log(const char* msg)
{
std::ofstream f("C:\\temp\\dll_log.txt", std::ios::app);
f << msg << std::endl;
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
Log(">>> DLL_PROCESS_ATTACH");
break;
case DLL_THREAD_ATTACH:
Log(">>> DLL_THREAD_ATTACH");
break;
case DLL_THREAD_DETACH:
Log(">>> DLL_THREAD_DETACH");
break;
case DLL_PROCESS_DETACH:
Log(">>> DLL_PROCESS_DETACH");
break;
}
return TRUE;
}
二、定义导出接口
推荐使用 __declspec(dllexport) 和 extern “C”,不用 .def 文件
推荐你采用的最终写法(只用修饰宏)
// MyDll.h
#pragma once
#define DY __cdecl
#ifdef MYDLL_EXPORTS
#define MYDLL_API __declspec(dllexport)
#else
#define MYDLL_API __declspec(dllimport)
#endif
extern "C" {
MYDLL_API int DY OpenReader();
}
def方式(可选)(不建议用)
; ReaderF83.def : Declares the module parameters for the DLL.
LIBRARY "ReaderF83.dll"
;DESCRIPTION 'SCPMReader Windows Dynamic Link Library'
EXPORTS
; Explicit exports can go here
OpenReader
RequestICC
CardReset
SendAPDU
EjectICC
CloseReader
GetUID
C++ Dll调用
HMODULE hDll = LoadLibrary("FtSmartPos.dll");
if (hDll)
{
{
CloseReader pFunc = (CloseReader)GetProcAddress(hDll, "CloseReader");
if (pFunc)
{
pFunc(NULL, NULL); // 弹出对话框
}
else
{
AfxMessageBox("CloseReader函数未找到");
}
}
FreeLibrary(hDll);
}
else
{
AfxMessageBox("FtSmartPos.dll未找到");
}
MFC 单对话框应用程序(EXE 工程)改为 DLL 工程
三步即可。
改为dll
加dllmain.cpp
// dllmain.cpp : Defines the entry point for the DLL application.
#include "pch.h"
DWORD start = 0;
BOOL APIENTRY DllMain( HMODULE hModule,
long ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH: {
logger.INFO_F("#DBG::VDBG:F83 plugin load VERSION:"+string(VERSION));
start = GetTickCount();
}break;
case DLL_THREAD_ATTACH:
logger.INFO_F("#DBG::VREL:F83 plugin unload1 耗时:" + to_string(GetTickCount() - start));
break;
case DLL_THREAD_DETACH:
logger.INFO_F("#DBG::VREL:F83 plugin unload2 耗时:" + to_string(GetTickCount() - start));
break;
case DLL_PROCESS_DETACH: {
logger.INFO_F("#DBG::VREL:F83 plugin unload3 耗时:"+ to_string(GetTickCount()-start));
}break;
}
return TRUE;
}
SCPMReader.cpp
// SCPMReader.cpp : Defines the initialization routines for the DLL.
#include "pch.h"
#define EXPORT __declspec(dllexport)
extern "C" {
EXPORT int WINAPIV SendAPDU(HANDLE hCard, const char* pCmd, int nCmdLen, char* pRsp, int& nRspLen, const char* pPath)
{
return 0;
}
}
两种定义 导致找不到函数名。
#define WINAPI __stdcall
#define WINAPIV __cdecl
改为 extern “C” + __declspec(dllexport) + __cdecl
用depandency找不到c语言的,为什么呢?
用Depandency能看到_GetUID@12
非常好,这个信息 关键!
你看到的是:
_GetUID@12
这说明你:
✅ 成功导出了 GetUID
函数
❌ 但它是被 __stdcall
调用约定修饰过的名字
🔥 你的问题的根本原因:
你在 DLL 中写的是:
__declspec(dllexport) int __stdcall GetUID(int a, int b, int c);
那么导出的名字就是:
_GetUID@12
表示:
GetUID
是__stdcall
函数- 参数共占 12 字节(3 个 int)
但你在主程序里写:
GetProcAddress(hDll, "GetUID")
这个查找的是未修饰名 "GetUID"
,当然 找不到!
✅ 解决方案:两种选择
✅ 方案一:改为 extern "C"
+ __declspec(dllexport)
+ __cdecl
extern "C" __declspec(dllexport)
int __cdecl GetUID(int a, int b, int c);
这样导出的函数名就是 "GetUID"
,可以直接 GetProcAddress(hDll, "GetUID")
。
这是一种标准 C 导出方式,推荐用于插件和跨模块 DLL。
✅ 方案二:保持 __stdcall
,改用修饰名 "_GetUID@12"
auto pfn = (decltype(&GetUID)) GetProcAddress(hDll, "_GetUID@12");
这当然也能用,但非常不优雅,容易因为参数变更导致名字变掉。
✅ 最佳实践:用头文件 + 宏控制导出名
// DllHeader.h
#ifdef MYDLL_EXPORTS
#define MYAPI extern "C" __declspec(dllexport)
#else
#define MYAPI extern "C" __declspec(dllimport)
#endif
MYAPI int __cdecl GetUID(int a, int b, int c);
✅ 验证你的 DLL 导出名
你也可以继续用 dumpbin /exports
查看 DLL 导出名:
dumpbin /exports MyDll.dll
你应该能看到类似:
_GetUID@12
如果你改成了 __cdecl
,应该就会看到:
GetUID
是否需要我帮你写一个包含主程序 + DLL 的最小可运行范例?你倾向保留 __stdcall
还是换成 __cdecl
?