출처 : Application Hacking

::SetForegroundWindow(), ::SetFocus()아 같은 API를 호출해서 프로그램 상에서 간단하게 상위 윈도우를 변경하거나 입력 포커스를 지정할 수 있지만, 이는 사용자가 마우스로 윈도우를 선택하거나 Alt+TAB, Alt+ESC를 입력해서 상위 윈도우를 지정하는 경우와는 다르게 동작한다.
::SetForegroundWindow(), ::SetFocus()등의 API를 호출하는 스레드가 현재 입력을 처리하고 있는 상위 스레드가 아니라면, API에 의해서 설정된 윈도우는 작업 표시줄을 통해서 활성화 상태만 표시되고 사용자의 입력은 여전히 이전에 입력을 처리하던 윈도우로 전달된다.

하지만 ::AttachThreadInput() API를 사용해서 현재 입력을 처리하고 있는 스레드와 입력 상태를 공유할 수 있다. 이를 이용해서 사용자의 입력을 처리하는 스레드가 아니더라도 사용자의 입력 윈도우를 강제적으로 변경할 수 있다.

BOOL __SetForegroundWindow(const HWND hWndTarget)
{
ASSERT(hWndTarget != NULL);

BOOL bRet = FALSE;
if (::IsWindow(hWndTarget) == FALSE)
return bRet;

BOOL bThreadAttached = FALSE;
DWORD dwSourceTID = 0, dwSourcePID = 0;
DWORD dwTargetTID = 0, dwTargetPID = 0;

__try
{
HWND hwndForeground = ::GetForegroundWindow();
if (hwndForeground == NULL)
__leave;

// 입력 윈도우를 소유한 스레드를 구한다.
dwSourceTID = ::GetWindowThreadProcessId(hwndForeground, &dwSourcePID);
{
if (::GetCurrentThreadId() == dwSourceTID)
__leave;
}

// 새롭게 입력을 받을 윈도우를 소유한 스레드를 구한다.
dwTargetTID = ::GetWindowThreadProcessId(hWndTarget, &dwTargetPID);

bThreadAttached = ::AttachThreadInput(dwTargetTID, dwSourceTID, TRUE);

if (bThreadAttached == FALSE)
__leave;
}
__finally
{
bRet = ::SetForegroundWindow(hWndTarget);
if (bThreadAttached == TRUE)
{
if (::AttachThreadInput(dwTargetTID, dwSourceTID, FALSE))
{
::OutputDebugString(_T("detach fail"));
}
}
}

return bRet;
}BOOL __SetForegroundWindow(const HWND hWndTarget)
{
ASSERT(hWndTarget != NULL);

BOOL bRet = FALSE;
if (::IsWindow(hWndTarget) == FALSE)
return bRet;

BOOL bThreadAttached = FALSE;
DWORD dwSourceTID = 0, dwSourcePID = 0;
DWORD dwTargetTID = 0, dwTargetPID = 0;

__try
{
HWND hwndForeground = ::GetForegroundWindow();
if (hwndForeground == NULL)
__leave;

// 입력 윈도우를 소유한 스레드를 구한다.
dwSourceTID = ::GetWindowThreadProcessId(hwndForeground, &dwSourcePID);
{
if (::GetCurrentThreadId() == dwSourceTID)
__leave;
}

// 새롭게 입력을 받을 윈도우를 소유한 스레드를 구한다.
dwTargetTID = ::GetWindowThreadProcessId(hWndTarget, &dwTargetPID);

bThreadAttached = ::AttachThreadInput(dwTargetTID, dwSourceTID, TRUE);

if (bThreadAttached == FALSE)
__leave;
}
__finally
{
bRet = ::SetForegroundWindow(hWndTarget);
if (bThreadAttached == TRUE)
{
if (::AttachThreadInput(dwTargetTID, dwSourceTID, FALSE))
{
::OutputDebugString(_T("detach fail"));
}
}
}

return bRet;
}

Related Posts

답글 남기기

이메일 주소는 공개되지 않습니다.