欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
上下文菜單創(chuàng )建

Creating ContextMenu Handlers

Creating Context Menu Handlers

When a user right-clicks a Shell object,the Shell displays its context menu. For file system objects there are a numberof standard items, such as Cut and Copy, that are on the menu bydefault. If the object is a file that is a member of a class, additionalitems can be specified in the registry.Finally, the Shell checks the registry to see if the file class is associatedwith any context menu handlers. If it is, the Shell queries the handlerfor additional context menu items.

A context menu handler is a shell extension handlerthat adds commands to an existing context menu. Context menu handlers areassociated with a particular file class and are called any time a context menuis displayed for a member of the class. While you can add items to a file classcontext menu with the registry, the items will be the same for all members ofthe class. By implementing and registering such a handler, you can dynamicallyadd items to an object's context menu, customized for the particular object.

A closely related Shell extension handleris the drag-and-drop handler. It is a context menu handler that addsitems to the context menu that is displayed when a user drags and drops a filewith the right mouse button.

The procedures for implementing andregistering a Shell extension handler are discussed in Creating ShellExtension Handlers. This document focuses on those aspects ofimplementation that are specific to context menu handlers.

The following topics are discussed.

·        How Context Menu HandlersWork

·        Registering a Context Menu Handler

·        Implementing IContextMenu

·        Creating Drag-and-Drop Handlers

Context menu handlers are a type of Shellextension handler. Like all such handlers, they are in-process Component ObjectModel (COM) objects implemented as DLLs. Context menu handlers must export twointerfaces in addition to IUnknown: IShellExtInit andIContextMenu. Optionally,a context menu handler can also export IContextMenu2 andIContextMenu3.These interfaces handle the messaging needed to implement owner-drawn menuitems. For more information on owner-drawn menu items, see the CreatingOwner-Drawn Menu Items section under Using Menus.

The IShellExtInit interface is usedby the Shell to initialize the handler. When the Shell calls IShellExtInit::Initialize,it passes in a data object with the object's name and the pointer to an itemidentifier list (PIDL) of the folder that contains the file. The hKeyProgIDparameter is not used with context menu handlers. The IShellExtInit::Initializemethod must extract the file name from the data object and store the name andthe folder's PIDL for later use. For further details, see Implementing IShellExtInit.

The remainder of the operation takes placethrough the handler's IContextMenu interface. The Shell first calls IContextMenu::QueryContextMenu.It passes in an HMENU handle that the method can use to add items to thecontext menu. If the user selects one of the commands, IContextMenu::GetCommandStringis called to retrieve the Help string that will be displayed on the MicrosoftWindows Explorer status bar. If the user clicks one of the handler's items, theShell calls IContextMenu::InvokeCommand.The handler can then execute the appropriate command.

Registeringa Context Menu Handler

Context menu handlers are associated witheither a file class or a folder. For file classes, the handler is registeredunder the following subkey.

·        HKEY_CLASSES_ROOT

o    Program ID

§  shellex

§  ContextMenuHandlers

Create a subkey under ContextMenuHandlersnamed for the handler, and set the subkey's default value to the string form ofthe handler's class identifier (CLSID) GUID.

You can also associate a context menuhandler with different kinds of folders. Register the handler the same way youwould for a file class, but under the following subkey, where FolderTypeis the name of the type of folder.

·        HKEY_CLASSES_ROOT

o    FolderType

§  shellex

§  ContextMenuHandlers

For a discussion of how to register Shellextension handlers and more information about which folder types you canregister handlers for, see Registering Shell Extension Handlers.

If a file class has a context menuassociated with it, double-clicking an object normally launches the defaultcommand. The handler's IContextMenu::QueryContextMenu method is notcalled. To specify that the handler's IContextMenu::QueryContextMenumethod should be called when an object is double-clicked, create a shellex\MayChangeDefaultMenusubkey under the handler's CLSID key. When an object associated with thehandler is double-clicked, IContextMenu::QueryContextMenu will be calledwith the CMF_DEFAULTONLY flag set in the uFlags parameter.

Note  Setting the MayChangeDefaultMenukey forces the system to load the handler's DLL when an associated item isdouble-clicked. If your handler does not change the default verb, you shouldnot set MayChangeDefaultMenu . Doing so causes the system to load yourDLL unnecessarily. Context menu handlers should set this key only if they mightneed to change the context menu's default verb.

The following example illustrates registryentries that enable a context menu handler for an example .myp file class. Thehandler's CLSID key includes a MayChangeDefaultMenu subkey toguarantee that the handler is called when the user double-clicks a relatedobject.

·        HKEY_CLASSES_ROOT

o     

o    .myp
(Default) = MyProgram.1

o    CLSID

§  {00000000-1111-2222-3333-444444444444}

§  InProcServer32
(Default) = C:\MyDir\MyCommand.dll
ThreadingModel = Apartment

§  shellex

§  MayChangeDefaultMenu

o    MyProgram.1
(Default) = MyProgram Application

§  shellex

§  ContextMenuHandler
MyCommand = {00000000-1111-2222-3333-444444444444}

ImplementingIContextMenu

Most of the operation described in How Context Menu HandlersWork is handled by the IContextMenuinterface. This section discusses how to implement its three methods.

QueryContextMenu Method

The Shell calls IContextMenu::QueryContextMenuto allow the context menu handler to add its menu items to the menu. It passesin the HMENU handle in the hmenu parameter. The indexMenuparameter is set to the index to be used for the first menu item that is to beadded.

Any menu items that are added by thehandler must have identifiers that fall between the idCmdFirst and idCmdLastparameters. Typically, the first command identifier is set to idCmdFirst,which is incremented by one (1) for each additional command. This practiceensures that you don't exceed idCmdLast and maximizes the number ofavailable identifiers in case the Shell calls more than one handler.

An item identifier's command offsetis the difference between the identifier and idCmdFirst. Store theoffset of each item that your handler adds to the context menu because theShell might use it to identify the item if it subsequently calls IContextMenu::GetCommandStringor IContextMenu::InvokeCommand.

You should also assign a verb to eachcommand you add. A verb is a language-independent string that can be usedinstead of the offset to identify the command when IContextMenu::InvokeCommandis called. It is also used by functions such as ShellExecuteEx toexecute context menu commands.

Only three of the flags that can be passedin through the uFlags parameter are relevant to context menu handlers.

CMF_DEFAULTONLY

The user has selected the default command, usually by double-clicking the object. IContextMenu::QueryContextMenu should return control to the Shell without modifying the menu.

CMF_NODEFAULT

No item in the menu should be the default item. The method should add its commands to the menu.

CMF_NORMAL

The context menu will be displayed normally. The method should add its commands to the menu.

Use either InsertMenu or InsertMenuItem toadd menu items to the list. Then return an HRESULT value with theseverity set to SEVERITY_SUCCESS. Set the code value to the offset of thelargest command identifier that was assigned, plus one (1). For example, assumethat idCmdFirst is set to 5 and you add three items to the menu withcommand identifiers of 5, 7, and 8. The return value should beMAKE_HRESULT(SEVERITY_SUCCESS, 0, 8 - 5 + 1).

The following example shows a simpleimplementation of IContextMenu::QueryContextMenu that inserts a singlecommand. The identifier offset for the command is IDM_DISPLAY, which is set tozero. The m_pszVerb and m_pwszVerb variables are privatevariables used to store the associated language-independent verb string in bothUnicode and ANSI formats.

Copy Code

#define IDM_DISPLAY 0

 

STDMETHODIMP CMenuExtension::QueryContextMenu(HMENUhMenu,

                                             UINT indexMenu,

                                             UINT idCmdFirst,

                                              UINT idCmdLast,

                                             UINT uFlags)

{

    HRESULT hr;

       

   if(!(CMF_DEFAULTONLY & uFlags))

    {

       InsertMenu(hMenu,

                  indexMenu,

                  MF_STRING | MF_BYPOSITION,

                  idCmdFirst + IDM_DISPLAY,

                  "&Display File Name");

 

        // TODO:Add error handling to verify HRESULT return values.

              

        hr =StringCbCopyA(m_pszVerb, sizeof(m_pszVerb), "display");

        hr =StringCbCopyW(m_pwszVerb, sizeof(m_pwszVerb), L"display");

 

        returnMAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(IDM_DISPLAY + 1));

    }

 

    returnMAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(0));

}

GetCommandString Method

If a user highlights one of the items addedby a context menu handler, the handler's IContextMenu::GetCommandStringmethod is called to request a Help text string that will be displayed on theWindows Explorer status bar. This method can also be called to request the verbstring that is assigned to a command. Either ANSI or Unicode verb strings canbe requested.

The idCmd parameter holds theidentifier offset of the command that was defined when IContextMenu::QueryContextMenuwas called. If a Help string is requested, uFlags will be set to either GCS_HELPTEXTA or GCS_HELPTEXTW, depending on whether an ANSI or Unicodestring is desired. Copy the Help string to the pszName buffer, castingit to an LPWSTR for the Unicode case. The verb string is requested bysetting uFlags to either GCS_VERBA or GCS_VERBW. Copy the appropriate string to pszName,just as with the Help string. The GCS_VALIDATEA and GCS_VALIDATEW flags are not used by context menu handlers.

The following example shows a simpleimplementation of IContextMenu::GetCommandString that corresponds to theIContextMenu::QueryContextMenu example given in the QueryContextMenu Methodsection. Since the handler adds only one menu item, there is only one set ofstrings that can be returned. The method simply tests whether idCmd isvalid and, if it is, returns the requested ANSI or Unicode string.

The StringCchCopyNfunction is used to copy the requested string to pszName to ensure thatthe copied string doesn't exceed the size of the buffer given by uMaxNameLen.The ANSI and Unicode versions of the function are used explicitly, because theformat defined when the handler is compiled might not match the requestedformat.

Copy Code

STDMETHODIMP CMenuExtension::GetCommandString(UINTidCommand,

                                              UINTuFlags,

                                             LPUINT lpReserved,

                                             LPSTR pszName,

                                             UINT uMaxNameLen)

{

    HRESULT  hr = E_INVALIDARG;

 

    if(idCommand !=IDM_DISPLAY)

    {

        return hr;

    }

 

    switch(uFlags)

    {

        caseGCS_HELPTEXTA:

            hr =StringCchCopyNA(pszName,

                                lstrlen(pszStr)/sizeof(pszStr(0)),

                                 "DisplayFile Name",

                                 uMaxNameLen);

            break;

 

        caseGCS_HELPTEXTW:

            hr =StringCchCopyNW((LPWSTR)pszName,

                                lstrlen(pszStr)/sizeof(pszStr(0)),

                                 L"Display File Name",

                                 uMaxNameLen);

            break;

 

        caseGCS_VERBA:

            hr =StringCchbCopyNA(pszName,

                                 lstrlen(pszStr)/sizeof(pszStr(0)),

                                 m_pszVerb,

                                  uMaxNameLen);

            break;

 

        caseGCS_VERBW:

            hr =StringCchCopyNW((LPWSTR)pszName,

                                lstrlen(pszStr)/sizeof(pszStr(0)),

                                m_pwszVerb,

                                 uMaxNameLen);

            break;

 

        default:

            hr =S_OK;

            break;

    }

    return hr;

}

InvokeCommandMethod

This method is called when a user clicks amenu item to tell the handler to run the associated command. The piciparameter points to a structure that contains the needed information.

Although pici is declared inShlobj.h as a CMINVOKECOMMANDINFOstructure, in practice it often points to a CMINVOKECOMMANDINFOEXstructure. This structure is an extended version of CMINVOKECOMMANDINFOand has several additional members that allow Unicode strings to be passed.

Check the cbSize member of picito determine which structure was passed in. If it is a CMINVOKECOMMANDINFOEXstructure and the fMask member has the CMIC_MASK_UNICODE flag set, cast pici to CMINVOKECOMMANDINFOEX.This allows your application to use the Unicode information contained in thelast five members of the structure.

The structure's lpVerb or lpVerbWmember is used to identify the command to be executed. There are two ways toidentify commands.

·        The command's verb string

·        The command's identifier offset

To distinguish between these two cases,check the high-order word of lpVerb for the ANSI case or lpVerbWfor the Unicode case. If the high-order word is nonzero, lpVerb or lpVerbWholds a verb string. If the high-order word is zero, the command offset is inthe low-order word of lpVerb.

The following example shows a simpleimplementation of IContextMenu::InvokeCommand that corresponds to the IContextMenu::QueryContextMenuand IContextMenu::GetCommandString samples given in the previoussections. The method first determines which structure is being passed in. Itthen determines whether the command is identified by its offset or verb. If lpVerbor lpVerbW holds a valid verb or offset, the method displays a messagebox.

Copy Code

STDMETHODIMPCShellExtension::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)

{

    BOOL fEx =FALSE;

    BOOL fUnicode =FALSE;

 

   if(lpcmi->cbSize == sizeof(CMINVOKECOMMANDINFOEX))

    {

        fEx = TRUE;

       if((lpcmi->fMask & CMIC_MASK_UNICODE))

        {

           fUnicode = TRUE;

        }

    }

 

    if( !fUnicode&& HIWORD(lpcmi->lpVerb))

    {

       if(StrCmpIA(lpcmi->lpVerb, m_pszVerb))

        {

            return E_FAIL;

        }

    }

 

    else if(fUnicode && HIWORD(((CMINVOKECOMMANDINFOEX *) lpcmi)->lpVerbW))

    {

       if(StrCmpIW(((CMINVOKECOMMANDINFOEX *)lpcmi)->lpVerbW, m_pwszVerb))

        {

            returnE_FAIL;

        }

    }

 

    elseif(LOWORD(lpcmi->lpVerb) != IDM_DISPLAY)

    {

        returnE_FAIL;

    }

 

    else

    {

       MessageBox(lpcmi->hwnd,

                  "The File Name",

                  "File Name",

                  MB_OK|MB_ICONINFORMATION);

    }

 

    return S_OK;

}

CreatingDrag-and-Drop Handlers

When a user right-clicks a Shell object todrag an object, a context menu is displayed when the user attempts to drop theobject. The following illustration shows a typical drag-and-drop context menu.

A drag-and-drop handler is a context menuhandler that can add items to this context menu. Drag-and-drop handlers aretypically registered under the following subkey.

·        HKEY_CLASSES_ROOT

o    Directory

§  shellex

§  DragDropHandlers

Add a subkey under the DragDropHandlerssubkey named for the drag-and-drop handler, and set the subkey's default valueto the string form of the handler's CLSID GUID. The following exampleenables the MyDD drag-and-drop handler.

·        HKEY_CLASSES_ROOT

o    Directory

§  shellex

§  DragDropHandlers

§  MyDD
(Default) = {MyDD CLSID GUID}

The basic procedure for implementing adrag-and-drop handler is the same as for conventional context menu handlers. However,context menu handlers normally use only the IDataObject pointer passed to thehandler's IShellExtInit::Initialize method to extract the object's name.A drag-and-drop handler could implement a more sophisticated data handler tomodify the behavior of the dragged object.

Windows外殼擴展編程 www.applevb.com
    在Windows下的一些軟件提供了這樣的功能:當安裝了這些軟件之后,當在Windows的Explore中鼠標右鍵單擊文件或者文件夾后,在彈出菜單中就會(huì )多出與該軟件操作相關(guān)的菜單項,點(diǎn)擊該項就會(huì )激活相應的程序對用戶(hù)選中的文件進(jìn)行相應的操作。例如安裝了Winzip之后,當用戶(hù)選中一個(gè)文件夾后單擊右鍵,在彈出菜單中就會(huì )多出一個(gè)Add ToZip和一個(gè) Add To xxx.zip的選項,其中xxx為選中的文件夾的名稱(chēng)。只要單擊上面的兩個(gè)菜單項中的一個(gè),就可以方便的壓縮目錄了。這樣的功能稱(chēng)為Windows外殼擴展(Shell Extensions)
外殼擴展概述

  下面是與外殼擴展相關(guān)的三個(gè)重要術(shù)語(yǔ):
(1)文件對象(File Object)
   文件對象是外殼中的一項,大家最熟識的文件對象是文件和目錄,此外,打印機、控制面板程序、共享網(wǎng)
       絡(luò )等也都是文件對象。
(2)文件類(lèi)(File Class)
       文件類(lèi)是具有某種共同特性的文件對象的集合,比如,擴展名相同的文件屬于同一文件類(lèi)。
(3)處理程序(Handler)
   處理程序是具體實(shí)現某個(gè)外殼擴展的代碼。

  Windows支持七種類(lèi)型的外殼擴展(稱(chēng)為Handler),它們相應的作用簡(jiǎn)述如下:
(1)Context menu handlers向特定類(lèi)型的文件對象增添上下文相關(guān)菜單;
(2)Drag-and-drop handlers用來(lái)支持當用戶(hù)對某種類(lèi)型的文件對象進(jìn)行拖放操作時(shí)的OLE數據傳輸;
(3)Icon handlers用來(lái)向某個(gè)文件對象提供一個(gè)特有的圖標,也可以給某一類(lèi)文件對象指定圖標;
(4)Property sheet handlers給文件對象增添屬性頁(yè),屬性頁(yè)可以為同一類(lèi)文件對象所共有,也可以給一個(gè)
       文件對象指定特有的屬性頁(yè);
(5)Copy-hook handlers在文件夾對象或者打印機對象被拷貝、移動(dòng)、刪除和重命名時(shí),就會(huì )被系統調用,
       通過(guò)為Windows增加Copy-hook handlers,可以允許或者禁止其中的某些操作;
(6)Drop target handlers在一個(gè)對象被拖放到另一個(gè)對象上時(shí),就會(huì )被系統被調用;
(7)Data object handlers在文件被拖放、拷貝或者粘貼時(shí),就會(huì )被系統被調用。

  Windows的所有外殼擴展都是基于COM(ComponentObject Model) 組件模型的,外殼是通過(guò)接口(Interface)來(lái)訪(fǎng)問(wèn)對象的。外殼擴展被設計成32位的進(jìn)程中服務(wù)器程序,并且都是以動(dòng)態(tài)鏈接庫的形式為操作系統提供服務(wù)的。因此,如果要對Windows的用戶(hù)界面進(jìn)行擴充的話(huà),則具備寫(xiě)COM對象的一些知識是十分必要的。

  寫(xiě)好外殼擴展程序后,必須將它們注冊才能生效。所有的外殼擴展都必須在Windows注冊表的HKEY_CLASSES_ROOT\CLSID鍵之下進(jìn)行注冊。在該鍵下面可以找到許多名字像{

  注冊表HKEY_CLASSES_ROOT主鍵下有幾個(gè)特殊的子鍵,如*、Folder、Drive以及Printer。如果把外殼擴展注冊在*子鍵下,那么這個(gè)外殼擴展將對Windows中所有類(lèi)型的文件有效;如果把外殼擴展注冊在Folder子鍵下,則對所有目錄有效。

    上面提到的在WindowsExplore中在鼠標右鍵菜單中添加菜單項(我們成為上下文相關(guān)菜單)的操作屬于外殼擴展的第一類(lèi),即Contextmenu handlers向特定類(lèi)型的文件對象增添上下文相關(guān)菜單。要動(dòng)態(tài)地在上下文相關(guān)菜單中增添菜單項,可以通過(guò)寫(xiě)Context Menu Handler來(lái)實(shí)現。
編寫(xiě)Context Menu Handler必須實(shí)現IShellExtInit和IContextMenu兩個(gè)接口。除了IUnknown接口所定義的函數之外,Context Menu Handler還需要用到QueryContextMenu、InvokeCommand和GetCommandString這三個(gè)非常重要的成員函數。

  (1)QueryContextMenu函數:每當系統要顯示一個(gè)文件對象的上下文相關(guān)菜單時(shí),它首先要調用該函數。為了在上下文相關(guān)菜單中添加菜單
項,我們在該函數中調用InsertMenu函數。

  (2)InvokeCommand函數:當用戶(hù)選定了某個(gè)ContextMenu Handler登記過(guò)的菜單項后,該函數將會(huì )被調用,系統將會(huì )傳給該函數一個(gè)指向
LPCMINVOKECOMMANDINFO結構的指針。在該函數中要執行與所選菜單項相對應的操作。

  (3)GetCommandString函數:當鼠標指針移到一個(gè)上下文相關(guān)菜單項上時(shí),在當前窗口的狀態(tài)條上將會(huì )出現與該菜單項相關(guān)的幫助信息,此
信息就是系統通過(guò)調用該函數獲取的。

    下面我通過(guò)具體的例程來(lái)說(shuō)明編寫(xiě)一個(gè)比較完整的上下文菜單程序,這個(gè)程序是一個(gè)文件操作程序,當安裝并注冊了外殼擴展的服務(wù)器動(dòng)態(tài)連接庫之后,當選擇一個(gè)或者多個(gè)文件并單擊鼠標右鍵后,在右鍵菜單中就會(huì )多出一個(gè)“執行文件操作”的上下文菜單,點(diǎn)擊菜單就會(huì )彈出相應的程序執行文件操作。
    在整個(gè)程序的編寫(xiě)中,外殼擴展的服務(wù)器動(dòng)態(tài)連接庫是有Delphi4.0編寫(xiě)的,而動(dòng)態(tài)連接庫調用的文件操作程序是由VB6編寫(xiě)的。下面首先介紹服務(wù)器動(dòng)態(tài)連接庫的編寫(xiě):
    服務(wù)器動(dòng)態(tài)連接庫的工程文件內容如下:

library contextmenu;
    uses
ComServ,
ContextMenuHandler in 'Unit2.pas';
//   contmenu_TLB in 'contmenu_TLB.pas';

exports
   DllGetClassObject,
   DllCanUnloadNow,
   DllRegisterServer,
   DllUnregisterServer;

{$R *.TLB}

{$R *.RES}

begin

end.

    將工程文件保存為contextmenu.dpr。
    服務(wù)器動(dòng)態(tài)連接庫的單位文件內容如下:   

unit ContextMenuHandler;

interface
   uses Windows,ActiveX,ComObj,ShlObj,Classes;

type
   TContextMenu = class(TComObject,IShellExtInit,IContextMenu)
   private
      FFileName: array[0..MAX_PATH] of Char;
   protected
      function IShellExtInit.Initialize =SEIInitialize; // Avoid compiler warning
      function SEIInitialize(pidlFolder: PItemIDList;lpdobj: IDataObject;
              hKeyProgID: HKEY): HResult; stdcall;
      function QueryContextMenu(Menu: HMENU;indexMenu, idCmdFirst, idCmdLast,
              uFlags: UINT): HResult; stdcall;
      function InvokeCommand(var lpici:TCMInvokeCommandInfo): HResult; stdcall;
      function GetCommandString(idCmd, uType: UINT;pwReserved: PUINT;
              pszName: LPSTR; cchMax: UINT): HResult; stdcall;
end;

const

   Class_ContextMenu: TGUID ='{19741013-C829-11D1-8233-0020AF3E

{全局唯一標識符(GUID)是一個(gè)16字節(128為)的值,它唯一地標識一個(gè)接口(interface)}
var
   FileList:TStringList;
   Buffer:array[1..1024]of char;

implementation

uses ComServ, SysUtils, ShellApi, Registry,UnitForm;

function TContextMenu.SEIInitialize(pidlFolder:PItemIDList; lpdobj: IDataObject;
   hKeyProgID: HKEY): HResult;
var
   StgMedium: TStgMedium;
   FormatEtc: TFormatEtc;
   FileNumber,i:Integer;
begin
   file://如果lpdobj等于Nil,則本調用失敗
   if (lpdobj = nil) then begin
      Result := E_INVALIDARG;
      Exit;
   end;

   file://首先初始化并清空FileList以添加文件
   FileList:=TStringList.Create;
   FileList.Clear;
   file://初始化剪貼版格式文件
   with FormatEtc do begin
      cfFormat := CF_HDROP;
      ptd := nil;
      dwAspect := DVASPECT_CONTENT;
      lindex := -1;
      tymed := TYMED_HGLOBAL;
   end;
   Result := lpdobj.GetData(FormatEtc, StgMedium);
   if Failed(Result) then Exit;

   file://首先查詢(xún)用戶(hù)選中的文件的個(gè)數
   FileNumber := DragQueryFile(StgMedium.hGlobal,$FFFFFFFF,nil,0);
   file://循環(huán)讀取,將所有用戶(hù)選中的文件保存到FileList中
   for i:=0 to FileNumber-1 do begin
      DragQueryFile(StgMedium.hGlobal, i, FFileName,SizeOf(FFileName));
      FileList.Add(FFileName);
      Result := NOERROR;
   end;

   ReleaseStgMedium(StgMedium);
end;

function TContextMenu.QueryContextMenu(Menu: HMENU;indexMenu, idCmdFirst,
   idCmdLast, uFlags: UINT): HResult;
begin
   Result := 0;
   if ((uFlags and $)= CMF_NORMAL) or
      ((uFlags and CMF_EXPLORE) <> 0) then begin
// 往Context Menu中加入一個(gè)菜單項
    InsertMenu(Menu, indexMenu, MF_STRING or MF_BYPOSITION,idCmdFirst,
      PChar('執行文件操作'));
// 返回增加菜單項的個(gè)數
   Result := 1;
   end;
end;

function TContextMenu.InvokeCommand(var lpici:TCMInvokeCommandInfo): HResult;
var
//   sFile:TFileStream;
   charSavePath:array[0..1023]of char;
   sSaveFile:String;
   i:Integer;
   F: TextFile;
   FirstLine: string;
begin
   // 首先確定該過(guò)程是被系統而不是被一個(gè)程序所調用
   if (HiWord(Integer(lpici.lpVerb)) <> 0) then
   begin
      Result := E_FAIL;
      Exit;
   end;
   // 確定傳遞的參數的有效性
   if (LoWord(lpici.lpVerb) <> 0) then begin
      Result := E_INVALIDARG;
      Exit;
   end;

   file://建立一個(gè)臨時(shí)文件保存用戶(hù)選中的文件名
   GetTempPath(1024,charSavePath);
   sSaveFile:=charSavePath+'chen0001.tmp';

   AssignFile(F,sSaveFile);   { nextfile in Files property }
   ReWrite(F);
   file://將文件名保存到臨時(shí)文件中
   for i:= 0 to FileList.Count -1 do begin
      FirstLine:=FileList.Strings[i];
      Writeln(F,FirstLine);    { Readthe first line out of the file }
   end;
   CloseFile(F);
   file://調用文件操作程序對用戶(hù)選中的文件進(jìn)行操作
  ShellExecute(0,nil,'c:\FileOP.exe',PChar(sSaveFile),charSavePath,SW_NORMAL);

   Result := NOERROR;
end;

function TContextMenu.GetCommandString(idCmd, uType: UINT;pwReserved: PUINT;
         pszName: LPSTR; cchMax: UINT):HRESULT;
begin
   if (idCmd = 0) then begin
   if (uType = GCS_HELPTEXT) then
      {返回該菜單項的幫助信息,此幫助信息將在用戶(hù)把鼠標移動(dòng)到該菜單項時(shí)出現在狀態(tài)條上。}
      StrCopy(pszName, PChar('點(diǎn)擊該菜單項將執行文件操作'));
      Result := NOERROR;
   end
   else
      Result := E_INVALIDARG;
end;

type
   TContextMenuFactory = class(TComObjectFactory)
   public
   procedure UpdateRegistry(Register: Boolean); override;
end;

procedure TContextMenuFactory.UpdateRegistry(Register:Boolean);
var
   ClassID: string;
begin
   if Register then begin
      inherited UpdateRegistry(Register);
      ClassID := GUIDToString(Class_ContextMenu);
      CreateRegKey('*\shellex', '', '');
      CreateRegKey('*\shellex\ContextMenuHandlers','', '');
     CreateRegKey('*\shellex\ContextMenuHandlers\OpenWithWordPad', '', ClassID);

    file://如果操作系統為WindowsNT的話(huà)
      if (Win32Platform = VER_PLATFORM_WIN32_NT) then
      with TRegistry.Create do
      try
         RootKey := HKEY_LOCAL_MACHINE;
        OpenKey('SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions', True);
         OpenKey('Approved', True);
         WriteString(ClassID, 'ContextMenu Shell Extension');
      finally
         Free;
      end;
   end
   else begin
     DeleteRegKey('*\shellex\ContextMenuHandlers\FileOpreation');
      DeleteRegKey('*\shellex\ContextMenuHandlers');
//      DeleteRegKey('*\shellex');
      inherited UpdateRegistry(Register);
   end;
end;

initialization
TContextMenuFactory.Create(ComServer, TContextMenu, Class_ContextMenu,
   '', 'Context Menu Shell Extension', ciMultiInstance,tmApartment);

end.

    將該單位文件保存為unit2.pas,文件同contextmenu.dpr位于同一個(gè)目錄下。
    打開(kāi)Delphi,選菜單中的 file | open project 打開(kāi)contextmenu.dpr文件,然后選 Project | build contextmenu菜單項編譯連接程序,如果編譯成功的話(huà),會(huì )建立一個(gè)contextmenu.dll的動(dòng)態(tài)連接庫文件,這個(gè)文件就是服務(wù)器動(dòng)態(tài)連接庫。

    下面來(lái)建立文件操作程序。打開(kāi)VB,建立一個(gè)新的工程文件,在Form1中加入一個(gè)ListBox控件和三個(gè)CommandButton控件,將ListBox的MultiSelect屬性設置為2。然后在Form1的代碼窗口中加入以下代碼:
Option Explicit

Private Type BrowseInfo
     hwndOwner As Long
     pIDLRoot As Long
     pszDisplayName As Long
     lpszTitle As Long
     ulFlags As Long
     lpfnCallback As Long
     lParam As Long
     iImage As Long
End Type
Private Type SHFILEOPSTRUCT
    hwnd As Long
    wFunc As Long       '對文件的操作指令
    pFrom As String     '源文件或路徑
    pTo As String       '目的文件或路徑
    fFlags As Integer   '操作標志
    fAnyOperationsAborted As Long
    hNameMappings As Long
    lpszProgressTitle As String
End Type

Const FO_COPY = &H2
Const FO_DELETE = &H3
Const FO_MOVE = &H1
Const FO_RENAME = &H4
Const FOF_ALLOWUNDO = &H40
Const BIF_RETURNONLYFSDIRS = 1
Const MAX_PATH = 260

Private Declare Function ShellAbout Lib"shell32.dll" Alias _
        "ShellAboutA" (ByVal hwndAs Long, ByVal szApp As _
        String, ByVal szOtherStuff AsString, ByVal hIcon As Long) _
        As Long
Private Declare Sub CoTaskMemFree Lib "ole32.dll" (ByVal hMem AsLong)
Private Declare Function lstrcat Lib "kernel32" Alias _
        "lstrcatA" (ByVallpString1 As String, ByVal lpString2 _
        As String) As Long
Private Declare Function SHBrowseForFolder Lib "shell32" (lpbi _
        As BrowseInfo) As Long
Private Declare Function SHGetPathFromIDList Lib "shell32" _
        (ByVal pidList As Long, ByVallpBuffer As String) As Long
Private Declare Function SHFileOperation Lib "shell32" _
        (lpFileOp As SHFILEOPSTRUCT) As Long
Private Declare Function GetWindowsDirectory _
        Lib "kernel32" Alias"GetWindowsDirectoryA" _
        (ByVal lpBuffer As String, ByValnSize As _
        Long) As Long

Dim DirString As String
Dim sFile As String

Sub UpdateList()
    'UpdateList函數檢查列表框中的文件是否存在,如果不存在,就將其
    '從文件列表中刪除
    Dim bEndList As Boolean
    Dim i As Integer
   
    bEndList = True
    i = 0
    While bEndList
        '檢查文件是否存在,如果不存在就刪除
        If Dir$(List1.List(i)) ="" Then
           List1.RemoveItem (i)
        Else    '如果文件存在就轉移到下一個(gè)列表項
            i = i + 1
            If i >List1.ListCount - 1 Then
               bEndList = False
            End If
        End If
    Wend
    Command1.Enabled = False
    Command2.Enabled = False
    Command3.Enabled = False
End Sub

Function BrowseForFolder(hwndOwner As Long, sPrompt AsString) As String
     Dim iNull As Integer
     Dim lpIDList As Long
     Dim lResult As Long
     Dim sPath As String
     Dim udtBI As BrowseInfo

    '初試化udtBI結構
     With udtBI
        .hwndOwner = hwndOwner
        .lpszTitle = lstrcat(sPrompt,"")
        .ulFlags = BIF_RETURNONLYFSDIRS
     End With
   
    '彈出文件夾查看窗口
     lpIDList = SHBrowseForFolder(udtBI)
    
     If lpIDList Then
        sPath = String$(MAX_PATH, 0)
        lResult =SHGetPathFromIDList(lpIDList, sPath)
        Call CoTaskMemFree(lpIDList)
        iNull = InStr(sPath, vbNullChar)
        If iNull Then sPath = Left$(sPath,iNull - 1)
     End If

     BrowseForFolder = sPath
End Function

Private Sub Command1_Click()    '執行文件拷貝操作
    Dim sPath As String
    Dim tCopy As SHFILEOPSTRUCT
    Dim i As Integer
   
    '選擇拷貝到的文件夾
    sPath = BrowseForFolder(Form1.hwnd, "選擇拷貝到的文件夾")
    If sPath <> "" Then
        With tCopy
            .hwnd =Form1.hwnd
            .lpszProgressTitle= "正在拷貝"
            .pTo = sPath
            .fFlags =FOF_ALLOWUNDO
            .wFunc =FO_COPY
        End With
        For i = 0 To List1.ListCount - 1
            IfList1.Selected(i) Then   '如果文件被選中則拷貝文件
               tCopy.pFrom = List1.List(i)
               SHFileOperation tCopy
            End If
        Next i
        UpdateList
    End If
    Kill sFile
End Sub

Private Sub Command2_Click()    '執行文件移動(dòng)操作
    Dim sPath As String
    Dim tCopy As SHFILEOPSTRUCT
    Dim i As Integer
   
    '選擇移動(dòng)到的文件夾
    sPath = BrowseForFolder(Form1.hwnd, "選擇轉移到的文件夾")
    If sPath <> "" Then
        With tCopy
            .hwnd =Form1.hwnd
           .lpszProgressTitle = "正在移動(dòng)"
            .pTo = sPath
            .fFlags =FOF_ALLOWUNDO
            .wFunc =FO_MOVE
        End With
        For i = 0 To List1.ListCount - 1
            IfList1.Selected(i) Then   '如果文件被選中則拷貝文件
               tCopy.pFrom = List1.List(i)
               SHFileOperation tCopy
            End If
        Next i
        UpdateList
    End If
    Kill sFile
End Sub

Private Sub Command3_Click()    '執行文件刪除操作
    Dim sPath As String
    Dim tCopy As SHFILEOPSTRUCT
    Dim i As Integer
   
    With tCopy
        .hwnd = Form1.hwnd
        .lpszProgressTitle = "正在刪除"
        .pTo = sPath
        .fFlags = FOF_ALLOWUNDO
        .wFunc = FO_DELETE
    End With
    For i = 0 To List1.ListCount - 1
        If List1.Selected(i) Then
            tCopy.pFrom= List1.List(i)
           SHFileOperation tCopy
        End If
    Next i
    UpdateList
    Kill sFile
End Sub

Private Sub Form_Load()
    Dim hFileHandle As Long
    Dim TextLine As String
   
    Command1.Caption = "拷貝"
    Command2.Caption = "移動(dòng)"
    Command3.Caption = "刪除"
    Command1.Enabled = False
    Command2.Enabled = False
    Command3.Enabled = False
   
    'sFile接受由Windows外殼擴展庫contextmenu.dll傳遞過(guò)來(lái)的文件參數
    sFile = Command$
    hFileHandle = FreeFile
    Open sFile For Input As hFileHandle
    Do While Not EOF(hFileHandle)
        Line Input #1, TextLine
        If Dir$(TextLine) <>"" Then
           List1.AddItem TextLine
        End If
    Loop
    Close hFileHandle
End Sub

Private Sub Form_Unload(Cancel As Integer)
    If Dir$(sFile) <> "" Then
        Kill sFile
    End If
End Sub

Private Sub List1_Click()
    If Not Command1.Enabled Then
        Command1.Enabled = True
        Command2.Enabled = True
        Command3.Enabled = True
    End If
End Sub
    保存文件并將工程文件編譯為FileOP.exe文件,將文件拷貝到C盤(pán)根目錄下。然后注冊contextmenu.dll,注冊的方法是,在DOS窗口中進(jìn)入Windows\system子目錄,輸入 Regsvr32 x:\xxxxx\contextmenu.dll 。其中x:\xxxxx\為Contextmenu.dll文件所在的驅動(dòng)器和目錄。如果注冊成功,系統會(huì )彈出對話(huà)框,顯示 DllRegisterServer in ..\xxx\contextmenu.dll Success 提示注冊成功。
    注冊成功后,再選擇文件并單擊右鍵,就會(huì )發(fā)現在彈出菜單中多了一個(gè)“執行文件操作”的菜單項,點(diǎn)擊該項,系統就會(huì )調用FileOP.exe執行文件操作,在窗口的列表框中會(huì )出現用戶(hù)選擇的文件名,點(diǎn)擊相應的文件并點(diǎn)擊“拷貝”、“移動(dòng)”或“刪除”按鈕就可以對列表框中的選中的文件進(jìn)行相應的操作。

 

 

 

由 ATL 想起的外殼擴展編程(一)


作者/李曉飛


下載源代碼


    好久沒(méi)有給VC知識庫發(fā)稿了,實(shí)在不好意思,由于前段時(shí)間實(shí)在太忙所以一直沒(méi)有時(shí)間閑下心來(lái)寫(xiě)點(diǎn)東西,期間也有不少朋友給我來(lái)信討論問(wèn)題,我很感謝大家對我的支持,我歡迎大家繼續來(lái)信,共同交流,共同進(jìn)步!這次我想和大家一起討論一下 Windows 的 Shell 擴展編程,首先在閱讀以下內容之前我還是推薦大家看一下《COM技術(shù)內幕》這本大作,不過(guò)即使您沒(méi)有有關(guān)的基礎知識其實(shí)也是無(wú)所謂的,因為以下講解是傻瓜式講解。

開(kāi)發(fā)環(huán)境

  • Windows Professional 2000
  • Microsoft Visual C++ 6.0 + ATL3.0

參考文獻

  • COM技術(shù)內幕
  • ATL應用與開(kāi)發(fā)指南(第二版)

Windows外殼擴展
    Windows外殼擴展的英文名稱(chēng)為:Windows Shell Extension。Windows外殼擴展是一類(lèi)特殊的COM對象,在這類(lèi)COM對象中用戶(hù)可以加入自己的特殊功能,而Windows外殼擴展最終都會(huì )被Windows Explorer所引用。舉個(gè)最簡(jiǎn)單的例子,比如 WinRar 應用程序,如果你安裝完 WinRar 后,它會(huì )在你的右鍵菜單中加入很多快捷菜單,如 圖1.1 所示:


圖1.1

而上圖卻僅僅是外殼擴展編程中一種:"Context Menu Handler"。難道外殼擴展也分類(lèi)嗎?是的,但是不多,并且它們的實(shí)現大都一致,總體來(lái)說(shuō)有如下幾種分類(lèi):

表(一)

處理器類(lèi)型

何時(shí)觸發(fā)

所做處理

Context menu 處理器

當用戶(hù)鼠標右擊文件或文件夾時(shí)觸發(fā)。但是在Shell V4.71+中,用戶(hù)在文件夾目錄的空白處點(diǎn)擊鼠標右鍵也會(huì )觸發(fā)該事件。

加入上下文菜單項。

Property sheet 處理器

當用戶(hù)鼠標右擊文件,選擇文件"屬性"菜單彈出文件屬性對話(huà)框時(shí)觸發(fā)。

加入用戶(hù)自定義屬性頁(yè)。

Drag and drop 處理器

當用戶(hù)在文件夾或桌面中用鼠標右鍵Drag/Drop文件或文件夾時(shí)觸發(fā)。

加入上下文菜單項。

Drop處理器

當某一數據對象被Drag Over/Dropped Into某一文件時(shí)觸發(fā)。

加入任何用戶(hù)自定義動(dòng)作。

QueryInfo 處理器(Shell V4.71+)

當用戶(hù)鼠標滑過(guò)某一個(gè)文件或某一Shell對象時(shí)觸發(fā)。

加入用戶(hù)自定義提示信息(ToolTips)。

    也許有人會(huì )問(wèn)我實(shí)現它們困難嗎?答案是:比較簡(jiǎn)單。實(shí)現它是不是必須得去看那些枯燥乏味的ATL模板類(lèi),或者生硬死板的 MFC 宏定義呢?答案是否定的。也許以上的問(wèn)題阻礙了大多數COM初學(xué)者的學(xué)習欲望,其實(shí)我剛接觸ATL時(shí)多的是迷惘,常常抱怨 ATL 的知識太深奧,MFC的構架太生硬,一般我是不太喜歡用#define來(lái)定義程序的全部(請參閱effective C++)。言歸正傳,我們再回到今天的話(huà)題上來(lái),那么為實(shí)現 圖1.1 所示功能可以通過(guò)哪些途徑呢?答案有二,第一:注冊表編程。第二:Shell Extension COM編程。通過(guò)注冊表方式實(shí)現其實(shí)十分簡(jiǎn)單,請參閱COM 組件注冊表實(shí)現,在這里本文不做重復介紹,再者也不是本文的主題所在。在以下的內容中我會(huì )以第一類(lèi)Shell 擴展編程---" Context Menu 處理器" 為例來(lái)講解 Handler 的實(shí)現過(guò)程。

組件功能
    該組件實(shí)現的功能為:當用戶(hù)在Explorer中鼠標右擊DLL類(lèi)型文件時(shí),在彈出的上下文菜單中注冊我們自己的菜單項,如圖1.2 所示:


圖1.2

"Register Component"和"UnRegisterComponent"菜單項既是我們自己的菜單項。并且這兩個(gè)菜單項分別完成進(jìn)程內組件(DLL)的注冊和反注冊,菜單項的功能倒很簡(jiǎn)單,只是簡(jiǎn)單地執行了 Windows 的 Regsvr32.exe而已,但是我們已經(jīng)感覺(jué)到它給我們帶來(lái)的實(shí)用和方便,難道你不覺(jué)得 "Over and Over" 手工輸入"Regsvr32 xxx.dll" 或者 "Regsvr32 /uxxx.dll" 很乏味嗎……。

編寫(xiě)組件  

  1. 建立工程: 打開(kāi)VC++,新建一個(gè)"ATL Com AppWizard"模板工程,工程名稱(chēng)為:SimpleExt。


    圖 1.3

    Shell擴展實(shí)例均為進(jìn)程內組件,它們均以動(dòng)態(tài)庫的形式存在,所以在接下來(lái)的向導中我們用默認設置:"Dynamic Link Library(DLL)",然后點(diǎn)擊"完成"。如 下圖所示:


    圖 1.4

    此時(shí)我們已經(jīng)擁有了一個(gè)沒(méi)有實(shí)現任何功能的進(jìn)程內 COM 組件,為什么說(shuō)"沒(méi)有實(shí)現任何功能"呢?那是因為我們沒(méi)有實(shí)現任何接口,再者在我們的DLL中也沒(méi)有任何可供外部使用的接口。
        如果我們的組件不繼承其他外部已有接口,那么這樣的COM組件實(shí)現起來(lái)則非常簡(jiǎn)單,它和編寫(xiě)普通類(lèi)代碼沒(méi)有任何不一樣的地方,只需要使用 ATL 接口的 Method 和Property 增/刪向導即可實(shí)現。
        顯然我們的組件要繼承 Shell 的擴展接口,并且還得實(shí)現所有繼承的 Shell 接口,所以我們就不能完全依賴(lài) ATL 的"自動(dòng)化"了,這里需要我們自己寫(xiě)代碼來(lái)實(shí)現該接口。首先我們通過(guò) AT L向導新增一個(gè)簡(jiǎn)單接口 SimpleShlExt,如下圖1.5,圖1.6 和 圖1.7 所示操作過(guò)程:


    圖 1.5


    圖 1.6


    圖 1.7

    然后一切默認即可,這樣ATL就為我們生成了一個(gè)組件框架,我們以下的討論都基于此框架。
  2. 添加代碼


    圖1.8 組件類(lèi)繼承關(guān)系

    圖1.8 中紅色方框是我們自己要實(shí)現的 Shell 擴展接口,它不是向導自動(dòng)生成代碼,需要我們手工輸入。

        我們從該框架中可以獲得很多好處,首先通過(guò) ATL 的模板類(lèi) CcomCoClass 我們就可以省去反復再三的 QueryInterface 接口的實(shí)現,而我們只需要綁定組件和接口的映射關(guān)系(如下圖1.9 所示)以及實(shí)現所繼承接口的全部虛函數即可,以及組件的注冊等它基本上都為我們做好了一切,好處大家就慢慢體會(huì )吧......。下面我們首先介紹繼承的各接口和其虛成員函數的作用,它們的聲明包含在<shlobj.h>頭文件中,首先頭文件你必須包含進(jìn)來(lái):


    圖1.9 建立組件和接口的映射關(guān)系

    圖1.9 紅色方框為 IShellExtInit 和 IContextMenu 接口和組件的接口映射關(guān)系,它不是向導自動(dòng)生成代碼,需要我們手工輸入。

    IShellExtInit 接口 :IShellExtInit 接口為 Shell 擴展編程必須要實(shí)現的接口。該接口主要用來(lái)初始化 Shell 擴展處理器(表一所列的處理器),它僅有一個(gè)虛成員函數Initialize,用戶(hù)所有的 Shell 擴展初始化動(dòng)作都由該函數完成。該函數的原型如下:
3.  HRESULT Initialize(
4.        LPCITEMIDLIST pidlFolder,
5.    LPDATAOBJECT lpdobj,
6.    HKEY hkeyProgID
 );

    在 Initialize 函數中,我們要做的事情就是獲取用戶(hù)鼠標右鍵點(diǎn)擊的文件名稱(chēng),但是有可能用戶(hù)選擇了多個(gè)文件,這里為了簡(jiǎn)單起見(jiàn)我們僅獲取文件列表中的第一個(gè)文件。在這里我們得補充一點(diǎn)內容:當用戶(hù)在一個(gè)擁有 WS_EX_ACCEPTFILES 風(fēng)格的窗體中Drag/Drop 文件時(shí)這些文件名會(huì )以同一種格式存儲,而且文件完整路徑的獲取也都以DragQueryFile API函數來(lái)實(shí)現。但是 DragQueryFile 需要傳入一個(gè) HDROP 句柄,該句柄即為 Drag/Drop 文件名稱(chēng)列表數據句柄(開(kāi)始存放數據的內存區域首指針)。而 HDROP 句柄的可以通過(guò)接口 " DATAOBJECTlpdobj" 的成員函數"GetData" 來(lái)獲取。以下為獲取第一個(gè)Drag/Drop 文件的完整文件路徑的具體代碼:

//數據存儲格式
FORMATETC fmt = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
 
//數據存儲內存句柄(常用于IDataObject和IAdviseSink接口的數據傳輸操作)
STGMEDIUM stg = { TYMED_HGLOBAL };
 
if(FAILED(pDataObj->GetData(&fmt, &stg)))
{
     //如果獲取數據內存句柄失敗則返回E_INVALIDARG,
     //返回E_INVALIDARG則Explorer不會(huì )再調用我們的Shell擴展接口
     return E_INVALIDARG;
}
 
//獲取實(shí)際數據內存句柄
HDROP hDrop = (HDROP)GlobalLock(stg.hGlobal);
if(NULL==hDrop)
{
     //在COM程序中養成良好的檢錯習慣是很重要的?。?!
     return E_INVALIDARG;
}
//獲取用戶(hù)Drag/Drop的文件數目
int nDropCount = ::DragQueryFile((HDROP)stg.hGlobal, 
   0xFFFFFFFF, NULL, 0);
 
//本示例程序僅獲取第一個(gè)Drag/Drop文件完整路徑
//以下注釋代碼為獲取所有文件完整路徑的實(shí)現代碼:
//for(int i = 0; i < nDropCount; ++i){
     //循環(huán)獲取每個(gè)Drag/Drop文件的完整文件名
     // ::DragQueryFile((HDROP)stg.hGlobal, i, m_pzDropFile, MAX_PATH);
//}
 
//如果用戶(hù)Drag/Drop的文件數目不為一個(gè)則不予處理
if(1==nDropCount)
{
     //pzDropFile為組件類(lèi)內部的private變量
     //它用來(lái)保存用戶(hù)Drag/Drop的文件完整文件名
     memset(m_pzDropFile, 0x0, MAX_PATH*sizeof(TCHAR));
     ::DragQueryFile((HDROP)stg.hGlobal, 0, m_pzDropFile, MAX_PATH);
}
 
//釋放內存句柄
::ReleaseStgMedium(&mdmSTG);

至此IShellExtInit 接口已經(jīng)完全實(shí)現,從此我們也可以看出進(jìn)程內組件編程的一些特點(diǎn),大體總結如下:"新建自己的接口,然后繼承某些接口,最后一一實(shí)現這些接口的所有虛成員函數或加入自己的成員函數,最后就是組件的注冊"?!?  IContextMenu 接口 :該接口和 "Context Menu 處理器" 一一對應,說(shuō)到此我們也順便說(shuō)一下 Shell 擴展接口編程中和(表一)中所列處理器各自對應的COM接口:

(表二)

處理器類(lèi)型

COM 接口

Context menu 處理器

IContextMenu

Property sheet 處理器

IShellPropSheetExt

Drag and drop 處理器

IContextMenu

Drop 處理器

IDropTarget

QueryInfo 處理器(Shell V4.71+)

IQueryInfo

其中 "Drag and drop 處理器" 的除了 COM 接口IContextMenu 實(shí)現外還得需要注冊表的特殊注冊才可以實(shí)現。其中 IContextMenu 接口有三個(gè)虛成員函數需要我們的組件來(lái)實(shí)現,其函數原型分別如下:

HRESULT QueryContextMenu(
      HMENU hmenu,
      UINT indexMenu,
      UINT idCmdFirst,
      UINT idCmdLast,
      UINT uFlags
 );

注:在QueryContextMenu成員函數中我們可以加入自己的菜單項,插入菜單項其實(shí)很簡(jiǎn)單,我們可以通過(guò) InsertMenu API 函數來(lái)實(shí)現,如下代碼所示:

::InsertMenu(hmenu, indexMenu, MF_STRING | MF_BYPOSITION, 
  idCmdFirst, IDM_REG_MNU_TXT);

QueryContextMenu 的處理過(guò)程十分簡(jiǎn)單,在這里無(wú)須多說(shuō)。

HRESULT GetCommandString(
      UINT idCmd,
      UINT uFlags,
      UINT *pwReserved,
      LPSTR pszName,
      UINT cchMax
    );

注:GetCommandString成員函數為 Explorer提供了在狀態(tài)欄顯示菜單命令提示信息的方法。在這個(gè)方法中 "LPSTR pszName" 是我們要關(guān)注的參數,我們只要根據 "UINT uFlags" 參數來(lái)填充 "LPSTR pszName" 參數即可。在這里可能會(huì )涉及到 ANSI 和 UNICODE 之間相互轉換的知識,不過(guò)在這里我要提醒大家的是:在 COM 編程中盡可能使用兼容的 TCHAR 類(lèi)型,同時(shí)對字符操作也盡量不要使用 C 類(lèi)的 <string.h> 和<stdio.h> 等等函數庫,因為這樣會(huì )使您無(wú)法通過(guò) "Win32Release Mindependency " 或其他 UINCode/Release 版本的編譯過(guò)程。

HRESULT InvokeCommand(
     LPCMINVOKECOMMANDINFO pici
 );

InvokeCommand 函數實(shí)現最終菜單項命令的執行。在"LPCMINVOKECOMMANDINFO pici" 參數中包含了當前用戶(hù)執行的菜單項ID和其他一些標志信息,如下代碼可獲取菜單項的ID:

//如果 nFlag 不為0則說(shuō)明 pici->lpVerb 指向一個(gè)以''\0''結尾的字符串
 int nFlag = HIWORD(pici->lpVerb);
 
 //用戶(hù)當前點(diǎn)擊的菜單項ID 
 int nMnuId = LOWORD(lpici->lpVerb);

一旦獲取了菜單項ID那么我們就可以根據不同的菜單項來(lái)執行相應的動(dòng)作,如圖1.2 所示的 "Register Component" 和 "UnRegister Component" 菜單項所對應的 "注冊/反注冊進(jìn)程內組件" 動(dòng)作。

  1. 組件必要的宏定義部分

    其實(shí)這一步十分簡(jiǎn)單,本可以忽略,但是為了把過(guò)程講的更清楚一點(diǎn)我還是列了出來(lái):
8.  //聲明組件注冊所用的注冊表REG資源
9.  //其中IDR_SIMPLESHLEXT為注冊表資源ID
10.DECLARE_REGISTRY_RESOURCEID(IDR_SIMPLESHLEXT)
11.//AddRef和Release成員函數的實(shí)現
12.DECLARE_PROTECT_FINAL_CONSTRUCT()
13.//組件接口映射部分,該部分映射主要是告訴QueryInterface能返回哪些接口給外部
14.BEGIN_COM_MAP(CSimpleShlExt)
15. COM_INTERFACE_ENTRY(ISimpleShlExt)
16. COM_INTERFACE_ENTRY(IDispatch)
17. COM_INTERFACE_ENTRY(IShellExtInit)  //IShellExtInit接口
18. COM_INTERFACE_ENTRY(IContextMenu)  //IContextMenu接口
END_COM_MAP()
  1. 組件注冊
    • 首先要在系統文件類(lèi)型".DLL"下注冊上下文菜單處理器

      創(chuàng )建注冊表項HKCR\dllfile\ShellEx\ContextMenuHandlers\SimpleShlExt,
      并設置其默認值為我們類(lèi)的GUID值即可。
    • 設置訪(fǎng)問(wèn)許可權(適應于WinNT構架的操作系統)

      如果您不設置該選項則只有 Administrator 權限的用戶(hù)才可以使用該 Shell 擴展
      在 HKCM\SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved 注冊表項下創(chuàng )建鍵值:

      鍵名為:類(lèi)的 GUID
      鍵值為:有關(guān)類(lèi)的描述信息(任意字符串,無(wú)特殊要求)。
    • 組件注冊的程序內部實(shí)現

      DLL文件上下文菜單的實(shí)現通過(guò) REG 注冊表資源文件實(shí)現,描述層次結構如下:
o    HKCR
o    {
o        NoRemove dllfile
o       {
o          NoRemove ShellEx
o          {
o              NoRemove ContextMenuHandlers
o              {
o         //類(lèi)的GUID字符串                   
o         ForceRemove SimpleShlExt = s''{
    • 訪(fǎng)問(wèn)許可權的注冊則在DllRegisterServer DLL輸出函數中完成。
      其實(shí)現只需要使用注冊表的Win32 API函數即可;相應的反注冊組件時(shí)則應在  DllUnregisterServer中刪除相應的注冊表鍵值即可。

    好了,"Context Menu 處理器" 的實(shí)現到此完畢,還是老規矩如有問(wèn)題請直接來(lái)信,我期待大家的來(lái)信,同時(shí)在以后的時(shí)間里我會(huì )繼續討論Shell擴展編程,并會(huì )對我以前的文章大家所存在的疑點(diǎn)做出合適的回答,再次感謝大家的支持和鼓勵


本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
Delphi編寫(xiě)windows外殼擴展
【精心整理】【實(shí)用】visualC中最常用的類(lèi)與API函數
利用Delphi編寫(xiě)Windows外殼擴展
android 上下文菜單Context Menu
VC++6.0常用菜單項
第1章 MATLAB操作基礎
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久