任務定義:同一程式的多個執行個體共用一份資料,一個執行個體對資料的修改會影響到其他執行個體,具體到視窗中就是,一個執行個體中的資料顯示變化引起其他各個執行個體中資料顯示的更新。
整體分析:編寫一個dll,其中包含被同一程式的不同執行個體共用的記憶體資料;類似於document-view模式,document對應dll中的共用記憶體資料,view對應各個執行個體的顯示視窗;由此可以實現一個執行個體對共用資料的修改,可以影響到其他執行個體,每次修改後當前執行個體向所有其他執行個體發送一個訊息,使得各個執行個體視窗資料同步更新。
關鍵程式碼分析:
1、共用資料區段
// 建立名為shared的共用資料區段
#pragma data_seg ("shared") // 必須對資料初始化,否則編譯器將把它們放在普通的未初始化資料區段中
int iTotal = 0 ; //標識字串總數
WCHAR szStrings [MAX_STRINGS][MAX_LENGTH + 1] = { '\0' } ; //關鍵共用字串數組,用來在各個執行個體中顯示
#pragma data_seg ()
#pragma comment(linker,"/SECTION:shared,RWS")
2、各個應用程式執行個體中的字串顯示函數
BOOL CALLBACK GetStrCallBack (PTSTR pString, CBPARAM * pcbp)
{
// 輸出字串
TextOut (pcbp->hdc, pcbp->xText, pcbp->yText,
pString, lstrlen (pString)) ;
// 輸出y座標遞增一個字元高度,並與視窗可輸出最大行比較
// 如果輸出行已到最大行,則執行
if ((pcbp->yText += pcbp->yIncr) > pcbp->yMax)
{
// 使y座標指向第一行
pcbp->yText = pcbp->yStart ;
// x座標增加一行字串最大寬度,並與可輸出最大列比較
// 如果輸出已到最大行和最大列,則返回FALSE
if ((pcbp->xText += pcbp->xIncr) > pcbp->xMax)
return FALSE ;
}
return TRUE ;
}
3、dll中的字串擷取並顯示函數(在內部調用了字串顯示函數)
EXPORT int CALLBACK GetStringsW (GETSTRCB pfnGetStrCallBack, PVOID pParam)
{
BOOL bReturn ;
int i ;
for (i = 0 ; i < iTotal ; i++)
{ //在這裡回調了應用程式執行個體中的字串顯示函數,因為顯示只能在各個執行個體中顯示,dll是不能協助顯示的,dll並不知道執行個體中的顯示參數或者顯示的上下文環境,因而不能在此直接寫出顯示的代碼,而是要待用執行個體中的函數,讓執行個體自己去做顯示的工作,因為只有它自己才知道如何根據顯示的DC來顯示。其實這個函數主要的工作就是擷取共用資料,因為資料是在dll的控制範圍之內的。
bReturn = pfnGetStrCallBack (szStrings[i], pParam) ;
if (bReturn == FALSE)
return i + 1 ;
}
return iTotal ;
}
4、全域訊息註冊
// 註冊字串更改訊息,這個訊息是各個執行個體都能收到的,可參考MSDN
iDataChangeMsg = RegisterWindowMessage (TEXT ("StrProgDataChange")) ;
5、向其他執行個體廣播訊息(當在對話方塊中做出操作之後,會引起共用資料變化,因此向其他執行個體廣播訊息,讓它們重新整理顯示)
// 建立輸入對話方塊(刪除對話方塊行為類似,不再分析)
if (DialogBox (hInst, TEXT ("EnterDlg"), hwnd, &DlgProc)
{
// 添加使用者輸入的字串到共用記憶體的字串數組中
if (AddString (szString))
// 將程式註冊的訊息發送給所有視窗,用於通知使用共用記憶體的其它程式
PostMessage (HWND_BROADCAST, iDataChangeMsg, 0, 0) ;
else
MessageBeep (0) ;
}
6、視窗收到廣播訊息(收到廣播訊息,立馬讓客戶區無效,從而引起重新整理)
default:
// 廣播訊息,重新整理客戶區
if (message == iDataChangeMsg)
InvalidateRect (hwnd, NULL, TRUE) ;
7、更新顯示(對應共用資料的修改,每個執行個體做出更新顯示)
case WM_PAINT:
hdc = BeginPaint (hwnd, &ps) ;
// 初始化輸出字串所需要的資料
cbparam.hdc = hdc ;
cbparam.xText = cbparam.xStart = cxChar ;
cbparam.yText = cbparam.yStart = cyChar ;
cbparam.xIncr = cxChar * MAX_LENGTH ;
cbparam.yIncr = cyChar ;
cbparam.xMax = cbparam.xIncr * (1 + cxClient / cbparam.xIncr) ;
cbparam.yMax = cyChar * (cyClient / cyChar - 1) ;
// 輸出字串
GetStrings ((GETSTRCB) GetStrCallBack, (PVOID) &cbparam) ;
EndPaint (hwnd, &ps) ;
return 0 ;
ps:其他的注意事項還有一些工程配置問題,以及應用程式對dll的依賴配置。