如何将自己的应用程序放到鼠标右键的快速连接?(像压缩软件WinRAR那样)

解决方案 »

  1.   

    在Windows中,用鼠标右键单击文件或者文件夹时弹出的那个菜单便称为上下文相关菜单。要动态地在上下文相关菜单中增添菜单项,可以通过写Context Menu Handler来实现。比如大家所熟悉的WinZip和UltraEdit等软件都是通过编写Context Menu Handler来动态地向菜单中增添菜单项的。如果系统中安装了WinZip,那么当用右键单击一个名为abc的文件(夹)时,其上下文相关菜单就会有一个名为Add to abc.zip的菜单项。本文要实现的Context Menu Handler与WinZip提供的上下文菜单相似,它将在任意类型文件的上下文菜单中增加一个名为“用写字板打开XXX”(其中XXX为当前选定的文件名称)的菜单项,只要你选择该菜单项,Windows就会启动写字板并打开当前所选的文件  编写Context Menu Handler必须实现IShellExtInit和IContextMenu两个接口。除了IUnknown接口所定义的函数之外,Context Menu Handler还需要用到QueryContextMenu、InvokeCommand和GetCommandString这三个非常重要的成员函数。  (1)QueryContextMenu函数:每当系统要显示一个文件对象的上下文相关菜单时,它首先要调用该函数。为了在上下文相关菜单中添加菜单项,我们在该函数中调用InsertMenu函数。  (2)InvokeCommand函数:当用户选定了某个Context Menu Handler登记过的菜单项后,该函数将会被调用,系统将会传给该函数一个指向LPCMINVOKECOMMANDINFO结构的指针。在该函数中要执行与所选菜单项相对应的操作。  (3)GetCommandString函数:当鼠标指针移到一个上下文相关菜单项上时,在当前窗口的状态条上将会出现与该菜单项相关的帮助信息,此信息就是系统通过调用该函数获取的。  具体编写方法请参阅网上的程序实例,网址为www.pccomputing.com.cn。  3.增添上下文相关菜单项说明  如果要静态地为目录或者某一类文件增添上下文相关菜单项,那么就用不着编写Context Menu Handler,可以通过直接修改Windows注册表来达到此目的。比如,可以将下面的内容存成一个扩展名为.REG的文件,然后双击它将其导入注册表,你会发现所有类型文件的上下文相关菜单中都多了一个名叫“记事本”的菜单项。  REGEDIT4  [HKEY_CLASSES_ROOT\u35760记事本]  [HKEY_CLASSES_ROOT\u35760记事本]  @="notepad.exe"%1""  通过比较,很容易发现这两种方式所得结果的差异。通过直接修改注册表来增添菜单项的确比较简单,然而它不具有交互性,所增添的菜单项是静态的,并且所能实现的功能也非常有限。但是Context Menu Handler则不同,它使我们可以根据上下文的具体情况动态地添加菜单项,比如可以判断当前选定的是哪一类文件、是不是文件夹、选定的文件(夹)的个数以及获取被选定文件(夹)的属性。有时,这些信息对于程序很有用,如果需要得到此类信息,并且需要根据不同的上下文来执行不同的操作,那么只好依靠Context Menu Hander来实现。本文的实例中,其动态性体现在仅当用户选定了一个文件时,才会在上下文相关菜单中增添菜单项,并且菜单项的名字随着所选文件名的不同而相应地变化。
      

  2.   

    用delphi实现外壳扩展作者:hubdog当用户在资源管理器中调用右键菜单时,会显示一个"属性"菜单项,点击属性菜单项会显示一个属性页,用户可以获得甚至修改文件信息。我们可以定制属性页通过实现属性页扩展。如下图所示,本文实现了一个显示wave(波形)文件的信息如声道数等信息的属性页扩展。属性页扩展通常是同某类文件相关联的来实现同之相关的操作和信息显示,另外可以同驱动器相关联,我们还可以用属性页扩展来替换控制面板程序的属性页。象其他外壳扩展程序一样,属性页扩展也是以动态连接库形式实现的进程内COM对象。它除了IUnknown接口外还要实现IShellExtInit和IShellPropSheetExt接口。建立同文件关联的属性页扩展首先,我们用命令File|New...,创建一个ActiveX Library,然后新建一个COM Object,实现的接口为IShellExtInit和IShellPropSheetExt。同文件建立关联需要注册属性页,要在注册表中同相应文件对应的表项下添加Shellex/PropertySheetHandlers子键 ,每增加一个页面就需要注册一个表项,最大可以添加的页面数是24,我们可以用一个扩展实现多个页面。这里我们通过从TComObjectFactory继承类实现的UpdateRegistry实现了注册。 typeTCXPropSheetFactory=class(TComObjectFactory)publicprocedure UpdateRegistry(Register: Boolean); override;end;procedure TCXPropSheetFactory.UpdateRegistry(Register: Boolean);varClassID: string;Str,KeyName : string;begininherited UpdateRegistry(Register);if Register thenbeginClassID:=GUIDToString(Class_CXPropSheet);with TRegistry.Create dotryRootKey:=HKEY_CLASSES_ROOT;OpenKey('.wav',TRUE);KeyName := ReadString('');if Keyname = '' thenbeginWriteString('','WaveFile');OpenKey('.wav',TRUE);KeyName := ReadString('');end;OpenKey('+KeyName+'',TRUE);WriteString('',Classid);finallyFree;end;if(Win32Platform=VER_PLATFORM_WIN32_NT)thenbeginwith TRegistry.Create dotryRootKey:=HKEY_LOCAL_MACHINE;OpenKey('SOFTWAREExtensions', True);OpenKey('Approved', True);WriteString(ClassID, 'Wave File Property Sheet');finallyFree;end;end;endelse删除注册表项.......................end;初始化扩展是通过IShellExtInit实现的,当外壳调用IShellExtInit.Initialize时,它传递一个数据对象包含来文件对应的目录的PIDL标识符。Initialize方法需要从数据对象中提取文件名,并把文件名和PIDL标识符保存起来为了以后使用。 function TCXPropSheet.SEIInitialize(pidlFolder: PItemIDList;lpdobj: IDataObject; hKeyProgID: HKEY): HResult;varStgMedium: TStgMedium;FormatEtc: TFormatEtc;szFile: array[0..MAX_PATH+1]of Char;filecount: integer;beginResult:=E_FAIL;if(lpdobj=nil)then beginResult:=E_INVALIDARG;messagebox(0, '1', '错误', mb_ok);Exit;end;with FormatEtc do begincfFormat:=CF_HDROP;ptd:=nil;dwAspect:=DVASPECT_CONTENT;lindex:=-1;tymed:=TYMED_HGLOBAL;end;Result:=lpdobj.GetData(FormatEtc, StgMedium);if Failed(Result)thenExit;// 如果只有一个文件被选中,获得文件名并保存。filecount:=DragQueryFile(stgmedium.hGlobal, $FFFFFFFF, nil, 0);if filecount=1 then beginResult:=NOERROR;DragQueryFile(stgmedium.hGlobal, 0, szFile, SizeOf(szFile));FFilename:=strpas(szFile);end;ReleaseStgMedium(StgMedium);end;添加页面的操作是通过IShellPropSheetExt接口来实现的。如果属性页是和文件相关联,外壳会调用 IShellPropSheetExt.AddPages给属性页添加一个页面。如果属性页同控制面板程序相关联,外壳调用 IShellPropSheetExt.ReplacePage来替换页面。IShellPropSheetExt.AddPages方法有两个参数,lpfnAddPage是一个指向AddPropSheetPageProc 回调函数的指针,回调函数用来提供要添加的页面信息给外壳。lParam是一个用户自定义的值,这里我们用它来返回给回调函数对象。一般的IShellPropSheetExt.AddPages方法实现步骤是:给PROPSHEETPAGE结构设定正确的值,特别是:把扩展的对象引用记数变量付值给pcRefParent成员,这可以防止页面还在显示时,扩展对象被卸载。
      

  3.   

    实现 PropSheetPageProc 回调函数来处理页面创建和销毁的情况。调用CreatePropertySheetPage函数来创建页面。调用lpfnAddPage指向的函数来来添加创建好的页面。function TCXPropSheet.AddPages(lpfnAddPage: TFNADDPROPSHEETPAGE;lParam: LPARAM): HResult;varPSP: TPropSheetPage;HPSP: HPropSheetPage;beginresult:=E_FAIL;trypsp.dwSize:=SizeOf(psp);psp.dwFlags:=PSP_USEREFPARENT or PSP_USETITLE or PSP_USECALLBACK;psp.hInstance:=hInstance;//这里我们使用了事先储存在wave.res中的对话框模板,模板是用delphi5自带的//resource workshop编辑的,使用delphi5.exe编译的。psp.pszTemplate:=MakeIntResource(100);//标题名psp.pszTitle:='波文件信息';//设定回调函数psp.pfnDlgProc:=@DialogProc;psp.pfnCallBack:=@PropCallback;//设定对象引用记数变量psp.pcRefParent:[email protected];//用lParam向回调函数传递对象psp.lParam:=integer(self);HPSP:=CreatePropertySheetPage(psp);if HPSP<>nil then beginif not lpfnAddPage(HPSP, lParam)then beginDestroyPropertySheetPage(HPSP);end else begin_addref;//增加引用记数,否则一脱离这个方法的作用域,delphi自动释放对象。result:=S_OK;endendexcepton e: exception do begine.message:='添加页面 '+e.message;messagebox(0, pchar(e.message), '错误', mb_ok);end;end;end;function TCXPropSheet.ReplacePage(uPageID: UINT;lpfnReplaceWith: TFNADDPROPSHEETPAGE; lParam: LPARAM): HResult;beginResult:=E_NOTIMPL;//同文件关联时,外壳不调用ReplacePage,所以不用实现end;回调函数处理属性页的消息,主要要响应WM_INITDIALOG消息来初始化页面显示信息,响应WM_COMMAND消息来处理用户交互,响应WM_NOTIFY消息来处理页面切换或关闭后处理操作结果。 function DialogProc(hwndDlg: HWnd; Msg: UINT; wParam: wParam;lParam: LPARAM): Bool; stdcall;varPageObj: TCXPropSheet;filename: string;displayName : string;buffer: array[0..255]of char;SheetHWnd: HWnd;beginresult:=false;tryif Msg=WM_INITDIALOG then begin//初始化界面//获得lparam传递过来的对象pageObj:=TCXPropSheet(PPropSheetPage(lParam)^.lParam);//保存对象信息SetWindowLong(hwndDlg, DWL_USER, integer(pageObj));//设置界面显示波文件信息SetDlgItemText(hwndDlg, 100, PChar(ExtractFileName(PageObj.FFileName)));OpenMedia(PageObj.FFileName);SetDlgItemText(hwndDlg, 101, PChar(IntToStr(GetWavStatus(MCI_WAVE_STATUS_AVGBYTESPERSEC))));SetDlgItemText(hwndDlg, 102, PChar(IntToStr(GetWavStatus(MCI_WAVE_STATUS_BITSPERSAMPLE))));SetDlgItemText(hwndDlg, 103, PChar(IntToStr(GetWavStatus(MCI_WAVE_STATUS_CHANNELS))));CloseMedia;SetWindowLong(hwndDlg, DWL_MSGRESULT, 0);Result:=TRUE;endelse if(Msg=WM_COMMAND)then beginif Lo(wParam)=110 then//用户点击了关于按钮(id=110)MessageBox(0,'作者:hubdog'+#13#10+'email:[email protected]','关于...',MB_OK);end else if(msg=WM_NOTIFY)then beginsheetHwnd:=getparent(hwndDlg);//获得属性页的窗口句柄case PNMHdr(lparam)^.code of//页面失去焦点PSN_KILLACTIVE:beginSetWindowLong(hwndDlg, DWL_MSGRESULT, 0);Result:=TRUE;end;end;end;excepton e: exception do begine.message:='回调处理'+e.message;messagebox(0, pchar(e.message), '错误', mb_ok);end;end;end; 建立同驱动器相关联的属性页扩展用同上面讲的有两点不同:IShellExtInit.Initialize方法传递过来的数据对象包含的驱动器路径可能是 CFSTR_MOUNTEDVOLUME 格式而不是 CF_HDROP格式的。标准驱动器是CF_HDROP格式的,而在NTFS文件系统中映射的远程设备则是CFSTR_MOUNTEDVOLUME格式的。注册表项是HKEY_CLASSES_ROOT子键。建立控制面板属性页扩展同上面讲的有两点不同:控制面板程序调用IShellPropSheetExt.ReplacePage方法来替换页面,它不调用 IShellPropSheetExt。AddPages方法。注册方式:子键可以在不同位置创建,这依赖于扩展是针对用户还是针对机器的。 对用户方式子键是HKEY_CURRENT_USER_PATH_CONTROLPANEL,否则子键是 HKEY_LOCAL_MACHINE_PATH_CONTROLSFOLDER。