C++ Dll创建与调用 查看dll函数 MFC 单对话框应用程序(EXE 工程)改为 DLL 工程

发布于:2025-05-10 ⋅ 阅读:(13) ⋅ 点赞:(0)

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


网站公告

今日签到

点亮在社区的每一天
去签到