ماذا علي أن أفعل لكي أجعل WH_SHELL أو WH_CBT ربط إجراءات تلقي الأحداث من الأخرى ؟
سؤال
أحاول استخدام SetWindowsHookEx
إعداد WH_SHELL
ربط الحصول على إخطار من نطاق المنظومة HSHELL_WINDOWCREATED
و HSHELL_WINDOWDESTROYED
أحداث.مررت 0 النهائي dwThreadId
الحجة التي ، وفقا مستندات, أن "ربط ربط الإجراء مع كل المواضيع الحالية التي تعمل في نفس سطح المكتب كما في موضوع الدعوة".كما تمر في التعامل مع بلدي DLL (HInstance
في دلفي) عن hMod
المعلمة وكذلك جميع الأمثلة نظرت.
حتى الآن أنا فقط من أي وقت مضى الحصول على إخطار من ويندوز إنشاؤه من قبل بلدي التطبيق - في أكثر الأحيان - بلدي التجارب النتيجة في سطح المكتب العملية في النيران بمجرد إغلاق التطبيق الخاص بي.قبل أن تسأل, أنا لا UnhookWindowsHookEx
.كما ندعو دائما CallNextHookEx
من داخل معالج.
أنا أدير عملي اختبار التطبيق من حساب مستخدم محدود ولكن حتى الآن لم أجد أي تلميحات تشير إلى أن هذا من شأنه أن تلعب دورا...(على الرغم من أن الواقع يدهشني)
AFAICT, لقد فعلت كل شيء من الكتاب (من الواضح أنني لم ولكن حتى الآن لا أستطيع أن أرى أين).
أنا باستخدام دلفي (2007) ولكن هذا لا يهم حقا ما أعتقد.
تحرير: ربما ينبغي لي أن ذكرت هذا من قبل:لم تحميل ومحاولة بعض الأمثلة (على الرغم من أن هناك للأسف أن العديد من المتاحة دلفي - خاصة لا WH_SHELL
أو WH_CBT
).في حين أنها لا تحطم النظام مثل اختبار التطبيق لا, أنها لا تزال التقاط الأحداث من العمليات الأخرى (على الرغم من أنني يمكن أن تحقق مع ProcessExplorer أن تحصل على تحميلها فيها حسنا).لذلك يبدو أن هناك أي شيء خاطئ مع نظام التكوين أو أمثلة خاطئة أو أنها ببساطة لا يمكن التقاط الأحداث من العمليات الأخرى.يمكن لأي شخص أن ينير لي ؟
EDIT2: حسنا هنا هو مصدر بلدي اختبار المشروع.
DLL التي تحتوي على هوك الإجراء:
library HookHelper;
uses
Windows;
{$R *.res}
type
THookCallback = procedure(ACode, AWParam, ALParam: Integer); stdcall;
var
WndHookCallback: THookCallback;
Hook: HHook;
function HookProc(ACode, AWParam, ALParam: Integer): Integer; stdcall;
begin
Result := CallNextHookEx(Hook, ACode, AWParam, ALParam);
if ACode < 0 then Exit;
try
if Assigned(WndHookCallback)
// and (ACode in [HSHELL_WINDOWCREATED, HSHELL_WINDOWDESTROYED]) then
and (ACode in [HCBT_CREATEWND, HCBT_DESTROYWND]) then
WndHookCallback(ACode, AWParam, ALParam);
except
// plop!
end;
end;
procedure InitHook(ACallback: THookCallback); register;
begin
// Hook := SetWindowsHookEx(WH_SHELL, @HookProc, HInstance, 0);
Hook := SetWindowsHookEx(WH_CBT, @HookProc, HInstance, 0);
if Hook = 0 then
begin
// ShowMessage(SysErrorMessage(GetLastError));
end
else
begin
WndHookCallback := ACallback;
end;
end;
procedure UninitHook; register;
begin
if Hook <> 0 then
UnhookWindowsHookEx(Hook);
WndHookCallback := nil;
end;
exports
InitHook,
UninitHook;
begin
end.
و النموذج الرئيسي من التطبيق باستخدام هوك:
unit MainFo;
interface
uses
Windows, SysUtils, Forms, Dialogs, Classes, Controls, Buttons, StdCtrls;
type
THookTest_Fo = class(TForm)
Hook_Btn: TSpeedButton;
Output_Lbx: TListBox;
Test_Btn: TButton;
procedure Hook_BtnClick(Sender: TObject);
procedure Test_BtnClick(Sender: TObject);
public
destructor Destroy; override;
end;
var
HookTest_Fo: THookTest_Fo;
implementation
{$R *.dfm}
type
THookCallback = procedure(ACode, AWParam, ALParam: Integer); stdcall;
procedure InitHook(const ACallback: THookCallback); register; external 'HookHelper.dll';
procedure UninitHook; register; external 'HookHelper.dll';
procedure HookCallback(ACode, AWParam, ALParam: Integer); stdcall;
begin
if Assigned(HookTest_Fo) then
case ACode of
// HSHELL_WINDOWCREATED:
HCBT_CREATEWND:
HookTest_Fo.Output_Lbx.Items.Add('created handle #' + IntToStr(AWParam));
// HSHELL_WINDOWDESTROYED:
HCBT_DESTROYWND:
HookTest_Fo.Output_Lbx.Items.Add('destroyed handle #' + IntToStr(AWParam));
else
HookTest_Fo.Output_Lbx.Items.Add(Format('code: %d, WParam: $%x, LParam: $%x', [ACode, AWParam, ALParam]));
end;
end;
procedure THookTest_Fo.Test_BtnClick(Sender: TObject);
begin
ShowMessage('Boo!');
end;
destructor THookTest_Fo.Destroy;
begin
UninitHook; // just to make sure
inherited;
end;
procedure THookTest_Fo.Hook_BtnClick(Sender: TObject);
begin
if Hook_Btn.Down then
InitHook(HookCallback)
else
UninitHook;
end;
end.
المحلول
المشكلة هي أن هوك الخاص بك DLL هو في الواقع يجري تحميلها في عدة مساحات العناوين.أي وقت Windows بالكشف عن الحدث في بعض الأجنبي العملية التي يجب معالجتها من قبل هوك الخاص بك, تحميل هوك DLL في تلك العملية (إذا لم تكن بالفعل تحميل, بالطبع).
بيد أن كل عملية لها مساحة العنوان.وهذا يعني أن وظيفة رد الاتصال المؤشر التي مرت في InitHook() معنى فقط في سياق EXE الخاص بك (وهذا هو السبب في أنه يعمل على الأحداث في التطبيق الخاص بك).في أي عملية أخرى هذا المؤشر القمامة;قد تشير إلى موقع ذاكرة غير صالحة أو (أسوأ) في بعض عشوائي رمز القسم.والنتيجة يمكن أن تكون إما إلى انتهاك حقوق الوصول أو الصمت تلف في الذاكرة.
عموما الحل هو استخدام نوع من interprocess (IPC) صحيح يخطر EXE الخاص بك.الأكثر مؤلم على الحالة سيكون آخر رسالة والالزام المعلومات المطلوبة (الحدث HWND) في WPARAM/LPARAM.يمكنك إما استخدام WM_APP+n أو إنشاء واحدة مع RegisterWindowMessage().تأكد من أن الرسالة نشرت و لا بعث ، لتجنب أي أزمات.
نصائح أخرى
قد تكون هذه العالي على سؤالك, ولكن كما ترونه, السنانير جدا من الصعب الحصول على الحق - إذا كان يمكنك تجنب استخدام هذا بأي وسيلة ، أن تفعل ذلك.أنت ذاهب إلى تشغيل في جميع أنواع المشاكل معهم ، وخاصة على ويندوز فيستا حيث سيكون لديك للتعامل مع UIPI.
فقط لتوضيح شيء "efotinis" ذكر عن نشر الرسائل إلى عملية wParam و lParam التي تنشرها الرئيسي العملية لا يمكن أن تكون مؤشرات يمكن أن يكون مجرد "أرقام".
على سبيل المثال, دعونا نقول لكم ربط WM_WINDOWPOSCHANGING رسالة windows يمر عليك مؤشر إلى WINDOWPOS في lparam.لا يمكنك فقط بعد أن lparam إلى عملية الرئيسي لأن الذاكرة lparam يشير إلى سارية فقط في العملية التي يتلقى الرسالة.
هذا هو ما "efotinis" يعني عندما قال: "يحشر المعلومات المطلوبة (الحدث HWND) في WPARAM/LPARAM".إذا كنت ترغب في تمرير أكثر تعقيدا رسائل الخاص بك سوف تحتاج إلى استخدام بعض IPC (مثل أنابيب الاتصال المسماة TCP أو تعيين الذاكرة الملفات).
لول, يبدو أن الخطأ هو في اختبار التعليمات البرمجية.
إذا قمت بإنشاء اثنين من أزرار منفصلة ، واحدة Init واحد UnInit (أنا أفضل الخروج).
procedure THooktest_FO.UnInitClick(Sender: TObject);
begin
UninitHook;
end;
procedure THooktest_FO.InitClick(Sender: TObject);
begin
InitHook(HookCallback)
end;
بدء تشغيل التطبيق.انقر على الحرف الأول ثم اختبار زر الإخراج التالي هو مبين:
created handle #1902442
destroyed handle #1902442
created handle #1967978
created handle #7276488
ثم messagebox هو مبين.
إذا كنت انقر فوق موافق يمكنك الحصول على:
destroyed handle #1967978
HTH
لقد وجدت دلفي قاعدة وثائق SetWindowsHookEx.ولكن النص غامضة بعض الشيء.
function SetWindowsHookEx(idHook: Integer; lpfn: TFNHookProc;
hmod: HInst; dwThreadId: DWORD): HHOOK;
hmod:مؤشر إلى وحدة (DLL) التي تحتوي على هوك وظيفة أشار إليه lpfn المعلمة.يجب تعيين هذه المعلمة إلى الصفر إذا dwThreadId يحدد الصفحات التي تم إنشاؤها بواسطة عملية الحالي وهو dlpfn نقاط ربط وظيفة تقع في قانون المرتبطة العملية الحالية.
dwThreadId:معرف مؤشر الترابط الذي يتم تثبيت ربط وظيفة سوف تكون مرتبطة.إذا تم تعيين هذه المعلمة إلى الصفر ، في هوك سوف يكون ربط مستوى النظام الذي يرتبط مع جميع المواضيع الحالية.
بالمناسبة ، hmod المعلمة يجب أن تستخدم وحدة التعامل معها.(HINSTANCE يشير إلى تطبيق مقبض).
hand := GetModuleHandle('hookhelper.dll');
Hook := SetWindowsHookEx(WH_SHELL, @HookProc, hand, 0);
ولكن على الرغم من أن اليد يختلف من HINSTANCE فإنه لا يزال يظهر نفس النتيجة.