Shell Extension Writing Complete Fool Guide (1) (Michael Dunn)

zhaozj2021-02-11  151

Shell extension

(Shell eXtention)

Is a to

Windows

shell

(

Resource manager

)

Add some features

COM

Object. This has a lot of content, but it is very little document about them to tell us how to write these housings.

(Shell)

program. If you want to do a deep understanding of the outer shell, I will try to recommend it to you.

Dino esposito

Very good book "

Visual C Windows Shell Programming

". But for those who don't have this book and just care about how to write the shell extension, I wrote a guide to you, if it is not so much, I can understand how to write a shell extension to provide good help. To read this guide, make sure you

COM

with

ATL

Be quite familiar.

The first part includes an introduction to the outline of the outer casing extension, and provides a context menu extension routine to make you interested in future parts.

What is the outer casing extension?

This has two parts, housing and extensions. The housing refers to the Explorer, and the extension is the code that is called by the Explorer when a subscribed event (such as right-click one .doc document) occurs. So the extension is a COM object that adds a feature to the resource manager.

A housing extension is a process in a process that implements some excuses with the resource manager. And in my opinion, ATL is the easiest way to quickly implement an extension and make it run because you don't have to write queryinterface () and addref () and addRef () over and over again. Moreover, it is easier to test the debt extension in Windows NT / 2000.

There are many extensions, each extension is called when different events occur. Below is some of the relatively generalized types and the situation they call:

Types of

When is it called?

What can you do?

Context menu

The user right-click when the file or directory is right. In the outer casing extension 4.71 or more, right-click on the background of the directory window and will be called.

Add a project to the context menu.

Attribute list

When the properties of the file are displayed.

Add an attribute page to the property order.

Drag

Users right-click to drag the project and throw it on a directory window to live on a desktop.

Add a project to the context menu.

throw

The user dragged an item and threw it on a file.

Anything you want to do.

Query information (shell version 4.71 )

The user mouse is hover on a file or on other housing objects like my computer.

Returns a resource manager string on the toolbar prompt.

So far you may now look in the resource manager. If you have Winzip installed (who is there?), It includes many cases of housing extensions, one of which is the context handle. The next world Winzip 8 is a screenshot of the context menu for compression files:

Winzip contains code that adds menu items and provides sensitive help (displays text on the resource manager status), and works in one of the users.

Winzip also includes dragging and throwing handles. This type and context menu extension is very similar, but it is only called only when the user is right-click dragging a file. Here is how WinZip's drag handle adds a context menu:

There are still many other types (Microsoft has been adding more in new versions). To now, we have seen the context menu extension because it is very easy to write, we will be easy to see its results (soon satisfied).

Before we started coding, there are some tips that will make us make more easier. When you make a housing extension is called by the resource manager, it will stay in memory in memory, so that it cannot be immediately reconstructed (Rebuild). In order to uninstall these extensions more frequently, create this registry key: HKLM / Software / Microsoft / Windows / CurrentVersion / Explorer / alwaysunloaddll

And set its default to "1". This is the best way in the Windows 9x series. In NT / 2000, to the following:

HKCU / Software / Microsoft / Windows / CurrentVersion / Explorer

Create a double-byte value called DesktopProcess so that its value is 1. This allows the desktop and taskbar to run in a process, and the post-sent resource manager runs in its own process. This means you can use a separate resource manager window to debug, and when you turn off it, your DLL will be unloaded, avoiding the file is still using the problem. To make your registry to change, you must log out and log in.

I will explain how to explain how to debug under WIN 9X.

Start a context menu extension - what can it do?

Let us start making an extension, it only pops up a message box indicating that it is already working. We set a hook for the file that extension .txt, so when the user right-click on a text file, our extension can be called.

Start using AppWizard

Ok, now it is when we start. what is that? I haven't told you how to expand the interface with a mysterious case? Don't worry, I will explain to you during the next process. I found that if a concept is explained, there is an example easier to understand that you can understand it through an example code. I will explain anything first, then give the code, but I found it or not easily absorbed. In short, start your MSVC, we have to start.

Run AppWizard and make a new ATL COM Wizard App. We call it simplexT. Keep all default options in the wizard, click Finish. We now have an empty ATL project that will generate a DLL, but we have to add your own housing to extend COM objects. In the ClassView tree, right-click SimpleExt Classes item, select New ATL Object.

In the ATL Object Wizard, the first panel has chosen Simple Object, as long as you click Next. In the second panel, enter SIMPLESHLEXT in the SHORT NAME editing control, and then click OK (other edit box in the panel will be completed automatically). This creates a class named CSIMpleshlext, which contains basic code that implements a COM object. We will add our code to this class.

Initialization interface

When our housing extension is loaded, the resource manager calls our QueryInterface () function to get a pointer to the ISHELLEXTINIT interface. This interface has only one way, initialize (), its prototype is as follows:

HRESULT Ishellextinit :: Initialize

LPCIDLIST PIDLFOLDER,

LPDataObject pdataobj,

HKEY HPROGID;

Resource Manager uses this method to give us different information. Pidlfolder is a PIDL (PIDL [POINTER TO AI ID LIST] is a data structure (PIDL [POINTER TO AI ID] is an object (whether it is a file system object) in the unique flag housing. Pdataobj is an IDATAOBJECT interface pointer, Through it we can get the file name of the file. Hprogid is an open HKEY, through which we can access registry keys containing our DLL registration data. In this simple extension, we only need to use pDataobj Parameters. Add this interface method to our COM object, open the simpleshlext.h file first, and add the code line written as follows:

#include

#include

Class ATL_NO_VTABLE CSIMPLESHLEXT:

Public CComobjectrootex ,

Public ccomcoclass ,

Public IDispatchImpl ,

Public ishellextinit

{

Begin_COM_MAP (CSIMpleshlex)

COM_IMPLACE_ENTRY (isimpleshlex)

COM_ITERFACE_ENTRY (Idispatch)

COM_ITERFACE_ENTRY (Ishellextinit)

END_COM_MAP ()

This COM_MAP is how the ATL is "QueryInterface (). This list tells ATL Other programs that use QueryInterface () to get anything from us.

Next, add the initialize () function among the declarations of the class. In addition, we also need a variable that saves the file name:

protected:

TCHAR M_SZFILE [MAX_PATH];

PUBLIC:

// ishellextinit

STDMETHOD (INITIALIZE) (LPDataObject, HKEY);

Next, in the SimpleShlext.cpp file, the definition of this function is added:

HRESULT CSIMPLESHLEXT :: Initialize

LPCIDLIST PIDLFOLDER,

LPDataObject pdataobj,

HKEY HPROGID)

What we have to do is to get the file name of the file that is right-click, and display it in a message box. If there are many files selected, you can access them through the PDATAOBJ interface pointer. But in order to maintain the simplicity of this example, I just get the file name of the first file.

The file name is saved to drag with you to drag and throw a file to the window using the WS_EX_ACCEPTFILES style. That means that we have used the same API: DragQueryFile (). We start this function by getting a handle that contains data in iDataObject:

{

Formatetc FMT = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};

STGMEDIUM STG = {TYMED_HGLOBAL};

HDrop HDROP;

// Look for CF_HDROP DATA in The Data Object.

IF (failed (pDataobj-> getData (& fmt, & strg))) {

// Nope! Return An "Invalid Argument" Error Back to Explorer.

Return E_INVALIDARG;

}

// Get a Pointer to the Actual Data.

HDrop = (HDROP) GlobalLock (strg.hglobal);

// make sure thing.

IF (null == HDROP)

{

Return E_INVALIDARG;

}

Note that error check is extremely important, especially pointers. Because our extension runs in the process space of the Explorer, if our program is destroyed, the resource manager is also destroyed. Under Win 9x, this may mean restart.

Now, we have an HDROP handle, we can get the name of the file we need.

// Sanity Check - make Sure The Is at Least One FileName.

Uint unumfiles = DragQueryFile (HDROP, 0xfffffffff, null, 0);

IF (0 == unmumfiles)

{

GlobalUnlock (strg.hglobal);

ReleaseSTGMEDIUM (& STG);

Return E_INVALIDARG;

}

HRESULT HR = S_OK;

// Get the name of the first file and store it is in our member variable m_szfile.

IF (0 == DragQueryfile (HDROP, 0, M_SZFILE, MAX_PATH))

{

HR = E_INVALIDARG;

}

GlobalUnlock (strg.hglobal);

ReleaseSTGMEDIUM (& STG);

Return HR;

}

If we return to E_INVALIDAR, the resource manager will call our extension when you right-click the event. If we return S_OK, the resource manager will call QueryInterface again () again to get another interface pointer we will add: iconTextMenu.

And the interface of the context menu interacts

Once the resource manager initializes our extension, it will call the icontextMenu to add menu items, sensitive to help and complete the user's choices.

Add IconTextMenu interfaces to our extension and add iShellextinit. Open SimpleShlext.h and add a red color code:

Class ATL_NO_VTABLE CSIMPLESHLEXT:

Public CComobjectrootex ,

Public ccomcoclass ,

Public IDispatchImpl ,

Public ishellextinit,

Public iconTextMenu

{

Begin_COM_MAP (CSIMpleshlex)

COM_IMPLACE_ENTRY (isimpleshlex)

COM_ITERFACE_ENTRY (Idispatch)

COM_INTERFACE_ENTRY (ishellextinit) com_interface_entry (iconTextMenu)

END_COM_MAP ()

Then add the prototype of the IconTextMenu method:

PUBLIC:

// icontextmenu

StdMethod (getcommandstring) (UINT, UINT, UINT *, LPSTR, UINT);

STDMETHOD (INVOKECMMAND) (LPCMINVokeCommandInfo);

STDMETHOD (QueryContextMenu (HMENU, UINT, UINT, UINT, UINT);

Change context menu

IconTextMenu has three ways. The first, queryContextMenu (), let us change the menu. Its prototype is:

HRESULT ICONTEXTMENU :: queryContextMenu

Hmenu Hmenu,

Uint umenuindex,

Uint uidfirstcmd,

UINT Uidlastcmd,

UINT UFLAGS;

HMENU is the handle of the context menu. UmenuIndex is the start position we started to add our menu items. UidFirstCMD and UidlastCMD are the range of command ID values ​​we can use to menu items. UFlags pointed out why the resource manager is calling queryContextMenu (), which we will see later.

You will get a different answer if you have a different answer if you ask if you ask different people. DINO Esposito's book says it makes the number of menu items added by queryContextMenu (). On MSDN, the VC 6 section said that it is the last command ID of the last menu item plus 1. The latest MSDN documents have the following description:

Set the value of the code [returned by HRESULT] to the assigned maximum command ID offset plus 1. For example, assuming that idcmdfirst is set to 5, you add 3 menu items to 5, 7 and 8, respectively. Its return value will be made_hresult (severity_success, 0, 8 - 5 1).

In all the code I wrote now, I accepted Dino's explanation, so working very well. In fact, the method of making the return value and the online MSDN method is the same, starting counting when you start adding your menu item using uidfirstcmd, each addition of an increase 1.

Our simple extension will only add a menu item, so the queryContextMenu () function is quite simple:

HRESULT CSIMPLESHLEXT :: QueryContextMenu

Hmenu Hmenu,

Uint umenuindex,

Uint uidfirstcmd,

UINT Uidlastcmd,

UINT UFLAGS)

{

// if the flags include cmf_defaultonly kil dam''t do anything.

IF (uflags & cmf_defaultonly)

{

Return make_hresult (severity_success, facility_null, 0);

}

INSERTMENU (HMENU, UmenuIndex, MF_BYPOSITION, UIDFIRSTCMD, _T ("Simpleshlext Test Item");

Return make_hresult (severity_success, facility_null, 1);

}

What we must do first is to check UFLAGS. You can query all the logo lists in MSDN, but only the same is important for the context menu extension: CMF_DEFAULTOONLY. This logo tells the namespace extension only adds the default menu item. If this flag is in, the housing extension will not add any menu items. That's why we immediately returned 0 reason when CMF_DEFAULTOONLY exists. If the flag does not exist, we change the menu (using the HMENU handle), then return 1 Tell the shell. We have added a menu item. Sensitive help in the status bar

IconTextMenu in the next method of call is getcommandstring (). If you right-click a text file in the Explorer window, or select a text file, and then click the File menu, you will display the sensitive help. Our getCommandString () function will return a string that allows the resource manager to display.

Getcommandstring () function prototype is as follows:

HRESULT ICONTEXTMENU :: getcommandstring

UINT IDCMD,

UINT UFLAGS,

Uint * pWRESERVED,

LPSTR PSZNAME,

Uint cchmax;

IDCMD is a number of items based on 0-based indication. Because we just add a menu item, IDCMD will always be 0. But if we added, I said, 3, IDCMD will be 0, 1 or 2. UFLAGS is another sign group. I will describe it later. We can ignore the PWRESERVED. PSZNAME is a pointer to a cache owned by the outer casing that saves the displayed help string. CCHMAX is the size of the cache. The return value is HRESULT constant, such as S_OK or E_FAIL.

GetCommandString () can also be used to get "verbs" of the menu item. "Verb" is a string that logs the action of the file, which is independent of the language. About the documentation for shellexecute () makes more instructions, and the topic of "verbs" is more suitable for another article, which is a brief description of the verbs listed in the registry (for example, "open" and "print" ), Or those "verbs" created with the context menu extension. This allows behavior implemented in the housing extension to be called by shellexecute ().

In short, I mention all these reasons are that we have to be sure why getcommandstring () is called. If the resource manager needs a sensitive help string, we will provide. If the resource manager requests a "verb", we will ignore it. This is where uflags work. If the bit of the uflags GCS_HELPText is set, the resource manager will request a sensitive help. Additional, if the GCS_UNICODE bit is set, we must return a Unicode string.

Our getcommandstring () code should look like this:

#include // for ATL STRING Conversion Macros

HRESULT CSIMPLESHLEXT :: getcommandstring

UINT IDCMD,

UINT UFLAGS,

Uint * pWRESERVED,

LPSTR PSZNAME,

Uint cchmax)

{

Uses_Conversion;

// Check IDCMD, IT Must BE 0 Since We Have Only One Menu Item.

IF (0! = IDCMD)

Return E_INVALIDARG;

// if Explorer is asking for a help string, Copy Our string Into the // support buffer.

IF (uflags & gcs_helptext)

{

LPCTSTR SZTEXT = _T ("This Is The Simple Shell Extension's Help");

IF (UFLAGS & GCS_UNICODE)

{

// we need to cast pszname to a uncode string, and then use the

// UNICODE STRING COPY API.

LSTRCPYNW ((LPWSTR) PSZNAME, T2CW (Sztext), CCHMAX;

}

Else

{

// use the ansi string copy API to return The help string.

LSTRCPYNA (Pszname, T2CA (Sztext), CCHMAX;

}

Return S_OK;

}

Return E_INVALIDARG;

}

Nothing; I just encode the string and convert it to the appropriate character set. If you have never used ATL transform macro, you just look at them first, because this will make me more easily understood a Unicode string to COM methods and OLE functions. In the above code, I use T2CW and T2CA to convert TCHAR strings into Unicode and ANSI separately. The Uses_Conversion macro in the function header has a local variable used by a transform macro.

An important thing to note is that the LSTRCPYN () API function guarantees the target string to end with NULL. This is the difference between it and the CRT function strNcpy (). If the length of the source string is greater than or equal to CCHMAX, StrNcPy () does not add end character NULL. I suggest you always use lstrcpyn () so you don't have to add an inspection after StrNcPy () to ensure that the string is NULL end.

Execute user selection

The last icontextMenu method is invokecommand (). This method will be called when the user clicks the menu item we add. Its prototype is as follows:

HRESULT ICONTEXTMENU :: InvokeCommand (lpcminvokecommandinfo pcmdinfo);

There are a lot of information in the CMinvokeCommandInfo structure, but according to our current intentions, we only need to care about LPVERB and HWND. LPVERB has a dual task - it can be both called "verbs" name, or one to tell us which menu item is selected to be indexed. HWnd is the handle of the resource manager window, where the user calls our extension.

We check LPVERB, because we only add a menu item, so if it is 0, then our menu is clicked. The easiest thing I can think is to pop up a message box, so we do this. This message box displays the file name of the selected file, prove it is indeed work.

HRESULT CSIMPLESHLEXT :: InvokeCommand (lpcminvokecommandinfo pcmdinfo)

{

// if lpverb really points to a string, Ignore this function call and bail out.

IF (0! = HiWord (pcmdinfo-> lpverb))

Return E_INVALIDARG;

// Get The Command Index - The Only Valid One IS 0.

Switch (pcmdinfo-> lpverb) {

Case 0:

{

Tchar Szmsg [MAX_PATH 32];

WSPRINTF (SZMSG, _T ("The SELECTED File WAS: / N / N% S"), M_SZFILE);

Messagebox (pcmdinfo-> hwnd, szmsg, _t ("simpleshlex)),

MB_ICONINFORMATION);

Return S_OK;

}

Break;

DEFAULT:

Return E_INVALIDARG;

Break;

}

}

Registered housing extension

Up to now, we have implemented our COM interface. But ... How to make the resource manager use our extension? ATL automatically generates a registered code of our DLL as a COM server, but it is just letting other programs to use our DLL. In order to tell the resource manager, we must register under the registry key to keep the text file:

HKEY_CLASS_ROOT / TXTFILE

In that button, a key called SHELLEX saves a list of enclosures that will be called for text files. Under Shellex, the ContextMenuHandlers key holds a list of context menu extensions. Each extension creates a word button under ContextMenuHandlers and sets his default to its GUID. So, for our extension, we create the following:

HKEY_CLASS_ROOT / TXTFILE / SHELLEX / ContextMenuHandlers / SimpleXT

And set its default to our GUID:

"{5E2121EE-0300-11D4-8D3B-444553540000}".

However, you don't have to do this. If you look at your list of files in the FileView page, you will find SIMPLESHLEXT.RGS. This is a text file parsed by ATL, which tells ATL what button is added when the server is registered, and what button is deleted when it is reversed. Below we specify the registry entry to add:

HKCR

{

Noremove TXTFILE

{

Noremove Shellex

{

Noremove ContextMenuHandlers

{

FORCEREMOVE SIMPLESHLEXT = S '{5E2121EE-0300-11D4-8D3B-444553540000}'

}

}

}

}

It is the abbreviation of "HKCR" - HKEY_CLASSES_ROOT, each line is the registry key name. Keywords Noremove means that the key cannot be deleted when the server is reversed. The last line is a bit complicated. Keyword forwardremove means deleting it before this button is written. The remaining part of this is specified a string in the default value that will be saved in the SimpleShlext key (that is, "s" means).

Here, I need to explain it. The key we registered is HKCR / TXTFILE. However, this name "TXTFILE" is not a permanent or pre-known. If you look at HKCR / .txt, the default value of that button is the place where this name is saved. This is two side effects:

We will not be reliable to use the RGS script because "TXTFILE" may not be the correct key name.

Some of the other text editors may be installed, they are associated with the .txt file. If they change the default value of the HKCR / .txt key, all existing shell extensions will stop working.

It seems that this is indeed my defect. I think Microsoft is also considering the same thing, because the recently created extension, like QueryInfo extension, is registered under the .txt key. Ok, explain it here. There is a final registration detail. In WIN NT / 2000, we must use our extensions to a "recognized" extension list. If we don't do this, those non-administrators will not be strong in our extension. This list is saved:

HKEY_LOCAL_MACHINE / SOFTWARE / Microsoft / Windows / CurrentVersion / Shell Extensions / Approved

Under this key, we create a string value that its name is our GUID. The content of the string can be anything. Doing the code of these things in our DllregisterServer () and DllunregisterServer () functions. I don't want to take these horses here because it is just a simple registry access. You can find them from this document.

Combating housing extension

In the end, you are writing this quite not easy to expand, and then you will debug it. Open your project settings (Project-> Settings), to the debug bar, enter the full path to the Explorer in the Executable for Debug Session editing box, such as "C: /Windows/explorer.exe". If you are using NT or 2000, and you have already set the DesktopProcess registry key, then when you start debugging in F5, there will be a new resource manager window to open. As long as you work in that window, you will not have problems when you rebuild the DLL, because when you turn off the window, your extension is also uninstalled.

Under Windows 9X, I am afraid you have to close your housing before debugging. Click Start -> "Off System". Press and hold Ctrl Alt Shift and click "Cancel". This will turn off the resource manager, then you see the taskbar disappeared. Switch to MSVC and press F5 to start debugging. Press SHIFT F5 to turn off the resource manager to stop debugging. When you finish debug, you can run Explorer to restart your housing.

What does it look like?

Here is the item we have added to the project:

This is our menu!

Here is the status bar of the resource manager when you are sensitive to help:

The following is the appearance of the message box, which shows the file name of the selected file:

This routine code download address (11K): http://www.codeproject.com/shell/shellexTGUIDE1/SHELLEXTGUIDE1_DEMO.ZIP

Next part ...

The next second part, a new context menu extension will tell you how to operate multiple files at the same time.

You can get the latest version of this and other articles from the following URL: http://home.inreach.com/mdunn/code/

About translation:

转载请注明原文地址:https://www.9cbs.com/read-5941.html

New Post(0)