ماذا علي أن أفعل لكي أجعل WH_SHELL أو WH_CBT ربط إجراءات تلقي الأحداث من الأخرى ؟

StackOverflow https://stackoverflow.com/questions/299370

  •  08-07-2019
  •  | 
  •  

سؤال

أحاول استخدام 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 فإنه لا يزال يظهر نفس النتيجة.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top