/* CallBackDll.cpp 2012年11月6日 23:34:20 */
#include <tchar.h>
#include <Windows.h>
#include <strsafe.h>
// 不同对象之间通信的结构体
typedef struct _tagMyStruct
{
INT nCount;
TCHAR szBuff[MAX_PATH];
}MyStruct, *LPMyStruct;
// 回调函数原型
typedef BOOL(__stdcall *pCallBack)(LPMyStruct lpMyStruct, LPVOID lpVoid);
// 即使在DLL内部,我还是习惯用C++类来操作.如需外部调用,则单独导出C函数.
class CLyc
{
public:
CLyc(void) {
}
~CLyc(void) {
}
// 生成数据测试函数
BOOL __stdcall Work(pCallBack MypCallBack, LPVOID lpVoid)
{
for(INT i=0; i<3; i++)
{
MyStruct Tmp = {0};
Tmp.nCount = i;
if(0 == Tmp.nCount)
{
StringCchCopy(Tmp.szBuff, MAX_PATH, TEXT("First"));
}
else if(1 == Tmp.nCount)
{
StringCchCopy(Tmp.szBuff, MAX_PATH, TEXT("Second"));
}
else if(2 == Tmp.nCount)
{
StringCchCopy(Tmp.szBuff, MAX_PATH, TEXT("Third"));
}
MypCallBack(&Tmp, lpVoid);
}
return TRUE;
}
};
// 全局变量...测试
CLyc g_Tmp;
// Dll导出函数
BOOL __stdcall DllExportTest(pCallBack MypCallBack, LPVOID lpVoid)
{
return g_Tmp.Work(MypCallBack, lpVoid);
}
/* CallBackDll.def 2012年11月6日 23:35:02*/
LIBRARY "CallBackDll"
EXPORTS
DllExportTest
/* Test.cpp 2012年11月6日 23:35:39 */
#include <tchar.h>
#include <list>
#include <Windows.h>
#include <strsafe.h>
using namespace std;
// 不同对象之间通信的结构体
typedef struct _tagMyStruct
{
INT nCount;
TCHAR szBuff[MAX_PATH];
}MyStruct, *LPMyStruct;
// 用于存储数据的类
class CData
{
public:
typedef list<MyStruct> MyStructList;
public:
CData(void) {
}
~CData(void) {
}
BOOL __stdcall Add(LPMyStruct lpMyStruct)
{
MyStructListData.push_back(*lpMyStruct);
return TRUE;
}
// 当然这里仅列出Add为例,也可添加如移除,查找等常用数据操作
private:
MyStructList MyStructListData;
};
// 回调函数原型
typedef BOOL(__stdcall *pCallBack)(LPMyStruct lpMyStruct, LPVOID lpVoid);
// 回调函数测试
BOOL __stdcall CallBackTest(LPMyStruct lpMyStruct, LPVOID lpVoid)
{
if (NULL != lpVoid)
{
CData * Tmp = (CData *)lpVoid;
Tmp->Add(lpMyStruct);
}
return TRUE;
}
// Dll导出函数原型
typedef BOOL(__stdcall *pDllExportTest)(pCallBack MypCallBack, LPVOID lpVoid);
int _tmain(int argc, _TCHAR* argv[])
{
BOOL bRet = FALSE;
HINSTANCE hDll = LoadLibrary(TEXT("CallBackDll.dll"));
pDllExportTest MypDllExportTest =
(pDllExportTest)GetProcAddress(hDll, "DllExportTest");
// 比较习惯动态加载DLL,这样子只要DLL接口不变,双方可以随意更改内部结构
if (NULL == MypDllExportTest)
{
return bRet;
}
CData MyCData;
bRet = MypDllExportTest(&CallBackTest, &MyCData);
FreeLibrary(hDll);
return bRet;
}
最近这个项目我负责所有的数据处理模块,原始数据是存储在自定义格式的文件中,主要是用结构体存储的,有固定大小的结构体,也有动态大小的结构体。
而我这部分所有的都必须用C++编程成DLL,然后提供接口供WPF界面调用。其中最让我头疼的问题,就是在A号DLL中所申请的资源(这里我使用的只有堆上的链表,不清楚其他类型的内存块),其他DLL访问会报异常,而且也不安全。于是只有通过回调函数,结合结构体(方便扩展)进行通信。
在这个项目中,我将数据处理的功能分割的比较细(最后一共写了12个DLL,我学编程三年了都没写过这么多,掩面~),基本上是瀑布式模型,A→B→C...→H,上层的WPF只会从H号DLL中寻找接口。
问题就在这里!
当WPF要显示我分析数据结果时,H号DLL中接口H1会根据参数将第一次分析的结果进行二次分析再回传给WPF。(请回忆第二段,不同DLL的资源不能共享。)所以H号DLL中需要用额外的List保存二次分析的数据,但是这里的List不能是全局或者静态的,否则当同一时刻WPF调用两次H1接口,那么两次二次分析的数据会冲突。所以只能用 C++类的对象来临时保存,二次分析完成后再回调。
昨天上班在公交车上想到之前看过一篇文章,大概也是讲的“回调函数与C++对象的关系”里面也是采用的传递对象指针。
造成此问题的原因是回调函数必须是全局的或C++的静态成员函数,回调函数地址必须是固定的!