【发布时间】:2019-09-13 00:24:31
【问题描述】:
我正在将最初在 Delphi 5 中创建的旧应用程序更新和转换为更现代的 XE7 版本并创建 64 位版本。到目前为止,我的转化按预期进行。
我已经归结为应用程序主要部分的最后两个功能。第一个功能是一个内部插件,它被分离成一个 DLL。第二个是全局键盘挂钩,用于激活应用程序的三个功能之一,而另一个应用程序是活动应用程序并具有焦点。
关于内部插件的问题。该插件使用记录在主应用程序之间传递信息。记录在其自己的单元中定义,主应用程序和插件 DLL 在构建时都使用该单元。目前,除了获取记录设置之外,我还没有在插件上工作过。
这是插件记录的问题。在插件 DLL 和主应用程序中,记录由指针访问。当我将应用程序构建为 32 位程序时,程序编译并运行没有任何错误。但是,如果我将应用程序构建为 64 位程序,它会在没有任何编译器错误的情况下编译和构建,但是当它运行时,我会在访问记录指针的每一行代码中收到有关访问冲突的运行时错误消息.
对于全局键盘挂钩,最初使用的代码基于this code。为此,有两个问题。第一个和上面一样,当访问指向记录的指针时。第二个问题涉及 WinAPI PostMessage() 函数的使用。在这两种情况下,应用程序都将作为 32 位程序编译、构建和运行而不会出现任何问题或错误,但作为 64 位程序会出现运行时错误 Access Violation。
插件记录代码:
unit memlocs;
interface
uses
db, dbclient, dialogs, sysutils, windows, registry, StrUtils, classes;
function GetMMFile: String;
type
TGlobal = record
InstanceCount: Cardinal;
Command: Integer;
Param1: ShortString;
Param2: ShortString;
Param3: ShortString;
Param4: ShortString;
Param5: ShortString;
Performed: ShortInt;
Result: ShortString;
Result2: ShortString;
PromptDiv: Integer;
Status: Byte;
DivideHandle: THandle;
end;
var
Global: ^TGlobal;
MapHandle: THandle;
const
MMFileName: String = 'Divide';
implementation
function GetMMFile: String;
var
sFile: String;
begin
sFile := MMFileName;
sFile := AnsiReplaceStr(sFile, ' ', '');
sFile := AnsiReplaceStr(sFile, '.', '');
sFile := AnsiReplaceStr(sFile, '(', '');
sFile := AnsiReplaceStr(sFile, ')', '');
Result := sFile;
end;
initialization
finalization
end.
访问记录导致访问冲突的代码:
Global.DivideHandle := Handle
全局键盘钩子中使用的记录代码:
{ The record type filled in by the hook dll}
THookRec = record
TheHookHandle : HHOOK;
TheAppWinHandle : HWND;
TheCtrlWinHandle : HWND;
TheKeyCount: DWORD;
Keys: ShortString;
StartStopKey: ShortString;
end;
{A pointer type to the hook record}
PHookRec = ^THookRec;
记录在公共部分的应用程序主窗体中实例化为:
lpHookRec: PHookRec;
访问记录并执行PostMessage() 的代码都导致访问冲突:
procedure TIDEEditor.tmKeysTimer(Sender: TObject);
begin
if (Trim(KeyStart) <> '')
and (KeyStart+',' = lpHookRec^.StartStopKey) then
begin
lpHookRec^.TheKeyCount := 0;
lpHookRec^.Keys := '';
lpHookRec^.StartStopKey := '';
Postmessage(self.handle, wm_user + 912, 789, 0);
end
else
if (Trim(KeyStop) <> '')
and (KeyStop+',' = lpHookRec^.StartStopKey) then
begin
lpHookRec^.TheKeyCount := 0;
lpHookRec^.Keys := '';
lpHookRec^.StartStopKey := '';
Postmessage(self.handle, wm_user + 913, 789, 0);
end
else
if (Trim(KeyStop) <> '')
and (KeyStop+',' = lpHookRec^.StartStopKey) then
begin
lpHookRec^.TheKeyCount := 0;
lpHookRec^.Keys := '';
lpHookRec^.StartStopKey := '';
Postmessage(self.handle, wm_user + 914, 789, 0);
end;
end;
提醒,所有这些代码都适用于 32 位版本的应用程序。无需修改。但是,当我构建 64 位版本的应用程序时,我得到了访问记录和 PostMessage() 的所有代码行的运行时错误访问冲突。
我已通过 Google 搜索了有关从 32 位到 64 位的指针更改的任何信息。而且我的发现似乎对我遇到的运行时错误没有任何帮助。
至于导致访问冲突的 WinAPI PostMessage()。我对此没有做太多研究。
所以,任何有关访问记录和PostMessage() 的帮助对我来说都是很大的帮助。
编辑:2019 年 9 月 13 日
为了进一步详细说明,当我构建 64 位版本的程序时,我还构建了一个新的 64 位版本的 dll。我只使用 64 位 dll 和 64 位程序。至于缺少的代码,我很抱歉。除了下面的代码之外,这些记录中没有其他方法或代码。 TGlobal 记录和全局指针在 memlocs 单元中定义,如前面在该单元的代码中所示。并且在uses接口uses子句中增加了memlocs单元。
在表单的 OnCreate 事件期间调用 OpenSharedData 方法。在表单的 OnDestroy 事件期间调用 CloseSharedData。
主应用中的剩余代码:
TIDEEditor = class(TForm)
{snip}
private
{snip}
// For the hooking of another process
hHookLib: THANDLE; {A handle to the hook dll}
GetHookRecPointer: TGetHookRecPointer; {Function pointer}
StartKeyBoardHook: TStartKeyBoardHook; {Function pointer}
StopKeyBoardHook: TStopKeyBoardHook; {Function pointer}
// Divide's constants
FKeyStart: string;
FKeyPause: string;
FKeyStop: string;
FMouseKey: string;
FKeyAC: boolean;
FKeyGlobal: Boolean;
{snip}
// for the hooking of another process
procedure CloseSharedData;
procedure OpenSharedData(sValue: String = '');
procedure StartHook;
procedure StopHook;
procedure ProcessStartKey(var Message: TMessage); message WM_USER + 912;
procedure ProcessStopKey(var Message: TMessage); message WM_USER + 913;
procedure ProcessMouseKey(var Message: TMessage); message WM_USER + 914;
protected
public
{ Public declarations }
{ snip }
lpHookRec: PHookRec; {A pointer to the hook record}
property KeyStart: string read FKeyStart write FKeyStart;
property KeyPause: string read FKeyPause write FKeyPause;
property KeyStop: string read FKeyStop write FKeyStop;
property MouseKey: string read FMouseKey write FMouseKey;
property KeyAC: boolean read FKeyAC write FKeyAC;
property KeyGlobal: Boolean read FKeyGlobal write FKeyGlobal;
{snip}
end;
procedure TIDEEditor.OpenSharedData(sValue: string = '');
var
iX: Integer;
iSize: Int64;
begin
iSize := SizeOf(TGlobal);
if sValue = '' then
MapHandle := CreateFileMapping($FFFFFFFF, nil, PAGE_READWRITE,
0, iSize, PChar(GetMMFile))
else
MapHandle := CreateFileMapping($FFFFFFFF, nil, PAGE_READWRITE,
0, iSize, PChar(sValue));
iX := GetLastError;
if MapHandle = 0 then
Exit;
Global := MapViewOfFile(MapHandle, FILE_MAP_ALL_ACCESS, 0, 0, iSize);
if Global = nil then
begin
CloseHandle(MapHandle);
MapHandle := 0;
Exit;
end;
if iX = ERROR_ALREADY_EXISTS then
begin
if Global.InstanceCount = 912 then
begin
UnmapViewOfFile(Global);
CloseHandle(MapHandle);
pnlNoDecal.Visible := True;
OpenSharedData('Divide' + IntToStr(TimeGetTime));
end
else
begin
Global.InstanceCount := 912;
StartHook;
end;
end
else
begin
Global.InstanceCount := 912;
vStartHook;
end;
end;
procedure TIDEEditor.CloseSharedData;
begin
if MapHandle <> 0 then
begin
StopHook;
Global.InstanceCount := Global.InstanceCount - 1;
UnmapViewOfFile(Global);
CloseHandle(MapHandle);
end;
end;
procedure TIDEEditor.StartHook;
begin
lpHookRec := NIL;
LibLoadSuccess := FALSE;
@GetHookRecPointer := NIL;
@StartKeyBoardHook := NIL;
@StopKeyBoardHook := NIL;
hHookLib := LoadLibrary('DivideHook.dll');
if hHookLib = 0 then
Exit;
@GetHookRecPointer := GetProcAddress(hHookLib, 'GETHOOKRECPOINTER');
@StartKeyBoardHook := GetProcAddress(hHookLib, 'STARTKEYBOARDHOOK');
@StopKeyBoardHook := GetProcAddress(hHookLib, 'STOPKEYBOARDHOOK');
if (@GetHookRecPointer = NIL)
or (@StartKeyBoardHook = NIL)
or (@StopKeyBoardHook = NIL) then
begin
FreeLibrary(hHookLib);
hHookLib := 0;
@GetHookRecPointer := NIL;
@StartKeyBoardHook := NIL;
@StopKeyBoardHook := NIL;
end
else
begin
LibLoadSuccess := True;
lpHookRec := GetHookRecPointer;
if (lpHookRec <> nil) then
begin
lpHookRec^.TheHookHandle := 0;
lpHookRec^.TheKeyCount := 0;
lpHookRec^.Keys := '';
StartKeyBoardHook;
end;
end;
end;
procedure TIDEEditor.StopHook;
begin
if not LibLoadSuccess then
Exit;
if (lpHookRec = nil) then
Exit;
if (lpHookRec^.TheHookHandle <> 0) then
StopKeyBoardHook;
FreeLibrary(hHookLib);
@GetHookRecPointer := NIL;
@StartKeyBoardHook := NIL;
@StopKeyBoardHook := NIL;
end;
procedure TIDEEditor.ProcessStartKey(var Message: TMessage);
var
s: String;
AValid: Boolean;
ARunning: Boolean;
APaused: Boolean;
begin
AValid := IDEEngine1.ActiveScript <> nil;
ARunning := AValid and IDEEngine1.Scripter.Running;
APaused := AValid and IDEEngine1.Scripter.Paused;
if Message.WParam = 789 then
if not KeyGlobal then
Exit
else
if not KeyAC then
Exit;
lpHookRec^.TheKeyCount := 0;
lpHookRec^.Keys := '';
if ARunning and not APaused then
acPauseExecute(nil)
else
acRunExecute(nil);
end;
procedure TIDEEditor.ProcessStopKey(var Message: TMessage);
var
AValid: Boolean;
ARunning: Boolean;
begin
AValid := IDEEngine1.ActiveScript <> nil;
ARunning := AValid and IDEEngine1.Scripter.Running;
lpHookRec^.TheKeyCount := 0;
lpHookRec^.Keys := '';
if ARunning then
acResetExecute(nil);
end;
procedure TIDEEditor.ProcessMouseKey(var Message: TMessage);
var
AValid: Boolean;
ARunning: Boolean;
begin
AValid := IDEEngine1.ActiveScript <> nil;
ARunning := AValid and IDEEngine1.Scripter.Running;
lpHookRec^.TheKeyCount := 0;
lpHookRec^.Keys := '';
if not ARunning then
acQuickMousePosExecute(nil);
end;
dll的代码:
library DivideHook;
uses
System.SysUtils,
System.Classes,
Windows, Winapi.Messages;
{$R *.res}
{Define a record for recording and passing information process wide}
type
PHookRec = ^THookRec;
THookRec = record
TheHookHandle: HHook;
TheAppWinHandle: HWND;
TheCtrlWinHandle: HWND;
TheKeyCount: DWORD;
Keys: ShortString;
StartStopKey: ShortString;
end;
var
hObjHandle: THandle; {Variable for the file mapping object}
lpHookRec: PHookRec; {Pointer to our hook record}
procedure MapFIleMemory(dwAllocSize: DWORD);
begin
{Create a process wide memory mapped variable}
hObjHandle := CreateFileMapping($FFFFFFFF, nil, PAGE_READWRITE, 0, dwAllocSize, 'DivideHookRecMemBlock');
if (hObjHandle = 0) then
begin
MessageBox(0, 'Divide Hook DLL', 'Could not create file map object', MB_OK);
exit;
end;
{Get a pointer to our process wide memory mapped variable}
lpHookRec := MapViewOfFile(hObjHandle, FILE_MAP_WRITE, 0, 0, dwAllocSize);
if (lpHookRec = nil) then
begin
CloseHandle(hObjHandle);
MessageBox(0, 'Divice Hook DLL', 'Could not map file', MB_OK);
exit;
end;
end;
procedure UnMapFileMemory;
begin
{Delete our process wide memory mapped variable}
if (lpHookRec <> nil) then
begin
UnmapViewOfFile(lpHookRec);
lpHookRec := nil;
end;
if (hObjHandle > 0) then
begin
CloseHandle(hObjHandle);
hObjHandle := 0;
end;
end;
function GetHookRecPointer: pointer stdcall;
begin
{Return a pointer to our process wide memory mapped variable}
result := lpHookRec;
end;
{The function that actually processes the keystrokes for our hook}
function KeyBoardProc(Code: integer; wParam: integer; lParam: integer): integer; stdcall;
var
KeyUp: bool;
IsAltPressed: bool;
IsCtrlPressed: bool;
IsShiftPressed: bool;
s: string;
begin
result := 0;
case Code of
HC_ACTION:
begin
{We trap the keystrokes here}
{Is this a key up message?}
KeyUp := ((lParam AND (1 shl 31)) <> 0);
{Is the Alt key pressed}
IsAltPressed := ((lParam AND (1 shl 29)) <> 0);
{Is the Control key pressed}
IsCtrlPressed := ((GetKeyState(VK_CONTROL) AND (1 shl 15)) <> 0);
{if the Shift key pressed}
IsShiftPressed := ((GetKeyState(VK_SHIFT) AND (1 shl 15)) <> 0);
{If KeyUp then increment the key count}
if (KeyUp <> FALSE) then
begin
if (wParam < VK_SHIFT) or (wParam > VK_MENU) then
begin
Inc(lpHookRec^.TheKeyCount);
s := '';
if IsAltPressed then
s := s + '@';
if IsCtrlPressed then
s := s + '^';
if IsShiftPressed then
s := s + '~';
s := s + FormatFloat('000', wParam) + ',';
if Length(lpHookRec^.Keys) > 200 then
begin
lpHookRec^.Keys := Copy(lpHookRec^.Keys,
Pos(',', lpHookRec^.Keys) + 1, Length(lpHookRec^.Keys));
end;
lpHookRec^.Keys := lpHookRec^.Keys + s;
lpHookRec^.StartStopKey := s;
end;
end;
result := 0;
end;
HC_NOREMOVE:
begin
{This is a keystroke message, but the keystroke message}
{has not been removed from the message queue, since an}
{application has called PeekMessage() specifying PM_NOREMOVE}
result := 0;
exit;
end;
end; {case code}
if (Code < 0) then
{Call the next hook in the hook chain}
result := CallNextHookEx(lpHookRec^.TheHookHandle, Code, wParam, lParam);
end;
procedure StartKeyBoardHook; stdcall;
begin
{If we have a process wide memory variable}
{and the hook has not already been set...}
if ((lpHookRec <> NIL) AND (lpHookRec^.TheHookHandle = 0)) then
begin
{Set the hook and remember our hook handle}
lpHookRec^.TheHookHandle := SetWindowsHookEx(WH_KEYBOARD, @KeyBoardProc, hInstance, 0);
end;
end;
procedure StopKeyBoardHook; stdcall;
begin
{If we have a process wide memory variable}
{and the hook has already been set...}
if ((lpHookRec <> NIL) AND (lpHookRec^.TheHookHandle <> 0)) then
begin
{Remove our hook and clear our hook handle}
if (UnHookWindowsHookEx(lpHookRec^.TheHookHandle) <> FALSE) then
begin
lpHookRec^.TheHookHandle := 0;
end;
end;
end;
procedure DllEntryPoint(dwReason : DWORD);
begin
case dwReason of
Dll_Process_Attach :
begin
{If we are getting mapped into a process, then get}
{a pointer to our process wide memory mapped variable}
hObjHandle := 0;
lpHookRec := NIL;
MapFileMemory(sizeof(lpHookRec^));
end;
Dll_Process_Detach :
begin
{If we are getting unmapped from a process then, remove}
{the pointer to our process wide memory mapped variable}
UnMapFileMemory;
end;
end;
end;
exports
KeyBoardProc name 'KEYBOARDPROC',
GetHookRecPointer name 'GETHOOKRECPOINTER',
StartKeyBoardHook name 'STARTKEYBOARDHOOK',
StopKeyBoardHook name 'STOPKEYBOARDHOOK';
begin
{Set our Dll's main entry point}
DLLProc := @DllEntryPoint;
{Call our Dll's main entry point}
DllEntryPoint(Dll_Process_Attach);
end.
【问题讨论】:
-
您没有显示实际分配
Global和lpHookRec的代码。请这样做。它们只是指针,它们实际上指向的是什么?这将是你问题的根源。 -
调试也会有所帮助。确定哪个指针无效。
-
您不应该构建 64 位版本的 DLL 吗?
-
基于您仅在 64 位应用程序中获得 AV 的事实,我怀疑您正在尝试在 64 位应用程序中使用 32 位版本的 DLL,这将不起作用。 64 位应用程序需要 64 位 DLL。
-
我不相信可以在 64 位应用程序中使用 32 位 .DLL。但可能是 64 位 .DLL 的接口是一个整数(在 64 位程序中仍然是 32 位 - 至少在 Windows 上),然后将其转换为指针。如果你想编程多位大小,你应该声明这样的整数“NativeInt”(它随着目标 CPU 大小而变化 - 至少在 Windows 上)。消息也有同样的问题,如果那是运输的话。从 64 位到 32 位程序,参数会被截断到 32 位,从 32 位到 64 位,一个是零扩展,另一个是符号扩展。
标签: delphi 64-bit delphi-xe7