2009-08-28 2 views
1

현재 포커스가있는 창을 하위 클래스로 만들려고합니다. CBT 후크를 사용하여 HCBT_ACTIVATE 이벤트를 모니터링하고 초점을 맞추고 이전에 포커스를 맞춘 창의 WndProc을 설정 및 설정 해제합니다.이 창 서브 클래 싱 코드가 충돌하는 이유는 무엇입니까?

문제는 코드가 인 어딘가에 설정된 중단 점이있을 때만 작동한다는 것입니다.

중단 점이 없으면 서브 클래 싱을 제거하고 원래 WndProc를 복원 했어도 내 응용 프로그램이 종료되면 서브 클래 싱 된 모든 윈도우가 순서대로 충돌합니다.

내 응용 프로그램이 종료 될 때마다 Unsubclass()이 호출됨을 확인했습니다.

// code extracts 
HINSTANCE hInst; 
HHOOK hHook; 

#pragma data_seg(".shared") 
HWND hWndSubclass = 0; 
FARPROC lpfnOldWndProc = NULL; 
#pragma data_seg() 
#pragma comment(linker, "/section:.shared,rws") 

void Unsubclass() 
{ 
    // if the window still exists 
    if (hWndSubclass != 0 && IsWindow(hWndSubclass)) 
    { 
     SetWindowLongPtr(hWndSubclass, GWLP_WNDPROC, (LPARAM)lpfnOldWndProc); 
     hWndSubclass = 0; 
    } 
} 

static LRESULT CALLBACK SubClassFunc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 
{ 
    if (message == WM_MOVING) 
    { 
     // this is just test code so I can see it works (it does) 
     RECT* r = (RECT*)lParam; 
     r->right = r->left + 500; 
     r->bottom = r->top + 500; 
     return TRUE; 
    } 
    else if (message == WM_DESTROY) 
    { 
     Unsubclass(); 
    } 
    return CallWindowProc((WNDPROC)lpfnOldWndProc, hWndSubclass, message, wParam, lParam); 
} 

void SubclassWindow(HWND hWnd) 
{ 
    // remove the subclassing for the old window 
    Unsubclass(); 
    // subclass the new window 
    lpfnOldWndProc = (FARPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LPARAM)SubClassFunc); 
    hWndSubclass = hWnd; 
} 

static LRESULT CALLBACK CBTProc(int nCode, WPARAM wParam, LPARAM lParam) 
{ 
    if (nCode == HCBT_ACTIVATE) 
    { 
     SubclassWindow((HWND)wParam); 
    } 
    return 0; 
} 

// ... code that initializes the CBT proc 
__declspec(dllexport) BOOL Setup() 
{ 
    hHook = SetWindowsHookEx(WH_CBT, CBTProc, hInst, 0); 
} 

__declspec(dllexport) BOOL Teardown() 
{ 
    UnhookWindowsHookEx(hHook); 
    Unsubclass(); 
} 

BOOL APIENTRY DllMain(HINSTANCE hInstance, 
         DWORD Reason, 
         LPVOID Reserved 
        ) 
{ 
    switch(Reason) 
    { 
     case DLL_PROCESS_ATTACH: 
      hInst = hInstance; 
      return TRUE; 
     case DLL_PROCESS_DETACH: 
      Unsubclass(); 
      return TRUE; 
    } 
    return TRUE; 
} 
+0

hWndServer는 누구입니까? – EFraim

+0

hWndServer는 관련이 없습니다 (CBT 후크를 설정하는 프로세스입니다) . 내가 그것에 대한 참조를 제거했습니다. –

+0

최종 Unsubclass 후 훅이 여전히 활성화되어 있고 종료하는 동안 여전히 윈도우를 서브 클래스화할 수 있습니까? –

답변

3

귀하의 문제는 여러 전선에서 경첩 :

  • UnHookWindowsHook 주입 DLL을 언로드하지 않습니다, 그것은하지 모든 후크 시저를 제거합니다. dll을 일종의 언로드 메커니즘을 개발하기 위해 언로드해야하는 경우.
  • 일반적으로 SetWindowLongPtr은 창을 소유하는 프로세스 이외의 프로세스에서 호출 될 때 실패합니다.

이 결과로 Windows 후크를 안전하게 제거하기가 매우 어렵습니다. 우선 OldWindowProc 포인터를 공유 데이터 영역에 저장하면 안됩니다. 그런 다음 하위 클래스를 제거하려면 하위 클래 싱되지 않은 프로세스를 공동으로 수행하여 하위 클래스 화를 수행 할 수 있어야합니다.

할 수있는 일은 먼저 새로운 고유 메시지 ID를 등록하고 RegisterWindowMessage를 사용하여 공유 영역에 배치하는 것입니다. WM_REMOVE_HOOK.서브 클래스의 PROC에서 당신이 후크를 제거해야 할 때마다 이제

UINT idWM_REMOVE_HOOK = RegisterWindowMessage("WM_REMOVE_HOOK"); 

,

SendMessage(hWndSubClass,idWM_REMOVE_HOOK,0,0); 

:

if(uMsg == WM_DESTROY || uMsg == idWM_REMOVE_HOOK) 
{ 
    Unsubclass(hwnd); 
} 

는 DLL_PROCESS_DETATCH에서 UnSubClass에 대한 호출을 제거합니다. 그것의 위험한 경쟁 조건은 다른 프로세스에서 잠재적으로 유효한 후크의 후크 데이터를 휴지통으로 만들기 위해 임의의 프로세스에서 언로드되는 dll을 발생시킬 것입니다.

0

lpfnOldWndProc 및 hWndSubclass는 전역 포인터입니다. 프로세스 당 하나만있는 것처럼 보입니다. 프로세스가 하나 이상의 창을 생성하면 어떻게 될까요?

그런 다음 마지막 서브 클래스 만 unsubclass합니다.

EDIT : 또한 DETACH 프로세스를 왜 분해합니까?

+0

당신을 올바르게 이해한다면 그건 중요하지 않습니다. 한 번에 하나의 창에만 초점을 맞출 수 있습니다. 포커스가 새 윈도우로 바뀔 때마다 이전 서브 클래스가 제거되고 포커스가 설정된 윈도우가 포커스를받습니다 –

+0

프로세스는 다중 UI 스레드를 가질 수 있습니다. – EFraim

+0

@EFraim : 하나 이상의 UI 스레드가 있어야합니까? 한 번에 하나의 창 * 인스턴스 *를 하위 클래스로 만들뿐입니다. –

0

DLL에 전역 시스템 전체 후크를 만듭니다. HHOOK 핸들과 하위 클래스 정보를 공유 메모리 블록에 저장하여 실행중인 모든 프로세스의 DLL 인스턴스에 모두 액세스 할 수 있도록해야합니다. 변수는 코드에서 전역으로 선언되지만 DLL의 각 개별 인스턴스에는 자체 로컬 복사본이 있으므로 DLL 인스턴스 (Setup()을 호출하는 인스턴스) 중 하나에서 초기화되지 않습니다. 대신 전체 시스템에서 전체적으로 공유해야합니다.

또한 DLL_PROCESS_DETACH에서 TearDown()을 호출하면 안됩니다. DLL의 모든 인스턴스는 해당 프로세스가 종료 될 때 TearDown()을 호출하지만 실제로 Setup()을 호출 한 인스턴스는 Teardown()을 호출하는 인스턴스 여야합니다.

+0

+1 : 이것은 돈처럼 들립니다. 나는 "공유 메모리에 있지 않은 전역"문제에 물 렸습니다. – DarkSquid

+0

1. HHOOK 핸들은 공유 메모리에있을 필요가 없습니다. 핸들은 Setup() 및 Teardown()을 호출하는 프로세스에서만 필요합니다. 2. 당신 말이 맞아요, Teardown()은 프로세스 분리에서 호출해서는 안됩니다. 그러나 그것은 Unsubclass()합니다. 나는 이것을 지금 고칠 것이다. 3. 서브 클래 싱 정보를 공유 메모리에두면 아무런 변화가 없습니다. 이것이 실행 중일 때 나는 터치하는 모든 윈도우의 프로세스가 내 앱을 종료 할 때 여전히 충돌한다. (나는 이것을 아무런 쓸모없이 시도했지만 질문에 포함하는 것을 잊었다.) –

+1

@Vegard : 로컬 응용 프로그램 프로세스 내에서 후크 함수가 호출 될 때마다 DLL의 모든 인스턴스가 동일한 HHOOK를 CallNextHookEx()에 전달해야하므로 HHOOK를 공유해야합니다. SetWindowsHookEx()를 호출하는 동일한 DLL 인스턴스에서 특정 훅 유형 만 호출됩니다. 다른 후크 유형은 그렇지 않으므로 HHOOK를 공유해야합니다. –

0

디버거가 중단 점을 추가하여 프로세스를 성공시키는 원인이 될 가능성이 높습니다. 이는 타이밍 문제입니다. 서브 클래 싱 된 창에서 하위 클래스를 다시 제거하는 데 필요한 메시지를 받기 바로 전에 주 응용 프로그램이 자체를 닫고 리소스를 해제하고 있습니다. unhooking과 unsubclassing 사이에 자신의 메시지를 처리하기 위해 처리주기를 약간 줄 수 있습니다. (델파이에서는 Application.ProcessMessages를 호출하여 C++ 버전에서이 작업을 수행 할 수 있습니다.) 그 대답을 모른다.