본문 바로가기
코드

윈도 탐색기로 부터 정보 빼오기 VC++

by umaking 2008. 7. 2.

때로 Sotware 개발은 새로운 것들을 만들어낸다. 하지만 때로는 가지고 있는 것들을 조합하기도 한다. 오늘의 문제는 두번째 문제에 대한 것 중의 하나다.

윈도 핸들을 가지고 어떤게 윈도 탐색기인지 알아낼 수 있다, 그리고 만약 그렇다면 어떤 폴더를 보고있고, 어떤 항목이 focus되어있을까..?

이는 본래부터 어려운 작업이 아니다. 단순히 수 많은 작은 조각들을 껴 맞추기만 하면 된다.

열린 shell window들을 가져올 수 있는  ShellWindows object에서 부터 시작하자. 그러면 Item프 로퍼티를 가지고 이 모든 항목들을 열거할 수 있다. 이 ShellWindows 객체는 Javascript나 Visual Basic과 같은 스크립팅 언어가 사용하도록 고안되어 있기 때문에 C++에서 사용하기에는 쫌..... 거시기하다.


 IShellWindows *psw;
if (SUCCEEDED(CoCreateInstance(CLSID_ShellWindows, NULL, CLSCTX_ALL,
IID_IShellWindows, (void**)&psw))) {
VARIANT v;
V_VT(&v) = VT_I4;
IDispatch *pdisp;
BOOL fFound = FALSE;
for (V_I4(&v) = 0; !fFound && psw->Item(v, &pdisp) == S_OK;
V_I4(&v)++) {
...
pdisp->Release();
}
psw->Release();
}

각각의 item으로부터 우리는 item의 윈도 핸들을 가져올 수 있고, 그게 우리가 원하는 것이면 볼 수도 있다.

   IWebBrowserApp *pwba;
if (SUCCEEDED(pdisp->QueryInterface(IID_IWebBrowserApp, (void**)&pwba))) {
HWND hwndWBA;
if (SUCCEEDED(pwba->get_HWND((LONG_PTR*)&hwndWBA)) &&
hwndWBA == hwndFind) {
fFound = TRUE;
...
}
pwba->Release();
}

됐다. 이제 우리는 IWebBrowserApp 인터페이스를 통해 폴더를 알아냈으니 Top shell browser로 가야한다.  이 작업은 SID_STopLevelBrowsser 서비스를 쿼리하고 IShellBrowser인터페이스를 요청함으로 해결된다.

       IServiceProvider *psp;
if (SUCCEEDED(pwba->QueryInterface(IID_IServiceProvider, (void**)&psp))) {
IShellBrowser *psb;
if (SUCCEEDED(psp->QueryService(SID_STopLevelBrowser,
IID_IShellBrowser, (void**)&psb))) {
...
psb->Release();
}
psp->Release();
}

IShellBrowser로부터 우리는 QueryActiveShellView 매서드를 통해 현재 Shell View를 가져올 수 있다.

           IShellView *psv;
if (SUCCEEDED(psb->QueryActiveShellView(&psv))) {
...
psv->Release();
}

당연한 이야기지만, 우리가 원하는것은 모든 알짜배기 데이터 값들이 저장되어있는 자동화 객체인 IFolderView인터페이스이다.

             IFolderView *pfv;
if (SUCCEEDED(psv->QueryInterface(IID_IFolderView,
(void**)&pfv))) {
...
pfv->Release();
}

OK, 우리는 다 얻었다. 이제 당신은 구한 view로 부터 무엇을 얻으려고 하는가? 현재 보여지고 있는 IShellFolder의 위치를 구해보는건 어떨까? 이 일을 하기 위해 우리는 IPersistFolder2::GetCurFolder매서드를 사용해야 한다. GetFolder 매서드는 우리에게 IPersistFolder2 인터페이스를 요구함으로 인해 shell folder로 접근이 가능하게 한다. ( 대부분의 경우 IShellFolder 인터페이스만 있으면 될 것이다. 왜냐면 여기서 대부분 대단한 작업(cool stuff)이 끝나기 때문이다. )

               IPersistFolder2 *ppf2;
if (SUCCEEDED(pfv->GetFolder(IID_IPersistFolder2,
(void**)&ppf2))) {
LPITEMIDLIST pidlFolder;
if (SUCCEEDED(ppf2->GetCurFolder(&pidlFolder))) {
...
CoTaskMemFree(pidlFolder);
}
ppf2->Release();
}

여기서 우리 노력의 결실을 출력하기 위해 pidl을 path로 바꾸자.

                   if (!SHGetPathFromIDList(pidlFolder, g_szPath)) {
lstrcpyn(g_szPath, TEXT("<not a directory>"), MAX_PATH);
}
...

우리가 얻은 것을 가지고 다른 어떤것을 또 할 수 있을까...? 아~~ 맞다. 현재 focus된 객체가 무엇인지 알아보자.

                   int iFocus;
if (SUCCEEDED(pfv->GetFocusedItem(&iFocus))) {
...
}

focus된 항목의 이름을 출력하자. 이를 위해 우리는 그 항목의 pidl과  IShellFolder가 필요하다.(자, 내가 대단한 물건(cool stuff)가 있는 곳에 IShellFolder가 있다고 말했지.) 우리가 원하는 항목은 Item 매서드를 통해 나온다(놀랄만큼 충분하다).

                     LPITEMIDLIST pidlItem;
if (SUCCEEDED(pfv->Item(iFocus, &pidlItem))) {
...
CoTaskMemFree(pidlItem);
}

(만약 우리가 선택된 항목들의 리스트릴 원했더라면 Items 매서드에 VGIO_SELECTION을 넣어 사용할 수 있었다.)

우리가 item의 pidl을 구한 후에도 역시 IShellFolder인터페이스가 필요하다 :

                       IShellFolder *psf;
if (SUCCEEDED(ppf2->QueryInterface(IID_IShellFolder,
(void**)&psf))) {
...
psf->Release();
}

그런 다음, 항목의 출력용 이름을 얻기 위해 GetDisplayNameOf매서드의 도움으로 이 2가지를 같이 끼워 넣는다.

                         STRRET str;
if (SUCCEEDED(psf->GetDisplayNameOf(pidlItem,
SHGDN_INFOLDER,
&str))) {
...
}

우리는 괴짜 STRRET구조체를 따분한 문자열 버퍼로 변환하기 위해 GetDisplayNameOf() 함수를 사용할 수 있다. (괴짜 STRRET 구조체의 이야기는 또 다른 날을 기다려야 할 것이다.)

                           StrRetToBuf(&str, pidlItem, g_szItem, MAX_PATH);

됐군, 이제 이 모든것을 함께 넣어보세. 이 모든것들을 보조 함수로 자르지 않고 커다란 함수 하나에 몽땅 집어 넣으니 오히려 추악해 보이는군... 하지만 "내 인생"에서는 이들이 더 관리가 잘 될 수있도록 작은 보조 함수들로 나눌것이다.

scratch program으로 시작하고 이 새로운 함수를 추가하라:

#include <shlobj.h>
#include <exdisp.h>

TCHAR g_szPath[MAX_PATH];
TCHAR g_szItem[MAX_PATH];

void CALLBACK RecalcText(HWND hwnd, UINT, UINT_PTR, DWORD)
{
HWND hwndFind = GetForegroundWindow();
g_szPath[0] = TEXT('\0');
g_szItem[0] = TEXT('\0');

IShellWindows *psw;
if (SUCCEEDED(CoCreateInstance(CLSID_ShellWindows, NULL, CLSCTX_ALL,
IID_IShellWindows, (void**)&psw))) {
VARIANT v;
V_VT(&v) = VT_I4;
IDispatch *pdisp;
BOOL fFound = FALSE;
for (V_I4(&v) = 0; !fFound && psw->Item(v, &pdisp) == S_OK;
V_I4(&v)++) {
IWebBrowserApp *pwba;
if (SUCCEEDED(pdisp->QueryInterface(IID_IWebBrowserApp, (void**)&pwba))) {
HWND hwndWBA;
if (SUCCEEDED(pwba->get_HWND((LONG_PTR*)&hwndWBA)) &&
hwndWBA == hwndFind) {
fFound = TRUE;
IServiceProvider *psp;
if (SUCCEEDED(pwba->QueryInterface(IID_IServiceProvider, (void**)&psp))) {
IShellBrowser *psb;
if (SUCCEEDED(psp->QueryService(SID_STopLevelBrowser,
IID_IShellBrowser, (void**)&psb))) {
IShellView *psv;
if (SUCCEEDED(psb->QueryActiveShellView(&psv))) {
IFolderView *pfv;
if (SUCCEEDED(psv->QueryInterface(IID_IFolderView,
(void**)&pfv))) {
IPersistFolder2 *ppf2;
if (SUCCEEDED(pfv->GetFolder(IID_IPersistFolder2,
(void**)&ppf2))) {
LPITEMIDLIST pidlFolder;
if (SUCCEEDED(ppf2->GetCurFolder(&pidlFolder))) {
if (!SHGetPathFromIDList(pidlFolder, g_szPath)) {
lstrcpyn(g_szPath, TEXT("<not a directory>"), MAX_PATH);
}
int iFocus;
if (SUCCEEDED(pfv->GetFocusedItem(&iFocus))) {
LPITEMIDLIST pidlItem;
if (SUCCEEDED(pfv->Item(iFocus, &pidlItem))) {
IShellFolder *psf;
if (SUCCEEDED(ppf2->QueryInterface(IID_IShellFolder,
(void**)&psf))) {
STRRET str;
if (SUCCEEDED(psf->GetDisplayNameOf(pidlItem,
SHGDN_INFOLDER,
&str))) {
StrRetToBuf(&str, pidlItem, g_szItem, MAX_PATH);
}
psf->Release();
}
CoTaskMemFree(pidlItem);
}
}
CoTaskMemFree(pidlFolder);
}
ppf2->Release();
}
pfv->Release();
}
psv->Release();
}
psb->Release();
}
psp->Release();
}
}
pwba->Release();
}
pdisp->Release();
}
psw->Release();
}
InvalidateRect(hwnd, NULL, TRUE);
}

이제 우리가 해야할 일은 이 함수를 주기적으로 호출하고 결과를 출력하는 것이다.

BOOL
OnCreate(HWND hwnd, LPCREATESTRUCT lpcs)
{
SetTimer(hwnd, 1, 1000, RecalcText);
return TRUE;
}

void
PaintContent(HWND hwnd, PAINTSTRUCT *pps)
{
TextOut(pps->hdc, 0, 0, g_szPath, lstrlen(g_szPath));
TextOut(pps->hdc, 0, 20, g_szItem, lstrlen(g_szItem));

}

이제 굴릴 준비가 되었다. 이 프로그램을 실행하고 옆에다 두어라. 그 다음에 윈도 탐색기를 실행하고 방금 실행한 프로그램을 보고 선택한 항목이 무엇인지 당신이 어떤 폴더에 있는지 추적하여라.

자... 어쨌든 나는 내 목적을 이루었다: 때론 당신이 원하는 조각들이 이미 거기에 있다 그리고 당신은 단지 어떻게 그것들을 함께 껴 넣을 수 있는지를 알기만 하면 된다. 각각의 그 조각들에 대해 그 자체는 아주 크지 않다는 것을 알아두라. 당신은 단시 얘네들이 흥미가 있는 방향으로 껴넣어질 수 있다는 것을 알아야 한다.

숙제 : 이 프로그램을 수정해서 폴더를 가져와서 detail view로 바꿔보도록 하라.


출처 : 데브피아

http://www.devpia.com/Maeul/Contents/Detail.aspx?BoardID=51&MAEULNO=20&no=7561&page=1

'코드' 카테고리의 다른 글

TShellWindows  (0) 2008.07.14
[BCB] HtmlElements - TTreeView  (0) 2008.07.10
RegExp - JS  (0) 2008.07.02
input-tag Validate  (0) 2008.06.28
한글체크 JS  (0) 2008.06.24