Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[18.119.125.7] |
|
Страницы: (2) [1] 2 все ( Перейти к последнему сообщению ) |
Сообщ.
#1
,
|
|
|
Везде приводится один и тот же совет - создать в HKEY_CLASSES_ROOT папку с произвольным именем и сослаться на нее из ключа с расширением. Т.е
WriteString('MyProg\Shell\Default_Action','','Edit file with MyProg'); WriteString('MyProg\Shell\Default_Action\command','',+MyProgFullPath+' "%1"'); затем WriteString('.bmp','','MyProg'); В XP все работает прекрасно, но вот в NT и 98 это приводит не только к появлению новой команды в системном меню, но и к перерегистрации действия по умолчанию при запуске данного типа файлов, чего мне совсем не нужно. Вопрос в том, как зарегить команду, не регистрируя действие по умолчанию? |
Сообщ.
#2
,
|
|
|
Вот примерчик:
C:\Program Files\Borland\Delphi7\Demos\ActiveX\ShellExt\contmenu.dpr если не разберешся, можно и поподробней... |
Сообщ.
#3
,
|
|
|
боюсь не разберусь даже если подробней. Много дыр заткнуть надо чтобы все это втереть. В англоязычном интерненте полно кстати даже готовых решений, но все утыкаются в эти комы чтоб им пусто было, и почему-то во всех примерах норовят регистрацию через dll проводить. Зачем такие сложности? Неужели просто в реестр записать нельзя?
Народ, дайте код, кто делал? Добавлено вот кстати, мож пригодится кому не такому дубине как я // Open Delphi select dynamic link library // Copy / paste this into the DLL // Then compile // You will have to customize this code. To suite your needs. // once the dll has been compiled you will now have to register this // com server. // Use regsvr32.exe sendtoweb.dll // now open windows explorer and you will see a new menu item // which can be accessed by the desktop also.. unit Sendtoweb; // Author C Pringle Cjpsoftware.com { Implementation of the context menu shell extension COM object. This COM object is responsible for forwarding requests to its partner TPopupMenu component. The TPopupMenu component must reside on the MenuComponentForm, and is referred to explicitly in this example. You can modify this code to make it more flexible and generic in the future. The TContextMenu component registers itself as a global context menu handler. This is accomplished by adding a key to the HKEY_CLASSES_ROOT\*\ShellEx\ContextMenuHandlers key in the registry. jfl } interface uses Classes, ComServ, ComObj, ActiveX, Windows, ShlObj, Interfaces, Menus, ShellAPI, SysUtils,registry; type TContextMenuFactory = class( TComObjectFactory ) public procedure UpdateRegistry( Register: Boolean ); override; end; TContextMenu = class( TComObject, IShellExtInit, IContextMenu ) private FFileName: String; function BuildSubMenu( Menu: HMENU; IndexMenu: Integer; var IDCmdFirst: Integer ): HMENU; protected szFile: array[0..MAX_PATH] of Char; // Required to disambiguate TComObject.Initialize otherwise a compiler // warning will result. function IShellExtInit.Initialize = IShellExtInit_Initialize; public { IShellExtInit members } function IShellExtInit_Initialize(pidlFolder: PItemIDList; lpdobj: IDataObject; hKeyProgID: HKEY): HResult; stdcall; { IContextMenu } 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; var // Must be set prior to instantiation of TContextMenu! GFileExtensions: TStringList; const MenuCommandStrings: array[ 0..3 ] of String = ( '','&STW Web Upload','&STW FTPClient','&STW Setup' ); implementation { TContextMenuFactory } { Public } Function ReadDefaultPAth: String; var path : String; Reg : TRegistry; begin Reg := TRegistry.CReate; try With Reg Do Begin RootKey := HKEY_LOCAL_MACHINE; Path := 'SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths'; If KeyExists(Path) Then Begin OpenKey(Path+'\sendtoweb.exe',false); Result := ReadString(#0); closekey; End; // Key Added to shell ext. End; Finally Reg.CloseKey; Reg.Free; End; End;// Custom registration code procedure TContextMenuFactory.UpdateRegistry( Register: Boolean ); begin inherited UpdateRegistry( Register ); // Register our global context menu handler if Register then begin CreateRegKey( '*\ShellEx\ContextMenuHandlers\SendToWeb', '', GUIDToString( Class_ContextMenu ) ); CreateRegKey( 'CLSID\' + GUIDToString( ClassID ) + '\' + ComServer.ServerKey, 'ThreadingModel', 'Apartment' ); end else begin DeleteRegKey( '*\ShellEx\ContextMenuHandlers\SendToWeb' ); end; end; { TContextMenu } { Private } { Build a context menu using the existing Menu handle. If Menu is nil, we create a new menu handle and return it in the function's return value. Note that this function does not handle nested (recursive) menus. This exercise is left to the reader. } function TContextMenu.BuildSubMenu( Menu: HMENU; IndexMenu: Integer; var IDCmdFirst: Integer ): HMENU; var i: Integer; menuItemInfo: TMenuItemInfo; begin if Menu = 0 then Result := CreateMenu else Result := Menu; // Build the menu items here with menuitemInfo do begin cbSize := SizeOf( TMenuItemInfo ); fMask := MIIM_CHECKMARKS or MIIM_DATA or MIIM_ID or MIIM_STATE or MIIM_SUBMENU or MIIM_TYPE or MIIM_CHECKMARKS ; fType := MFT_STRING; fState := MFS_ENABLED ; hSubMenu := 0; hbmpChecked := 0; hbmpUnchecked := 0; end; for i := 0 to High( MenuCommandStrings ) do begin if i = 0 then menuitemInfo.fType := MFT_SEPARATOR else menuiteminfo.ftype := MFT_String; if i = 1 then menuitemInfo.fstate := MFS_ENABLED OR MFS_DEFAULT Else menuitemInfo.fstate := MFS_ENABLED; menuitemInfo.dwTypeData := PChar(MenuCommandStrings[ i ]); menuitemInfo.wID := IDCmdFirst; InsertMenuItem( Result, IndexMenu + i, True, menuItemInfo ); Inc( IDCmdFirst ); end; end; { IShellExtInit } function TContextMenu.IShellExtInit_Initialize( pidlFolder: PItemIDList; lpdobj: IDataObject; hKeyProgID: HKEY ): HResult; var medium: TStgMedium; fe: TFormatEtc; begin with fe do begin cfFormat := CF_HDROP; ptd := Nil; dwAspect := DVASPECT_CONTENT; lindex := -1; tymed := TYMED_HGLOBAL; end; // Fail the call if lpdobj is Nil. if lpdobj = Nil then begin Result := E_FAIL; Exit; end; // Render the data referenced by the IDataObject pointer to an HGLOBAL // storage medium in CF_HDROP format. Result := lpdobj.GetData(fe, medium); if Failed(Result) then Exit; // If only one file is selected, retrieve the file name and store it in // szFile. Otherwise fail the call. if DragQueryFile(medium.hGlobal, $FFFFFFFF, Nil, 0) = 1 then begin DragQueryFile(medium.hGlobal, 0, szFile, SizeOf(szFile)); Result := NOERROR; end else Result := E_FAIL; ReleaseStgMedium(medium); end; { IContextMenu } function TContextMenu.QueryContextMenu( Menu: HMENU; indexMenu, idCmdFirst, idCmdLast, uFlags: UINT ): HResult; var extension: String; I: Integer; idLastCommand: Integer; begin Result := E_FAIL; idLastCommand := idCmdFirst; // Extract the filename extension from the file dropped, and see if we // have a handler registered for it // extension := UpperCase( ( FFileName ) ); //for i := 0 to GFileExtensions.Count - 1 do // if Pos(Lowercase(GFileExtensions[ i ]),lowercase(extension))=0 then // begin BuildSubMenu( Menu, indexMenu, idLastCommand ); // Return value is number of items added to context menu Result := idLastCommand - idCmdFirst; // Exit; // end; end; function TContextMenu.InvokeCommand( var lpici: TCMInvokeCommandInfo ): HResult; var idCmd: UINT; begin if HIWORD( Integer(lpici.lpVerb) ) <> 0 then Result := E_FAIL else begin idCmd := LOWORD( lpici.lpVerb ); Result := S_OK; // Activate the Dialog And prepare to send data to the // web case idCmd of 1: Begin ShellExecute( GetDesktopWindow, nil,Pchar(ExtractFileName(ReadDefaultPath)), Pchar('Direct'+'"'+szfile+'"'), nil, SW_SHOW ); End; 3:Begin ShellExecute( GetDesktopWindow, nil,Pchar(ExtractFileName(ReadDefaultPath)), Pchar('Path'), nil, SW_SHOW ); End; 2: ShellExecute( GetDesktopWindow, nil, Pchar(ExtractFileName(ReadDefaultPath)), PChar(''), nil, SW_SHOW ); else Result := E_FAIL; end; end; end; function TContextMenu.GetCommandString( idCmd, uType: UINT; pwReserved: PUINT; pszName: LPSTR; cchMax: UINT ): HResult; begin // StrCopy( pszName, 'Send To The Web') ; Result := S_OK; end; initialization { Note that we create an instance of TContextMenuFactory here rather than TComObjectFactory. This is necessary so that we can add some custom registry entries by overriding the UpdateRegistry virtual function. } TContextMenuFactory.Create( ComServer, TContextMenu, Class_ContextMenu, 'ContextMenu', 'Send To The Web', ciMultiInstance ); // Initialize the file extension list GFileExtensions := TStringList.Create; // GFileExtensions.Add( 'setup msn' ); finalization GFileExtensions.Free; end. |
Сообщ.
#4
,
|
|
|
Значится смотри сюда
Для подобных действий пишется маленький комсервер задача которого лишь реализовать 2 интерфейса IShellExtInit и IContextMenu. Для чего это делается - операционная система при инициализации меню проверит твою библиотеку на предмет: поддерживает ли она эти интерфейсы и если да - то вызовет нужные их методы. Ну а уж при срабатывании данных методов ты и добавляешь свои пункты меню. вот реализация сервера: // Test COM Server Shell Context menu extention library CONTMENU; uses ComServ, ContextM in 'ContextM.pas'; exports DllGetClassObject, DllCanUnloadNow, DllRegisterServer, DllUnregisterServer; begin end. unit ContextM; interface uses Windows, ActiveX, ComObj, ShlObj; type TContextMenu = class(TComObject, IShellExtInit, IContextMenu) private FFileName: array[0..MAX_PATH] of Char; TmpFileNames:String; protected { IShellExtInit } function IShellExtInit.Initialize = SEIInitialize; function SEIInitialize(pidlFolder: PItemIDList; lpdobj: IDataObject; hKeyProgID: HKEY): HResult; stdcall; { IContextMenu } 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; resourcestring IDC_TEST1 = 'Тестовая строка номер 1'; IDC_TEST2 = 'Тестовая строка номер 2'; const Class_ContextMenu: TGUID = '{45C15F61-ACAD-48C6-8D86-321ED8A6CFC6}'; implementation uses ComServ, SysUtils, ShellApi, Registry, Graphics; // Тут наше меню инициализируется // на вход приходит интерфейс IDataObject из которого мы можем получить // список файлов и папок над которыми будут происходить действия function TContextMenu.SEIInitialize(pidlFolder: PItemIDList; lpdobj: IDataObject; hKeyProgID: HKEY): HResult; var StgMedium: TStgMedium; FormatEtc: TFormatEtc; FilesCount,I:Integer; begin if (lpdobj = nil) then begin Result := E_INVALIDARG; Exit; end; 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; TmpFileNames := ''; FilesCount := DragQueryFile(StgMedium.hGlobal, $FFFFFFFF, nil, 0); for I:= 0 to FilesCount - 1 do begin DragQueryFile(StgMedium.hGlobal, I, FFileName, SizeOf(FFileName)); TmpFileNames := TmpFileNames + '"'+FFileName+'" '; end; Result := NOERROR; ReleaseStgMedium(StgMedium); end; // Создание меню // по этому событию мы добавляем новые элементы меню... function TContextMenu.QueryContextMenu(Menu: HMENU; indexMenu, idCmdFirst, idCmdLast, uFlags: UINT): HResult; begin Result := MakeResult(SEVERITY_SUCCESS, FACILITY_NULL, 0); if ((uFlags and $0000000F) = CMF_NORMAL) or ((uFlags and CMF_EXPLORE) <> 0) then begin // Разделитель InsertMenu(Menu, indexMenu, MF_SEPARATOR or MF_BYPOSITION, 0, nil); // первый пункт меню InsertMenu(Menu, indexMenu + 1, MF_STRING or MF_BYPOSITION, idCmdFirst, PChar(IDC_TEST1)); // второй пункт меню InsertMenu(Menu, indexMenu + 2, MF_STRING or MF_BYPOSITION, idCmdFirst + 1, PChar(IDC_TEST2)); // разделитель InsertMenu(Menu, indexMenu + 3, MF_SEPARATOR or MF_BYPOSITION, 0, nil); // указываем сколько пунктов меню мы добавили // 2 пункта - т.к. разделители не считаются Result := MakeResult(SEVERITY_SUCCESS, FACILITY_NULL, 2); end; end; // данная функция срабатывает при нажатии на наш элемент меню function TContextMenu.InvokeCommand(var lpici: TCMInvokeCommandInfo): HResult; begin Result := E_FAIL; if (HiWord(Integer(lpici.lpVerb)) <> 0) then Exit; Result := NOERROR; // Выбор элементов меню идет по возрастающей в том порядке // в каком они были добавлены case LoWord(lpici.lpVerb) of 0: // первый элемент меню // тут собственно и нужно делать реакцию на нажатие ;) MessageBox(lpici.hWnd, PChar(TmpFileNames), PChar(IDC_TEST1 + ' Pressed'), MB_OK); 1: // второй элемент меню MessageBox(lpici.hWnd, PChar(TmpFileNames), PChar(IDC_TEST2 + ' Pressed'), MB_OK); else Result := E_INVALIDARG; end; end; // Данная функция вызывается когда статус бар в эксплорере активен // и в нем отображается краткая информация о подсвеченном пункте меню function TContextMenu.GetCommandString(idCmd, uType: UINT; pwReserved: PUINT; pszName: LPSTR; cchMax: UINT): HRESULT; begin Result := S_OK; if uType = GCS_HELPTEXT then case idCmd of 0: begin StrCopy(pszName, 'Справочная информация по первому пункту меню'); end; 1: begin StrCopy(pszName, 'Справочная информация по второму пункту меню'); end else Result := E_INVALIDARG end end; type TContextMenuFactory = class(TComObjectFactory) public procedure UpdateRegistry(Register: Boolean); override; end; // Это процедура которая будет выполнятся при вызове библиотеки из командной строки // regsvr32 C:\CONTMENU.dll - регистрация библиотеки // regsvr32 C:\CONTMENU.dll -unregister - снятие библиотеки с регистрации procedure TContextMenuFactory.UpdateRegistry(Register: Boolean); var ClassID: string; begin if Register then begin inherited UpdateRegistry(Register); ClassID := GUIDToString(Class_ContextMenu); CreateRegKey('Test\shellex', '', ''); CreateRegKey('Test\shellex\ContextMenuHandlers', '', ''); CreateRegKey('Test\shellex\ContextMenuHandlers\ContMenu', '', ClassID); 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, 'Test Context Menu Shell Extension'); finally Free; end; end else begin DeleteRegKey('Test\shellex\ContextMenuHandlers\ContMenu'); DeleteRegKey('Test\shellex\ContextMenuHandlers'); DeleteRegKey('Test\shellex'); inherited UpdateRegistry(Register); end; end; initialization TContextMenuFactory.Create(ComServer, TContextMenu, Class_ContextMenu, '', 'Test Context Menu Shell Extension', ciMultiInstance, tmApartment); end. Вот и все, компилишь этот код и у тебя готовый ком сервер... Регистрировать билиотеку из своей программы так: // Установка... procedure TForm1.btnRegClick(Sender: TObject); begin with TRegistry.Create do try RootKey := HKEY_CLASSES_ROOT; OpenKey('CLSID\{45C15F61-ACAD-48C6-8D86-321ED8A6CFC6}\InprocServer32', True); WriteString('','C:\CONTMENU.dll'); WriteString('ThreadingModel','Apartment'); CloseKey; finally Free; end; with TRegistry.Create do try RootKey := HKEY_LOCAL_MACHINE; OpenKey('SOFTWARE\Classes\CLSID\{45C15F61-ACAD-48C6-8D86-321ED8A6CFC6}\InprocServer32', True); WriteString('','C:\CONTMENU.dll'); WriteString('ThreadingModel','Apartment'); CloseKey; finally Free; end; with TRegistry.Create do try RootKey := HKEY_LOCAL_MACHINE; OpenKey('SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved', True); WriteString('{45C15F61-ACAD-48C6-8D86-321ED8A6CFC6}', 'Test Context Menu Shell Extension'); CloseKey; finally Free; end; with TRegistry.Create do try RootKey := HKEY_CLASSES_ROOT; OpenKey('*\shellex\ContextMenuHandlers\Test', True); WriteString('','{45C15F61-ACAD-48C6-8D86-321ED8A6CFC6}'); CloseKey; finally Free; end; with TRegistry.Create do try RootKey := HKEY_CLASSES_ROOT; OpenKey('Folder\shellex\ContextMenuHandlers\Test', True); WriteString('','{45C15F61-ACAD-48C6-8D86-321ED8A6CFC6}'); CloseKey; finally Free; end; end; а снимать с регистрации вот так: // Удаление ... procedure TForm1.btnUnRegClick(Sender: TObject); begin with TRegistry.Create do try RootKey := HKEY_CLASSES_ROOT; OpenKey('CLSID', True); DeleteKey('{45C15F61-ACAD-48C6-8D86-321ED8A6CFC6}'); CloseKey; finally Free; end; with TRegistry.Create do try RootKey := HKEY_LOCAL_MACHINE; OpenKey('SOFTWARE\Classes\CLSID', True); DeleteKey('{45C15F61-ACAD-48C6-8D86-321ED8A6CFC6}'); CloseKey; finally Free; end; with TRegistry.Create do try RootKey := HKEY_LOCAL_MACHINE; OpenKey('SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved', True); DeleteValue('{45C15F61-ACAD-48C6-8D86-321ED8A6CFC6}'); CloseKey; finally Free; end; with TRegistry.Create do try RootKey := HKEY_CLASSES_ROOT; OpenKey('*\shellex\ContextMenuHandlers', True); DeleteKey('Test'); CloseKey; finally Free; end; with TRegistry.Create do try RootKey := HKEY_CLASSES_ROOT; OpenKey('Folder\shellex\ContextMenuHandlers', True); DeleteKey('Test'); CloseKey; finally Free; end; end; |
Сообщ.
#5
,
|
|
|
Спасибо! А можно сделать так чтобы пункты меню возникали только над определенными типами файлов?
|
Сообщ.
#6
,
|
|
|
Да конечно, при вызове QueryContextMenu проверяй какие файлы находятся в TmpFileNames, если данные типы файлов тебе не подходят, то выходи из процедуры с результатом
Result := MakeResult(SEVERITY_SUCCESS, FACILITY_NULL, 0); |
Сообщ.
#7
,
|
|
|
Все классно! И хорошо объяснено. Но появились вопосы:
После запуска и работы библиотеки, она становится защищенной от записи. Т.е. Удалить я ее уже не могу, пока не перезагружу комп. Как это лечить? И очень хотелось бы поподробнее узнать о: InsertMenu(Menu, indexMenu + 1, MF_STRING or MF_BYPOSITION, idCmdFirst, PChar(IDC_TEST1)); Что за параметры, какие могут принимать значения и т.д. Спасибо. |
Сообщ.
#8
,
|
|
|
Цитата Patriot @ После запуска и работы библиотеки, она становится защищенной от записи. Т.е. Удалить я ее уже не могу, пока не перезагружу комп. Как это лечить? В реестре вот по этому пути HKEY_LOCAL_MASHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer установи строковое значение AlwaysUnloadDLL равным "1" (если такого значения нет, тогда создай его) Цитата Patriot @ Что за параметры, какие могут принимать значения и т.д. Все еесть вот тут: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/resources/menus/menureference/menufunctions/insertmenu.asp |
Сообщ.
#9
,
|
|
|
А можно указать четкую ширину меню? Или заставить автоматически переносится строчки? А то нужно вставть большой текст и все растягивается по экрану. Еще есть ли возможность Выделять жирным, курсив, подчеркивание, изменение цвета текста, фона? Т.е. форматирование и оформление? Если не сложно, то можно на этом же примере показать?
Спасибо. |
Сообщ.
#10
,
|
|
|
Чесно говоря не эксперементировал с этим, но думаю что нужно смотреть в сторону MF_OWNERDRAW вместо MF_STRING.
После этого обычно отлавливают сообщения WM_DRAWITEM для отрисовки элемента и WM_MEASUREITEM для выставления высоты и ширины элемента. Вот правда где их ловить в СОМ сервере, не знаю... Вероятно нужно делать IShellMenu.GetMenu ... |
Сообщ.
#11
,
|
|
|
Подскажите если кто знает как это можно на Visual C++ сделать(добавить пунк меню)?
Пожалуйста!!! Добавлено Пожалуйста! |
Сообщ.
#12
,
|
|
|
Цитата P-Lib @ Подскажите если кто знает как это можно на Visual C++ сделать(добавить пунк меню)? Создаешь ATL проект и следуешь инструкциям вот по этому линку: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/shell/programmersguide/shell_int/shell_int_extending/extensionhandlers/contextmenuhandlers.asp |
Сообщ.
#13
,
|
|
|
Поднему эту тему, а ка в Vista\7 32 bit и 64 bit быть ? Что там, что там не пашет
|
Сообщ.
#14
,
|
|
|
Что именно не пашет?
|