安徽省民航局 周毅(longsoft@ah163.com)
一、前言
.NET平臺是微軟公司推出的作為未來軟件運行和開發的環境,C#是微軟力薦的在.NET平臺下開發應用軟件的首選語言。本文將討論在.NET環境下如何使用C#語言開發Windows Shell擴展問題。如今Windows家族已發展到XP世代了,想必每個程序員都對Shell Extension不會感到陌生吧,在這里我不想花太多的時間介紹Shell Extension的原理知識,本文中將通過一個實例介紹用C#創建一個Shell Extension,在此過程中也會簡單介紹一些Shell Extension的原理知識(如果想詳細了解Shell擴展原理知識,請參閱MSDN)。
二、開發環境
(1)、Windows2000 專業版。
(2)、Visual Studio.NET Beta 2.0或正式版1.0。
三、原理介紹
本實例實現一個ShellExecuteEx Win32調用的鉤子操作,Windows Explorer常常會用到這個調用,如打開、編輯、打印等等Shell操作都要用到這個調用。在Windows注冊表HKLM\Software\Microsoft\Windows\CurrentVersion\Explorer\ShellExecuteHooks項下安裝了所有實現Shell擴展的組件信息。當Windows Explorer執行Shell操作前,先在注冊中查找到已注冊的Shell擴展組件,并將其實例化,每個Shell擴展組件必須至少實現了IShellExecuteHook接口,此接口提供了一個Execute()函數,Explorer將通過組件實例對象調用Execute()函數,如此函數返回為S_FALSE繼續后面的操作,如返回S_OK則停止后面的所有操作。根據以上原理,本實例要實現Shell擴展就必須要實現一個支持IShellExecuteHook接口的COM組件。
接口聲明
因C#不能像C++那樣用一句#include "shlguid.h"語句就可以完成IShellExecuteHook接口聲明,它必須要求在程序中聲明接口的具體信息,聲明如下:
[ComImpor,InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("000214FB-0000-0000-C000-000000000046")]
/* Guid("000214FB-0000-0000-C000-000000000046") 相當于shlguid.h中的DEFINE_SHLGUID(IID_IShellExecuteHookW, 0x000214FBL, 0, 0); */
public interface IShellExecuteHook{
[PreserveSig()] /* 允許返回值為COM HRESULT */
int Execute(SHELLEXECUTEINFO sei);
}
結構聲明
在Execute()方法中有一個SHELLEXECUTEINFO結構體參數sei,接下來要聲明結構體:
[StructLayout(LayoutKind.Sequential)]
public class SHELLEXECUTEINFO {
public int cbSize;
public int fMask;
public int hwnd;
[MarshalAs(UnmanagedType.LPWStr)]
public string lpVerb; /* 動作,如edit,open,print... */
[MarshalAs(UnmanagedType.LPWStr)]
public string lpFile; /* 根據lpVerb的值而定,常為文件名 */
[MarshalAs(UnmanagedType.LPWStr)]
public string lpParameters; /* 參數字符串 */
[MarshalAs(UnmanagedType.LPWStr)]
public string lpDirectory; /* 路徑名 */
public int nShow;
public int hInstApp;
public int lpIDList;
public string lpClass;
public int hkeyClass;
public int dwHotKey;
public int hIcon;
public int hProcess;
}
SHELLEXECUTEINFO結構體的元素是不是夠多的,它們的具體說明就不一一介紹了,如果你有空的話可以看看MSDN。
四、實現步驟
介紹了ISellExecuteHook接口的聲明以及SHELLEXECUTEINFO結構體的聲明后,我們就著手實現這個應用實例,這個實例很簡單,每當Explorer對一個Shell對象執行某動作前將會彈出一個對話框,在其上顯示執行的動作內容、對象名以及參數內容。
打開VS.NET,按下面步驟工作:
1.新建一個空項目(項目名:ExtenShell)。
2.添加一個新類(類名:ExtenShell.cs)。
3.將下面代碼作為ExtenShell.cs的內容。
/* ExtenShell.cs */
using System;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows.Forms;
[assembly: AssemblyKeyFile(@"..\..\ESKey.snk")] /*密鑰文件*/
namespace ShellExtension
{
//接口聲明。
[ComImport,InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("000214FB-0000-0000-C000-000000000046")]
/* Guid("000214FB-0000-0000-C000-000000000046") 相當于shlguid.h中的DEFINE_SHLGUID(IID_IShellExecuteHookW, 0x000214FBL, 0, 0); */
public interface IShellExecuteHook
{
[PreserveSig()] /* 允許返回值為COM HRESULT */
int Execute(SHELLEXECUTEINFO sei);
}
//結構聲明。
[StructLayout(LayoutKind.Sequential)]
public class SHELLEXECUTEINFO
{
public int cbSize;
public int fMask;
public int hwnd;
[MarshalAs(UnmanagedType.LPWStr)]
public string lpVerb;
[MarshalAs(UnmanagedType.LPWStr)]
public string lpFile;
[MarshalAs(UnmanagedType.LPWStr)]
public string lpParameters;
[MarshalAs(UnmanagedType.LPWStr)]
public string lpDirectory;
public int nShow;
public int hInstApp;
public int lpIDList;
public string lpClass;
public int hkeyClass;
public int dwHotKey;
public int hIcon;
public int hProcess;
}
[Guid("027F9368-A83E-42clearcase/" target="_blank" >cc-85B2-1DC5E23C4608"), ComVisible(true)]
/* 用Guid生成工具創建一個新的GUID作為類對象的GUID標識。 */
public class ExtenShell : IShellExecuteHook
{
private int S_OK=0;
private int S_FALSE=1;
public int Execute(SHELLEXECUTEINFO sei)
{
try
{
MessageBox.Show(null, "[ Verb ]: " + sei.lpVerb + "\n[ File ]: " + sei.lpFile + "\n[ Parameters ]:" + sei.lpParameters + "\n[ Directory ]:" + sei.lpDirectory , "ShellExtensionHook",MessageBoxButtons.OK, MessageBoxIcon.Information);
}
catch(Exception e)
{
Console.Error.WriteLine("Unknown exception : " + e.ToString());
}
return S_FALSE;
//如果返回值為S_OK則SHELL將停止對Shell對象的以后的所有動作。
}
}
}
4. 在命令行上運行:sn -k ESKey.snk ( sn.exe在 C:\Programe Files\Microsoft.NET\FrameworkSDK\Bin下可以找到 ),將ESKey.snk添加到項目中。
5. 打開<項目> --> <屬性>,將輸出類型改成類庫。
6. 編譯完成。
7. 因.NET可控代碼生成的COM組件注冊后要到assembly目錄中尋找實體執行,故應將編譯好的ExtenShell.dll文件拷貝到c:\Winnt\assembly目錄中?!?BR> 8. 注冊組件。在命令行上運行:regasm {項目路徑}\Bin\Debug\ExtenShell.dll。( regasm.exe在c:\Winnt\Microsoft.NET\Framework\v1.0.2914下可以找到)
9.最后,在HKLM\Software\Microsoft\Windows\CurrentVersion\Explorer\ShellExecuteHooks項下新建一個字符串值,其名為{027F9368-A83E-42cc-85B2-1DC5E23C4608},值可以為空也可以加入一串描述性文字。
五、結 束
這是一個簡單的Shell擴展的例子,雖然不是一個完整的應用,但是作者想通過此實例向讀者介紹Shell擴展和.NET平臺下的COM組件開發技術,希望它能起拋磚引玉的作用。
原文轉自:http://www.anti-gravitydesign.com