By jingzhongrong
每个Win32的可执行程序以及DLL文件都是由许多个节组成,像程序代码会被放入.text节中,初始化过的数据被放置在.data节中,而未经初始化的数据会被放入.bss节中。
我们可以通过让编译器创建一个用于共享数据的共享节,用于存放用来共享的数据。
下面指令告诉编译器和链接程序在编译的时候创建一个节,
#pragma data_seg(“SectionName”)
我们可以在程序中创建这样一个节,里面包含了一个LONG值的数据,
volatile LONG g_ApplicationInstanceCounters = 0 ;
#pragma data_seg()
当编译器对代码进行编译时,会创建一个名为InstCounter的节,并将数据变量存放入该节中,需要注意的是,编译器只是将初始化过的变量存放在节中。
当创建了用于共享的共享节后,通过下面的指令告诉链接程序,通知其这个数据节是要用于
共享的,
#pragma comment(linker,”/SECTION:InstCounter,RWS)
上面语句中分别用R代表Read,W代表Write,S代表Shared。具体的说明请参见MSDN。
下面将通过简单的例子分别说明可执行文件多个示例之间的数据共享以及运用DLL来实现多个不同程序之间的数据共享。
1、可执行文件多个示例之间的数据共享。下面用一个例子来简单说明。程序运行后会显示该程序在系统中的实例数,也就是这个程序运行了多少个。
volatile LONG g_ApplicationInstanceCounters = 0 ;
#pragma data_seg()
#pragma comment(linker,"/SECTION:InstCounter,RWS")
通过上面代码创建一个名为InstCounter的共享节,该节可以读取、写入以及共享。在这个节中,包含了用于保存程序实例数目的LONG型变量。所有该程序的实例都可以共享这个变量。当程序执行的时候,我们将g_ApplicationInstanceCounters增加1,并广播消息给其他程序实例,程序响应该消息,更新信息。在程序退出时,将变量g_ApplicationInstanceCounters减去1,并广播消息到系统中的顶层窗口,由程序的其他实例响应消息并更新信息。
我们在WinMain入口函数中将变量自增1,并广播消息。
广播消息要使用PostMessage,在使用之前需要通过RegisterWindowMessage函数注册一个消息。
// 注册消息
UINT g_CounterUpdateMessage = INVALID_ATOM;
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
// 注册消息
// by jingzhongrong
g_CounterUpdateMessage = RegisterWindowMessage(TEXT( " ApplicationInstanceCounterUpdate " ));
// g_ApplicationInstanceCounters自增1
// 调用InterlockedExchangeAdd防止多个实例间的相互的改变
InterlockedExchangeAdd((PLONG) & g_ApplicationInstanceCounters, 1 );
// 创建对话框并注册消息处理函数
DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, AppInstDialogProc);
// g_ApplicationInstanceCounters自减1
InterlockedExchangeAdd((PLONG) & g_ApplicationInstanceCounters, - 1 );
// 广播程序退出的消息以便其他程序更新信息
PostMessage(HWND_BROADCAST,g_CounterUpdateMessage, 0 , 0 );
return 0 ;
}
对话框初始化函数,广播消息
{
PostMessage(HWND_BROADCAST,g_CounterUpdateMessage, 0 , 0 );
return TRUE;
}
对话框消息处理函数,
HWND hwndDlg, // handle to dialog box
UINT uMsg, // message
WPARAM wParam, // first message parameter
LPARAM lParam // second message parameter
)
{
if (uMsg == g_CounterUpdateMessage)
{
SetDlgItemInt(hwndDlg,IDC_COUNT,g_ApplicationInstanceCounters,FALSE);
}
switch (uMsg)
{
chHANDLE_DLGMSG(hwndDlg,WM_INITDIALOG,AppInstOnInitDialog);
chHANDLE_DLGMSG(hwndDlg,WM_COMMAND,AppInstOnCommand);
}
return FALSE;
}
2、通过DLL在多个程序间共享数据也是使用同样的方法,创建一个共享节,并在程序中载入这个DLL,实现数据共享。
在Win32中,对于每个载入DLL的进程,Windows都会对每个程序建立一个唯一的数据空间的副本,也就是说通过使用全局静态变量是不能够进行数据共享的,因为在不同程序中,这个静态变量是不一样的,而如果使用共享节来共享数据,就可以实现数据的共享,不过需要注意的是载入不同程序中的DLL副本必须是同一个DLL文件,如果使用的是相同DLL的不同副本,那么将不能实现进程之间的数据共享。比如在目录DLLA下有程序app1.exe以及shared.dll,而在另外一个目录DLLB下,有程序app2.exe以及shared.dll,那么就不能通过shared.dll来实现进程间的数据共享。
char ShareStrings[ 200 ] = TEXT( "" );
volatile BOOL bInCriticalSection = FALSE;
#pragma data_seg()
#pragma comment(linker,"/SECTION:SharedData,RWS")
// By jingzhongrong
下面实现两个用于测试的简单函数,一个是将字符串从共享区读出,一个是将字符串写入共享区中,
VCDLL_DECLARE LPSTR GetStrings()
{
while (bInCriticalSection)
Sleep( 1 );
return ShareStrings;
}
// by jingzhongrong
VCDLL_DECLARE void SetStrings(LPCSTR str)
{
while (bInCriticalSection)
Sleep( 1 );
cs.Lock();
bInCriticalSection = TRUE;
strcpy(ShareStrings,str);
bInCriticalSection = FALSE;
cs.Unlock();
}
其中VCDLL_DECLARE的宏定义如下
#define VCDLL_DECLARE extern "C" _declspec (dllexport)
#else
#define VCDLL_DECLARE _declspec (dllexport)
#endif
编译成功后,可以在程序代码中通过下面语句加载DLL
#pragma comment(lib,”ShareDll.lib”)
或者使用动态加载的方法
LoadLibrary进行加载,便可以使用GetStrings和SetStrings进行进程间通信了
在通信的时候可以使用消息在不同程序之间进行事件通知。告诉其他程序,共享区的变量已经设置,给其他程序进行响应。