구글


Window Hook ... 이렇게 한번 해 볼까??? ▶SW_Coding

이전에.. Window Vista에서 PC의 기본 사운드 장치를 프로그램으로 변경 하기 위한 방법을 찾다가
window hook 을 사용하게 되었습니다..
레퍼런스도 많이 없는 터라.. 고생을 많이 했는데요..혹시 도움이 될까 해서 올립니다..

먼저 Hook을 하기 위해 먼저 생각해야하는 사항입니다.
1. 가급적 Hook을 담당하는 별도의 Win32 Dynamic-Link Library 로 만든.. dll을 만들도록 하세요.. 그래야만..
좀더 시스템에 독립적으로 Hook을 할수 있답니다..

2. 아래와 같이 공유 데이터를 하나 만들어야 합니다..  왜냐면.. 다른 Window영역과 자신의 영역에서 서로 사용할수 있는 최소한의 정보가 필요하기 때문입니다.

#pragma data_seg (".shared")
    int  g_nSubclassed = 0; // START button subclassed?
    UINT WM_HOOKEX  = 0;
    HWND g_hWnd   = 0;  // handle of START button
    HWND g_hWndTarget = 0;
    HHOOK g_hHook   = 0;
    HINSTANCE hDll  = 0;
    WNDPROC OldProc   = 0; 
#pragma data_seg ()

#pragma comment(linker,"/SECTION:.shared,RWS")

3. 다른 Window 영역을 Hooking을 할수 있게 되었다면 자기가 하고 싶은 일들을 할수 있도록 콜백 Proc을 하나 만들어야 겠지요?
  
    LRESULT CALLBACK NewProc( HWND,UINT,WPARAM,LPARAM );

이 세가지를 생각 하셨다면..
이제 어떻게..Hooking을 하는지 알아 보도록 하죠..

먼저..  SetWindowsHookEx을 통해 Hooking 할 window를 하나 등록 합니다..

HHOOK SetWindowsHookEx(
  int
idHook,        // type of hook to install
  HOOKPROC lpfn,     // address of hook procedure
  HINSTANCE hMod,    // handle to application instance
  DWORD dwThreadId   // identity of thread to install hook for
);

여기서 중요한건 .. 4번째 dwThreadid 입니다.. 여기서 많은 분들이.. 힘들어 하는것 같더라구요..
절대..자신의 Threadid를 넣으면 안됩니다..  hooking 하고자 하는 윈도우 핸들을 넣어야 겠지요...
g_hHook =
SetWindowsHookEx( WH_CALLWNDPROC,(HOOKPROC)HookProc, hDll, GetWindowThreadProcessId(hWnd,NULL) );


여기 GetWindowThreadProcessId 에서  hWnd는 Hooking 하고자하는 Window핸들입니다.. 왜 NULL을 써야 할까요?
한번 찾아 보세요.. 그럼 아실겁니다..
그리고 WH_CALLWNDPROC  이 파라미터에 대해서도 찾아 보세요.. 이것두 중요하거든요..

이렇게 등록 하고 나면..

LRESULT HookProc ( int code, WPARAM wParam, LPARAM lParam )
{
     CWPSTRUCT * pMsg = ( CWPSTRUCT  *) lParam;
     long lProc;

     if (pMsg->hwnd == g_hWnd && g_nSubclassed == 0)
     {
          g_nSubclassed = 1;
  
          lProc = ::GetWindowLong( pMsg->hwnd, GWL_WNDPROC );
          if( lProc != ( long )NewProc )
          {
                // Hooking해서 뭘 할거니??  NewProc 봐봐 ...
               OldProc  = ( WNDPROC ) SetWindowLong( pMsg->hwnd , GWL_WNDPROC , (LONG) NewProc);
          }
      }
 
//  내가 Hook 하고 너한테 보낼께.. 

 return ::CallNextHookEx( g_hHook, code, wParam, lParam);
}

그럼.. NewProc는 어떤 작업을 할것인가? (참고로..전 Vista의 사운드 제어판에 있는 ListCtl을 Hooking 했습니다.)

LRESULT CALLBACK NewProc(
  HWND hwnd,      // handle to window
  UINT uMsg,      // message identifier
  WPARAM wParam,  // first message parameter
  LPARAM lParam   // second message parameter
)
{
     LVITEM lvi;
     char szBuffer[ MAX_BUFF_SIZE ];
     LRESULT result;
     COPYDATASTRUCT copydata;
     
    // 리스트 아이템 명을 을 달라고? (이 메세지는 물론 dll에서 요청 한거 겠지요?
    // 이렇게 (::SendMessage( hwnd ,WM_GETLISTITEMTEXT, (WPARAM)nPos, (LPARAM)0);

     if( uMsg == WM_GETLISTITEMTEXT ) 
     { 
          result = -2;
          memset(&lvi, 0, sizeof(LVITEM));
          memset( szBuffer, 0, sizeof( szBuffer ) );
          ZeroMemory( &copydata, sizeof(copydata));

          if( g_hWndTarget != NULL )
          {
               lvi.mask  = LVIF_TEXT;
               lvi.stateMask = LVIS_SELECTED ;
               lvi.iSubItem = 1;
               lvi.cchTextMax = sizeof(szBuffer);
               lvi.pszText  = szBuffer;   
               uMsg   = LVM_GETITEMTEXT;  
              // 실제 Window 핸들에게 요청하자...
               result   = ::CallWindowProc( OldProc,hwnd,uMsg,wParam, ( LPARAM )&lvi );
               // 값을 복사해서.. hook을 요청한 쪽으로 보내자..
               copydata.lpData = szBuffer;
               copydata.dwData = LVM_GETITEMTEXT;
               copydata.cbData = strlen(szBuffer);
               ::SendMessage( g_hWndTarget, WM_COPYDATA , (WPARAM)LVM_GETITEMTEXT, (LPARAM)&copydata);
             }
  
          return result;
     }
    // 리스트를 선택하라고 ?
   // 그럼 이렇게 요청했겠구만...::SendMessage( hwnd,WM_SETLISTCHEK, (WPARAM)nPos, (LPARAM)bCheck);
     else if ( uMsg ==  WM_SETLISTCHEK ) 
     {
          result = 0;
          if( g_hWndTarget != NULL )
          {
               memset(&lvi, 0, sizeof(LVITEM));   

               lvi.stateMask = LVIS_SELECTED;
               lvi.state  = LVIS_SELECTED;
   
               uMsg   = LVM_SETITEMSTATE;
                // 실제 windowd에게 요청해야지...
               result   = ::CallWindowProc( OldProc,hwnd,uMsg, wParam, ( LPARAM )&lvi ); 
              
                // 그리고 처리 결과를 보내고...
               copydata.lpData = szBuffer;
               copydata.dwData = WM_SETLISTCHEK;
               copydata.cbData = strlen(szBuffer);
               ::SendMessage( g_hWndTarget, WM_COPYDATA , (WPARAM)LVM_GETITEMTEXT, (LPARAM)&copydata);
   
          }

          return result;
     }
    // Hook 종료 가 왔네....
     else if (uMsg == WM_HOOKSTOP)
     {
        // 그럼.. Hook을 풀고.. 실제 window로 제어권을 넘기자...
          ::SetWindowLong( hwnd, GWL_WNDPROC , (LONG) OldProc);
          ::SendMessage( g_hWndTarget, WM_HOOKSTOP , 0, 0);
          return 0L; 
     }
     
     // 내가 필요하지 않는  메세지는 그냥 Pass !!!
      CallWindowProc( OldProc,hwnd,uMsg,wParam,lParam );
      return  -1;
}

이렇게 Hook을 설정 하고.. 처리를 하면 됩니다...
마지막으로 설정한 Hook을 풀어 줘야 겠지요?

::SendMessage( g_hWnd, WM_HOOKSTOP, 0, 0);를 보내..  Hook 해제 한다고 알려 실제 Window로 제어권을 넘기고...
::UnhookWindowsHookEx( g_hHook ); 으로 풀어 풀어 주시면 됩니다..

조금 복잡하고.. 지저분하지만..
나름 차근 차근 생각 해 보시면.. 이해가 가실겁니다..

물론 제가 잘못 설명을 드렸을수도 있구요...

암튼..도움이 되셨으면..합니다..




덧글

  • 2013/01/07 15:39 # 삭제 답글 비공개

    비공개 덧글입니다.
  • 토리 2013/01/07 17:11 # 답글

    하하..
    배움에 도는 없죠..무조건 부디쳐봐야 합니다... 용기에 박수 한번..
    사실 저도 이부분은 거의 4~5년 전에 구현한 거라 가물가물~ 합니다..

    일단.. XP / Window 2000 이전 버전에서는 레지스트리를 변경하는것으로 기본 사운드 장치를 변경 할수 있었습니다..
    밧트 Vista로 넘어 오면서 MS는 미디어를 담당하는 개발자들에게 엄청난 고통과 시련을 안겨 주었습니다..

    Vista가 처음 나왔을때는 API를 이용하여 사운드 제어 Dialog를 강제로 띄우고 Hooking을 통해 각각의 버튼과 Text를 제어하는것 말고는 없었습니다..지금은 모르겠지만...

    Hooking 프로그램은 위에 말씀드린것 처럼 Dynamic-Link Library 로 만들어야 합니다..
    소스는 Hooking dll 프로그램과 Hooking 프로그램을 사용하는 Window Application 2개가 있긴한데..저작권에 걸려 있어..저도 맘대로 드릴수는 없네요...

  • 무명인 2016/06/20 17:43 # 삭제 답글

    위 내용에 대한 소스를 공유해 줄 수 없습니까?
  • 무명인 2016/06/20 17:43 # 삭제 답글

    위 내용에 대한 소스를 공유해 줄 수 없습니까?
댓글 입력 영역