Chinaunix首页 | 论坛 | 博客
  • 博客访问: 744422
  • 博文数量: 769
  • 博客积分: 6000
  • 博客等级: 准将
  • 技术积分: 4985
  • 用 户 组: 普通用户
  • 注册时间: 2008-10-15 16:37
文章分类

全部博文(769)

文章存档

2011年(1)

2008年(768)

我的朋友

分类:

2008-10-15 16:39:53

一、前言

.NET平台是微软公司推出的作为未来软件运行和开发的环境,是微软力荐的在.NET平台下开发应用软件的首选语言。本文将讨论在.NET环境下如何使用语言开发 Shell扩展问题。如今家族已发展到XP世代了,想必每个程序员都对Shell Extension不会感到陌生吧,在这里我不想花太多的时间介绍Shell Extension的原理知识,本文中将通过一个实例介绍用创建一个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#不能像那样用一句#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-42cc-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_OKSHELL将停止对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目录中。 

8. 注册组件。在命令行上运行:regasm {项目路径}\Bin\Debug\ExtenShell.dll( regasm.exec:\Winnt\Microsoft.NET\Framework\v1.0.2914下可以找到)
 

    9.
最后,在HKLM\Software\Microsoft\Windows\CurrentVersion\Explorer\ShellExecuteHooks项下新建一个字符串值,其名为{027F9368-A83E-42cc-85B2-1DC5E23C4608},值可以为空也可以加入一串描述性文字。

五、结 束

    这是一个简单的Shell扩展的例子,虽然不是一个完整的应用,但是作者想通过此实例向读者介绍Shell扩展和.NET平台下的COM组件开发技术,希望它能起抛砖引玉的作用。

【责编:Amy】

--------------------next---------------------

阅读(485) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~