Windows 規定:DLL 不擁有其打開的文件或分配的全局內存塊,這些由直接/間接調用 DLL 的應用程序擁有。應用程序結束時自動關閉文件、釋放內存。這導致保存在 DLL 全局變量中的句柄在 DLL 無感知下變非法,給其他使用該 DLL 的程序造成困難。
解決方案:文件/全局內存句柄不作 DLL 全局變量,而是作過程/函數參數傳遞,調用者負責維護。
特例:DLL 可擁有自身全局內存塊,須用 GMEM_DDESHARE 屬性分配,此類內存塊直到 DLL 顯式釋放或卸載前保持有效。此為應用程序間數據傳輸新途徑。
GMEM_DDESHARE 分配的全局內存塊特殊處:
var hMem: THandle;function GetGlobalMem: THandle; export;
begin Result := hMem; end;
begin
hMem := GlobalAlloc(GMEM_MOVEABLE and GMEM_DDESHARE, Num);
if hMem = 0 then MessageDlg('Could not allocate memory', mtWarning, [mbOK], 0);
end.
GlobalAlloc 從全局堆分配內存返回句柄。兩參數:
| 標誌 | 含義 |
|---|---|
| GMEM_DDESHARE | 可被應用程序共享的內存 |
| GMEM_DISCARDABLE | 可丟棄內存(需配合 MOVEABLE) |
| GMEM_FIXED | 固定內存 |
| GMEM_MOVEABLE | 可移動內存 |
| GMEM_NOCOMPACT | 堆內存不可壓縮/丟棄 |
| GMEM_NODISCARD | 堆內存不可丟棄 |
| GMEM_NOT_BANKED | 不可分段內存 |
| GMEM_NOTIFY | 丟棄時調用 GlobalNotify |
| GMEM_ZEROINIT | 內容初始化為零 |
常用組合:GHND = GMEM_MOVEABLE and GMEM_ZEROINIT、GPTR = GMEM_FIXED and GMEM_ZEROINIT。第二參數為字節數(32 倍數,實際分配可能更大)。GMEM_DDESHARE 分配的內存在分配模塊終止時自動丟棄,無需 GlobalFree 顯式釋放。
包含 DLL 調用聲明:
function GetGlobalMem: THandle; far; external 'c:\dlls\glbmem';
寫入數據前必須鎖定內存(GlobalLock),防止 Windows 移動內存位置:
var hMem: THandle; pMem: PChar;
begin
hMem := GetGlobalMem;
if hMem <> 0 then
begin
pMem := GlobalLock(hMem);
if pMem <> nil then
begin
StrPCopy(pMem, Memo1.Text); // 寫入數據
GlobalUnlock(hMem); // 盡早解鎖利於內存管理
end
else MessageDlg('Could not Lock memory block', mtWarning, [mbOK], 0);
end;
end;
幾乎同服務端,差別在讀取數據:
var hMem: THandle; pMem: PChar;
begin
hMem := GetGlobalMem;
if hMem <> 0 then
begin
pMem := GlobalLock(hMem);
if pMem <> nil then
begin
Memo1.Text := StrPas(pMem); // 讀取數據
GlobalUnlock(hMem);
end
else MessageDlg('Could not Lock memory block', mtWarning, [mbOK], 0);
end;
end;