때로 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 |