C++编写的DLL与第三方通信中回调函数的巧妙使用

/* CallBackDll.cpp 2012年11月6日 23:34:20 */
#include 
#include 
#include 

// 不同对象之间通信的结构体
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 
#include 
#include 
#include 

using namespace std;


// 不同对象之间通信的结构体
typedef struct _tagMyStruct
{
	INT nCount;
	TCHAR szBuff[MAX_PATH];
}MyStruct, *LPMyStruct;


// 用于存储数据的类
class CData
{
public:
	typedef list 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++的静态成员函数,回调函数地址必须是固定的!

你可能感兴趣的:(C++)