Perfil de Guang记忆的碎片FotosBlogListasMás Herramientas Ayuda
20 mayo

汶川伤,国之殇

谨纪念在这场灾难中逝去的人们
16 mayo

割草

Long time 没有更新这里了,除了贴那些怕自己忘了的文章...想写点什么,却突然觉得现在想写个长句子都很费劲-_-!,更别说洋洋洒洒的写一篇正经东西了。还是用短句记录下这两个月的一些经历和感想吧,也算是简洁明快了,嘿嘿。

最近很忙,真的很忙,感觉是到M$后最忙的一个多月。太多的文档要写,太多的技术点要调研,太多的代码要调......每天晚上睡觉之前都在想着那些没有解决的问题,每天早晨醒来都感觉自己还欠了好多东西没有做。有点厌倦这样的日子,亏得那个什么DISC培训的讲师还说我现在的日子过的太安逸了......

26岁生日过了,眼看着就要加入“奔3”大军了,发现自己仍然一事无成......发自内心的羡慕那些二十一,二岁的“年轻人”,好想再回到自己的那个年纪。现在才知道,以前大人们常说的那句“好羡慕你们的年纪”原来并不是奉承。

IPHONE不错,以前总觉得苹果的东西是华而不实,也曾经对那些苹果的疯狂粉丝嗤之以鼻。自己用过后竟也不知不觉成了那多半拉苹果的“俘虏”。还有IPOD,IMAC,最近“吃”的苹果真的不少......嘘~~~这样的话放在心里就好了,不好在M$里乱讲。

星际......很古老的游戏了。现在新机器的显卡甚至“高级”的已经不能再玩这样256色的全屏游戏了,不过在我们这里仍然有着大批星际的拥趸,几乎每天都会上演的乱战已经成了HIC独特的风景。在经受了一次又一次的“打击”之后,终于抛弃了我最爱的zerg,这个群“P”的时代,用z实在跟人家没法“打招呼”......

5.12。远隔千里的汶川一震,让我第一次经历了有感地震。在18楼的高度,足足晃了一分钟,让我在那一刻真真正正的体会到了恐惧。看了网上的照片和视频,让我有了窒息感觉,想哭却哭不出来......这个奥运年,我们的祖国在经历着前所未有的挑战,不幸接二连三的发生,在考验着每个人的神经。在这个时候,恐怕只有我们团结在一起,才能抵御这样的压力。

两天里分别看了《钢铁侠》和《功夫之王》,着实了看清了中国“大片”和好莱坞大片的差距。同样是商业片,同样是大制作,同样是大明星,一个让我出了电影院还在回味刚刚的情节,满脑子是斯塔克的飞来飞去的画面;一个让我差点在电影院的沙发上睡着,一根猴毛引发的“血案”,让人不得不对那个编剧“肃然起敬”......

最近好想出去旅游。两个人,背着简单的背包,去看海,去看山;远离生活和工作的压力,远离城市的喧嚣,多少次都在憧憬着这样的日子,只希望今年内可以成行一次。

“北京欢迎你”,这些天都在听的歌。说不出的感觉,喜欢它的歌词,更喜欢它的MV;尤其是它让我第一次为自己的家在北京而感到自豪。祈盼奥运的那段时间,北京每天都是笑脸,北京每天都是晴天。

08年的五月,又是一段不平凡的日子。其实我们每天的生活都是不平凡的,只是有时候我们不善于发现平凡中的特别,才使得原本是丰富多彩的生活变得平庸罢了。在08年的五月,我祈福,希望中华平安,希望家人和爱人平安,希望朋友平安,希望自己平安.......

12 mayo

Host Windows Explorer in your applications using the new Vista hosting APIs

漫长的等待,可爱的微软总算是支持这个feature了。只是不知道到现在有多少人用了vista。。。:)

这个老外写的很详细,试验了一下在Dialog里同样可以work。

clip_image001

Introduction

A neat but barely documented feature of Windows Vista is the API support to host a Windows Explorer window. The image above shows the screenshot of the sample MFC application hosting the Explorer window. The COM based API is quite simple, and it allows a considerable control over the Windows Explorer window, in the following ways:

  1. Ability to control the different view types: Large Icon, Small Icon, Tiles, Details, Thumbnails etc.
  2. Automatic TravelLog: The user's browser history is maintained by the hosted Explorer window, and the hosting application can control the forward/back/up navigation.
  3. Ability to handle the Explorer window navigation events and take appropriate actions to control the navigation from within the hosted application.
  4. Ability to display navigational frames like that in the Explorer.

This feature is extremely useful in applications such as Installer IDEs, CD Burners, or any other application which shows a view of the Shell or file system and allows users to drag and drop items from the Shell view to the application window. Another use of this feature may be in a Visual Studio add-in that can display a file system view within the IDE. There might be more uses in your custom application with the only (big) drawback being that this feature is only available in Windows Vista.

Preparing the Build Environment

To start, you need to install the Microsoft Windows Vista SDK. The installation is available as a web install or a DVD image. The web install is a small exe which you run to start the installation, and the exe downloads the files from internet on an as needed basis. The web install normally takes around 2-3 hours to complete. However, if you want to install the SDK on multiple machines, or reinstall on the same machine at a later time, you should download the DVD image of the installation. You can download the SDK from this link.

Once you have downloaded and installed the SDK, you need to integrate the SDK with VS2005. The SDK ships with new C++ header and library files that contain the definition of the new APIs available in Windows Vista. Thus, you need to set the path of your VS2005 header and library files to point to the installation directory of VS2005. The easiest way to do that is to launch a bat file that comes with the Windows SDK installation. This can be launched from the Start menu, through a shortcut as shown below:

clip_image002

Once you have run this tool, you can verify that the path of the include, header, and executable file of VS2005 has changed, by launching the Tools -> Options dialog:

clip_image003

An important thing to note here is that the executable files directory, include directory, and the library files directory should be modified. The Windows SDK ships with newer versions of tools such as the MIDL compiler, which are needed to build applications for Windows Vista, and so that path of executable files should also be set correctly.

Once you have verified that the build environment is set up properly, you can start developing for Windows Vista. To get all the Vista APIs, you need to define the macros WINVER, WIN32_WINNT, and WIN32_IE appropriately:

#define WINVER 0x0600

#define _WIN32_WINNT 0x0600

#define _WIN32_IE 0x0700

Now, you can start using the Windows Vista APIs in your application. Let's move next to see how the Explorer can be hosted in an MFC application.

Hosting the Explorer Window in the View

In this sample application, we will host the Explorer window in a CView derived class. There is no restriction on what window the Explorer can be hosted on. For example, it can even be hosted in a dialog. I found it the hard way that the view should have the WS_CLIPSIBLINGS and WS_CLIPCHILDREN Windows styles set, otherwise the hosted Explorer has some painting issues. This can be done in the PreCreateWindow function.

BOOL CExplorerBrowserView::PreCreateWindow(CREATESTRUCT& cs)

{

if (!CView::PreCreateWindow(cs))

return FALSE;

cs.style |= WS_CLIPCHILDREN | WS_CLIPSIBLINGS;

return TRUE;

}

The actual hosting API is a COM API, and the interface which we need to use is the IExplorerBrowser interface. A pointer to this interface should be a member of the CView derived class, and it should have the same lifetime as the CView derived class. In the sample application, a smart pointer to the IExplorerBrowser interface is made a member of the CExplorerBrowserView class.

class CExplorerBrowserView : public CView

{

private:

CComPtr<IExplorerBrowser> m_spExplorerBrowser;

Next, we need to create the actual Explorer window as a child of the view window. This can be done in the OnCreate method which gets called when the window is created. Also, we can handle any error that may occur when hosting the browser. First, we need to instantiate the ExplorerBrowser COM class which has the CLSID of CLSID_ExplorerBrowser. Once the object is instantiated successfully, a call to the Initialize method creates the Explorer window as a child of the view window. The following is the code to create the Explorer window:

int CExplorerBrowserView::OnCreate(LPCREATESTRUCT lpCreateStruct)

{

if (CView::OnCreate(lpCreateStruct) == -1)

return -1;

HRESULT hr = SHCoCreateInstance(NULL, &CLSID_ExplorerBrowser, NULL,

IID_PPV_ARGS(&m_spExplorerBrowser));

if (SUCCEEDED(hr))

{

if (theApp.ShowFrames())

m_spExplorerBrowser->SetOptions(EBO_SHOWFRAMES);

FOLDERSETTINGS fs;

fs.fFlags = 0;

fs.ViewMode = FVM_DETAILS;

hr = m_spExplorerBrowser->Initialize(m_hWnd, CRect(0, 0, 0, 0), &fs);

}

return SUCCEEDED(hr) ? 0 : -1;

}

Notice the call to the SetOptions method before calling the Initialize method. The SetOptions allows you to specify more options on how the display should look like. For example, you can include frames when displaying the Explorer window. The frames consist of the main details view, the places bar, the folders tree, and the details pane, as shown in the screenshot below:

clip_image004

The default is to not show the frames, and the view looks like the following:

clip_image005

The call to SetOptions(EBO_SHOWFRAMES) displays the frames. Once the Initialize method is called, it is not possible to turn on or turn off the frames. If you intend to show frames dynamically, you have to destroy the Explorer browser and call the Initialize method again.

Cleaning Up After the View is Destroyed

When the view is destroyed, the Explorer browser object should be cleaned up. Note that merely releasing the COM interface pointer is not sufficient. You have to call IExplorerBrowser::Destroy in the OnDestroy method as shown below:

void CExplorerBrowserView::OnDestroy()

{

CView::OnDestroy();

m_spExplorerBrowser->Destroy();

}

Sizing the Explorer Browser Window

If the view consists of entirely the Explorer browser window, it needs to be sized to fit the view. The Explorer Browser object provides the SetRect method that can be used to resize the window. The code to resize is placed in the OnSize method of the view object, and is shown below:

void CExplorerBrowserView::OnSize(UINT nType, int cx, int cy)

{

CView::OnSize(nType, cx, cy);

m_spExplorerBrowser->SetRect(NULL, CRect(0, 0, cx, cy));

}

So far, we have learnt how to create the Explorer browser window, size it, and destroy it. But it still does not do anything interesting. Now in the next section, we will see how to navigate using the Explorer browser.

Navigating Using the Explorer Browser

The hosted Explorer can be navigated to any Shell folder which includes file system folders and non-file system folders like Network, Printers, and Control Panel. Because the Windows Shell items are not limited to just the file system, items in the Shell are not identified by the path, rather they are identified by item ID lists (PIDL). (The Item ID List structure is described in more detail in the Shell documentation available on MSDN.) The IExplorerBrowser provides a function named BrowseToIDList which accepts the item ID list of a Shell folder to which the browser navigates. To navigate to a file system path, we have to convert the file system path to an item ID list. The following code shows how the Explorer browser can be navigated to a folder specified by a file path.

bool CExplorerBrowserView::NavigateTo(LPCTSTR szPath)

{

HRESULT hr = S_OK;

LPITEMIDLIST pidlBrowse;

if (FAILED(hr = SHParseDisplayName(szPath, NULL, &pidlBrowse, 0, NULL)))

{

ATLTRACE("SHParseDisplayName Failed! hr = %d\n", hr);

return false;

}

if (FAILED(hr = m_spExplorerBrowser->BrowseToIDList(pidlBrowse, 0)))

ATLTRACE("BrowseToIDList Failed! hr = %d\n", hr);

ILFree(pidlBrowse);

return SUCCEEDED(hr);

}

The NavigateTo function accepts the path of a folder. It first converts the folder to PIDL using the SHParseDisplayName function, and finally it calls the BrowseToIDList function to make the Explorer browser navigate to that folder. Finally, it frees the allocated PIDL by calling ILFree.

The Explorer browser also maintains a travel log of all the folders visited. It is possible to programmatically navigate back or forward. This can be done using the BrowseToIDList function as shown below:

void CExplorerBrowserView::OnViewBack()

{

m_spExplorerBrowser->BrowseToIDList(NULL, SBSP_NAVIGATEBACK);

}

void CExplorerBrowserView::OnViewForward()

{

m_spExplorerBrowser->BrowseToIDList(NULL, SBSP_NAVIGATEFORWARD);

}

Listening to Navigation Events

In addition to programmatic navigation, the Explorer browser also provides means to listen to navigation events which occur when the user navigates using the Explorer user interface (e.g., by double clicking on a folder). This can be done by supplying the Explorer browser object with an object implementing the IExplorerBrowserEvents. The CExplorerBrowserEvents class in the sample implements the IExplorerBrowserEvents and delegates to the main view class. Here is the code for CExplorerBrowserEvents:

class CExplorerBrowserEvents :

public CComObjectRootEx<CComSingleThreadModel>,

public IExplorerBrowserEvents

{

private:

CExplorerBrowserView* m_pView;

public:

BEGIN_COM_MAP(CExplorerBrowserEvents)

COM_INTERFACE_ENTRY(IExplorerBrowserEvents)

END_COM_MAP()

public:

void SetView(CExplorerBrowserView* pView)

{

m_pView = pView;

}

public:

STDMETHOD(OnNavigationPending)(PCIDLIST_ABSOLUTE pidlFolder)

{

return m_pView ? m_pView->OnNavigationPending(pidlFolder) : E_FAIL;

}

STDMETHOD(OnViewCreated)(IShellView *psv)

{

if (m_pView)

m_pView->OnViewCreated(psv);

return S_OK;

}

STDMETHOD(OnNavigationComplete)(PCIDLIST_ABSOLUTE pidlFolder)

{

if (m_pView)

m_pView->OnNavigationComplete(pidlFolder);

return S_OK;

}

STDMETHOD(OnNavigationFailed)(PCIDLIST_ABSOLUTE pidlFolder)

{

if (m_pView)

m_pView->OnNavigationFailed(pidlFolder);

return S_OK;

}

};

All four events fired by the Explorer browser and handled in the CExplorerBrowserEvents class are delegated back to the hosting CExplorerBrowserView instance. The view itself declares the event handling methods as virtual so that they can be overridden by the derived class.

class CExplorerBrowserView : public CView

{

protected:

virtual HRESULT OnNavigationPending(PCIDLIST_ABSOLUTE pidlFolder);

virtual void OnNavigationComplete(PCIDLIST_ABSOLUTE pidlFolder);

virtual void OnViewCreated(IShellView *psv);

virtual void OnNavigationFailed(PCIDLIST_ABSOLUTE pidlFolder);

...

}

Let's take a look at these event methods in a little more detail. Three of these methods take the PIDL of the folder as parameter.

  • The OnNavigationPending method is called when the navigation is initiated by either the user or programmatically. The actual navigation process can be canceled by returning a failure HRESULT from the method.
  • The OnNavigationComplete method is called when the navigation has completed; this method is the right place to update the UI so that it is in sync with the navigated folder.
  • The OnViewCreated method is called just after the navigation pending method. The Explorer destroys and recreates the list view window each time it navigates to a folder. This method takes a pointer to the IShellView interface which provides additional methods to query and control the view.
  • Finally, the OnNavigationFailed method is called if the folder navigation fails for some reason.

Once we have implemented the IExplorerBrowserEvents, we need to create an instance of the implementing class, CExplorerBrowserEvents in this case, and supply it to the Explorer browser object. This is done by calling the IExplorerBrowser::Advise method. This method should be called before navigating the browser to a particular folder. In the case of CExploreBrowserView, this function is called in the OnCreate function just after the call to Initialize.

int CExplorerBrowserView::OnCreate(LPCREATESTRUCT lpCreateStruct) {

....

CComObject<CExplorerBrowserEvents>* pExplorerEvents;

if (SUCCEEDED(CComObject<CExplorerBrowserEvents>::

CreateInstance(&pExplorerEvents)))

{

pExplorerEvents->AddRef();

pExplorerEvents->SetView(this);

m_spExplorerBrowser->Advise(pExplorerEvents, &m_dwAdviseCookie);

pExplorerEvents->Release();

}

}

The call to the Advise method ensures that the CExplorerBrowserEvents class receives a notification for each of the events. A matching call to the Unadvise methods stops the Explorer browser from notifying events to the object. The Advise method returns a DWORD cookie which can be supplied to the Unadvise method. The call to Unadvise is in the OnDestroy method of CView:

void CExplorerBrowserView::OnDestroy()

{

CView::OnDestroy();

if(m_dwAdviseCookie)

{

m_spExplorerBrowser->Unadvise(m_dwAdviseCookie);

}

m_spExplorerBrowser->Destroy();

}

Obtaining the List of Selected Items

In some applications, it might be necessary to obtain the selected items in the Explorer browser and take some action on them. To obtain the selected items is a two step process:

  1. Obtain an interface pointer to the IShellView interface by calling the GetCurrenView method.

2. CComPtr<IShellView> spSV;

if (SUCCEEDED(m_spExplorerBrowser->GetCurrentView(IID_PPV_ARGS(&spSV))))

  1. The IShellView pointer thus obtained can be used to get the selected items as an OLE DataObject.

4. CComPtr<IDataObject> spDataObject;

5. if (SUCCEEDED(spSV->GetItemObject(SVGIO_SELECTION,

IID_PPV_ARGS(&spDataObject))))

  1. Finally, the data object can be used to get the selected items as shown below:

7. //Code adapted from http://www.codeproject.com/shell/shellextguide1.asp

8. FORMATETC fmt = { CF_HDROP, NULL, DVASPECT_CONTENT,

9. -1, TYMED_HGLOBAL };

10. STGMEDIUM stg;

11. stg.tymed = TYMED_HGLOBAL;

12.

13. if (SUCCEEDED(spDataObject->GetData(&fmt, &stg)))

14. {

15. HDROP hDrop = (HDROP) GlobalLock ( stg.hGlobal );

16.

17. UINT uNumFiles = DragQueryFile ( hDrop, 0xFFFFFFFF, NULL, 0 );

18. HRESULT hr = S_OK;

19.

20. for(UINT i = 0; i < uNumFiles; i++)

21. {

22. TCHAR szPath[_MAX_PATH];

23. szPath[0] = 0;

24. DragQueryFile(hDrop, i, szPath, MAX_PATH);

25.

26. if (szPath[0] != 0)

27. arrSelection.Add(szPath);

28. }

29.

30. GlobalUnlock ( stg.hGlobal );

ReleaseStgMedium ( &stg );

The above code adds the path of the selected items to a CStringArray object.

The CExploreBrowserView class provides a method called GetSelectedItems which accepts a CStringArray reference; on return, the string array is filled with the path of each of the selected items.