ATL로 프로젝트를 만드는 이유 중의 하나는 가볍다는 것이다.
그러나 가볍게 만들기 때문에 의존성이 있는 라이브러리를 포함하지 않고 빌드한다.
이로인해 배포 후 다양한 사용자 환경 때문에 환장하는 결과가 찾아오기도 한다.

예전에는 그냥 같은 디렉토리에 포함되기만 해도 되었는데 2005이후 SxS Assembly 때문에 짜증나게 바뀌었다.

이에 대한 예방법이 기본적으로 Static으로 빌드를 하는 것이다. 이것은 atl90.dll 과 manifast 파일을 포함하게 된다. 이로서 최종 빌드된 파일의 용량은 대략 25k 정도 증가한다.

사용자 삽입 이미지




허나 이것으로 해결이 된 것이 아니다.

정적 빌드로 된 것으로 배포 후 사용하려 하면 다양한 의존성 관련 에러메세지를 내뱉어내고 ActiveX DLL인 경우는 죽어도 웹에 바인딩이 되지 않는다. 아주 환장할 노릇!

해결점은 다음과 같다.

0. VS의 셋업 프로젝트를 이용한다. -_-

1. Microsoft Visual C++ 2008 재배포 가능 패키지를 포함한다. -_-

2. .Net Framework를 설치한다. -_-

3. Private Assembly를 설치하는 것이다. Install 파일을 만들때 Microsoft.VC90.CRT.manifest, msvcp90.dll, msvcr90.dll를 포함하여 사용할 DLL과 같은 디렉토리에 설치하는 것이다. regsvr32등은 해줄 필요가 없다. 허나 새로운 버전의 DLL이 나왔을 때 패치되지 않는다.

여기 까지는 그나마 행복한 InstallShield를 사용했을 때다.

4. ActiveX를 위해 만든 ATL은 cab 파일을 만들 때 저 파일들을 포함시켜줘야 한다. 그리고 inf 파일에 [Add.Code]에 넣어주고 아래도 같이 적어주면 된다.

[Add.Code]
   msvcr90.dll=msvcr90.dll
   msvcp90.dll=msvcp90.dll
   Microsoft.VC90.CRT.manifest=Microsoft.VC90.CRT.manifest

[Microsoft.VC90.CRT.manifest]
   file-win32-x86=thiscab

[msvcp90.dll]
   file-win32-x86=thiscab

[msvcr90.dll]
   file-win32-x86=thiscab

대충 이렇다.

5. 이 짓들을 모두 무마하는 설정이 있으니... 그것은 바로바로바로바로바로...
런타임 라이브러리를 다중 쓰레드 DLL에서 다중 쓰레드로 변경하는 것이다. -_)
사용자 삽입 이미지

Visual C++ 2008 Feature Pack Release 설치기

Windows 2008. 4. 8. 10:44 posted by deneb

고대하던 정식판이 나와주셨다.

beta에서 특정 버전만 깔리는 까칠함을 보여주셨는데

이번엔 그나마 영문판 이라면 패스해 주시는 관대함을 배푸셨다.

한글판은 대략 6월 넘길듯 -_-

사용자 삽입 이미지
이번엔 속지 않고 확실하게 정독했다.
대충읽었다가 하루에 세번 깐거 생각하면 -_-

사용자 삽입 이미지
설치 시작 화면

사용자 삽입 이미지
TOC 주저리주저리 라이센스 확인 선택 후 다음 클릭

사용자 삽입 이미지
잘 설치되고 있는 모습

사용자 삽입 이미지
갑자기 원본 넣어달래신다 -_- 당황하지 말고 마운트 시켜주는 센스

사용자 삽입 이미지
설치 완료 모습

듀얼코어 6550, 4GB, Server 2008 기준 20~30 사이로 걸린다.

자 프로젝트를 시작 해보자.

기본 MDI에 오피스모양에 리본 선택 해서 그냥 컴파일 한 모습

사용자 삽입 이미지

미나씨 만나러 가서 많이 봐온 모습이다.

겉모냥이 바뀐것 가지고 흥분하시면 초짜다.

Feature Pack 의 핵심은 TR1이다.

그렇다고 필자도 스마트 모인터인 shared_ptr를 사용하고 정규식 파서가 있고 해쉬 씨리즈를 막 가져다 쓸 수 있다는 것 외에는 잘 모르겠다. -_-

아직도 stl은 컨테이너 밖에 안써서 -_);;;
이거 제대로 당했다.

Server 2008로 과감히 올라가면서 개발환경을 다시 잡았다.
Visual Studio(이하 VS) 쪽은 통상적으로 하위 개발 버전을 깔아야 문제가 생기지 않는다. 개인적 경험으론 VS2k5를 깔고 VS2k3을 설치했을 경우에 아무런 문제가 없었던 적도 있기 때문에 항상 그렇다고 생각하진 않고 순서야 어떻게 되든 환경 설정만 잘 해주면 된다로 생각하고 있다. 그래도 상위를 깔고 하위를 설치하게 되면 제대로 찜찜하다. 환경 설정을 한다고해도 뭔가 이상한 느낌은 지울수 없기에 통상적인 방법을 선택하고 믿고 있는 것이 편하다.

이번에도 역시나 일반적인 방법으로 설치했다.

VS2k8을 사용하는 목적이야 많겠지만 실버라이트니 .netfx 3.5 통합환경이 하는 것 보다 VC++ 2008 Libraries Feature Pack "Beta" 를 직접 사용해 보고자 했던 마음이었다. 이것이 거의 늪으로 들어가는 화근이었다.

VS2k3 관련 설치후  한글판 Visual Studio Team System(이하 VSTS)를  깔고 Windows SDK 설치. 마지막으로 VS90-VCFeaturePack-Beta-KB945273-x86x64-ENU 설치 시도!

사용자 삽입 이미지

설치 불가 -_-

다운로드 사이트에 가서보니 http://www.microsoft.com/downloads/details.aspx?FamilyID=d466226b-8dab-445f-a7b4-448b326c48e7&DisplayLang=en

사용자 삽입 이미지
그렇다. 영문판 이어야 한다. -_-

VSTS 2k8 한글판 제거 T-T

VSTS 2008 Development Edition 2k8 설치 -_-; (이게 실수다)

다시 해당 프로그램 찾을 수 없다는 다이얼로그 T-T

VS2k3 제거, WIndow SDK 제거 -_-;

그래도 해당 프로그램 찾을 수 없다는 다이얼로그 T-T

구글링 모드 돌입
사용자 삽입 이미지
라는 글 발견!

내용은 설치 파일에서 생성하는 임시 디렉토리에 있는 msp 파일을 다른데로 옮겨서 실행해라라!

시도 해봤지만...

사용자 삽입 이미지

역시나... T-T

다른 글을 보니
사용자 삽입 이미지
이런~ 다운로드 페이지에 있는 문구를 건성으로 봤다.
네트워크 경로 설치 했으면 해당 경로를 접근 할 수 있게 하고 DVD로 설치 했다면 DVD에 해당 타이틀을 넣으라는 소리

DVD 이미지 마운트 후 시도!

그래도 해당 프로그램 찾을 수 없다는 다이얼로그 T-T

다시 구글링...
사용자 삽입 이미지
사용자 삽입 이미지
뭐라고? VSTS Development Edition 불가? VSTS Team Suite 영문판과 VS Professional Edition 영문판만 된다는 것이었다.

다시보니 너무 확연히 보인다.
사용자 삽입 이미지
그렇다... 너무 슬쩍봐서 삽질중이었던 것이다. o_o

주의하자.
현재 나와있는 VC++ 2008 Libraries Feature Pack "Beta"(파일명 : VS90-VCFeaturePack-Beta-KB945273-x86x64-ENU.exe)은 VSTS Team Suite 영문판과 VS Professional Edition 영문판 에서만 설치 가능하다.

홧김에 Windows 까지 다시 설치하고 설치 했다. -_-

리소스 전용 DLL 만들기

Windows 2008. 2. 15. 09:43 posted by deneb

from : http://msdn2.microsoft.com/ko-kr/library/24b2tcy0(VS.80).aspx

리소스 전용 DLL은 아이콘, 비트맵, 문자열 및 대화 상자 등의 리소스만 들어 있는 DLL입니다. 리소스 전용 DLL을 사용하면 여러 프로그램 간에 동일한 리소스 집합을 쉽게 공유할 수 있습니다. 여러 언어로 지역화된 리소스가 있는 응용 프로그램을 제공하는 것도 좋은 방법입니다(MFC 응용 프로그램의 지역화된 리소스: 위성 DLL 참조).

리소스 전용 DLL을 만들려면 새로운 Win32 DLL(비 MFC) 프로젝트를 만든 다음 이 프로젝트에 리소스를 추가합니다.

  • 새 프로젝트 대화 상자에서 Win32 프로젝트를 선택한 다음 Win32 프로젝트 마법사에서 DLL 프로젝트 형식을 지정합니다.

  • 해당 DLL에 사용할 문자열 또는 메뉴와 같은 리소스가 포함된 새 리소스 스크립트를 만든 다음 .RC 파일로 저장합니다.

  • 프로젝트 메뉴에서 기존 항목 추가를 선택한 다음 새로 만든 .rc 파일을 프로젝트에 삽입합니다.

  • /NOENTRY 링커 옵션을 지정합니다. /NOENTRY 옵션은 링커가 _main에 대한 참조를 DLL에 링크하지 않도록 하며, 리소스 전용 DLL을 만들 때 반드시 사용해야 합니다.

  • DLL을 빌드합니다.

리소스 전용 DLL을 사용하는 응용 프로그램은 LoadLibrary를 호출하여 명시적으로 DLL에 링크해야 합니다. 리소스에 액세스하려면 모든 종류의 리소스에 대해 작동하는 일반 함수인 FindResourceLoadResource를 호출하거나 다음과 같은 특정 리소스 관련 함수 중 하나를 호출해야 합니다.

  • FormatMessage

  • LoadAccelerators

  • LoadBitmap

  • LoadCursor

  • LoadIcon

  • LoadMenu

  • LoadString

응용 프로그램이 리소스 사용을 마친 경우에는 FreeLibrary를 호출해야 합니다.

: 그렇게 스크랩은 올리지 않으려 했건만...

0. 들어가기 전에
 Microsoft(
이하 MS)에서 Windows XP(이하 XP) SP2 발표하면서 개발자 (윈도우 서버)관리자 중심으로 세미나를 하였지만 이번에 Windows Vista(이하 비스타) 발표하면서 하는 세미나들에 비하면 횟수나 질로 보나 낮았다. 이런 아주 눈에 보이는 단순한 사실에 근거하여 말하는 것도 우습지만 MS 이번 비스타에 대해서 얼마나 많은 것을 걸고 있고 그에 대한 반대급부에 대해서 민감한지가 느껴질 정도다. 과거 MS-DOS에서 Windows 환경으로 그리고 Win16(Windows 95, 98 SE)에서 Win32(Windows XP) 환경으로 넘어 오면서 약간은 위에서 아래를 내려다 보는 시선으로 사용자들을 대한 감이 있었는데 이번 비스타에 대해서는 오히려 눈치를 보고 있다고 있다. 그런 것인가 나름대로 정리한 생각은 MS XP SP2 발표하면서 데스크탑 OS 대한 생각을 고쳐 먹었다. 또한 약간은 차례 있어왔던 인터넷 대란에 대해서 어느 정도 책임에 대해 적극적이지는 않지만 소극적인 반응의 결과로 변화를 시도하고 있다고 보고 있다. 세미나에서 강사가 한마디로 요약해서 해준 말을 인용하자면 이전의 MS 데스크탑 OS 모든 것을 열어두고 문제가 생기면 막자 주의였다. 모든 사용자들은 기본적으로 Administrators 권한으로 OS 사용하게 된다. 그렇기 때문에 발생한 문제가 모르는 사람들의 드라이버설치, 인터넷이나 메일에 의한 감염에 그대로 방치하는 것이 었다. 결과 이상한 공식이 나왔는데 'Windows 불안하다'라는 것이다. 어디선가 이야기로는 윈도우에서 블루스크린을 보게되는 현상의 대부분은 윈도우 자체 결함이 아닌 같이 사용된 드라이버의 문제라고 글을 본적이 있다. MS 대변하는 의미로 적기 보다는 만큼 많은 영역을 소화하기 위해서는 '일반적인 사용자들' 사용자 권한으로 행동을 취하게 해야 하는 것이 OS로서 책임을 지는 것이라는 것을 이제야 비스타에서 적용한다는 것이다.
 
그럼 비스타에서 바뀐 것은 무엇인가 사용자들은 비스타를 설치하는 되는 것이 없더라, 불편해서 못써먹겠네 등의 말을 하는 것일까. UAC, 보호모드, 가상화, 보안 정책 등등을 들어서 설명할 수도 있겠지만 그런 것은 이미 세미나에서 지겹도록 들었기 때문에 그냥 정리된 내용을 적기로 하였다. 그간 우리는 올바른 철학을 적용하여 설계한 OS 사용했기 보다는 어찌 보면 우리나라 IT 맞는 철학에 입각하여 만들어진 것이라고 느껴지는 OS 사용하다가 갑자기 표준이라는 것을 어느 정도 적용시킨 OS 사용하다 보니 나오는 말이라고 있다. 안전한 = 편한 이라는 공식이 적용 있는 사례는 거의 없다. 안전하다는 것은 적어도 한번이라도 불편함을 초래하고 마는 것이다. 하지만 그것으로 갑자기 불편함을 감수해라 라는 의미로 말하는 것인가? 그것은 아니라고 본다. 개발환경, 플랫폼, 워크프레임 등의 기반이 바뀐다는 것은 단지 프로그램의 버전이 올라가서 바뀌는 것을 적응하는 보다 많은 부분에서 사용자의 생각이 바뀌어야 한다는 것을 의미한다. 리눅스의 그놈을 사용하면서 윈도우는 이랬는데 하면서 차이점을 지적하여 사용 못해먹겠다는 자세보다는 이런 것이 리눅스와 윈도우의 차이점이고 이렇게 만든 이유는 무엇인가 있겠구나 하는 자세가 필요하다는 것이다. 다음으로 그에 합당한 프로그램을 만들어야 하는데 아직 그런 것들을 생산할 만큼의 충분한 시간적 여유가 없었다는 것을 의미한다. , 전체적인 환경이 숙성될 만한 시간이 부족한데 궁여지책으로 사용해야 한다는 것이다. 전압으로 예를 들면(비약이 것을 염두하자) 110V에서 어느날 갑자기 220V 바꿨다고 한다면 110V에서 사용하던 기기들은 어떻게 될까? 당연히 트랜스(변압기) 사용하여 사용하여야 한다. 하지만 언제까지 그렇게 사용하지 않는다. 신제품이 나온다면 그에 맞는 220V 나오게 되고 사람들은 슬슬 트랜스를 이용한 110V 기구 사용대신에 진짜 220V 제품을 쓰게 되고 비율은 점차 확대되는 것이다. 이처럼 현재(혹은 과거) 기술을 비스타라는 새로운 환경에 적용시켜서 해내라는 보다는 새로운 환경이 적용된 프로그램으로 옮겨가는 것을 바라는 것이 올바르다. 하지만 비스타는 Win32 무서운 갈굼을 계속 받아 사용자들에게 새로운 환경을 경험시키는데 만족할 것이고 차세대 에서 완벽하게 적용된 OS 발표되고 사용자들은 현재보다 훨씬 수월하게 다른 환경으로 옮겨갈 것이다
.

들은 세미나는 다음과 같았다
.
1. Windows Vista
에서의 ActiveX 컨트롤

 
세미나의 핵심은 윈도우의 변화 과정(XP -> XP SP2 -> XP SP2 + IE7 -> 비스타) 파악해야 하고 그에 따른 과거의 기술을 어떻게 사용할 있을까를 예를 들어서 설명하였다. 전체적으로 예시 보다는 바뀐 비스타와 XP 차이점을 요약 정리 주었고, 우리나라는 ActiveX 사용하게 되었는가에 대해서도 까칠하게 짚고 넘어갔다. 역시나 전체적인 IT 업계와 개발 트렌드를 파악하여 과거 기술을 재사용하는 것에 집착하는 보다 새로운 플랫폼을 파악해 과거 기술을 전이시킬까를 고민하자고 하였다.

2. Windows Vista
보안에 맞는 Application 개발

 
세미나의 핵심은 매니지드 코딩(Managed Coding) 하자. 앞서 세미나들과 전체적으로 곂치는 내용이었지만 핵심을 요약해 주고 강사님이 입심도 좋으셔서(이빨까기 유명함 -_-) 가장 재미있는 세미나 같았다. 그리고 현재 개발 트렌드인 전체를 배포하는 것이 아니라 환경이 배포되고 그에 맞는 콘텐트들을 배포하는 방식(예를 들면 플래시 플레이어 - 기반의 프로그램들) 택하고 윈도우도 작업을 이미 시작했으니 어서 빡시게 공부하자라는 내용이었다.

Using COM to explore the namespace

Windows 2007. 2. 8. 11:23 posted by deneb

출처 : http://netez.com/2xExplorer/shellFAQ/bas_xplore2.html

There are no two ways about it, using low-level COM to enumerate folders is quirky. Note that I didn't say "difficult", it just takes some time getting used to it. With COM you have to manage objects yourself: create the right one, ask for a specific interface for the functionality you are after, then use it and finally take care to release it. In contrast, in regular API-based programming the operating system is doing the management for you; you just call functions directly, oblivious about objects and things behind the scenes.

To enumerate a folder via COM, you need an IShellFolder pointer to the folder object you're after. We've seen a sketch of how to obtain this interface by parsing a path name in the previous section. The next step is to ask the object to enumerate itself using its EnumObjects method, which creates a new object which exposes an IEnumIDList interface. This enumerator object has the contents we are after, as a collection of local PIDLs, one for each item in the folder.

The best way to illustrate all this is with an example. Let's try to create the COM-equivalent of the EnumerateFolderFS() sample presented earlier, which read the contents of a filesystem folder given a full path to it. The following EnumerateFolder() sample produces the same results in a completely different approach.

#include <shlobj.h>

void EnumerateFolder(LPCTSTR path)
{
   HRESULT hr; // COM result, you'd better examine it in your code!
   hr = CoInitialize(NULL); // initialize COM
   // NOTE: usually COM would be initialized just once in your main()

   LPMALLOC pMalloc = NULL; // memory manager, for freeing up PIDLs
   hr = SHGetMalloc(&pMalloc);

   LPSHELLFOLDER psfDesktop = NULL; // namespace root for parsing the path
   hr = SHGetDesktopFolder(&psfDesktop);

   // IShellFolder::ParseDisplayName requires the path name in Unicode.
   OLECHAR olePath[MAX_PATH]; // wide-char version of path name
   MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, path, -1, olePath, MAX_PATH);

   // parse path for absolute PIDL, and connect to target folder
   LPITEMIDLIST pidl = NULL; // general purpose
   hr = psfDesktop->ParseDisplayName(NULL, NULL, olePath, NULL, &pidl, NULL);
   LPSHELLFOLDER psfFolder = NULL;
   hr = psfDesktop->BindToObject(pidl, NULL, IID_IShellFolder, 
                                 (void**)&psfFolder);
   psfDesktop->Release(); // no longer required
   pMalloc->Free(pidl);

   LPENUMIDLIST penumIDL = NULL; // IEnumIDList interface for reading contents
   hr = psfFolder->EnumObjects(NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, 
                               &penumIDL);
   while(1) {
      // retrieve a copy of next local item ID list
      hr = penumIDL->Next(1, &pidl, NULL);
      if(hr == NOERROR) {
         WIN32_FIND_DATA ffd; // let's cheat a bit :)
         hr = SHGetDataFromIDList(psfFolder, pidl, SHGDFIL_FINDDATA, &ffd, 
                                  sizeof(WIN32_FIND_DATA));

         cout << "Name = " << ffd.cFileName << endl;
         cout << "Type = " << ( (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
                                ? "dir\n" : "file\n" );
         cout << "Size = " << ffd.nFileSizeLow << endl;
         
         pMalloc->Free(pidl);
      }
      // the expected "error" is S_FALSE, when the list is finished
      else break;
   }

   // release all remaining interface pointers
   penumIDL->Release();
   psfFolder->Release();
   pMalloc->Release();

   CoUninitialize(); // shut down COM
}

Oh dear, that sure was hard work. 56 lines of code to do exactly the same thing EnumerateFolderFS() managed in just 23 lines — less than half. But hey, that's C++ baby, either take it or leave it and join the VB club <g>. Since this is the first real COM example I presented, I'll try to be gentle and explain it thoroughly. Many typical issues in COM programming appear in this code:

  • COM initialization. Before you use any of the COM services and objects you need to call CoInitialize. Conversely, after you're done with COM you must call CoUninitialize. Typically an application that relies heavily on COM will initialize the subsystem just once when it starts, not inside each function that utilizes COM. Still it's no harm calling CoInitialize more than once, as long as it is balanced by a CoUninitialize.
    ADVANCED. If your program is multi-threaded, then COM must be initialized in each thread that uses its services. Initializing it once in the main thread won't be enough.
  • Object management. The code sample used four distinct COM objects to read the folder contents. Using desktop's IShellFolder the path was parsed, generating a pidl to the target folder. This second object was created using desktop's BindToObject. The enumerator (third object, the one we were after all along) was created by EnumObjects. Finally the shell memory manager was created by SHGetMalloc to free the various PIDLs that were allocated for us. All COM objects are dynamically created and must be released to free up the associated memory and resources via the object's Release method, inherited from IUnknown. Note that PIDLs are not objects, only pointers to allocated memory. That's why they are Free'd and not Release'd, something that took me some time to realise in the early dayz.
  • UNICODE strings. In case you haven't heard about them, UNICODE strings have wide characters, i.e. 2-bytes each. Apparently the orientals are responsible for this major upset <g>. COM has adopted the UNICODE system whole-heartedly, and most system (and shell) objects expect strings to be wide. The problem is that windows 95/98 (dunno about Me) work with multibyte strings, which are basically regular single-byte strings, despite what their name suggests. If you want your proggy to work in all windows platforms then you'd have to stick with single-byte strings and do manual conversions whenever a wide string is required. That is exactly what MultiByteToWideChar function does above. If you use MFC, you can take advantage the CString class, which simplifies string management and conversions. The drawback is the performance penalty involved for all these conversions; unfortunately there's no way around it, except if you are willing to distribute separate ANSI and UNICODE versions of your programs.
  • Enumerators. These fancy-name objects are frequent denizens of the COM landscape. Any VB people in the audience will be familiar with the term collections. Enumerators are simple objects that hold collections of similar items. The internal organization is transparent; enumerators expose an IEnumxxx interface, as IEnumIDList. Once initialized, items can be accessed sequentially by calling the Next method repeatedly, until some error indicates that the list is exhausted. Personally I feel it's a grand omission that all those objects don't have a method like Count to give the total number of elements in the list. An important detail is that enumerators usually return duplicates of their listed items which you are responsible to cleanup. In the code sample above note that each pidl is Free'd after use.

You can now appreciate why people say that COM is a technology with a steep learning curve. One has to be familiar with dozens of details before even the simplest "Hello COM" program can be build. And you'd better hurry in that climbing while learning, because mikro$oft have already prepared the COM+ mountain for you to climb next, slippery slopes an'all. Will this torment ever end? <g>

Of course, these issues are all for starters. The main dish is learning about all the COM objects supplied by the framework, finding out what they can do for you, and how to work with them, what interfaces they support, etc. The online documentation is guaranteed to be the most complete and up-to-date source of reference information. As they say, if you don't like reading, probably software development is not your ideal vocation. What you read in that good book published in 1998 may already be obsolete, plus there will be dozens of new objects introduced since then.

ADVANCED: Fending for your address space
You may have noticed in the sample code the abundance of NULL arguments to interface methods, most of the time implying a default action. Although this is easy to do, there's a possible risk here, if the NULL is a pointer meant to receive some result from the method. Take for example pdwAttributes parameter of ParseDisplayName. The docs state clearly that by passing NULL you specify that are not interested to receive any attributes at this time. In an ideal world you would be safe, but in this world there are many cowboys out there developing shell and namespace extensions, who wouldn't think twice before attempting to write on a NULL pointer without doing the proper checks first. Since that offending amateur object runs in the same address space as your app, it will drag you down in its demise. The workaround is to provide dummy variables for all such potential trouble-makers; for our example this would mean defining a "DWORD dummyAttrs = 0;" and passing it on to ParseDisplayName, even if you don't have any intention of using it in the end.

Ok, but what about that namespace exploring?

Sorry folks, I got a bit carried away there. So, let's get back to the subject, folder contents enumeration. The EnumerateFolder() sample will produce almost the same results as a the filesystem version EnumerateFolderFS(). Even the order of the items is the same, which hints that EnumObjects down deep must be using FindFirstFile et al for doing the actual folder reading. But almost the same implies there are some differences:

  • We no longer read the '.' and '..' pseudo-items. That's progress since I consider these two plain nuisances. <g>
  • We don't get automatic filename filtering; the best we can do is specify whether we want files, folders or both, via the SHCONTF_FOLDERS and SHCONTF_NONFOLDERS flags that EnumObjects understands.

The most important difference however, is the file date/time information. SHGetDataFromIDList doesn't fill in the file details in WIN32_FIND_DATA as thoroughly as an equivalent FindFirstFile would. Only the modification date is filled in, and even that is rounded to the nearest even second. That's the reason why I mentioned that you need both COM and traditional API to obtain complete information for filesystem folders.

ADVANCED: Stale PIDLs
SHGetDataFromIDList gets all the file data directly from the PIDL, without accessing the disc at all. Microsoft's implementation of filesystem PIDLs stores quite a large amount of data in each SHITEMID (cf. my earlier suggestion), except for those unfortunate creation etc dates of course. This is an advantage since the cached information can be accessed quickly, but it has to be interpreted carefully. A PIDL you obtained yesterday won't necessarily contain accurate information, if for example the file was modified in the meantime. Still, even a stale PIDL is good enough to uniquely identify a file. To convince yourselves, take two PIDLs to the same file, obtained at different times, and see what CompareIDs will return.


Shell을 이용하여 디렉토리 정보등을 알아 올 때 알아야 할 것들을 알차게 설명해 놓았다.

How To Convert a File Path to an ITEMIDLIST

Windows 2007. 2. 6. 23:30 posted by deneb

How To Convert a File Path to an ITEMIDLIST

Article ID : 132750
Last Review : July 11, 2005
Revision : 1.3
This article was previously published under Q132750

SUMMARY

When developing an application that interacts with the Windows Explorer shell, you may need to convert an arbitrary path to a file to an ITEMIDLIST. You can do this using the IShellFolder::ParseDisplayName API.

MORE INFORMATION

Following is an example of how to use the IShellFolder interface to convert the path to the file Readme.txt in the current directory to an ITEMIDLIST. The example is written in C. If the program is written using Visual C++, accessing member functions through the lpVtbl variable is unnecessary.
   LPITEMIDLIST  pidl;
   LPSHELLFOLDER pDesktopFolder;
   char          szPath[MAX_PATH];
   OLECHAR       olePath[MAX_PATH];
   ULONG         chEaten;
   ULONG         dwAttributes;
   HRESULT       hr;

   // 
   // Get the path to the file we need to convert.
   // 
   GetCurrentDirectory(MAX_PATH, szPath);
   lstrcat(szPath, "\\readme.txt");

   // 
   // Get a pointer to the Desktop's IShellFolder interface.
   // 
   if (SUCCEEDED(SHGetDesktopFolder(&pDesktopFolder)))
   {
       // 
       // IShellFolder::ParseDisplayName requires the file name be in
       // Unicode.
       // 
       MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, szPath, -1,
                           olePath, MAX_PATH);

       // 
       // Convert the path to an ITEMIDLIST.
       // 
       hr = pDesktopFolder->lpVtbl->ParseDisplayName(pDesktopFolder,
                                                     NULL,
                                                     NULL,
                                                     olePath,
                                                     &chEaten,
                                                     &pidl,
                                                     &dwAttributes);
       if (FAILED(hr))
       {
           // Handle error.
       }

       // 
       // pidl now contains a pointer to an ITEMIDLIST for .\readme.txt.
       // This ITEMIDLIST needs to be freed using the IMalloc allocator
       // returned from SHGetMalloc().
       // 

       //release the desktop folder object
         pDesktopFolder->lpVtbl->Release();
   }
				

APPLIES TO
Microsoft Platform Software Development Kit-January 2000 Edition

ITEMIDLIST management library

Windows 2007. 2. 6. 23:18 posted by deneb
출처 : http://www.codeproject.com/shell/pxshlapi.asp

This library simplifies management of Windows Shell ITEMIDLISTs (further PIDLs). For me, it always has been trouble to keep track of all allocated PIDLs and to free them in time. So, I wrote this library.

As time goes, the library has became rather messy (it's because I put there all PIDL-related functions that may be reusable). But it still helps me a lot.

The core of this library is CPidl class. All other functions exported here are mostly supplemental.

Constructors

CPidl::CPidl()
Constructs empty CPidl instance
CPidl::CPidl(LPITEMIDLIST other)
Constructs CPidl instance and attaches (not copies) ITEMIDLIST allocated somewhere else. At destruction time attached ITEMIDLIST will be deallocated.
CPidl::CPidl(const CPidl& other)
Copy constructor. Copies one instance of CPidl into another.
CPidl::CPidl(LPCTSTR pszPath)
Constructs CPidl instance from file or folder path. In case of error, the PIDL will be empty. It uses IShellFolder::ParseDisplayName() for getting PIDL.
CPidl::CPidl(int nSpecialFolder)
Constructs CPidl instance from special folder ID. (See CSIDL_XXX constants in MSDN).

Member functions

LPITEMIDLIST CPidl::Detach()
Detaches contained PIDL from wrapper
LPITEMIDLIST CPidl::Copy()
returns a copy of contained PIDL
void CPidl::Free()
frees contained PIDL. This member function is called automatically at destruction time.
CPidl::operator bool()
conversion operator for using in boolean expressions. Will return true if contained PIDL is not NULL
CPidl::operator LPITEMIDLIST()
Useful conversion operator that allows to use CPidlas a function argument in place of LPITEMIDLIST
CPidl::operator LPITEMIDLIST*()
CPidl::operator LPCITEMIDLIST*()
Useful conversion operator that allows to use CPidl as a function argument in place of LPITEMIDLIST*. Usually the pointer to LPITEMIDLIST is used to receive when a PIDL from function. Make sure you call CPidl::Free() before using CPidl instance for receiving PIDLs!
CPidl& CPidl::operator=(LPITEMIDLIST other)
CPidl& CPidl::operator=(const CPidl& other)
Assignment operators

Global functions

IShellFolderPtr& GetDesktopFolder()
returns a reference to static instance of IShellFolder interface for root namespace folder.
IMallocPtr& GetMalloc()
returns a reference to static instance of IMalloc interface. This interface is essential for working with PIDLs.
int GetItemIDSize(LPCITEMIDLIST pidl)
returns PIDL size in bytes.
int GetItemIDCount(LPCITEMIDLIST pidl)
returns number of elements in PIDL. If you don't know what does it mean, reread "Working with Item ID Lists" article in MSDN.
LPBYTE GetItemIDPos(LPCITEMIDLIST pidl, int nPos)
gets pointer to nPos'th element in PIDL or NULL if nPos exceeds number of elements in PIDL.
LPITEMIDLIST CopyItemID(LPCITEMIDLIST pidl, int cb=-1)
makes a copy of PIDL. cb specifies number of bytes to copy. If it's equal -1, entire PIDL will be copied.
LPITEMIDLIST MergeItemID(LPCITEMIDLIST pidl,...)
Merges two or more PIDLs (usually relative ones) into absolute (or fully-qualified) PIDL. The programmer should know what he's doing when he calls this function :-). Typical usage is to make fully-qualified PIDL from folder PIDL and PIDL returned by IShellFolder::EnumObjects()
int CompareItemID(LPCITEMIDLIST pidl1,LPCITEMIDLIST pidl2)
int CompareItemID(LPCITEMIDLIST pidl1,int nSpecialFolder)
int CompareItemID(int nSpecialFolder,LPCITEMIDLIST pidl2)
Compares two PIDLs. Returns 0 if equal and -1 otherwise.
LPITEMIDLIST GetItemIDFromPath(LPCTSTR pszPath)
returns PIDL for file or folder name. Or NULL if error
HRESULT SHBindToParent(LPCITEMIDLIST pidl, REFIID riid, VOID **ppv, LPCITEMIDLIST *ppidlLast)
Closely resembles standard shell function SHBindToParent(). It was neccessary to write it becuase it's not available on Windows 95/NT. Refer to MSDN for arguments description.
BOOL TrackItemIDContextMenu(LPCITEMIDLIST pidlShellItem, UINT nFlags, LPPOINT ptPoint, HWND hWnd)
BOOL TrackItemIDContextMenu(LPCTSTR pszShellItemPath, UINT nFlags, LPPOINT ptPoint, HWND hWnd)
This function builds, shows and tracks shell context menu for PIDL or or file/folder path. It returns TRUE if user clicked on some context menu item. In case of error or if user didn't choose anything from menu it returns FALSE.
  • LPCITEMIDLIST pidlShellItem, LPCTSTR pszShellItemPath: a PIDL or path name of the shell item to show context menu for.
  • UINT nFlags: a set of TPM_XXX flags. See function TrackPopupMenu() for description
  • LPPOINT ptPoint: a point in screen coordinates where menu should appear.
  • HWND hWnd: a handle to the window - owner of the menu. It cannot be NULL

쉘을 다루는데 있어서 애로사항이 꽃피는 경우가 파다한데 그나마 쉽게 접근 할 수 있는 클래스