<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    上善若水
    In general the OO style is to use a lot of little objects with a lot of little methods that give us a lot of plug points for overriding and variation. To do is to be -Nietzsche, To bei is to do -Kant, Do be do be do -Sinatra
    posts - 146,comments - 147,trackbacks - 0
    引用自:http://blog.163.com/yesaidu@126/blog/static/51819307200861853827582/

    Part I: A step-by-step tutorial on writing shell extensions

    第一節:Windows shell擴展初步:上下文菜單擴展

     

    作者:Michael Dunn

    譯者:yesaidu

     

    源代碼下載:1       2

     

    目錄

    README

    系列緒言

    第一部分緒言

    AppWizard開始

    初始化接口

    上下文菜單交互接口

    更改上下文菜單

    在狀態欄顯示拉線式(fly-by)幫助

    執行用戶選擇

    其他代碼細節

    注冊Shell擴展

    調試Shell擴展

    所有的外觀

    版權與許可

    修訂歷史

     

     

    README

             我想,你在行動之前,或者你在本手冊的討論板發帖之前應該閱讀這份材料。

             本手冊最初是用VC 6編寫的。現在,VC8都出來了,我感覺是時候對本手冊進行升級到VC7.1了。(通過VC7.1自動升級VC6項目,并不一定會完全地完成代碼轉換;因此,VC7.1用戶可能碰到這樣的現象,即在轉換、編譯示例代碼后,運行時可能沒有效果或出錯。)只要我仔細檢查并更新本手冊,本手冊將體現VC7.1的新特點。我將會提供VC7.1項目的源碼下載。

             VC2005用戶要注意了:VC2005體驗版(Express edition沒有一同發布ATLMFC。既然本手冊用到了ATL,有時還使用了MFC,因此,你不能用VC2005體驗版來編譯示例代碼。

             如果你正使用VC6,那么,你應該設法取得最新的平臺SDK。你可以使用WEB安裝版web install version),或者下載CAB文件或者ISO鏡像包,安裝它們到本地。確認把SDKINCLUDELIB目錄添加到了VC的搜索路徑中。你能在PSDK程序組中找到Visual Studio Registration目錄。這是一個好主意,無論你使用VC7,還是用VC8,你都能取得最新的PSDK頭文件和庫文件。
    Windows Shell擴展編程傻瓜手冊大全:上下文菜單擴展 - yesaidu - yesaidu的博客

    VC7用戶注意了:如果你沒有更新PSDK必須改變默認的INCLUDE路徑。確信“VC++目錄”-“包含文件”列表的第一項是$(VCInstallDir)PlatformSDK\include,它在($VCInstallDir)include前面,如下圖:
    Windows Shell擴展編程傻瓜手冊大全:上下文菜單擴展 - yesaidu - yesaidu的博客

             由于一直沒有使用過VC 8,因此我不確定示例代碼在VC 8上是否可以通過編譯。只是希望,把VC7項目升級到VC8的自動轉換功能比從VC6VC7的要好些。如果你使用VC8編譯示例時遇到了任何疑惑,請在討論板發帖。

     

     

    手冊緒言

             所謂shell擴展就是能增加某些功能到Windows資源管理器的COM對象。Shell擴展有很多內容,但關于它們的文檔資料卻非常少見。(自從我最先發表這份手冊的六年來,我相信情況要好多了。)如果你想深入Windows shell的內部,極力推薦Dino Esposito的巨作Visual C++ Windows Shell Programming (ISBN 1861001843)。對于沒有這本書的人,或者僅僅對shell 擴展感興趣的朋友,我將給你一個驚喜:一本有關shell 擴展編程的傻瓜手冊。即使本手冊并未讓你感到驚喜,那么,對你理解如何編寫shell擴展也會提供很好的幫助。本手冊假定你理解并掌握了COMATL的基本原理和應用。如果你還需要學習COM基本原理,請參考Intro to COM

             第一節介紹了shell擴展的概要,并提供了一個上下文菜單擴展的示例,使你對后面的章節充滿興趣。

             從字面上看,shell擴展包括兩個方面:shell和擴展。所謂shell,就是資源管理器Explorer;而擴展就是指在預定的事件發生時由Explorer調用執行的代碼(比如,在.DOC文件上右擊)。因此,shell擴展就是為Explorer增添功能的COM對象。

             shell擴展是一個進程內服務器,它實現了跟Explorer通信的接口。ATL是設計一個shell擴展,并使之運行的最簡單辦法;這樣你就不用為一遍又一遍的編寫QueryInterface()AddRef()而大傷腦筋。在Windows NT下調試shell擴展要更容易些,這點,我在后面還會談到。

             Shell擴展有很多種類型,每一類型都有其被調用的時機:即每種類型在不同的事件發生時被調用執行。下表列出了一些較常見的類型,以及它們被調用的情況:

    類型

    被調用的時機

    它可以做什么

    Context menu擴展處理器

    用戶在文件對象或文件夾對象或目錄窗口背景(需要shell v 4.71+以上)單擊右鍵

    在上下文菜單中添加菜單項

    Property sheet擴展處理器

    文件屬性對話框顯示時

    在屬性對話框中定制屬性頁

    Drag and drop擴展處理器

    用戶用右鍵拖放文件到文件夾窗口或桌面時

    在上下文菜單中添加菜單項

    Drop handler擴展處理器

    用戶拖對象并將其放到文件上時

    任何你想做的

    QueryInfo 擴展處理器
    (
    需要shell version 4.71+)

    用戶在文件、“我的電腦”等其他shell對象的圖標上懸停時

    返回一個Explorer顯示在工具提示中的字符串


     

     

     

    第一節緒言

             現在,你可能有很多的疑問:為什么擴展看起來像Explorer?它到底是什么樣的?一個例子就是WinZip(或者WinRAR,我沒安裝WinZip ^_^――譯者)——它包含了多種shell擴展,其中之一就是上下文擴展。下圖是WinZip(其實是WinRA ^_^――譯者)為壓縮文件在上下文菜單中添加的菜單項:

    Windows Shell擴展編程傻瓜手冊大全:上下文菜單擴展 - yesaidu - yesaidu的博客

             WinZip編寫了增加菜單項的代碼,提供了Explorer狀態欄上的菜單項幫助提示(fly-by help),并在用戶選擇一個WinZip菜單命令時執行相應的操作。

             WinZip還提供了拖曳擴展處理,此類型跟上下文菜單擴展非常相似,但它是在用戶通過右鍵拖曳文件時才被觸發。下圖是WinZip(也是WinRA ^_^――譯者)拖曳文件彈出的菜單項:
    Windows Shell擴展編程傻瓜手冊大全:上下文菜單擴展 - yesaidu - yesaidu的博客

             還有很多的shell擴展類型,Microsoft不斷向每一個新的Windows版本中增加更多的類型。現在,讓我們把注意力放到上下文菜單擴展上,因為它易于編寫,效果也很明顯(能夠立即讓你滿意)。

             在動手編碼之前,有一些便于編碼和調試的小技巧:當Explorer調用shell擴展(由用戶觸發)后,shell擴展暫時駐于內存中;此時,你無法重新編譯此擴展的DLL文件。為讓Explorer更迅速卸載擴展,可以在注冊表中創建下面的鍵:

    HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Explorer\AlwaysUnloadDLL

    并設置其默認值為“1”。在Win9x平臺上,這是最好的辦法。在WinNT上,可以在下面的鍵

    HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer

    創建一個DWORD DesktopProcess,也設置它的值為1。(譯者:如下圖,Win9x系統太少見了)這使得“桌面”和“任務欄”運行于一個進程,其他的Explorer窗口運行在其獨立進程。這意味著,你可以調試單個Explorer窗口,當你關閉該窗口時,相關的擴展DLL就會被自動卸載,這樣就避免了DLL文件正被Windows使用而無法替換的問題。要使注冊表修改生效,需要注銷后重新登錄。

    Windows Shell擴展編程傻瓜手冊大全:上下文菜單擴展 - yesaidu - yesaidu的博客

             稍后,我將說明Win9x下如何進行調試。

     

     

    使用AppWizard開始

             我們先做一個簡單的擴展,它僅僅彈出一個消息框以表明工作正常。我們把它關聯到文本文件,這樣,當我們在一個文本文件上右擊時,該擴展就會被調用。

             好了,讓我們開始吧!什么?我還沒有告訴你如何使用那些神秘的shell擴展接口?別著急,我會邊進行邊解釋。我覺得,給出一個概念,緊跟著一個示例代碼,這樣做有助于理解。當然,我也可以先解釋所有的概念,然后列出示例代碼,不過這樣很難吸引注意力。不管怎樣,開啟你的VC,我們要開始了。

             運行AppWizard,生成一個名為“SimpleExt”的ATL COM工程:

    Windows Shell擴展編程傻瓜手冊大全:上下文菜單擴展 - yesaidu - yesaidu的博客去掉屬性化,保留其它默認選項,點擊“完成”。
    Windows Shell擴展編程傻瓜手冊大全:上下文菜單擴展 - yesaidu - yesaidu的博客
    現在,我們有了一個空的ATL項目,它可以編譯生成一個DLL,但我們還需要添加shell擴展COM對象。在類視圖中,右擊“SimpleExt”項,選擇“新建ATL 對象” VC7,選擇“添加”→“添加類”,下圖。本文的環境是Windows XPVC7.1,因此附圖都是VC7的)。

    Windows Shell擴展編程傻瓜手冊大全:上下文菜單擴展 - yesaidu - yesaidu的博客

             ATL對象向導中,第一頁已經選擇了“簡單對象”,點擊“下一步”。
    Windows Shell擴展編程傻瓜手冊大全:上下文菜單擴展 - yesaidu - yesaidu的博客

    在第二頁,在“簡稱”編輯框中輸入“SimpleShlExt”(其他編輯框會自動完成):
    Windows Shell擴展編程傻瓜手冊大全:上下文菜單擴展 - yesaidu - yesaidu的博客

             在默認情況下,向導將創建以C和腳本客戶端為基礎的OLE自動化兼容的COM對象。我們的擴展僅僅由Explorer調用,因此我們去掉自動化支持。在“屬性”頁,選擇“接口”類型為“自定義”,并且選擇“聚合”為“否”:
    Windows Shell擴展編程傻瓜手冊大全:上下文菜單擴展 - yesaidu - yesaidu的博客

             點擊“完成”,就創建了CSimpleShlExt類,它包含了實現COM對象的最基本代碼。我們將向這個類加入代碼。

     

     

    初始化接口

             當我們的shell擴展被加載時,Explorer將調用QueryInterface()函數,以取得IShellExtInit接口指針。該接口僅有一個方法Initialize(),其函數原型如下:

    HRESULT IShellExtInit::Initialize (

                                   LPCITEMIDLIST pidlFolder,

                                   LPDATAOBJECT pDataObj,

                                   HKEY hProgID )

    Explorer通過該方法向我們傳遞各種各樣的信息。pidlFolder是用戶所操作文件所在的文件夾的PIDLPIDL [pointer to an ID list],指向ID列表的指針,是一個數據結構,它唯一標識了在shell空間的任何對象,這個對象是或者不是文件系統的對象。) pDataObj是一個IDataObj接口變量,通過它可以取得用戶正操作的文件名。 hProgID是一個HKEY接口變量,通過它可以取得擴展DLL的注冊信息。在本例中,僅僅需要pDataObj參數。

             要添加這一方法到我們的COM對象,打開文件SimpleShlExt,加入下列粗體的行。AppWizard生成了一些不必需的COM關系代碼,既然我們不實現我們自己的接口,所以我指出這些失敗的能被移除的代碼(帶刪除線的那些):

           #include <shlobj.h>

           #include <comdef.h>

     

           class ATL_NO_VTABLE CSimpleShlExt :

              public CComObjectRootEx<CComSingleThreadModel>,

              public CComCoClass<CSimpleShlExt, &CLSID_SimpleShlExt>,

              public ISimpleShlExt,

              public IShellExtInit

           {

              BEGIN_COM_MAP(CSimpleShlExt)

                  COM_INTERFACE_ENTRY(ISimpleShlExt)

                  COM_INTERFACE_ENTRY(IShellExtInit)

              END_COM_MAP()

    COM_MAPATL實現QueryInterface的宏,它告訴ATL其它程序能從COM對象取得哪些接口。

             在類聲明中,加入Initialize函數。此外,還需要一個變量來保存文件名:

           protected:

               TCHAR m_szFile[MAX_PATH];

     

           public:

               // IShellExtInit

               STDMETHODIMP Initialize(LPCITEMIDLIST, LPDATAOBJECT, HKEY);

    接著,在文件SimpleShlExt.cpp中,添加Initialize的實現代碼:

           STDMETHODIMP CSimpleShlExt::Initialize (

                                              LPCITEMIDLIST pidlFolder,

                                              LPDATAOBJECT pDataObj,

                                              HKEY hProgID)

             我們要做的是取得右鍵單擊選中的文件名,并把它顯示在消息框中。如果選中了多個文件,可以通過pDataObj接口指針來訪問它們,不過為了保持例子的簡單,我們只獲取第一個文件名。

             文件名的格式和拖曳文件到WS_EX_ACCEPTFILES風格的窗口是的文件名格式一致,這樣說來,我們可以通過同樣的APIDragQueryFile來取得文件名。我們先取得包含在IDataObject中的數據句柄:

           void CSimpleShlExt::Initialize (LPCITEMIDLIST pidlFolder, LPDATAOBJECT pDataObj, HKEY hProgID)

           {

               FORMATETC fmt = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };

               STGMEDIUM stg = { TYMED_HGLOBAL };

               HDROP      hDrop;

     

               // 在數據對象內查找CF_HDROP類型數據。

               // 如果沒有數據,返回一個錯誤(“無效參數”)給Explorer

               if ( FAILED( pDataObj->GetData ( &fmt, &stg ) ))

                  return E_INVALIDARG;

     

               // 取得指向實際數據的指針。

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

     

               // 確保非NULL

               if ( NULL == hDrop )

                  return E_INVALIDARG;

             注意,錯誤檢查是極其重要的,尤其是對指針的檢查。因為我們的擴展運行于Explorer進程空間,如果我們的程序掛了,Explorer會跟著掛。在Win9x系統上,這樣的崩潰可能導致需要重啟系統。

             現在,我們有了HDROP句柄,可以取得所需的文件名了:

               // 有效性檢查,至少有一個文件名

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

               HRESULT hr = S_OK;

     

               if ( 0 == uNumFiles )

               {

                  GlobalUnlock ( stg.hGlobal );

                  ReleaseStgMedium ( &stg );

                  return E_INVALIDARG;

               }

     

               // 取得第一個文件名,保存到 m_szFile

               if ( 0 == DragQueryFile ( hDrop, 0, m_szFile, MAX_PATH ) )

                  hr = E_INVALIDARG;

     

               GlobalUnlock ( stg.hGlobal );

               ReleaseStgMedium ( &stg );

     

               return hr;

    }

    如果返回E_INVALIDARGExplorer不會在右鍵事件時再調用我們的擴展。如果返回S_OKExplorer將再次調用QueryInterface(),以取得另一接口:IContextMenu

     

     

    與上下文菜單交互的接口

             一旦Explorer初始化了我們的擴展,它將調用IContextMenu方法來增加菜單項、提供狀態欄幫助(fly-by help),以及響應用戶的選擇。

             添加IContextMenu接口與IShellExtInit相類似。打開文件SimpleShlExt.h,加入下列加粗的行:

    class ATL_NO_VTABLE CSimpleShlExt :

                 public CComObjectRootEx<CComSingleThreadModel>,

                 public CComCoClass<CSimpleShlExt, &CLSID_SimpleShlExt>,

                 public IShellExtInit,

                   public IContextMenu

           {

              BEGIN_COM_MAP(CSimpleShlExt)

                  COM_INTERFACE_ENTRY(IShellExtInit)

    COM_INTERFACE_ENTRY(IContextMenu)

              END_COM_MAP()

    接著,添加IContextMenu方法的函數原型:

           public:

               // IContextMenu

               STDMETHODIMP GetCommandString (UINT, UINT, UINT*, LPSTR, UINT);

               STDMETHODIMP InvokeCommand (LPCMINVOKECOMMANDINFO);

               STDMETHODIMP QueryContextMenu (HMENU, UINT, UINT, UINT, UINT);

     

     

    修改上下文菜單

             IContextMenu有三個方法。第一個QueryContextMenu()修改上下文菜單。它的原型如下:

           HRESULT IContextMenu::QueryContextMenu (

                                              HMENU hmenu,

                                              UINT uMenuIndex,

                                              UINT uidFirstCmd,

                                              UINT uidLastCmd,

                                              UINT uFlags );

    hmenu是上下文菜單句柄。uMenuIndex是我們要添加菜單項的開始位置。uidFirstCmd uidLastCmd是菜單命令ID值范圍。uFlags表明Explorer調用QueryContextMenu()的緣由,這個后面還會談到。

             關于此方法的返回值,你翻閱不同的文檔,可能得到不同的答案。Dino Esposito在他的書中認為這個返回值是所添加的菜單項的數目。但VC6MSDN卻說它是最后一個菜單項的命令ID1;然而,聯機MSDN卻說:

    如果函數成功,返回的HRESULT值就是分配的菜單項命令ID的最大差值加1。例如,uidFirstCmd5,你添加了3個菜單項,它們的命令ID分別是578。那么,返回值應該是MAKE_HRESULT(SEVERITY_SUCCESS,  0,  8 - 5 + 1)。否則,返回一個OLE錯誤。

             一直以來,我都按照Dino的解釋來編寫代碼,這些代碼工作地很好。實際上,他的解釋與聯機MSDN是一致的,只要將uidFirstCmd作為第一項菜單項ID,后續的菜單項依次累加1

             我們這里的擴展簡單的加入一個菜單項,因此QueryContextMenu()函數非常簡單:

     

           STDMETHODIMP CSimpleShlExt::QueryContextMenu (

               HMENU hmenu, UINT uMenuIndex, UINT uidFirstCmd,

               UINT uidLastCmd, UINT uFlags )

           {

               // 如果標識包含了 CMF_DEFAULTONLY,那么,我們啥都不做

               if ( uFlags & CMF_DEFAULTONLY )

                  return MAKE_HRESULT ( SEVERITY_SUCCESS, FACILITY_NULL, 0 );

     

               InsertMenu ( hmenu, uMenuIndex, MF_BYPOSITION, uidFirstCmd, _T("簡單SHELL擴展測試") );

     

               return MAKE_HRESULT ( SEVERITY_SUCCESS, FACILITY_NULL, 1 );

           }

             首先,我們檢查uFlags的值。在MSDN內,你能找到所有的標識和它們的解釋,但對于上下文菜單擴展而言,僅僅一個值是有意思的:即CMF_DEFAULTONLY。該標識告訴shell命名空間擴展保留默認的菜單項;(如果設置了它的話,)shell擴展將不增加任何的菜單項。這也是我們為什么返回0的原因。如果未設置它,我們就可以通過句柄hmenu來修改菜單,并返回1告訴shell增加了一個菜單項。

     

     

    在狀態欄顯示提示幫助(fly-by help

             下一個要被調用的IContextMenu方法是GetCommandString()。當用戶在Explorer窗口中右擊文本文件,或者選中文本文件后點擊“文件”菜單,鼠標指到我們添加的菜單項時,狀態欄將顯示提示信息。GetCommandString()函數返回一個字符串供Explorer顯示。

             GetCommandString()原型如下:

           HRESULT IContextMenu::GetCommandString (

                                UINT idCmd, UINT uFlags, UINT* pwReserved,

                                LPSTR pszName, UINT cchMax );

    idCmd是基于0的計數器,它表明了被選中的菜單項。由于我們只有一個菜單項,所以idCmd總為0。不過,如果我們添加了,比如說,3個菜單項,idCmd就是012uFlags是另外的一組標識,這個留待后面再討論。pwReserved可以被忽略。pszNameshell所有的緩沖區,用于顯示的幫助信息將拷貝到它中。cchMax是上述緩沖區的尺寸。返回值是HRESULT常量,比如說S_OKE_FAIL

             GetCommandString()也能用來取得菜單項的“動作”(verb)。動作是與語言無關的串,它標識了能作用于文件對象的動作。關于這點,ShellExecute()的文檔中有更詳細的說明;有關動作的內容最好留待另外的文章(可以就這方面的內容另外一篇文章),這里簡要的說,是列在注冊表中的動作(比如“打開”和“打印”),或者有上下文菜單擴展動態創建的動作。這可以通過ShellExecute()來調用shell擴展的動作。

             總之,我7li8li的說了這么多,就是為了解釋清楚GetCommandString()的作用。如果Explorer要提示信息,我們就給它;如果Explorer請求一個動作,就忽視它。這就是uFlags的作用。如果uFlags設置了GCS_HELPTEXT位,Explorer請求提示信息。另外,如果uFlags設置了GCS_UNICODE,我們必須給它一個UNICODE串。

             本例中GetCommandString()如下:

           #include <atlconv.h>  // ATL串轉換宏

     

           STDMETHODIMP CSimpleShlExt::GetCommandString (

               UINT idCmd, UINT uFlags, UINT* pwReserved, LPSTR pszName, UINT cchMax )

           {

               USES_CONVERSION;

     

               // 由于這里只有一個菜單項,所以idCmd 必須為0

               if ( 0 != idCmd )

                  return E_INVALIDARG;

     

               // 如果Explorer請求提示信息,拷貝串到提供的緩沖區

               if ( uFlags & GCS_HELPTEXT )

               {

                  LPCTSTR szText = _T("簡單的SHEEL擴展幫助(fly-by help)");

     

                  if ( uFlags & GCS_UNICODE )

                  {

                      // 這里,需要把 pszName 轉換為 UNICODE

                      lstrcpynW ( (LPWSTR) pszName, T2CW(szText), cchMax );

                  }

                  else

                  {

                      // ANSI版本

                      lstrcpynA ( pszName, T2CA(szText), cchMax );

                  }

     

                  return S_OK;

               }

     

               return E_INVALIDARG;

           }

             這沒什么奇怪的;我使用了硬編碼并將它轉換為合適的字符集。如果你從沒用過ATL轉換宏,你最好去學一下。它們在你向COM方法和OLE函數傳遞參數時,十分有用。

             一個需要注意的重要事項是,lstrcpyn()函數保證字符串是以NULL結束的。這和CRTC運行時)函數strncpy()不同,后者在源串的長度大于等于cchMax時并不在串最后插入NULL。我建議總是使用lstrcpyn(),這樣就無需在調用strncpy()后總是檢查以確保串是否以NULL結束。

     

     

    執行用戶的選擇

             IContextMenu接口最后的方法是InvokeCommand()。此方法在用戶點擊我們增加的菜單項后被調用,其函數原型如下:

           HRESULT IContextMenu::InvokeCommand ( LPCMINVOKECOMMANDINFO pCmdInfo );

    結構CMINVOKECOMMANDINFO9個成員,就我們的目的而言,僅僅需要關注lpVerbhwndslpVerb有兩個用途:它既可以是被引發的動作名,也可以是被點擊的菜單向索引。hwnds是用戶引發我們的擴展時所在的Explorer窗口句柄;我們可以將其作為我們用來顯示信息的窗口的父窗口。

             由于我們只有一個菜單項,所以只需要檢查lpVerb:如果它為0,那么我們的菜單項就被選中了。最簡單的事情是彈出消息框,這里的代碼也就能干這事兒。消息框顯示了被選中的文件名,這表明代碼工作正常。

           STDMETHODIMP CSimpleShlExt::InvokeCommand ( LPCMINVOKECOMMANDINFO pCmdInfo )

           {

               // 如果 lpVerb 指向一個實際串,忽略此次調用并退出

               if ( 0 != HIWORD( pCmdInfo->lpVerb ) )

                  return E_INVALIDARG;

     

               // 取得命令索引,這里,唯一有效的值為0

               switch ( LOWORD( pCmdInfo->lpVerb) )

               {

               case 0:

                  {

                      TCHAR szMsg [MAX_PATH + 32];

     

                      wsprintf ( szMsg, _T("被選中的文件:\n\n%s"), m_szFile );

     

                      MessageBox ( pCmdInfo->hwnd, szMsg, _T("SimpleShlExt"),

                         MB_ICONINFORMATION );

     

                      return S_OK;

                  }

                  break;

     

               default:

                  return E_INVALIDARG;

                  break;

               }

           }

     

     

    其它代碼細節

             這里,集中說明如何移除AppWizard生成的多余的OLE自動化特性方面的代碼。首先,可以移除SimpleShlExt.rgs(這個文件的用途在下一節詳述)中些注冊表入口:

        HKCR

        {

           SimpleExt.SimpleShlExt.1 = s 'SimpleShlExt Class'

           {

               CLSID = s '{1CE1EBEB-1254-4880-B807-809CC31E8D2C}'

           }

           SimpleExt.SimpleShlExt = s 'SimpleShlExt Class'

           {

               CLSID = s '{1CE1EBEB-1254-4880-B807-809CC31E8D2C}'

               CurVer = s 'SimpleExt.SimpleShlExt.1'

           }

           NoRemove CLSID

           {

               ForceRemove {1CE1EBEB-1254-4880-B807-809CC31E8D2C} = s 'SimpleShlExt Class'

               {

                  ProgID = s 'SimpleExt.SimpleShlExt.1'

                  VersionIndependentProgID = s 'SimpleExt.SimpleShlExt'

                  InprocServer32 = s '%MODULE%'

                  {

                      val ThreadingModel = s 'Apartment'

                  }

                  val AppID = s '%APPID%'

                  'TypeLib' = s '{172391D4-B01E-4EF5-AC3E-34C99889D8B0}'

               }

           }

        }

             我們也能移除DLL資源中的類型庫。(VC7)在“資源視圖”中,選中“SimpleExt.rc”,右擊,選中“資源包括”:
    Windows Shell擴展編程傻瓜手冊大全:上下文菜單擴展 - yesaidu - yesaidu的博客

    在“資源包括”對話框的“編譯時指令”中有一行類型庫包括:
    Windows Shell擴展編程傻瓜手冊大全:上下文菜單擴展 - yesaidu - yesaidu的博客

    移除這行,VC彈出警告,點擊“確定”:
    Windows Shell擴展編程傻瓜手冊大全:上下文菜單擴展 - yesaidu - yesaidu的博客

             移除類型庫后,我們還需要修改兩處代碼,以告訴ATL,它不應通過類型庫來處理。在文件SimpleExt.cpp中的DllRegisterServer()/DllUnregisterServer()函數,設置RegisterServer()/UnregisterServer()的參數為。

           STDAPI DllRegisterServer (void)

           {

               // ...

               return _Module.RegisterServer(TRUE FALSE);

           }

    STDAPI DllUnregisterServer (void)

           {

               // ...

               return _Module.UnregisterServer(TRUE FALSE);

           }

     

     

    注冊Shell擴展

             現在,我們實現所有的COM接口。不過,怎么才能讓Explorer使用我們的擴展呢?ATL自動生成注冊COMD服務器DLL 的代碼,但那是給其它程序使用。為了讓Explorer知道擴展存在,需要在文本文件的下述注冊表鍵下注冊我們的擴展:

             HKEY_CLASSES_ROOT\txtfile

    在這個注冊表鍵下,名為ShellEx的鍵保存了有關文本文件的shell擴展列表;在它的下一級,名為ContextMenuHandlers的鍵保存了上下文菜單擴展列表。每個擴展都擁有一個ContextMenuHandlers的子鍵,其默認值為shell擴展的GUID。本文簡單擴展的示例,將創建如下子鍵:

             HKEY_CLASSES_ROOT\txtfile\ShellEx\ContextMenuHandlers\SimpleShlExt

    并設置其默認值為擴展COMGUID,如“{1CE1EBEB-1254-4880-B807-809CC31E8D2C }”。

             不必編寫代碼來完成COM的注冊。在“解決方案管理器”中有文件SimpleShlExt.rgs;這是一個文本文件,它被ATL解析,指導ATL在該服務器注冊時添加哪些鍵,在卸載時刪除哪些鍵。下面是注冊該擴展所要添加的注冊表鍵:

        HKCR

        {

           NoRemove txtfile

           {

               NoRemove ShellEx

               {

                  NoRemove ContextMenuHandlers

                  {

                      ForceRemove SimpleShlExt = s '{1CE1EBEB-1254-4880-B807-809CC31E8D2C}'

                  }

               }

           }

        }

    每一行都代表一個注冊鍵名。“HKCR”是HKEY_CLASSES_ROOT的縮寫。關鍵字NoRemove表明這個鍵在服務器卸載時不用刪除。關鍵字ForceRemove表明在寫入新鍵之前,如果該鍵存在,那么就要先刪除它;這行剩下的部分指定了一個字符串(這就是“s”的意思),它是SimpleShlExt的默認值。

             這里,我插入幾句。我們的擴展注冊在HKEY_CLASSES_ROOT\txtfile下;然而,“txtfile”并不是持久的或者預先定好的名字。如果你查看一下HKEY_CLASSES_ROOT\.txt,它的默認值就是“txtfile”。這樣,有兩個副作用:

    1、  既然“txtfile”可能不是正確的鍵名,因此RGS腳本并不可靠。

    2、  某些文件編輯軟件可能在安裝到系統時,就把它自身關聯到文本文件。如果它改變了HKEY_CLASSES_ROOT\.txt的默認值,那么所有的shell擴展就不能用了。

    在我看來,這確實是一個設計上的漏洞。Microsoft可能也這么看,因為最新的擴展,如QueryInfo擴展就注冊在HKEY_CLASSES_ROOT\.txt下。

           好了,到此為止。還有一個注冊的細節,即在WinNT上,為了讓非管理員帳號也能使用我們的擴展,得把它放到“approved ”擴展列表中:

             HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved

    在這個鍵下,創建以擴展的GUID為名的串值,其內容任意。代碼在DllRegisterServer()DllUnregisterServer()中,都是一些簡單的注冊表訪問,我就不羅列了。你可以在示例代碼中找到。

     

     

    調試Shell擴展

             總有一天,你將寫一個不是這么簡單的shell擴展;那時,你不得不調試它。打開項目的屬性對話框,在“調試”的“命令”編輯框輸入“C:\windows\explorer.exe”。如果是WinNT系統,設置DesktopProcess鍵(前述),當你按F5時就啟動了一個新的Explorer窗口。只要是在這個窗口完成所有的工作,那么在關閉這個窗口時,擴展就會被卸載,這樣就不影響后面的重建DLL

             Win9x上,在運行調試器前,必須關閉shell:點擊“開始”,點擊“注銷”。按下Ctlr+Alt+Shift,并點“取消”。這將關閉Explorer,任務欄(桌面)消失了。切換到MSVC,按F5開始調試。要中止調試,按下“Shift+F5”關閉Explorer。完成調試后,可以從“開始”“運行”“Explorer.exe”,讓其正常啟動。

     

    擴展的外觀

    增加擴展后的上下文菜單項
    Windows Shell擴展編程傻瓜手冊大全:上下文菜單擴展 - yesaidu - yesaidu的博客

    Explorer狀態欄提示(fly-by help
    Windows Shell擴展編程傻瓜手冊大全:上下文菜單擴展 - yesaidu - yesaidu的博客

    彈出的消息框,顯示了所選中的文件名
    Windows Shell擴展編程傻瓜手冊大全:上下文菜單擴展 - yesaidu - yesaidu的博客

     

     

    版權與許可

    作者:Michael Dunn

    譯者:Yesaidu



    posted on 2012-07-12 11:11 DLevin 閱讀(3605) 評論(1)  編輯  收藏 所屬分類: 收藏

    FeedBack:
    # re: 【轉】Windows Shell擴展編程傻瓜手冊大全:上下文菜單擴展
    2014-06-25 12:51 | shell
    如果要所有文件都右擊都能顯示,要怎么改  回復  更多評論
      
    主站蜘蛛池模板: 亚洲AV无码乱码国产麻豆穿越| 亚洲永久精品ww47| 无码人妻一区二区三区免费| 99国产精品免费视频观看| 美女被免费网站91色| 老司机精品视频免费| 另类小说亚洲色图| 免费福利在线观看| 一级毛片免费毛片毛片| a级毛片免费高清视频| 国产无限免费观看黄网站| 五月婷婷免费视频| 成人国产精品免费视频| a级毛片在线免费| 中文字幕日本人妻久久久免费| 亚洲精品第一综合99久久| 亚洲中文字幕无码爆乳app| 亚洲国产精品无码第一区二区三区| 337p日本欧洲亚洲大胆裸体艺术| 日本XXX黄区免费看| 嘿嘿嘿视频免费网站在线观看| 久久不见久久见免费影院www日本| 国产亚洲福利在线视频| 亚洲色偷精品一区二区三区| 亚洲欧洲专线一区| 亚洲AV无码精品国产成人| 黄色网址大全免费| 日批视频网址免费观看| 91成人在线免费观看| 午夜时刻免费入口| 亚洲国产天堂久久综合| 久久亚洲精品成人综合| 亚洲人成影院77777| 国产亚洲精品91| 免费无码又爽又刺激网站直播| 一区二区视频在线免费观看| 午夜精品射精入后重之免费观看| 国产免费高清69式视频在线观看| 久久人午夜亚洲精品无码区| 两个人日本WWW免费版| 免费国产作爱视频网站|