日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

[转]Windows Shell 编程 第十一章 【来源:http://blog.csdn.net/wangqiulin123456/article/details/7987992】...

發(fā)布時(shí)間:2025/7/14 93 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [转]Windows Shell 编程 第十一章 【来源:http://blog.csdn.net/wangqiulin123456/article/details/7987992】... 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

第十一章?探索Shell

?????????我們現(xiàn)在將注意力從API轉(zhuǎn)向Windows Shell本身。從這一章往后,我們的目標(biāo)主要集中在清晰和全面的揭示探測(cè)器的工作原理和Shell?命名空間的構(gòu)成對(duì)象上,最后給出客戶化探測(cè)器和擴(kuò)展其特征和行為的示例程序。

?????????Windows Shell,也稱之為探測(cè)器,是一群特殊模塊的集合,這些模塊一起形成了Shell的命名空間,并且給出了執(zhí)行大量特殊任務(wù)的能力,例如,探測(cè)文件夾,顯示特定的目錄子樹,裝入外部模塊并與它通訊等。盡管最終結(jié)果可能會(huì)有差異,只要這些對(duì)象被調(diào)用和顯示,探測(cè)器總是進(jìn)入后臺(tái)操作。

?????????在許多情況下,探測(cè)器提供的服務(wù)都是通過命令行與給定程序通訊來進(jìn)行的,因而,如果你希望探測(cè)器有效地管理你的程序,就需要了解應(yīng)用應(yīng)該向探測(cè)器提供什么,這是十分重要的。(這個(gè)題目將在14章中更詳細(xì)地討論)

?????????在這一章中我們將揭示下面科目:

???????????????????探測(cè)器的命令行

???????????????????運(yùn)行系統(tǒng)對(duì)話框的RunDll32程序

????????Shell對(duì)象‘我的公文包’,‘控制面板’,‘打印機(jī)’和‘任務(wù)調(diào)度’

????????零碎對(duì)象

這一章給出的源代碼的示例提供了一個(gè)有用的工具,其中涵蓋了許多我們以前解釋過的技術(shù),如快捷方式和注冊(cè)表處理等。這個(gè)工具是NewLink,它提供了在桌面或其它任何地方建立快捷方式的許多靈活方法。如果愿意,可以安裝它作為快捷方式處理器來取代標(biāo)準(zhǔn)的Windows大師。

?

探測(cè)器的命令行

?????????探測(cè)器有一個(gè)可以取四種選項(xiàng)開關(guān)的命令行,下面是幾種可能的組合結(jié)果:

?

explorer.exe [/n [, <folder>]]

[/e [, <folder>]]

[, /root, <object>]

[[, /select], <sub object>]

?

注意,命令行結(jié)構(gòu)中逗號(hào)的使用。這肯定是不常看到的。下面是這些開關(guān)的意義:

?

開關(guān)

描述

/n

在新的單框觀察窗口中打開指定的文件夾。單框觀察窗口是一個(gè)基于列表觀察的窗口。

/e

在新的雙框觀察窗口中打開指定文件夾。這是典型的探測(cè)器觀察。左邊是命名空間的樹觀察,細(xì)節(jié)在右邊框中。

/root

使指定的文件夾作為觀察樹的根。需要一種/e類型的觀察。

/select

在左框中選擇指定項(xiàng)(樹觀察)

?

最簡(jiǎn)單的開關(guān)是?/select?選項(xiàng),它用于在打開的文件夾中選擇特殊的子項(xiàng)。下面是一個(gè)例子:

explorer /e, /select, c:/windows

這個(gè)標(biāo)志需要與?/e?聯(lián)合使用,因?yàn)?/select?標(biāo)志要求樹觀察。在右框或單框文件夾中沒有辦法選擇項(xiàng)。在使用?/n?和?/e?開關(guān)時(shí),可以在逗號(hào)后指定文件夾名:

explorer /e, c:/

explorer /n, c:/

explorer c:/

上面的第二和第三行產(chǎn)生相同的結(jié)果(單框觀察)—這就是說?/n?是默認(rèn)選項(xiàng)。

?

/root?開關(guān)

?????????我們經(jīng)常看到類似探測(cè)器觀察的情況,但是它們都有一個(gè)特殊的文件夾作為根。你可以通過組合使用?/e?和?/root?打開這樣的探測(cè)器觀察,例如:

explorer /e, /root, c:/windows

注意,當(dāng)打開一個(gè)觀察使用的根不是桌面時(shí),用戶就不能沿著樹向上行進(jìn)。如果探測(cè)器使用c:/windows作為根,則用戶不能訪問這個(gè)樹中的?c:/?或任何與?/windows?同層的目錄。

?

使用特殊的文件夾作為根

????特殊文件夾,如‘回收站’,與文件系統(tǒng)文件夾沒有一對(duì)一對(duì)應(yīng)。就象我們前面討論過的,這些命名空間擴(kuò)展是進(jìn)程內(nèi)服務(wù)器,是由CLSID所標(biāo)識(shí)的。/root?開關(guān)使你可以指定一個(gè)CLSID作為一個(gè)文件夾使用,但是需要滿足兩個(gè)限制條件:

????這個(gè)COM服務(wù)器必須實(shí)現(xiàn)所有命名空間擴(kuò)展所要求的接口

你必須在探測(cè)器命令行上使用::前綴在CLSID之前來引用它。其語法為::(CLSID)。這使得它被處理成普通的目錄名。

例如:

explorer ::{645FF040-5081-101B-9F08-00AA002F954E}

這將打開一個(gè)回收站新窗口,你也可以使用自己的客戶文件夾這么做。下面的表中給出了幾個(gè)可以在桌面上找到的文件夾對(duì)象CLSIDs,但是需要注意,并不是桌面上所有對(duì)象都是文件夾—例如‘Inbox’和‘我的公文包’就不是文件夾而是應(yīng)用。

CLSID

對(duì)象

645FF040-5081-101B-9F08-00AA002F954E

回收站

20D04FE0-3AEA-1069-A2D8-08002B30309D

我的計(jì)算機(jī)

208D2C60-3AEA-1069-A2D7-08002B30309D

網(wǎng)上鄰居

871C5380-42A0-1069-A2EA-08002B30309D

IE?瀏覽器

21EC2020-3AEA-1069-A2DD-08002B30309D

控制面板

992CFFA0-F557-101A-88EC-00DD010CCC48

撥號(hào)連接

2227A280-3AEA-1069-A2DE-08002B30309D

打印機(jī)

?

注意,在這里你可以把CLSIDs看作傳統(tǒng)的文件夾,并且可以使用斜線連接它們。例如要訪問打印機(jī)文件夾,它是‘我的計(jì)算機(jī)’的子文件夾,你可以使用下面的文法:

explorer ::{20D04FE0-3AEA-1069-A2D8-08002B30309D}/

::{2227A280-3AEA-1069-A2DE-08002B30309D}

?

關(guān)于rundll32.exe

?????????我們?cè)f過在探測(cè)器組織中命令行扮演著重要的角色。許多在文件對(duì)象上執(zhí)行的功能都是通過命令行實(shí)現(xiàn)的。為了進(jìn)一步增強(qiáng),Windows9x和NT4.0以后的版本推出了rundll32.exe輔助程序。它允許你直接從命令行調(diào)用DLL輸出的函數(shù)。這個(gè)實(shí)用程序簡(jiǎn)單地封裝了幾個(gè)需要?jiǎng)討B(tài)執(zhí)行的API調(diào)用。

?????????下面的偽代碼說明了rundll32的操作:

void DoRunDll32(LPCTSTR szDllName, LPCTSTR szFuncName, LPCTSTR szCmdLine)

{

//?裝入這個(gè)庫

HANDLE hLib = LoadLibrary(szDllName);

//?取得請(qǐng)求執(zhí)行的函數(shù)地址

FARPROC pFunc = GetProcAddress(hLib, szFuncName);

//?使用指針執(zhí)行函數(shù)

pFunc(GetFocus(), hLib, szCmdLine, SW_SHOW);

//?釋放庫

FreeLibrary(hLib);

}

在實(shí)際使用中,rundll32接收它所解析的單個(gè)字符串,從中抽取動(dòng)態(tài)庫和函數(shù)名,以及選擇的函數(shù)變量。命令行有下面形式:

rundll32 dllname,funcname [arguments]

DLL名和函數(shù)名之間用逗號(hào)分割,并且不能有空格。如果調(diào)用rundll32沒有指定DLL的全路徑,它搜索所有標(biāo)準(zhǔn)路經(jīng)包括應(yīng)用的目錄,Windows目錄和當(dāng)前目錄。

?????????這個(gè)程序的缺點(diǎn)是在錯(cuò)誤情況下通常不能返回足夠的信息。如果你試圖調(diào)用的DLL函數(shù)缺失(比如,鍵入的名字錯(cuò)),則它可以使你相當(dāng)快地知道發(fā)生了什么,然而,如果這個(gè)函數(shù)可以被調(diào)用,但是在執(zhí)行時(shí)失敗,此時(shí)你就必須自己猜測(cè)究竟發(fā)生了什么。

?????????rundll32的接口通常用于調(diào)用某些不熟知的系統(tǒng)對(duì)話框,別的用戶也可以用它來調(diào)用你的DLL,要這樣做的唯一要求是需要可調(diào)用函數(shù)的預(yù)定義原型。

?

rundll32.exe可調(diào)用的函數(shù)

?????????在上面的偽代碼中,看一下實(shí)際調(diào)用DLL函數(shù)那一行。Rundll32僅僅可以調(diào)用具有下面原型的函數(shù)—有四個(gè)參數(shù),而僅僅有一個(gè)用戶可以設(shè)置的:

void CALLBACK FuncName(HWND hwnd, // Window handle

HINSTANCE hinst, // Instance handle

LPTSTR lpszCmdLine, // Command line

int nCmdShow); // ShowWindow() parameter

hwnd參數(shù)指定函數(shù)建立任何窗口的父窗口,也就是說,這個(gè)對(duì)話框是相對(duì)于父窗口的模式對(duì)話框。事實(shí)上,這個(gè)參數(shù)總是等價(jià)于桌面窗口(即,hwnd?是?NULL),所以,相對(duì)于任何Shell中打開的窗口,新窗口總是非模式的。第二個(gè)參數(shù)是HINSTANCE類型的Handle,是一個(gè)由LoadLibrary()返回的庫Handle。第四個(gè)參數(shù)nCmdShow確定窗口的顯示方式,而且在通過rundll32調(diào)用函數(shù)時(shí)應(yīng)該總是SW_SHOW。

????唯一一個(gè)可控制參數(shù)是lpszCmdLine,通過這個(gè)參數(shù),函數(shù)接收數(shù)據(jù)變量,例如,假設(shè)有一個(gè)函數(shù)接收兩個(gè)數(shù)字,定義如下:

void MyFunc(int iFirst, long lSecond)

{

...

}

要使這個(gè)函數(shù)可通過rundll32接口調(diào)用,它應(yīng)該變形為:

void MyFunc1(HWND hwnd, HINSTANCE hinst, LPTSTR lpszCmdLine, int nShow)

{

int iFirst;

long lSecond;

//?解析命令行和截?cái)喑鰠?shù)

ParseCommandLine(lpszCmdLine, &iFirst, &lSecond);

//?用參數(shù)調(diào)用已經(jīng)抽取的函數(shù)

MyFunc(iFirst, lSecond);

}

在NT下,要調(diào)用的函數(shù)首先搜索Unicode名,然后是ANSI名,最后才是實(shí)際所寫的名。也就是說,調(diào)用MyFunc()被依次轉(zhuǎn)換成調(diào)用MyFuncW(),?MyFuncA()和?MyFunc(),當(dāng)然,此時(shí)的參數(shù)串也被傳遞成寬字符串,封裝為L(zhǎng)PWSTR,而不是LPSTR。

?

rundll32.exe能做些什么

?????????rundll32(其16位祖先是rundll)主要用于顯示系統(tǒng)對(duì)話框和在僅能使用命令行的情況下調(diào)用DLL函數(shù)。初始它作為微軟內(nèi)部使用的工具而設(shè)計(jì),因此,故意限制其實(shí)際設(shè)置是顯然的。

?????????在測(cè)試代碼時(shí),這個(gè)程序可以使加載和卸載DLL明顯地節(jié)省時(shí)間。有時(shí)它也能幫助你檢查DLL輸出的對(duì)話框。反過來,它也強(qiáng)制函數(shù)調(diào)用采用固定的格式,因而也就沒有數(shù)據(jù)返回調(diào)用者的機(jī)理。然而,它有一種特殊的功能是你想使用的。

?????????你可以使用rundll32來獲得對(duì)某些系統(tǒng)對(duì)話框的訪問,包括那些由于資料缺乏不容易訪問的對(duì)話框。在許多情況下訪問某些東西的唯一推薦的(和資料說明的)方法是使用rundll32。例如‘添加新打印機(jī)’操作:

??????????????

?

?

這個(gè)操作是系統(tǒng)通過調(diào)用sysdm.cpl庫中的一個(gè)函數(shù)實(shí)現(xiàn)的。擴(kuò)展名說明這個(gè)庫是‘控制面板’DLL。資料中建議這樣調(diào)用:

rundll32.exe sysdm.cpl,InstallDevice_Rundll printer

而不是直接調(diào)用InstallDevice_Rundll()函數(shù)。這個(gè)函數(shù)的四個(gè)參數(shù)我們已經(jīng)討論過了,使用‘printer’作為變量來確定要做什么。事實(shí)上盡管沒有資料說明,我們也能夠使用相同的語法來安裝新的調(diào)制解調(diào)器或監(jiān)視器,只須簡(jiǎn)單地替換‘printer’為‘modem’或‘monitor’即可。

?

RunDll()?函數(shù)

?????????許多系統(tǒng)對(duì)話框和應(yīng)用大師僅支持使用rundll32的調(diào)用。這可能是因?yàn)榭梢赃m應(yīng)將來的變化,其中使用rundll32可以屏蔽新的細(xì)節(jié)。還有可能是因?yàn)檫@些函數(shù)和對(duì)話框不是完全公共的,再有就是使用rundll32接口可以隱藏它們的許多方面。下面的清單顯示了用C++?函數(shù)模仿rundll32的可能的實(shí)現(xiàn)。回顧一下我們?cè)缦冉o出的偽代碼例子,這里加入了錯(cuò)誤檢查和指定父窗口的方法:

void RunDll(HWND hwnd, LPCTSTR szDllName, LPCTSTR szFunc, LPCTSTR szCmdLine)

{

HANDLE hLib = NULL;

hLib = LoadLibrary(szDllName);

if(hLib == NULL)

return;

FARPROC pFunc = NULL;

pFunc = GetProcAddress(hLib, szFunc);

if(!pFunc == NULL)

pFunc(hwnd, hLib, szCmdLine, SW_SHOW);

FreeLibrary(hLib);

}

以這種方式封裝我們自己的函數(shù),就可以把我們想要的任何窗口作為父窗口傳遞給子對(duì)話框,這就允許我們顯示模式對(duì)話框。

?

Rundll32.exe保護(hù)錯(cuò)

?????????你當(dāng)然可以用rundll32調(diào)用不是為了與rundll32一起工作而設(shè)計(jì)的函數(shù)。但是這樣做需要冒險(xiǎn)—可能出問題。例如,如果試圖運(yùn)行一個(gè)對(duì)話框,在關(guān)閉對(duì)話框時(shí)可能會(huì)有保護(hù)錯(cuò)發(fā)生。試著在‘運(yùn)行’對(duì)話框中運(yùn)行下面代碼:

rundll32 appwiz.cpl,ConfigStartMenu

這個(gè)命令顯示一個(gè)可以修改程序菜單內(nèi)容的窗口:

???????????????????????

?

?

當(dāng)你關(guān)閉這個(gè)對(duì)話框時(shí),rundll32產(chǎn)生這個(gè)錯(cuò)誤:

??????????????????

?

?

然而我們發(fā)現(xiàn),如果使用你自己的代碼在程序中輸出相同的命令,使用上面的RunDll()函數(shù),它正好能正常地工作。

?????????這只是一個(gè)例子。如果你搜索MSDN知識(shí)庫文檔,使用‘rundll’或‘rundll32’文字搜索,你將會(huì)找到很多描述由rundll32引起的退出對(duì)話框的各種錯(cuò)誤。

?

通常使用的命令

?????????在某些情況下,這些沖突來自于函數(shù)的不正確使用。如果你請(qǐng)求rundll32執(zhí)行一個(gè)函數(shù)具有非推薦原型,將可能獲得錯(cuò)誤。rundll32實(shí)際也不可能知道此處的函數(shù)是否為安全可調(diào)用函數(shù)。但是我們可以保證,如果函數(shù)有一個(gè)自解釋的RunDll前綴或后綴,它將正常工作。在深入研究了知識(shí)庫文章和新聞組等信息后,我們給出下表總結(jié)了允許訪問無資料系統(tǒng)對(duì)話框的調(diào)用:

?

對(duì)話框

命令行

Internet?屬性

Rundll32 Inetcpl.cpl,LaunchInternetControlPanel

刪除快捷方式/文件夾

Rundll32 appwiz.cpl,ConfigStartMenu

打開文件

Rundll32 shell32.dll,OpenAs_RunDLL file

連接到‘我的連接’

Rundll32 rnaui.dll,RnaDial My Connection

建立新連接

Rundll32 RnaUI.dll,RnaWizard

添加打印機(jī)

Rundll32 sysdm.cpl,InstallDevice_Rundll printer

安裝新的調(diào)制解調(diào)器

Rundll32 sysdm.cpl,InstallDevice_Rundll modem

安裝新監(jiān)視器

Rundll32 sysdm.cpl,InstallDevice_Rundll monitor

添加新硬件

Control.exe sysdm.cpl,Add New Hardware

?

‘internet?屬性’是已知的標(biāo)簽對(duì)話框,當(dāng)你在控制面板上雙擊Internet下的小程序時(shí)顯示這個(gè)對(duì)話框,或在IE的‘觀察?| Internet選項(xiàng)…’菜單下,或從聯(lián)系IE圖標(biāo)的關(guān)聯(lián)菜單中選擇‘屬性’時(shí)也顯示這個(gè)對(duì)話框。下一個(gè)是‘刪除快捷方式?/?文件夾’對(duì)話框,而‘打開文件’對(duì)話框則是打開文件時(shí)彈出的對(duì)話框。‘連接到’對(duì)話框可以通過指定的撥號(hào)連接到Internet網(wǎng),注意不需要把表示連接的串用引號(hào)括起來,即使名字中包含空格也不用。‘建立新連接’運(yùn)行系統(tǒng)大師,并添加新連接到‘撥號(hào)網(wǎng)絡(luò)’文件夾。正像我們提到過的,還有一個(gè)‘添加?/?建立’對(duì)話框允許你添加新的modem?,打印機(jī)或監(jiān)視器到系統(tǒng)硬件配置中。

?????????表中最后一項(xiàng)‘添加新硬件’是默認(rèn)的PC搜索即插即用硬件的大師,然而,它并沒有綁定到rundll32,而是由control.exe取代,這是一個(gè)在控制面板后臺(tái)的可執(zhí)行程序。我們之所以在這里引出它是因?yàn)樗鲆粋€(gè)新課題:探測(cè)器委托對(duì)象。

????如果運(yùn)行非英語班本的Windows,‘添加新硬件’的命令行不能工作,我們可以告訴你問題所在—是由于串‘添加新硬件’應(yīng)該使用本地版本。

?

探測(cè)器對(duì)象

?????????下圖中顯示了Shell命名空間的一個(gè)觀察。所有你所看到的文件夾在桌面下都是形成Shell的對(duì)象‘我的計(jì)算機(jī)’和‘網(wǎng)上鄰居’包含了這臺(tái)PC的詳細(xì)信息和它所連接的網(wǎng)絡(luò)。IE節(jié)點(diǎn)是一個(gè)表現(xiàn)Internet的虛擬文?件夾。如果給出一個(gè)連接,你就可以展開它來查看任何web頁面,就像任何普通文件一樣顯示。‘回收站’和‘我的公文包’則完全是桌面對(duì)象的列表。

???????????????

?

?

?????????在第10章中我們已經(jīng)談到過‘回收站’。它是局部驅(qū)動(dòng)器上多個(gè)物理文件夾的狀態(tài)集合,這些文件夾中的每一個(gè)都包含了對(duì)標(biāo)志為刪除文件的引用。

?????????‘我的公文包’是有趣的,但是我們很少了解它的特征,它幫助你在多臺(tái)PC上保持文件和目錄的同步。后面將詳細(xì)介紹。

?????????在‘我的計(jì)算機(jī)’節(jié)點(diǎn)下有一些更特殊的文件夾。從程序員的觀點(diǎn)看,最有趣的是:

???????????????????打印機(jī)

???????????????????控制面板

???????????????????撥號(hào)網(wǎng)絡(luò)

???????????????????任務(wù)調(diào)度

另一些特殊文件夾在Windows目錄下,它們包含了子描述,下載的程序文件,以及站點(diǎn)訪問歷史。

?

控制面板

?????????‘控制面板’是描述和配置硬軟件部件的對(duì)話框庫。系統(tǒng)自動(dòng)提供一定數(shù)量的對(duì)話框,也可以添加你自己的。‘控制面板’文件夾是由讀取所有在‘system’目錄中找到的?.cpl?信息充填的命名空間擴(kuò)展。還有一個(gè)可執(zhí)行程序control.exe,功能是簡(jiǎn)單地請(qǐng)求Shell打開這個(gè)文件夾,和管理用戶的活動(dòng),據(jù)此通知這些小程序(applets)。

?????????當(dāng)然,你所寫的應(yīng)用程序作為控制面板項(xiàng),需要有一個(gè)不同于.cpl的擴(kuò)展名。首先也是最重要的是這個(gè)程序必須是一個(gè)DLL,并且還要滿足附加的條件:

?????????必須輸出CPlApplet()函數(shù)

????必須適當(dāng)?shù)仨憫?yīng)一定的消息

????必須提供圖標(biāo)和對(duì)話框

控制面板小程序全都是圍繞配置對(duì)話框構(gòu)建的——每個(gè)小程序,一個(gè)對(duì)話框。

?????????無論現(xiàn)在applets意義如何,它最常用于描述小的java模塊。在微軟資料中很長(zhǎng)時(shí)間以來都表示為控制面板部件。

?

開發(fā)控制面板小程序

????控制面板小程序DLL必須輸出CPlApplet()函數(shù),這個(gè)函數(shù)的原型如下:

LONG CPlApplet(HWND hwndCPl, //?對(duì)話框父窗口的Handle

UINT uMsg, //?接收的消息

LONG lParam1, //?由消息指定的頭一個(gè)變量

LONG lParam2); //?有消息指定的第二個(gè)變量

這個(gè)函數(shù)基本上是小程序的窗口過程——通過消息控制應(yīng)用與小程序通訊。特別,可以請(qǐng)求DLL實(shí)現(xiàn)的多個(gè)小程序,以及請(qǐng)求圖標(biāo)信息,名字和給定小程序的描述信息。很多.cpl文件僅實(shí)現(xiàn)單個(gè)小程序,然而并沒有限制你在其中實(shí)現(xiàn)多個(gè)小程序。下表列出了所有可能的消息:

?

消息

描述

CPL_DBLCLK

小程序的圖標(biāo)已經(jīng)被雙擊,所以應(yīng)該顯示相關(guān)的對(duì)話框。lParam變量基于0開始的數(shù),表示小程序在DLL的序號(hào),這個(gè)消息在CPL_INQUIRE和CPL_NEWINQUIRE之后發(fā)送。lParam2變量包含CPLINFO結(jié)構(gòu)的lData成員定義的用戶數(shù)據(jù)(見下面)。

CPL_EXIT

這個(gè)消息在CPL_STOP之后,并且在小程序卸載之后立即發(fā)生,它沒有參數(shù)。

CPL_GETCOUNT

詢問DLL返回它所實(shí)現(xiàn)的小程序數(shù)。它在CPL_INIT之后發(fā)送,沒有參數(shù)。

CPL_INIT

在小程序被加載時(shí)立即發(fā)送。沒有參數(shù)。

CPL_INQUIRE

用于獲取小程序的信息。它僅被調(diào)用一次,并且返回的信息由系統(tǒng)緩存。lParam1變量是DLL中小程序的基于0的序號(hào)。lParam2是一個(gè)指向CPLINFO結(jié)構(gòu)的指針,它必須被填充。

CPL_NEWINQUIRE

與CPL_INQUIRE目的相同,它使用不同結(jié)構(gòu),會(huì)話期間可以多次發(fā)送。lParam1變量是DLL中基于0的對(duì)話框序號(hào),lParam2是指向NEWCPLINFO結(jié)構(gòu)的指針,它必須被填充。

CPL_SELECT

在所有Win32平臺(tái)上作廢的和不支持的消息。

CPL_STOP

只發(fā)送一次,表示對(duì)話框打算關(guān)閉。lParam是DLL中基于0的對(duì)話框序號(hào),lParam2變量表示在CPLINFO結(jié)構(gòu)中l(wèi)Data成員定義的用戶數(shù)據(jù)。

?

如上所述,除了沒有參數(shù)的消息,lParam1程序總是表示DLL中對(duì)話框的索引。相反,lParam2變量有兩個(gè)意義:表示一個(gè)客戶的32位緩沖,或指向數(shù)據(jù)結(jié)構(gòu)的指針,這個(gè)結(jié)構(gòu)是用于收集對(duì)話框信息的。

?????????有兩個(gè)消息是控制應(yīng)用用來獲取給定小程序顯示的對(duì)話框信息的:CPL_INQUIRE和?CPL_NEWINQUIRE。在此情況下lParam2指向兩個(gè)不同的結(jié)構(gòu)——CPLINFO和NEWCPLINFO——其定義如下:

typedef struct tagCPLINFO

{

int idIcon; //?小程序的圖標(biāo)資源ID

int idName; //?對(duì)話框短名字串的資源ID

int idInfo; //?對(duì)話框描述串的資源ID

LONG lData; //?應(yīng)用定義的數(shù)據(jù)

} CPLINFO;

?

typedef struct tagNEWCPLINFO

{

DWORD dwSize; //?結(jié)構(gòu)尺寸

DWORD dwFlags; //?省略

DWORD dwHelpContext; //?省略

LONG lData; //?應(yīng)用定義的數(shù)據(jù)

HICON hIcon; //?小程序圖標(biāo)的?Handle

TCHAR szName[32]; //?對(duì)話框的短名字

TCHAR szInfo[64]; //?對(duì)話框的描述

TCHAR szHelpFile[128]; //?省略

} NEWCPLINFO;

如上所見,盡管聲明不同,但是結(jié)構(gòu)包含了相同的信息。控制面板小程序應(yīng)該回答至少一個(gè)相關(guān)的消息,在絕大多數(shù)情況下,因?yàn)榫彺嫘畔?#xff0c;所以CPL_INQUIRE消息提供較好的性能。如果有任何返回的信息在會(huì)話期間改變了,則你需要支持CPL_NEWINQUIRE消息,事實(shí)上后一個(gè)消息在控制器每次使用小程序信息時(shí)都被發(fā)送。另一個(gè)敏感的差異是CPLINFO信息要求存儲(chǔ)在小程序資源中的信息。而NEWCPLINFO則可以使用緩沖返回它們。

?????????可能有一種情況,你希望關(guān)聯(lián)小程序的全程狀態(tài)信息——隨沒有必要,但有時(shí)會(huì)有幫助,這就是lData起作用的地方。下面的清單顯示了一個(gè)示例CPlApplet()函數(shù):

LONG CPlApplet(HWND hwndCPl, UINT uMsg, LONG lParam1, LONG lParam2)

{

//?保存操作對(duì)話框點(diǎn)索引

int iDlgIndex = lParam1;

switch(uMsg)

{

//?作需要的初始化操作

case CPL_INIT:

return 1;

//?返回DLL中小程序的數(shù)量

case CPL_GETCOUNT:

return g_iNumOfApplets;

//?充填CPLINFO?結(jié)構(gòu)的字段

case CPL_INQUIRE:

LPCPLINFO pCPL = reinterpret_cast<LPCPLINFO>(lParam2);

pCPL->idIcon = g_iIconIndex;

pCPL->idName = g_pszAppName;

pCPL->idInfo = g_pszDesc;

break;

//?在接收雙擊后顯示對(duì)話框

case CPL_DBLCLK:

DialogBox(GetModuleHandle(NULL),

MAKEINTRESOURCE(g_iDlgID), hwndCPl, pfnDlgProc);

break;

}

return 1;

}

?

運(yùn)行控制面板小程序

?????????我們前面說過control.exe不是控制面板文件夾后面的程序,而簡(jiǎn)單地是一個(gè)存根,是調(diào)用探測(cè)器來顯示可以找到的所有.cpl文件內(nèi)容的導(dǎo)引。‘控制面板’不是物理文件夾——其表現(xiàn)和所有管理工作都由實(shí)現(xiàn)這個(gè)文件夾的命名空間擴(kuò)展完成。如果你想要從一個(gè)程序中運(yùn)行控制面板小程序,最好的方法是使用rundll32來執(zhí)行Control_RunDLL()函數(shù),這是一個(gè)由shell32.dll庫輸出的函數(shù):

rundll32.exe shell32.dll,Control_RunDLL applet.cpl

上面代碼通過發(fā)送CPL_DBLCLK到DLL輸出的CPlApplet()函數(shù)執(zhí)行名為applet.cpl的小程序。你也可以使用我們前面給出的RunDll()函數(shù)調(diào)用Control_RunDLL():

RunDll(hDlg, "shell32.dll", "Control_RunDLL", "desk.cpl,,3");

這個(gè)命令的輸出顯示如圖所示:

??????????????????????

?

?

這個(gè)命令調(diào)用顯示小程序,并告訴它顯示第四個(gè)頁面(命令行中的3,基于0的記數(shù),引用第四個(gè)元素)。因?yàn)橥ㄟ^Control_RunDLL()調(diào)用控制面板小程序時(shí),指定的三個(gè)參數(shù)有兩個(gè)是可選的,頭一個(gè)是.cpl文件名,第二個(gè)是DLL中基于0的小程序號(hào),默認(rèn)為0,必須有前綴@。第三個(gè)變量是基于0的頁面索引,這個(gè)頁面是想要初始選擇的。當(dāng)然,這個(gè)小程序僅是對(duì)頁面對(duì)話框,并且有0為默認(rèn)頁面。記住,上面的串‘desk.cpl,,3’必須讀作“顯示desk.cpl中的頭一個(gè)小程序的第四個(gè)頁面”。在后面的例子中我們考慮sysdm.cpl模塊,其中包含兩個(gè)小程序:‘系統(tǒng)’和‘添加新硬件’。要顯示頭一個(gè)小程序的第二個(gè)頁面,它列出了系統(tǒng)中所安裝的設(shè)備,你可以使用下面的命令行:

RunDll(hDlg, "shell32.dll", "Control_RunDLL", "sysdm.cpl,,1");

這等價(jià)于:

RunDll(hDlg, "shell32.dll", "Control_RunDLL", "sysdm.cpl,@0,1");

反之為了啟動(dòng)‘添加新硬件大師’,應(yīng)該使用:

RunDll(hDlg, "shell32.dll", "Control_RunDLL", "sysdm.cpl,@1");

這個(gè)調(diào)用反映出,我們正在調(diào)用DLL的第二個(gè)小程序,它是一個(gè)沒有分頁的對(duì)話框。

?

Control_RunDLL()?與. Control.exe

?????????前面我們給出過另一種導(dǎo)出‘添加新硬件大師’的方法,使用control.exe程序:

control.exe sysdm.cpl,Add New Hardware

而要使它工作,有一個(gè)重要的缺點(diǎn),就是命令行將根據(jù)本地位置而變化,因此它不如Control_RunDLL()方案。如果你運(yùn)行的是意大利版的系統(tǒng),串就變?yōu)椤甆uovo Hardware’,而不是‘Add New Hardware’。也就是說,命令行必須變?yōu)?#xff1a;

control.exe sysdm.cpl,Nuovo Hardware

在sysdm.cpl庫的串表中,本地化串有ID 202索引,這也是通過CPlApplet()接口返回的。

?

RunDll32.exe和RunDll()的交換

?????????rundll32.exe和我們的函數(shù)RunDll()提供了相同的功能。在二者各有自己的的特點(diǎn)。rundll32.exe程序是操作系統(tǒng)的標(biāo)準(zhǔn)部件,它可能隨版本的升級(jí)而更新,也就是說即使微軟改變了rundll32.exe的編程接口——正確調(diào)用函數(shù)所需要的原型——程序仍能正常工作。

????相反,如果你在NT上開發(fā)16位代碼,不可能有等價(jià)16位的rundll32.exe(在Windows9x上有一個(gè)rundll.exe)。更重要的是,rundll32是一個(gè)你不能直接控制的程序,并且它總是啟動(dòng)新的進(jìn)程。相反RunDll()則是運(yùn)行在調(diào)用者空間上的。

?

打印機(jī)文件夾

?????????‘打印機(jī)’文件夾也不映射到真實(shí)的文件系統(tǒng)文件夾——它是虛擬文件夾,提供對(duì)系統(tǒng)中可用的打印設(shè)備進(jìn)行訪問的能力,比如打印機(jī)和傳真機(jī)。在往PC上安裝新打印機(jī)時(shí),在隱藏的子目錄中建立一個(gè)新文件,這個(gè)隱藏的目錄是Windows下的一個(gè)目錄。這個(gè)子目錄與‘回收站’的‘Recycled’文件夾(第10章中已經(jīng)說明了)有同樣的作用。這個(gè)目錄稱為‘PrintHood’,可以使用SHGetSpecialFolderPath() API?函數(shù)恢復(fù)這個(gè)路徑。

?????????我們?cè)诘?章中檢視過怎樣瀏覽‘打印機(jī)’文件夾的內(nèi)容,在下一章中我們返回到這個(gè)科目,我們將查看一個(gè)新的腳本Shell對(duì)象,它提供了許多我們先前使用自動(dòng)控制構(gòu)建的功能。

?????????關(guān)于打印機(jī),我們需要查看SHInvokePrinterCommand()函數(shù)。它允許我們向打印機(jī)對(duì)象發(fā)送命令。這個(gè)函數(shù)僅在4.71以上版操作系統(tǒng)上支持。它的原型為:

BOOL SHInvokePrinterCommand(HWND hwnd,

UINT uAction,

LPCTSTR lpBuf1,

LPCTSTR lpBuf2,

BOOL fModal);

?

參數(shù)

描述

hwnd

由函數(shù)顯示的任何對(duì)話框或窗口的父窗口

uAction

打印機(jī)執(zhí)行活動(dòng)的標(biāo)識(shí)代碼

lpBuf1

包含相關(guān)活動(dòng)附加信息的緩沖,總是打印機(jī)名。

lpBuf2

包含相關(guān)活動(dòng)附加信息的緩沖

fModal

如果設(shè)置為TRUE,函數(shù)返回之前必須等待活動(dòng)完成。

?

喚醒打印機(jī)命令

?????????uAction參數(shù)可以是下面顯示的值:

?

命令

描述

PRINTACTION_OPEN

打開顯示打印機(jī)狀態(tài)的窗口

PRINTACTION_PROPERTIES

顯示打印機(jī)的屬性

PRINTACTION_TESTPAGE

打印測(cè)試頁

PRINTACTION_OPENNETPRN

與PRINTACTION_OPEN相同,但是是網(wǎng)絡(luò)打印機(jī)

PRINTACTION_NETINSTALL

安裝指定的網(wǎng)絡(luò)打印機(jī)

PRINTACTION_NETINSTALLLINK

建立指定網(wǎng)絡(luò)打印機(jī)的快捷方式,lpBuf2指向這個(gè)快捷方式的路徑。

?

在所有情況下lpBuf1都指向打印機(jī)名,可以是本地的,網(wǎng)絡(luò)的或共享的。如果是網(wǎng)絡(luò)打印機(jī),名字必須遵循UNC格式(//server/printer)。相對(duì)的lpBuf2參數(shù)僅與PRINTACTION_NETINSTALLLINK標(biāo)志一起使用。在NT下還有另外兩個(gè)標(biāo)志支持網(wǎng)絡(luò)打印機(jī):

PRINTACTION_SERVERPROPERTIES,?顯示服務(wù)器屬性

PRINTACTION_DOCUMENTDEFAULTS,?顯示打印機(jī)上默認(rèn)文檔屬性

通常,可以使用AppWizard快速建立測(cè)試SHInvokePrinterCommand()函數(shù)的應(yīng)用。

?

函數(shù)的返回

?????????資料說明函數(shù)成功返回非零值,但是經(jīng)過測(cè)試,我們不能使它返回?0?值,這個(gè)函數(shù)即使在指定了不存在的打印機(jī)的情況下也頑強(qiáng)地返回TRUE。與執(zhí)行命令相關(guān)的是可能獲得錯(cuò)誤消息,告訴你發(fā)生了什么。

?

撥號(hào)網(wǎng)絡(luò)

?????????‘撥號(hào)網(wǎng)絡(luò)’是一個(gè)虛擬文件夾,它聚集了所有可用的Internet和網(wǎng)絡(luò)連接。相關(guān)的函數(shù)由rnaui.dll輸出,是RnaDial()(撥號(hào)連接)和RnaWizard()(建立新連接)。這兩個(gè)函數(shù)都支持rundll32接口,因而可以由前面描述過的方法運(yùn)行。如果想要連接到Internet而不使用特定的連接,你可以求助于WinInetAPI。尤其是InternetAutoDial()函數(shù)可以通過定義在‘撥號(hào)網(wǎng)絡(luò)’文件夾中的默認(rèn)連接連接上網(wǎng)。

?

離線瀏覽

?????????IE4.0引進(jìn)了離線瀏覽——一種瀏覽模式,從本地專有緩存中瀏覽頁面。你可以通過WinInet API中的InternetQueryOption()函數(shù)感知這種狀態(tài),而InternetGoOnline()函數(shù)則導(dǎo)出選擇對(duì)話框,詢問是否連接或保持離線狀態(tài)。所有這些科目都在Internet客戶SDK資料中有詳細(xì)說明,更多的關(guān)于WinInet API資料請(qǐng)參見在線幫助。

?

任務(wù)調(diào)度

?????????任務(wù)調(diào)度(或調(diào)度代理)是一個(gè)與Windows95和NT4.0下活動(dòng)桌面一起引進(jìn)的模塊,在后來的Windows98中也包含了這個(gè)模塊。它的主要用途就是提供在特殊時(shí)間或預(yù)定義的狀態(tài)出現(xiàn)時(shí)運(yùn)行指定任務(wù)的能力。從程序員的角度上看,任務(wù)調(diào)度器是一個(gè)COM服務(wù)器,它給出定義任務(wù)和觸發(fā)任務(wù)執(zhí)行的功能。基本上,任務(wù)調(diào)度是一個(gè)簡(jiǎn)單的監(jiān)視器應(yīng)用程序,它花費(fèi)所有時(shí)間監(jiān)視一定的日期、時(shí)間、周的組合,以及標(biāo)志為‘感興趣’的時(shí)間,然后執(zhí)行要求的活動(dòng)。

?

Windows NT支持的調(diào)度

?????????這個(gè)與Shell4.71版一起引進(jìn)的調(diào)度代理是一個(gè)功能上類似于NT提供的AT命令的應(yīng)用程序。它與AT的差別是采用了COM接口,而AT則是NetSchedule API。

?

調(diào)度代理

?????????Windows9x的調(diào)度代理是mstask.exe程序,而在NT下它是Schedule服務(wù)。這個(gè)代理默認(rèn)情況下不啟動(dòng),除非運(yùn)行的是Windows98,代理也包含一個(gè)特殊文件夾,用來保持被調(diào)度任務(wù)的軌跡。Internet客戶SDK提供了幾段代碼說明怎樣啟動(dòng)和驅(qū)動(dòng)這個(gè)代理。

????代理管理的對(duì)象是任務(wù),任務(wù)基本上是可執(zhí)行文件,在每一個(gè)任務(wù)上可以有一個(gè)或多個(gè)觸發(fā)條件,用以確定什么時(shí)候運(yùn)行它。

????調(diào)度代理是一個(gè)服務(wù)器應(yīng)用程序,它管理所有確定的任務(wù),在正確的時(shí)間導(dǎo)出任務(wù),以及返回最近執(zhí)行時(shí)間和全部可執(zhí)行任務(wù)數(shù)信息等。調(diào)度代理的功能完全由ITaskScheduler接口描述。

?

任務(wù)和觸發(fā)器

?????????任務(wù)由IScheduledWorkItem和ITask接口描述,而且接口輸出了與在快捷方式中看到的完全不同的方法。你可以使用諸如應(yīng)用名、工作目錄、參數(shù)、優(yōu)先級(jí)、最大允許執(zhí)行時(shí)間,以及——最重要的——觸發(fā)器等信息設(shè)置任務(wù)。ITask從IScheduledWorkItem中導(dǎo)出,是這個(gè)接口的一個(gè)更特殊的版本。在將來可以定義很多不同類型的工作項(xiàng),但是現(xiàn)在它僅僅支持任務(wù)項(xiàng)。任務(wù)可以是32位或16位的應(yīng)用,OS2或MS-DOS應(yīng)用,批處理文件(*.bat),命令文件(*.cmd),或注冊(cè)的任何文件類型的處理器應(yīng)用。

????觸發(fā)器是用于辨識(shí)正確時(shí)段以運(yùn)行工作項(xiàng)的事件。在許多情況下,觸發(fā)器是一個(gè)唯一的時(shí)間,如"12:00:00?1998/12/3"。另一些情況下,它是可重復(fù)的,如"6:00:00?在每個(gè)月的第三個(gè)星期一"。用ITaskTrigger接口和TASK_TRIGGER結(jié)構(gòu)操作觸發(fā)器,它定義了任務(wù)的開始時(shí)間、重復(fù)頻率和參數(shù)。

????任務(wù)調(diào)度所涉及的所有相關(guān)接口和結(jié)構(gòu)在Internet客戶SDK中都有完整的資料說明。你可以參考這個(gè)幫助文檔獲得更詳細(xì)的信息和示例。

?

我的公文包

?????????‘我的公文包’是Windows95上的一個(gè)實(shí)用程序,設(shè)計(jì)用于幫助用戶在不同計(jì)算機(jī)上維護(hù)相同文檔的多份拷貝。一旦你把文檔放入‘我的公文包’文件夾,這個(gè)軟件將注意保持公文包中的備份與初始文檔同步。當(dāng)你使用桌面PC和筆記本電腦協(xié)同工作時(shí),這個(gè)同步是有用的。通常把桌面機(jī)器中的文檔作為初始文檔,并可以用來自筆記本電腦修改過的版本替換這個(gè)文檔,只需在‘我的公文包’中點(diǎn)擊這個(gè)文件名,它就可以檢查和同步這個(gè)備份:

???????????????????

?

?

如果在上一次同步操作后有一個(gè)文件已經(jīng)改變,Windows自動(dòng)用修改過的備份替換未修改的文件。如果兩個(gè)文件都有改變,就發(fā)生合并操作。合并操作涉及到要實(shí)現(xiàn)某些接口來專門處理文檔的合并:

????????????????

?

實(shí)現(xiàn)這些接口的對(duì)象稱為協(xié)調(diào)者(reconcilers),用來確定相同文件的兩個(gè)版本文檔是否要并列。如果兩個(gè)文檔都有改變,這個(gè)模塊可以提供合并內(nèi)容產(chǎn)生新的備份。也可以根據(jù)需要進(jìn)行交互操作,和放棄殘余的文件。精確的合并操作依賴于協(xié)調(diào)者的特殊實(shí)現(xiàn)。

?????????關(guān)于協(xié)調(diào)者的詳細(xì)信息可以在Internet客戶SDK資料中找到。

?

零碎對(duì)象

你是否從未從微軟的Word文檔中選擇一段文字,然后移動(dòng)或拷貝它到Windows的桌面上。這樣做時(shí),鼠標(biāo)的光標(biāo)變成鼓勵(lì)操作的矢量而不是禁止符號(hào)。也就是說,你可以直接拖動(dòng)Word文檔的文字段到桌面(或其它Windows文件夾)。此時(shí),你建立了一個(gè)零碎對(duì)象

???????????????????????????????????

?

?

基本上你所看到的是對(duì)象的一個(gè)連接,零碎文檔是一個(gè)具有.shs擴(kuò)展名的文件,它在IDataObject實(shí)現(xiàn)的對(duì)象被拖動(dòng)到文件夾上或桌面時(shí)自動(dòng)建立。要讀出零碎文檔的內(nèi)容有一個(gè)由shscrap.dll輸出的rundll32兼容的函數(shù)。這個(gè)庫在‘System’目錄下,是沒有正規(guī)資料說明的,這是因?yàn)榱闼槲臋n的支持應(yīng)該自動(dòng)由建立它的完全OLE應(yīng)用所提供。然而,如果你有興趣測(cè)試,shscrap.dll輸出的函數(shù)OpenScrap_RunDLL()可以用于打開任何.shs文件。

?

新快捷方式處理器

?????????到目前為止在這本書中我們討論了一定數(shù)量的課題,也討論了在應(yīng)用中集成特殊特征的方法。然而在這一章中,我們開始探索Shell的組成部分,主要集中在文件夾和命令行方面。現(xiàn)在,我們打算介紹一個(gè)有意義的例子,它使用了我們解釋過的許多基本課題(快捷方式,圖標(biāo),和文件夾),以及未來將要涉及的Shell客戶化技術(shù)。我們打算組成一個(gè)類似于Windows標(biāo)準(zhǔn)部件的工具,然后探索怎樣用我們自己的工具替換Windows標(biāo)準(zhǔn)部件。

?????????如果試圖通過右擊在桌面或任何探測(cè)器文件夾上建立一個(gè)快捷方式,或通過選擇‘文件?|?新文件?|?快捷方式’菜單建立快捷方式,則出現(xiàn)一個(gè)建立大師,它允許你指定目標(biāo)對(duì)象和要建立的文件名。.lnk文件的目標(biāo)文件夾總是設(shè)定在進(jìn)程啟動(dòng)的文件夾上。不能指定描述文字,熱鍵,甚至圖標(biāo),也不能決定在哪里建立這個(gè)快捷方式。

????????????????

?

?

為了繞過這些限制,我們打算構(gòu)建我們自己的快捷方式建立器,更進(jìn)一步,我們還打算用它來取代Windows標(biāo)準(zhǔn)的快捷方式大師工具。用這種方法,每次右擊建立新的快捷方式時(shí),無論在Shell的任何地方,我們的應(yīng)用都替代標(biāo)準(zhǔn)對(duì)話框彈出。

?

用戶界面

?????????這個(gè)建立快捷方式的新大師將提供全部目標(biāo)文件對(duì)象的輸入,如,描述,熱鍵和圖標(biāo)。此外還允許用戶選擇路經(jīng)和最終快捷方式的名。路經(jīng)可以用絕對(duì)驅(qū)動(dòng)器和目錄方式表示,也可以用指定文件夾的ID方式表示,例如,‘桌面’,‘發(fā)送到’,‘程序’,‘開始菜單’等。

??????????

?

?

上面截圖顯示了這個(gè)應(yīng)用的用戶界面。頭一個(gè)編輯框包含有目標(biāo)文件名,這可以瀏覽選擇。第二個(gè)編輯框是快捷方式描述文字,而第三個(gè)指定用于喚醒快捷方式的組合鍵。

?????????下一個(gè)區(qū)域可以選擇快捷方式相關(guān)的圖標(biāo),既可以選擇源文件(使用‘選擇圖標(biāo)路經(jīng)’按鈕)也可以選擇文件內(nèi)圖標(biāo)的索引,我們使用SHBrowseForIcon()函數(shù)(在第9章中定義的)實(shí)現(xiàn)這個(gè)功能。在‘Save as’區(qū)域,可以鍵入路徑名或從下拉框選取預(yù)定義項(xiàng),預(yù)定義項(xiàng)是特殊文件夾,如‘桌面’,‘發(fā)送到’等。右手邊的編輯框包含了快捷方式文件名,沒有.lnk擴(kuò)展名。最后,要建立快捷方式還需要按‘建立’按鈕來喚醒SHCreateShortcutEx()函數(shù),這是在第6章中定義的函數(shù)。

????到此,我們的應(yīng)用是基于AppWizard生成的對(duì)話框應(yīng)用——我們稱之為NewLink。當(dāng)從Shell調(diào)用這個(gè)程序時(shí)(我們將簡(jiǎn)短地說明怎樣做),它接收一個(gè)臨時(shí)文件名,這是Shell在喚醒這個(gè)大師時(shí)自動(dòng)建立的。這個(gè)名字被傳遞給WinMain()函數(shù)的lpsz變量,由于我們對(duì)它不感興趣,因此,第一個(gè)活動(dòng)就是刪除這個(gè)文件。而后我們卻使用這個(gè)名字作為輸出的.lnk文件。

?

舊函數(shù)

?????????完成對(duì)話框界面布局之后,下面我們開始編碼WinMain()函數(shù),刪除臨時(shí)文件,但是保留名字為以后使用:

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevious,LPTSTR lpsz, int iCmd)

{

//?刪除任何由Shell建立的臨時(shí)文件

if(lstrlen(lpsz))

DeleteFile(lpsz);

//?保留全程數(shù)據(jù)

g_hIconLarge = static_cast<HICON>(LoadImage(hInstance, "APP_ICON", IMAGE_ICON,

GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CXICON), 0));

g_hIconSmall = static_cast<HICON>(LoadImage(hInstance, "APP_ICON", IMAGE_ICON,

GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CXSMICON), 0));

lstrcpy(g_szNewLinkName, lpsz);

//?允許通用控件

INITCOMMONCONTROLSEX iccex;

iccex.dwSize = sizeof(INITCOMMONCONTROLSEX);

iccex.dwICC = ICC_WIN95_CLASSES;

InitCommonControlsEx(&iccex);

//?初始化COM?以使用SHCreateShortcutEx()函數(shù)

CoInitialize(NULL);

//?運(yùn)行主對(duì)話框

BOOL b = DialogBox(hInstance, "DLG_MAIN", NULL, APP_DlgProc);

CoUninitialize()

//?退出

DestroyIcon(g_hIconLarge);

DestroyIcon(g_hIconSmall);

return b;

}

對(duì)話框過程調(diào)用所有新按鈕的處理器:

case WM_COMMAND:

switch(wParam)

{

case IDC_CREATE:

DoCreateShortcut(hDlg);

return FALSE;

case IDC_BROWSEPATH:

OnBrowse(hDlg, IDC_PATH);

return FALSE;

case IDC_CHOOSEICON:

OnChooseIcon(hDlg);

return FALSE;

case IDC_BROWSETARGET:

OnBrowse(hDlg, IDC_TARGET);

return FALSE;

case IDC_BROWSEICON:

OnBrowse(hDlg, IDC_ICONPATH);

return FALSE;

case IDCANCEL:

EndDialog(hDlg, FALSE);

return FALSE;

}

break;

后三個(gè)是預(yù)定義的函數(shù),OnInitDialog()初始化所有控件:

void OnInitDialog(HWND hDlg)

{

//?設(shè)置圖標(biāo)(T/F?大/小圖標(biāo))

SendMessage(hDlg, WM_SETICON, FALSE, reinterpret_cast<LPARAM>(g_hIconSmall));

SendMessage(hDlg, WM_SETICON, TRUE, reinterpret_cast<LPARAM>(g_hIconLarge));

//?指定可用的文件夾

HWND hwndCbo = GetDlgItem(hDlg, IDC_PATH);

int i = ComboBox_AddString(hwndCbo, "Desktop");

ComboBox_SetItemData(hwndCbo, i, CSIDL_DESKTOP);

i = ComboBox_AddString(hwndCbo, "Favorites");

ComboBox_SetItemData(hwndCbo, i, CSIDL_FAVORITES);

i = ComboBox_AddString(hwndCbo, "Programs");

ComboBox_SetItemData(hwndCbo, i, CSIDL_PROGRAMS);

i = ComboBox_AddString(hwndCbo, "My Documents");

ComboBox_SetItemData(hwndCbo, i, CSIDL_PERSONAL);

i = ComboBox_AddString(hwndCbo, "SendTo");

ComboBox_SetItemData(hwndCbo, i, CSIDL_SENDTO);

i = ComboBox_AddString(hwndCbo, "Start Menu");

ComboBox_SetItemData(hwndCbo, i, CSIDL_STARTMENU);

ComboBox_SetCurSel(hwndCbo, 0);

//?初始化熱鍵控件,加前綴?Ctrl-Alt

SendDlgItemMessage(hDlg, IDC_HOTKEY, HKM_SETRULES,

HKCOMB_NONE | HKCOMB_S | HKCOMB_A | HKCOMB_C,

HOTKEYF_CONTROL | HOTKEYF_ALT);

SetDlgItemText(hDlg, IDC_TARGET, "C://");

SetDlgItemText(hDlg, IDC_ICONINDEX, "0");

//?處理通過命令行接收的文件名

if(lstrlen(g_szNewLinkName))

{

LPTSTR pszBuf = g_szNewLinkName;

LPTSTR psz = strrchr(g_szNewLinkName, '//');

SetDlgItemText(hDlg, IDC_LNKFILE, ++psz);

pszBuf[psz - pszBuf] = 0;

SetDlgItemText(hDlg, IDC_PATH, pszBuf);

}

else

SetDlgItemText(hDlg, IDC_LNKFILE, "NewLink");

}

開始的活動(dòng)是使用特殊文件夾名充填‘Save As’中的下拉框內(nèi)容,這些特殊文件夾可以接收我們建立的快捷方式文件。為了使進(jìn)一步的處理容易些,我們?yōu)槊恳粋€(gè)串關(guān)聯(lián)了一些項(xiàng)數(shù)據(jù)。接下來的工作是初始化熱鍵控件。加一個(gè)前綴Ctrl-Alt,對(duì)于快捷方式這是必須的。然后設(shè)置目標(biāo)串和圖標(biāo)索引的默認(rèn)值,使用命令行上傳遞來的文件名作為連接名的基礎(chǔ)。

?

新函數(shù)

?????????第二個(gè)處理器函數(shù),也是我們加到應(yīng)用中的第一個(gè)新函數(shù),在對(duì)話框含有的三個(gè)瀏覽按鈕中的任何一個(gè)被按下時(shí)被調(diào)用,第二個(gè)變量用于區(qū)分這三個(gè)按鈕:

void OnBrowse(HWND hDlg, WPARAM wItemType)

{

//?僅瀏覽目錄...

if(wItemType == IDC_PATH)

{

LPMALLOC pMalloc = NULL;

TCHAR szDir[MAX_PATH] = {0};

LPITEMIDLIST pidl = NULL;

BROWSEINFO bi;

ZeroMemory(&bi, sizeof(BROWSEINFO));

bi.hwndOwner = hDlg;

bi.lpszTitle = "Choose a folder:";

pidl = SHBrowseForFolder(&bi);

SHGetPathFromIDList(pidl, szDir);

SetDlgItemText(hDlg, IDC_PATH, szDir);

SHGetMalloc(&pMalloc);

pMalloc->Free(pidl);

pMalloc->Release();

return;

}

//?瀏覽文件...

TCHAR szFile[MAX_PATH] = {0};

OPENFILENAME ofn;

ZeroMemory(&ofn, sizeof(OPENFILENAME));

ofn.lStructSize = sizeof(OPENFILENAME);

switch(wItemType)

{

case IDC_TARGET:

ofn.lpstrFilter = "All files/0*.*/0";

break;

case IDC_ICONPATH:

ofn.lpstrFilter = "Icons/0*.exe;*.dll;*.ico/0";

break;

}

TCHAR szWinDir[MAX_PATH] = {0};

ofn.nMaxFile = MAX_PATH;

GetWindowsDirectory(szWinDir, MAX_PATH);

ofn.lpstrInitialDir = szWinDir;

ofn.lpstrFile = szFile;

if(!GetOpenFileName(&ofn))

return;

SetDlgItemText(hDlg, wItemType, ofn.lpstrFile);

//?默認(rèn)顯示頭一個(gè)圖標(biāo)

HICON hIcon = ExtractIcon(GetModuleHandle(NULL), ofn.lpstrFile, 0);

SendDlgItemMessage(hDlg, IDI_ICON, STM_SETICON,

reinterpret_cast<WPARAM>(hIcon), 0);

}

如果正在瀏覽路經(jīng),我們可以使用SHBrowseForFolder() API函數(shù)使用戶找到路經(jīng)并將其顯示在適當(dāng)?shù)木庉嬁丶?#xff0c;然后返回。如果在瀏覽文件,我們填寫適合于要查找文件類型的濾波串,然后調(diào)用GetOpenFileName(),把文件名顯示在適當(dāng)?shù)木庉嬁丶?#xff0c;并且把文件的第一個(gè)圖標(biāo)作為默認(rèn)圖標(biāo)顯示。

?????????‘選擇圖標(biāo)路經(jīng)’按鈕的處理器簡(jiǎn)單地使用SHBrowseForIcon()函數(shù),它是第9章從文件中選擇圖標(biāo)的函數(shù):

void OnChooseIcon(HWND hDlg)

{

TCHAR szFileName[MAX_PATH] = {0};

GetDlgItemText(hDlg, IDC_ICONPATH, szFileName, MAX_PATH);

HICON hIcon;

int iIconIndex = SHBrowseForIcon(szFileName, &hIcon);

if(iIconIndex >= 0)

{

SetDlgItemText(hDlg, IDC_ICONPATH, szFileName);

SetDlgItemInt(hDlg, IDC_ICONINDEX, iIconIndex, TRUE);

SendDlgItemMessage(hDlg, IDI_ICON, STM_SETICON,

reinterpret_cast<WPARAM>(hIcon), 0);

}

}

在獲得了所有數(shù)據(jù)之后,這個(gè)應(yīng)用的主要工作就是‘建立’按鈕的處理器:

void DoCreateShortcut(HWND hDlg)

{

TCHAR szTarget[MAX_PATH] = {0};

TCHAR szDesc[MAX_PATH] = {0};

//?取得熱鍵

SHORTCUTSTRUCT ss;

ss.wHotKey = static_cast<WORD>(SendDlgItemMessage(

hDlg, IDC_HOTKEY, HKM_GETHOTKEY, 0, 0));

//?取得目標(biāo)和描述

GetDlgItemText(hDlg, IDC_TARGET, szTarget, MAX_PATH);

GetDlgItemText( hDlg, IDC_DESCRIPTION, szDesc, MAX_PATH);

ss.pszTarget = szTarget;

ss.pszDesc = szDesc;

//?取得圖標(biāo)

TCHAR szIcon[MAX_PATH] = {0};

GetDlgItemText(hDlg, IDC_ICONPATH, szIcon, MAX_PATH);

ss.pszIconPath = szIcon;

ss.wIconIndex = 0;

//?確定快捷方式文件名

//?取得目標(biāo)文件夾和最后的反斜杠

HWND hwndCbo = GetDlgItem(hDlg, IDC_PATH);

int i = ComboBox_GetCurSel(hwndCbo);

DWORD nFolder = ComboBox_GetItemData(hwndCbo, i);

TCHAR szPath[MAX_PATH]= {0};

if(nFolder)

SHGetSpecialFolderPath(hDlg, szPath, nFolder, FALSE);

else

GetDlgItemText(hDlg, IDC_PATH, szPath, MAX_PATH);

if(szPath[lstrlen(szPath) - 1] != '//')

lstrcat(szPath, "//");

TCHAR szLnkFile[MAX_PATH] = {0};

GetDlgItemText(hDlg, IDC_LNKFILE, szLnkFile, MAX_PATH);

lstrcat(szPath, szLnkFile);

lstrcat(szPath, ".lnk");

//?建立...

SHCreateShortcutEx(szPath, &ss);

}

這里做的所有事情就是收集屏幕上各個(gè)控件的信息,封裝信息到SHORTCUTSTRUCT結(jié)構(gòu),然后調(diào)用SHCreateShortCutEx()做實(shí)際建立的操作。(這里所涉及的函數(shù)和結(jié)構(gòu)在第6章中已經(jīng)定義了)。要編譯這段代碼,你需要添加通常的頭文件和連接庫,需要#includes?resource.h,?shlobj.h commdlg.h,?以及連接ole32.lib?和comdlg32.lib.庫。

?

怎樣替換Windows的大師

?????????如果能用我們的應(yīng)用替換掉Windows標(biāo)準(zhǔn)的建立快捷方式大師程序就好了。事實(shí)上這并不困難,就像我們期望的,關(guān)鍵在于注冊(cè)表。快捷方式是.lnk類型的文件,所以頭一個(gè)要查看的地方應(yīng)該是:

HKEY_CLASSES_ROOT

/.lnk

在這個(gè)鍵下,我們發(fā)現(xiàn)了ShellNew鍵。在從Shell建立一個(gè)給定類型的新文件時(shí)——即,通過‘新文件’菜單——探測(cè)器總是搜索文件類子樹的ShellNew鍵。進(jìn)入這個(gè)鍵,‘命令’的值顯示了命令行,你應(yīng)該看到,它設(shè)置為:

runDLL32 AppWiz.Cpl,NewLinkHere %2

?

???

?

?

注意,使用rundll32.exe來運(yùn)行DLL函數(shù)作為命令行指令。替換標(biāo)準(zhǔn)的大師所有做的全部工作就是改變‘命令’的值,使之執(zhí)行我們的程序,如下:

c:/Utility/NewLink/NewLink.exe %2

注意最后的%2是使命令行正常工作的基礎(chǔ)——?jiǎng)h除或置換都將使對(duì)話框消失。當(dāng)你從桌面選擇建立新快捷方式時(shí),將會(huì)看到我們的對(duì)話框出現(xiàn)。

?????????NewLink.exe程序感知和使用由Shell作為變量傳遞的文件名——在喚醒快捷方式建立器之前,Shell總是建立一個(gè)空文件并把名字傳遞給程序,然而對(duì)于我們,這個(gè)文件名的處理不是問題。

?

編輯注冊(cè)表

?????????替換默認(rèn)的快捷方式大師需要編輯注冊(cè)表,這可以通過注冊(cè)表編輯器手動(dòng)完成,或使用腳本文件編程實(shí)現(xiàn)。例如,你可以恢復(fù)初始狀態(tài),簡(jiǎn)單的把初始值賦給‘命令’實(shí)體就可以了:

; restore.reg

REGEDIT4

[HKEY_CLASSES_ROOT/.lnk/ShellNew]

"Command" = "RunDLL32 appwiz.cpl,NewLinkHere %2"

用相同的方法,你可以用腳本來安裝處理器到同一個(gè)地方:

; replace.reg

REGEDIT4

[HKEY_CLASSES_ROOT/.lnk/ShellNew]

"Command" = "c://utility//newlink//newlink.exe %2"

一定要保證在.reg腳本中輸入路徑時(shí)總是使用雙斜線,還要記住例子中置換的路徑應(yīng)該是newlink.exe文件所在的路徑。

?

小結(jié)

?????????我們第一次Windows Shell的旅行到這里就結(jié)束了。我們已經(jīng)視察了探測(cè)器的命令行,并且發(fā)現(xiàn)了一個(gè)有趣的實(shí)用程序rundll32.exe,這個(gè)程序允許我們以命令行的形式使用DLL函數(shù)。在討論rundll32.exe特征期間,我們還探索了怎樣編程訪問系統(tǒng)對(duì)話框,其編程接口是沒有說明資料的,例如‘添加打印機(jī)大師’,‘添加新硬件’,‘建立新連接’和‘打開文件’。

這一章的第二部分討論了某些特殊的虛擬文件夾,它們實(shí)現(xiàn)為Shell對(duì)象,例如‘打印機(jī)’,‘撥號(hào)網(wǎng)絡(luò)’‘任務(wù)調(diào)度’和‘我的公文包’。我們給出了這些項(xiàng)目的概覽,和進(jìn)一步資料的來源。最后,我們給出了結(jié)合許多曾經(jīng)討論過的科目的一個(gè)例子。這個(gè)快捷方式的處理器還初步說明了我們將在第14章討論的科目。概括地講,這一章包含了:

?????????探測(cè)器命令行

?????????RunDLL32編程接口

?????????訪問無資料函數(shù)顯示系統(tǒng)對(duì)話框

?????????回顧某些虛擬文件夾如‘打印機(jī)’和‘我的公文包’

?????????零碎對(duì)象

?????????怎樣編寫和安裝新的,客戶建立快捷方式的模塊。

在下兩章中我們繼續(xù)探索和發(fā)掘兩個(gè)方面的有用技術(shù),一是腳本Shell對(duì)象,這使你能編程訪問任何Shell特征,從對(duì)話框到文件夾,從窗口到快捷方式。此后我們聚焦于非常有前途的子系統(tǒng)——Windows腳本環(huán)境(WSH),這使DOS批處理文件的概念被帶到了Windows中。

轉(zhuǎn)載于:https://www.cnblogs.com/songtzu/p/3239842.html

總結(jié)

以上是生活随笔為你收集整理的[转]Windows Shell 编程 第十一章 【来源:http://blog.csdn.net/wangqiulin123456/article/details/7987992】...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。