Windows Shell 编程第八章--2_/*蜉游天地*/_百度空间

感知默认的浏览器

         为了确定机器的默认浏览器,你需要指定一个.htm文件到FindExecutable()函数,一个自包含例程可以快速建立空文件,调用FindExecutable()函数,然后删除这个空文件:

void GetDefaultBrowser(LPTSTR szBrowserName)

{

HFILE h =_lcreat("dummy.htm", 0);

_lclose(h);

FindExecutable("dummy.htm", NULL, szBrowserName);

DeleteFile("dummy.htm");

}

当然,要感觉默认浏览器是IE还是Netscape,你还可以直接检查注册表HKEY_CLASSES_ROOT\.htm键下的Default实体值。如果是htmlfile则浏览器是IE,如果是NetscapeMarkup,则浏览器是Netscape。每个浏览器都在注册表子树中设置自己的不同值。只要改变了这个值,默认浏览器就改变了。

连接到URL

         如果你需要知道浏览器名以便连接到远程URL,或查看HTML文件,有一个快速方案:ShellExecute()。

ShellExecute(NULL, NULL, __TEXT(""), NULL, NULL, SW_SHOW);

这个函数本身就执行恢复和导出浏览器的操作(如果有一个安装的)。在文件名有一个前缀‘http’时,ShellExecute()函数在HKEY_CLASSES_ROOT\http\shell\open\command下进行搜索。

发送Email消息

         要编程发送e-mail消息,你可以有几个选择:有协作数据对象,消息API,或依赖于其它应用提供的服务如微软的OutLook。我们总是在HTML页上采用这个任务的简化形式,这只需要一个特定的‘mailto’协议连接:

<A href=mailto:desposito@infomedia.it>Dino Esposito</A>

这里,采用ShellExecute(),同样便捷的操作对于Windows程序也是可用的:

ShellExecute(NULL, NULL, __TEXT(""),

NULL, NULL, SW_SHOW);

再次需要注册表中的键:

HKEY_CLASSES_ROOT

\mailto

\shell

\open

\command

http 和 mailto是可插入协议的例子—构建于一个进程内COM服务器中,客户URL协议通过访问这个资源过程指导浏览器操作。使用ShellExecute(),你就可以通过任何注册的协议唤醒资源,甚至是象res这样的客户协议。

打印文档

         只要允许使用命令行打印某种文档的程序存在,你就可以输出如下命令:

ShellExecute(NULL, __TEXT("print"), szDocName, NULL, NULL, SW_SHOW);

一般的习惯是允许在命令行上使用 /p 选项打印文档,但也有一种习惯—不使用任何选项来表示打印操作。

查找文件和文件夹

         如果需要从特定文件夹开始运行‘查找’对话框,这样调用比较容易:

ShellExecute(NULL, __TEXT("find"), szDirName, NULL, NULL, SW_SHOW);

如果指定了一个NULL或空串作为文件夹名,这个对话框将显示准备在C驱动器上开始工作。如果传递一个非零的串,指向一个不存在文件夹,函数将返回错误。

ShellExecute()对CreateProcess()

         为了与CreateProcess()函数比较,我们已经足够详细地解释了ShellExecute()。哪一个函数比较好这一点,使用哪一个函数建立进程,这一点是无法确定的(它们有相当多的不同,二者都非常有用)。

         {dy}个要说明的是,ShellExecute()内部调用了CreateProcess(),所以ShellExecute()必然是CreateProcess()函数的一个更小和用法更简单的封装。反之,ShellExecute()在打开和打印文档方面是足够灵活的。没有涉及可用文档类的特定动词。除非你需要使用CreateProcess()建立探索高级属性的进程(Debug模式,环境设置,启动信息等),否则我们建议你总应该选择ShellExecute(),它有一个更简单的文法规则。

为什么应该使用ShellExecute()来运行程序

         另一个使天平向ShellExecute()倾斜的论点来自微软新需求方案的建议—这是一本在建立微软Windows98和WindowsNT兼容的应用时需要参考的手册。

         微软建议你使用ShellExecute()来运行外部应用,是因为它能确保系统管理员采用的任何策略限制被仔细地检查。系统策略允许管理者确定哪些应用能或不能从Windows启动。ShellExecute()采用黑名单执行这个过程,而CreateProcess()没有。

策略

         策略简单地是相关设置集,它们一般保存在系统注册表中。其中最有趣的一个是Shell约束策略,其中所包含的注册表实体使你可以控制‘开始’菜单和探测器的功能。

         其中你可以做的是防止Shell在开始菜单中显示‘运行’或‘查找’项。同样,你已可以禁止使用‘控制板’或任务条‘属性’对话框改变设置。下面就让我们看一看怎样做这些设置。

Shell约束策略

         Shell约束策略涉及的注册表键是:

HKEY_CURRENT_USER

\Software

\Microsoft

\Windows

\CurrentVersion

\Policies

\Explorer

为了说明其轮廓,你需要建立某些不存在的新实体,它们应有默认设置0或1:

实体
描述

NoRun
如果这个实体设置为1,则在开始菜单中隐藏‘运行…’命令

NoFind
如果为1,则在开始菜单中隐藏‘查找’命令

NoSetFolders
如果为1,则在开始菜单中隐藏所有标准的‘设置’命令

NoSetTaskbar
如果为1,则隐藏‘任务条属性’对话框

要使更新发生,所有实体都必须是DWORD。在以这种方式删除命令时,改变立即发生,然而用户界面在重新引导之前并不更新。此时如果试着使用一个命令,将会得到下面的信息框:

            

关于实现策略的注册键信息来源可以在MSDN库的平台SDK范围找到。

扩展ShellExecute()

         尽管支持策略设置,ShellExecute()还是有一个难于使用的重大障碍:它不能返回或使你知道新建进程的Handle。也就是说,你不能导出程序并在继续执行之前等待它终止。换句话说ShellExecute()受到了它的16位血统的损害,它仅仅发掘了新的和更有威力的函数CreateProcess()的一个特征子集—WinExec()也支持的子集。

         然而在4.0以后版本中引进了一个新函数:ShellExecuteEx()。它有一个Shell函数典型的原型,支持多标志,以及上述所有功能,通过提供对进程同步和PIDLs的支持扩展了ShellExecute()。

ShellExecuteEx()函数

    ShellExecuteEx()函数明确地取代了ShellExecute()。它在shellapi.h中声明:

BOOL ShellExecuteEx(LPSHELLEXECUTEINFO lpExecInfo);

SHELLEXECUTEINFO定义如下:

typedef struct _SHELLEXECUTEINFO

{

DWORD cbSize;

ULONG fMask;

HWND hwnd;

LPCTSTR lpVerb;

LPCTSTR lpFile;

LPCTSTR lpParameters;

LPCTSTR lpDirectory;

int nShow;

HINSTANCE hInstApp;

// 可选的成员

LPVOID lpIDList;

LPCSTR lpClass;

HKEY hkeyClass;

DWORD dwHotKey;

HANDLE hIcon;

HANDLE hProcess;

} SHELLEXECUTEINFO, FAR *LPSHELLEXECUTEINFO;

在使用这个结构之前,我们极力建议你把它充填为0,并设置cbSize到结构的实际长度,操作如下:

SHELLEXECUTEINFO sei;

ZeroMemory(&sei, sizeof(SHELLEXECUTEINFO));

sei.cbSize = sizeof(SHELLEXECUTEINFO);

正如声明中的注释所说,结构的成员分成了两组。实际上,头一组使ShellExecuteEx()的功能等价于ShellExecute()。而选项成员组使函数更有力,这正是‘Ex’后缀的由来。

        

hwnd, lpVerb, lpFile, lpParameters, lpDirectory 和 nShow成员等价于ShellExecute()的参数,这是我们已经看到的。而hInstApp成员则是一个输出缓冲,这将由ShellExecute()的返回值填写。

         nShow成员总是表示建立窗口的风格,即使lpFile是一个应用程序,它也仅仅说明应用应该怎样显示。无论lpFile是应用程序还是文档文件,nShow必须总是赋值为SW_型常量,你是知道的,如果设置为0将获得隐藏窗口。

         下面是调用ShellExecuteEx()的最简单方法:

SHELLEXECUTEINFO sei;

ZeroMemory(&sei, sizeof(SHELLEXECUTEINFO));

sei.cbSize = sizeof(SHELLEXECUTEINFO);

sei.lpFile = __TEXT("explorer.exe");

sei.nShow = SW_SHOW;

sei.lpVerb = __TEXT("open");

ShellExecuteEx(&sei);

可选成员

         没有在ShellExecute()中对应参数的成员之一是fMask。它可以是一个或多个下面值的组合:

标志
描述

SEE_MASK_CLASSKEY
应该使用 hkeyClass 成员

SEE_MASK_CLASSNAME
应该使用 lpClass 成员

SEE_MASK_CONNECTNETDRV
lpFile将被解释成UNC(通用命名习惯)格式的文件名

SEE_MASK_DOENVSUBST
任何在lpDirectory和lpFile成员中的环境变量都将被展开,例如,%WINDIR% 打开Windows文件夹

SEE_MASK_FLAG_DDEWAIT
如果函数启动DDE会话,在返回之前等待它终止。

SEE_MASK_FLAG_NO_UI
在错误情况下不显示消息框

SEE_MASK_HOTKEY
应该使用 dwHotkey 成员

SEE_MASK_ICON
应该使用 hIcon 成员

SEE_MASK_IDLIST
强制函数使用lpIDList内容代替lpFile

SEE_MASK_INVOKEIDLIST
引起函数使用lpIDList中指定的PIDL。如果这个成员为NULL,则建立一个lpFile的PIDL,并使用这个PIDL。这个标志重载了SEE_MASK_IDLIST

SEE_MASK_NOCLOSEPROCESS
用进程Handle设置hProcess成员。lpIDList成员可以包含一个用于代替lpFile的PIDL。hProcess返回导出的HPROCESS类型的新进程handle

附加的特征

         可选字段适用于某些超出ShellExecute()的附加功能。{dy}点,也是最重要的一点,可以使用PIDLs来运行应用和打开文件夹。下面是打开‘打印机’文件夹的代码:

LPITEMIDLIST pidl;

SHGetSpecialFolderLocation(NULL, CSIDL_PRINTERS, &pidl);

SHELLEXECUTEINFO sei;

ZeroMemory(&sei, sizeof(SHELLEXECUTEINFO));

sei.cbSize = sizeof(SHELLEXECUTEINFO);

sei.nShow = SW_SHOW;

sei.lpIDList = pidl;

sei.fMask = SEE_MASK_INVOKEIDLIST;

sei.lpVerb = __TEXT("open");

ShellExecuteEx(&sei);

如果指定了SEE_MASK_DOENVSUBST标志,则可以在lpFile和lpDirectory中使用任何环境变量。例如,要打开Windows目录,可以表示为%WINDIR%。

         {zh1},我们获得了由ShellExecuteEx()导出的应用的同步能力。在设置了SEE_MASK_NOCLOSEPROCESS位到fMask成员后,新进程的handle将由hProcess成员返回,因此这一行代码:

WaitForSingleObject(sei.hProcess, INFINITE);

将导致调用的应用阻塞,等待另一个应用终止。

显示文件属性对话框

    SEE_MASK_INVOKEIDLIST标志是一个重要标志,因为这是ShellExecuteEx()另一个优于ShellExecute()的亮点:它允许函数象执行静态动词那样唤醒动态动词。前面解释过,动态动词是运行时由Shell扩展的关联菜单添加的。其工作方法是:如果ShellExecuteEx()不能在静态动词列表中找到这个动词,它就试图寻找给定文件的关联菜单。这个搜索引出IContextMenu接口指针。然后通过接口暴露的方法唤醒动态动词。

    作为这个操作的结论,我们可以很容易地显示文件的属性对话框—与右击文件,然后选择属性显示的对话框相同。这里是一个简单的例子函数:

void ShowFileProperties(LPCTSTR szPathName)

{

SHELLEXECUTEINFO sei;

ZeroMemory(&sei, sizeof(SHELLEXECUTEINFO));

sei.cbSize = sizeof(SHELLEXECUTEINFO);

sei.lpFile = szPathName;

sei.nShow = SW_SHOW;

sei.fMask = SEE_MASK_INVOKEIDLIST;

sei.lpVerb = __TEXT("properties");

ShellExecuteEx(&sei);

}

ShellExecuteEx()函数的返回值

         这个函数返回一个布尔值用来描述调用是否成功:TRUE,成功,而FALSE,则失败。GetLastError()和hInstApp中的返回值可以用来获得调用时发生事件的更多信息。

例子:程序执行器

下面的截图显示了一个示例程序Execute的界面,它允许你测试动词,它是一个基于对话框的应用程序。


              


可以通过键入名字或浏览按钮来选择特定的测试文件。在‘操作’编辑框中应该输入想要在文件上执行的动词名。头两个按钮—ShellExecute 和 ShellExecuteEx—可以分别测试各自的函数。而FindExecutable按钮则返回打开(总是动词‘打开’)指定文件注册的执行程序名,这个名字显示在‘Executable found’编辑框中,而‘Return’中显示FindExecutable()函数的返回码。

         实现这个应用的功能,实际上就是提供这四个按钮的处理器操作代码。OnBrowse()是最容易的,代码如下:

void OnBrowse(HWND hDlg)

{

TCHAR szFile[MAX_PATH] = {0};

TCHAR szWinDir[MAX_PATH] = {0};

GetWindowsDirectory(szWinDir, MAX_PATH);

OPENFILENAME ofn;

ZeroMemory(&ofn, sizeof(OPENFILENAME));

ofn.lStructSize = sizeof(OPENFILENAME);

ofn.lpstrFilter = __TEXT("All files\0*.*\0");

ofn.nMaxFile = MAX_PATH;

ofn.lpstrInitialDir = szWinDir;

ofn.lpstrFile = szFile;

if(!GetOpenFileName(&ofn))

return;

else

SetDlgItemText(hDlg, IDC_FILENAME, ofn.lpstrFile);

}

其次是OnShellExecute(),简单地从对话框中抽取文件名和操作,生成ShellExecute()函数的调用,以及显示返回值:

void OnShellExecute(HWND hDlg)

{

TCHAR sFile[MAX_PATH] = {0};

TCHAR sOp[MAX_PATH] = {0};

TCHAR sRC[MAX_PATH] = {0};

GetDlgItemText(hDlg, IDC_FILENAME, sFile, MAX_PATH);

GetDlgItemText(hDlg, IDC_OPERATION, sOp, MAX_PATH);

HINSTANCE h = ShellExecute(NULL, sOp, sFile, NULL, NULL, SW_SHOW);

wsprintf(sRC, __TEXT("%ld"), h);

SetDlgItemText(hDlg, IDC_RETVAL, sRC);

return;

}

第三步是OnShellExecuteEx(),这与上项恰好相同,只是使用了SHELLEXECUTEINFO结构:

void OnShellExecuteEx(HWND hDlg)

{

TCHAR sFile[MAX_PATH] = {0};

TCHAR sOp[MAX_PATH] = {0};

TCHAR sRC[MAX_PATH] = {0};

GetDlgItemText(hDlg, IDC_FILENAME, sFile, MAX_PATH);

GetDlgItemText(hDlg, IDC_OPERATION, sOp, MAX_PATH);

SHELLEXECUTEINFO sei;

ZeroMemory(&sei, sizeof(SHELLEXECUTEINFO));

sei.cbSize = sizeof(SHELLEXECUTEINFO);

sei.lpFile = sFile;

sei.nShow = SW_SHOW;

sei.fMask = SEE_MASK_DOENVSUBST | SEE_MASK_INVOKEIDLIST;

sei.lpVerb = sOp;

DWORD rc = ShellExecuteEx(&sei);

wsprintf(sRC, __TEXT("%ld"), rc);

SetDlgItemText(hDlg, IDC_RETVAL, sRC);

return;

}

{zh1}是OnFindExec(),使用FindExecutableEx()函数,把前面给出的代码在这里汇集到一起:

void OnFindExec(HWND hDlg)

{

TCHAR sFile[MAX_PATH] = {0};

TCHAR sPrg[MAX_PATH] = {0};

TCHAR sRC[MAX_PATH] = {0};

GetDlgItemText(hDlg, IDC_FILENAME, sFile, MAX_PATH);

HINSTANCE h = FindExecutableEx(sFile, NULL, sPrg);

wsprintf(sRC, __TEXT("%ld"), h);

SetDlgItemText(hDlg, IDC_RETVAL, sRC);

SetDlgItemText(hDlg, IDC_EXE, sPrg);

return;

}

添加#includes shlobj.h, commdlg.h 和resource.h到源程序的顶部,并保证连接到comdlg32.h文件,就可以编译和连接这个应用了。下面的截图显示了它所取得的一个GIF文件的‘属性’对话框:

         


多监视器支持

         作为结束关于ShellExecute()和ShellExecuteEx()函数的讨论,我们给出一个Windows98下的一个新特征—多监视器支持。这是一种编程使输出能够扩张到多个监视器的能力。在Windows98下有一令人惊异的函数MonitorFromPoint(),到目前为止我们还不甚了解这个函数。多监视器支持与ShellExecute()之间有什么关系呢?正好,Windows98 版的这个函数支持多监视器。这就是说,任何子进程都与父进程显示在同一个监视器上。然而,这仅仅是默认行为。如果指定了hwnd参数,你就可以重定向新窗口到有这个hwnd参数窗口所在的监视器上。

ShellExecute()函数上的钩子

         你有没有听说过IShellExecuteHook接口?就象它的名字一样,其逻辑遵循传统的Windows钩子模型,而实现则要求你写一个进程内COM服务器。接口方法由ShellExecute()和 ShellExecuteEx()两个函数调用,以使用户获得对启动进程这个过程的更多控制。通过使用IShellExecuteHook接口,模块可以解析(客户方法解析)被执行的命令行,和导出正确的程序。

         例如,在MS-DOS下,我们有时写一个小的批处理过程,使用短的或容易键入的名字,以此种方法,我们可以快速并容易地运行程序和执行重复的任务。现在,IShellExecuteHook接口给了我们在Windows下做差不多相同工作的能力。实现了IShellExecuteHook接口的模块在ShellExecute()或 ShellExecuteEx()执行文件动词操作时,无论文件类型如何都被唤醒。这个模块夹在操作中间,可以作任何适合的操作,比如:

         跟踪(记录到文件)所有由Shell启动的应用

         防止非授权访问某些程序或文件夹

         实现命名对象—即,映射到特殊程序或活动的关键字

实现IShellExecuteHook接口实际上是相当容易的。不幸的是没有任何资料说明怎样使Shell知道你已经实现了它,关于这一点,我们将在下一节中讨论。

注册IShellExecuteHook处理器

{dy},IShellExecuteHook处理器是一个COM服务器,它必须在下面的路径上适当地注册:

HKEY_CLASSES_ROOT

\CLSID

当然只有这些还远远不够,Windows Shell 必须知道这个处理器存在,以及它放在哪儿。由于IShellExecuteHook处理器与浏览器辅助对象没有多少差别,因此我们认为它们的注册方式应该类似,事实证明我们是正确的。辅助对象和Shell执行钩子二者都必须注册在下面的位置:

HKEY_LOCAL_MACHINE

\Software

\Microsoft

\Windows

\CurrentVersion

\Explorer

辅助对象在‘Browser Helper Objects’键名下,而执行钩子在‘ShellExecuteHooks’键名下:

            

如上图所示,每一个键下可以包含一个CLSID串集合。Shell遍历这个列表,并装入这些服务器。

IShellExecuteHook接口

         IShellExecuteHook是我们目前为止所看到的最简单的COM接口之一。它只由一个函数Execute()组成。下面是这个函数的声明:

HRESULT Execute(LPSHELLEXECUTEINFO pei);

SHELLEXECUTEINFO与我们在ShellExecuteEx()中介绍的结构相同。这个函数由系统在新应用或文档被打开之前通过Shell接口唤醒。换言之,这个钩子在运行新应用,或以下面方法执行一个文档的动词时被唤醒:

    编程,由ShellExecute()或ShellExecuteEx()。

    通过‘运行’对话框。

    从探测器双击。

如果通过CreateProcess()或WinExec()运行其它的程序,这个钩子模块不能得到通知。同样,如果通过DOS运行程序或打开文档,或使用任何其它底层技术,也不能唤醒钩子。

    利用作为变量传递的结构,Execute()方法接受动词,文件名,变量,目录,以及任何用户传递给ShellExecute()或其它关联函数的数据。

    前面已经提到,ShellExecute()和ShellExecuteEx()二者都最终调用CreateProcess()。然而,它们除了取得和传递命令行到CreateProcess()外还要做更多其它的操作,而一开始,它们就处理策略和支持这个钩子。

从钩子返回

         钩子返回S_FALSE,则Shell通常可以继续,和建立需要的进程。然而,如果不能继续要求的操作—即,钩子不想要Shell启动进程—则应该返回S_OK。这是因为钩子检查了某些条件,想要防止当前连接的用户运行程序或文档,另一种可能性是钩子代码想要自己运行这个文档。给出一个非标准的优先线程。这要求你自己处理CreateProcess()调用。更重要的是,如果返回S_OK到Shell,我们还需要适当地设置SHELLEXECUTEINFO结构中的hInstApp成员。

         “适当设置hInstApp成员”,就是说给它赋一个值,向Shell表示我们的处理成功或失败(使用相关的错误消息)。如果我们自己运行这个应用,则这个值应是新进程的HINSTANCE。如果我们截断处理,则这个值可以是任何大于32的值,以防止Shell显示任何错误消息框。

         下面是一个例子,假设我们确定阻塞任何新进程:

HRESULT Execute(LPSHELLEXECUTEINFO lpsei)

{

return S_OK

}

无论我们接收的参数如何,都立即返回S_OK。此时Shell发现hInstApp成员地值是0,解释返回值为错误码。然后适当地显示一个消息框,如下图所示:

           


写IShellExecuteHook处理器

    写COM服务器,活动模版库(ATL)是重要的资源。运行ATL COM应用大师,生成一个Hook名的COM 服务器框架,选择‘简单对象’后,我们就可以在其中添加新类了:

                 


新类名为CShowHook,应该从IShellExecuteHook导出。这个接口要求代码包含shlobj.h文件。由于比直接从IShellExecuteHook导出要好,我们定义一个普通的实现类(以习惯的ATL风格)命名为IShellExecuteHookImpl:

// IShellExecuteHookImpl.h

//

//////////////////////////////////////////////////////////////////////

#include <AtlCom.h>

#include <ShlObj.h>

class ATL_NO_VTABLE IShellExecuteHookImpl : public IShellExecuteHook

{

public:

// IUnknown

STDMETHOD(QueryInterface)(REFIID riid, void** ppvObject) = 0;

_ATL_DEBUG_ADDREF_RELEASE_IMPL(IShellExecuteHookImpl)

// IShellExecuteHook

STDMETHOD(Execute)(LPSHELLEXECUTEINFO lpsei)

{

return S_FALSE;

}

};

实际的CShowHook类声明如下:

#include "resource.h"

#include "comdef.h"

#include "IShellExecuteHookImpl.h"

///////////////////////////////////////////////////////////////////////////

// CShowHook

class ATL_NO_VTABLE CShowHook :

public CComObjectRootEx<CComSingleThreadModel>,

public CComCoClass<CShowHook, &CLSID_ShowHook>,

public IShellExecuteHookImpl,

public IDispatchImpl<IShowHook, &IID_IShowHook, &LIBID_SHOWLib>

{

public:

CShowHook()

{

}

STDMETHOD(Execute)(LPSHELLEXECUTEINFO lpsei);

DECLARE_REGISTRY_RESOURCEID(IDR_SHOWHOOK)

DECLARE_PROTECT_FINAL_CONSTRUCT()

BEGIN_COM_MAP(CShowHook)

COM_INTERFACE_ENTRY(IShowHook)

COM_INTERFACE_ENTRY(IDispatch)

COM_INTERFACE_ENTRY(IShellExecuteHook)

END_COM_MAP()

// IShowHook

public:

};

现在还缺少这个钩子的实现。我们开始时提出了三个钩子的作用:跟踪,授权和命名。现在让我们看一看这三方面必要的代码:

HRESULT CShowHook::Execute(LPSHELLEXECUTEINFO lpsei)

{

// 跟踪程序/文件的打开操作

TCHAR szTime[50] = {0};

GetTimeFormat(LOCALE_SYSTEM_DEFAULT, 0, NULL, NULL, szTime, 50);

TCHAR szText[1024] = {0};

wsprintf(szText, __TEXT("%s: %s at %s"),lpsei->lpVerb, lpsei->lpFile, szTime);

FILE *f;

f = fopen(__TEXT("c:\\ShowHook.txt"), __TEXT("a+t"));

fseek(f, 0, SEEK_END);

fprintf(f, __TEXT("%s: %s at %s\n\r"),lpsei->lpVerb, lpsei->lpFile, szTime);

fclose(f);

// 检查快捷方式列表和运行程序

TCHAR szFileName[MAX_PATH] = {0};

GetPrivateProfileString(__TEXT("GoldList"), lpsei->lpFile,

"", szFileName, MAX_PATH, __TEXT("c:\\showhook.ini"));

if(lstrlen(szFileName))

{

lpsei->hInstApp = reinterpret_cast<HINSTANCE>(WinExec(szFileName,

SW_SHOW));

return S_OK;

}

// 如果名字包含DEBUG,则禁止做任何操作

strlwr(const_cast<LPTSTR>(lpsei->lpFile));

if(strstr(lpsei->lpFile, __TEXT("debug")))

{

lpsei->hInstApp = reinterpret_cast<HINSTANCE>(42);

return S_OK;

}

// 让Shell继续

return S_FALSE;

}

编辑注册表脚本

         在分析上面代码之前,需要多做一点事情,以使服务器成为xx自注册服务器。这涉及到对大师给出的注册表脚本代码的补充。加入使Shell执行钩子的特殊信息,把这些信息放在文件的尾部:

HKLM

{

SOFTWARE

{

Microsoft

{

Windows

{

CurrentVersion

{

Explorer

{

ShellExecuteHooks

{

val {4F43D133-2951-11D2-BC00-7CA506C10000} = s ''

}

}

}

}

}

}

}

这个钩子包含在ShowHook.dll中,服务器的注册是自动的。这里我们已经做的是正确地在ShellExecuteHooks键下注册钩子的CLSID。

钩子怎样工作

         跟踪就是把使用的动词,活动的文件名,和调用时间保存到磁盘。下面的图中显示了跟踪的结果。注意,在记录中还包含了一个重启动活动的跟踪(SysTray.exe的实例)。

               

上面的代码还涉及到命名—识别键名列表,然后把它们转换成应用。这个列表保存在根目录下的一个.ini文件中,其典型内容为:

[GoldList]

reg=regedit.exe

tt=notepad.exe

AddNewHardware=control.exe sysdm.cpl,Add New Hardware

左边的字是由钩子识别的并把它转换成右边的命令行。这就允许我们键入AddNewHardware到‘运行’框中。

         {zh1}是Execute()函数的授权部分,防止名字中包含‘debug’串的任何文件夹或文件被打开。注意,如果忘记了返回值要大于32,则可能有一个讨厌的错误消息框出现。这正好说明了ShellExecute()实际上是经常由探测器调用的,所以应该在钩子代码中仔细地考量尺寸和效率。

小结

         在这一章中我们讨论了大量的基础知识,并且指出了所用方法的一些缺陷。我们从讨论WinExec()函数开始,随后是CreateProcess()函数,{zh1}讨论了ShellExecute()函数。在讨论这些函数的特征和bugs后,我们论及了FindExecutable()函数,它也有几个瑕疵。

         总体上,ShellExecuteEx()似乎组合了CreateProcess()和ShellExecute()函数的功能。支持PIDLs和策略,以及钩子能力使ShellExecuteEx()成为‘{zh0}的Windows程序执行器’。这与我们的观点相同,我们解释了:

    ShellExecute()和FindExecutable()的特征与Bugs。

为什么Windows98资料推荐使用ShellExecute()/ShellExecuteEx()而不

是CreateProcess(),

    ShellExecuteEx()在那些方面扩展了ShellExecute()的功能。

    怎样使用钩子扩展ShellExecuteEx()。

本文来自CSDN博客,转载请标明出处:



郑重声明:资讯 【Windows Shell 编程第八章--2_/*蜉游天地*/_百度空间】由 发布,版权归原作者及其所在单位,其原创性以及文中陈述文字和内容未经(企业库qiyeku.com)证实,请读者仅作参考,并请自行核实相关内容。若本文有侵犯到您的版权, 请你提供相关证明及申请并与我们联系(qiyeku # qq.com)或【在线投诉】,我们审核后将会尽快处理。
—— 相关资讯 ——