home *** CD-ROM | disk | FTP | other *** search
- {.$DEFINE DFS_DEBUG}
-
- { The DFSKbMon.DLL library. }
-
- { Intended for use with the TDFSStatusBar component (DFSStatusBar.pas), and
- intended to be accessed by the DFSKb unit. }
-
- { This DLL provides a notification mechanism for system wide monitoring of the
- Caps, Num & Scroll lock keys. Interested parties are notified via a custom
- windows message as returned by the RegisterKeyboardHook function. }
-
- library DFSKbMon;
-
- uses
- {$IFDEF DFS_DEBUG}
- Debug, SysUtils,
- {$ENDIF}
- Windows;
-
- const
- MAX_CLIENTS = 256;
-
- type
- PHookData = ^THookData;
- THookData = record
- KeyboardHookHandle: HHOOK;
- UsageCount: integer;
- ClientIndex: integer;
- Clients: array[0..MAX_CLIENTS-1] of HWND;
- end;
-
- var
- MapFile: THandle;
- WM_INDICATORKEY: UINT;
- HookData: PHookData;
-
-
- procedure MapFileMemory;
- var
- ZeroMem: boolean;
- begin
- MapFile := CreateFileMapping($FFFFFFFF, NIL, PAGE_READWRITE, 0,
- SizeOf(THookData), 'DFSKbMon Client List');
- if (MapFile = 0) then
- begin
- MessageBox(0, 'DFSKbMon DLL', 'Could not create file map object', MB_OK);
- end else begin
- ZeroMem := GetLastError <> ERROR_ALREADY_EXISTS;
- HookData := MapViewOfFile(MapFile, FILE_MAP_ALL_ACCESS, 0, 0,
- SizeOf(THookData));
- if (HookData = NIL) then
- begin
- CloseHandle(MapFile);
- MessageBox(0, 'DFSKbMon DLL', 'Could not map file', MB_OK);
- end else
- if ZeroMem then
- FillChar(HookData^, SizeOf(THookData), #0);
- end;
- end;
-
- procedure UnmapFileMemory;
- begin
- if (HookData <> NIL) then
- begin
- UnMapViewOfFile(HookData);
- HookData := NIL;
- end;
- if (MapFile <> 0) then
- begin
- CloseHandle(MapFile);
- MapFile := 0;
- end;
- end;
-
- procedure AddClient(Wnd: HWND);
- begin
- if (HookData <> NIL) then
- begin
- {$IFDEF DFS_DEBUG}
- Debug.Log(TRUE, 'DFSKbMon: Added ' + IntToHex(Wnd, 8) + ' at index ' +
- IntToStr(HookData^.ClientIndex));
- {$ENDIF}
- HookData^.Clients[HookData^.ClientIndex] := Wnd;
- inc(HookData^.ClientIndex);
- end;
- end;
-
- procedure RemoveClient(Wnd: HWND);
- var
- x: integer;
- begin
- if (HookData <> NIL) then
- begin
- {$IFDEF DFS_DEBUG}
- Debug.Log(TRUE, 'DFSKbMon: Trying to remove ' + IntToHex(Wnd, 8));
- {$ENDIF}
- for x := 0 to HookData^.ClientIndex-1 do
- begin
- if HookData^.Clients[x] = Wnd then
- begin
- {$IFDEF DFS_DEBUG}
- Debug.Log(TRUE, ' Found at index ' + IntToStr(HookData^.ClientIndex));
- {$ENDIF}
- if x < HookData^.ClientIndex-1 then
- Move(HookData^.Clients[x+1], HookData^.Clients[x],
- (HookData^.ClientIndex - x - 1) * SizeOf(HWND));
- dec(HookData^.ClientIndex);
- break;
- end;
- end;
- end;
- end;
-
-
-
- // Keyboard hook callback
- function KeyboardHookCallBack(Code: integer; KeyCode: WPARAM;
- KeyInfo: LPARAM): LRESULT; stdcall;
- var
- x: integer;
- State: ShortInt;
- begin
- if (Code >= 0) and (HookData <> NIL) then
- begin
- // Is it one of the indicator keys, and is it not a repeat
- if ((KeyCode = VK_CAPITAL) or (KeyCode = VK_NUMLOCK) or
- (KeyCode = VK_SCROLL)) and
- // This checks to see if the key is being pressed (bit 31) and if it was
- // up before (bit 30). We don't care about key releases or keys that
- // were already down. That just makes us flicker...
- (((KeyInfo SHR 31) and 1) = 0) {and (((KeyInfo SHR 30) and 1) = 0)} then
- begin
- State := GetKeyState(KeyCode);
- for x := 0 to HookData^.ClientIndex-1 do
- PostMessage(HookData^.Clients[x], WM_INDICATORKEY, KeyCode, State);
- end;
- end;
-
- Result := CallNextHookEx(HookData^.KeyboardHookHandle, Code, KeyCode, KeyInfo);
- end;
-
- // Utility routins for installing the windows hook for keypresses
- function RegisterKeyboardHook(Handle: HWND): UINT; stdcall;
- { This is really silly, but that's the way it goes. The only way to get the }
- { module handle, *not* instance, is from the filename. The Microsoft example}
- { just hard-codes the DLL filename. I think this is a little bit better. }
- function GetModuleHandleFromInstance: THandle;
- var
- s: array[0..512] of char;
- begin
- { Find the DLL filename from the instance value. }
- GetModuleFileName(hInstance, s, sizeof(s)-1);
- { Find the handle from the filename. }
- Result := GetModuleHandle(s);
- end;
- begin
- if (HookData <> NIL) then
- begin
- if HookData^.KeyboardHookHandle = 0 then
- { See the Microsoft KnowledgeBase, PSS ID Number: Q92659, for a }
- { discussion of the Windows bug that requires GetModuleHandle to be used.}
- HookData^.KeyboardHookHandle := SetWindowsHookEx(WH_KEYBOARD,
- KeyboardHookCallBack, GetModuleHandleFromInstance, 0);
-
- inc(HookData^.UsageCount);
- AddClient(Handle);
-
- Result := WM_INDICATORKEY;
- end else
- Result := 0;
- end;
-
- procedure DeregisterKeyboardHook(Handle: HWND); stdcall;
- begin
- if (HookData <> NIL) then
- begin
- dec(HookData^.UsageCount);
- RemoveClient(Handle);
- if HookData^.UsageCount < 1 then
- begin
- UnhookWindowsHookEx(HookData^.KeyboardHookHandle);
- HookData^.KeyboardHookHandle := 0;
- end;
- end;
- end;
-
- procedure LibraryProc(Reason: Integer);
- begin
- case Reason of
- DLL_PROCESS_ATTACH:
- begin
- {$IFDEF DFS_DEBUG}
- Debug.Log(TRUE, 'DFSKbMon: loaded.');
- {$ENDIF}
- MapFile := 0;
- HookData := NIL;
- MapFileMemory;
- end;
- DLL_PROCESS_DETACH:
- begin
- UnmapFileMemory;
- {$IFDEF DFS_DEBUG}
- Debug.Log(TRUE, 'DFSKbMon: unloaded.');
- {$ENDIF}
- end;
- end;
- end;
-
- exports
- RegisterKeyboardHook,
- DeregisterKeyboardHook;
-
-
- begin
- WM_INDICATORKEY := RegisterWindowMessage('DFSKbMon.WM_INDICATORKEY');
- DLLProc := @LibraryProc;
- LibraryProc(DLL_PROCESS_ATTACH);
- end.
-