[转]Windows Shell 编程 第十一章 【来源:http://blog.csdn.net/wangqiulin123456/article/details/7987992】...
第十一章?探索Shell
?????????我們現在將注意力從API轉向Windows Shell本身。從這一章往后,我們的目標主要集中在清晰和全面的揭示探測器的工作原理和Shell?命名空間的構成對象上,最后給出客戶化探測器和擴展其特征和行為的示例程序。
?????????Windows Shell,也稱之為探測器,是一群特殊模塊的集合,這些模塊一起形成了Shell的命名空間,并且給出了執行大量特殊任務的能力,例如,探測文件夾,顯示特定的目錄子樹,裝入外部模塊并與它通訊等。盡管最終結果可能會有差異,只要這些對象被調用和顯示,探測器總是進入后臺操作。
?????????在許多情況下,探測器提供的服務都是通過命令行與給定程序通訊來進行的,因而,如果你希望探測器有效地管理你的程序,就需要了解應用應該向探測器提供什么,這是十分重要的。(這個題目將在14章中更詳細地討論)
?????????在這一章中我們將揭示下面科目:
???????????????????探測器的命令行
???????????????????運行系統對話框的RunDll32程序
????????Shell對象‘我的公文包’,‘控制面板’,‘打印機’和‘任務調度’
????????零碎對象
這一章給出的源代碼的示例提供了一個有用的工具,其中涵蓋了許多我們以前解釋過的技術,如快捷方式和注冊表處理等。這個工具是NewLink,它提供了在桌面或其它任何地方建立快捷方式的許多靈活方法。如果愿意,可以安裝它作為快捷方式處理器來取代標準的Windows大師。
?
探測器的命令行
?????????探測器有一個可以取四種選項開關的命令行,下面是幾種可能的組合結果:
?
explorer.exe [/n [, <folder>]]
[/e [, <folder>]]
[, /root, <object>]
[[, /select], <sub object>]
?
注意,命令行結構中逗號的使用。這肯定是不常看到的。下面是這些開關的意義:
?
| 開關 | 描述 |
| /n | 在新的單框觀察窗口中打開指定的文件夾。單框觀察窗口是一個基于列表觀察的窗口。 |
| /e | 在新的雙框觀察窗口中打開指定文件夾。這是典型的探測器觀察。左邊是命名空間的樹觀察,細節在右邊框中。 |
| /root | 使指定的文件夾作為觀察樹的根。需要一種/e類型的觀察。 |
| /select | 在左框中選擇指定項(樹觀察) |
?
最簡單的開關是?/select?選項,它用于在打開的文件夾中選擇特殊的子項。下面是一個例子:
explorer /e, /select, c:/windows
這個標志需要與?/e?聯合使用,因為?/select?標志要求樹觀察。在右框或單框文件夾中沒有辦法選擇項。在使用?/n?和?/e?開關時,可以在逗號后指定文件夾名:
explorer /e, c:/
explorer /n, c:/
explorer c:/
上面的第二和第三行產生相同的結果(單框觀察)—這就是說?/n?是默認選項。
?
/root?開關
?????????我們經常看到類似探測器觀察的情況,但是它們都有一個特殊的文件夾作為根。你可以通過組合使用?/e?和?/root?打開這樣的探測器觀察,例如:
explorer /e, /root, c:/windows
注意,當打開一個觀察使用的根不是桌面時,用戶就不能沿著樹向上行進。如果探測器使用c:/windows作為根,則用戶不能訪問這個樹中的?c:/?或任何與?/windows?同層的目錄。
?
使用特殊的文件夾作為根
????特殊文件夾,如‘回收站’,與文件系統文件夾沒有一對一對應。就象我們前面討論過的,這些命名空間擴展是進程內服務器,是由CLSID所標識的。/root?開關使你可以指定一個CLSID作為一個文件夾使用,但是需要滿足兩個限制條件:
????這個COM服務器必須實現所有命名空間擴展所要求的接口
你必須在探測器命令行上使用::前綴在CLSID之前來引用它。其語法為::(CLSID)。這使得它被處理成普通的目錄名。
例如:
explorer ::{645FF040-5081-101B-9F08-00AA002F954E}
這將打開一個回收站新窗口,你也可以使用自己的客戶文件夾這么做。下面的表中給出了幾個可以在桌面上找到的文件夾對象CLSIDs,但是需要注意,并不是桌面上所有對象都是文件夾—例如‘Inbox’和‘我的公文包’就不是文件夾而是應用。
| CLSID | 對象 |
| 645FF040-5081-101B-9F08-00AA002F954E | 回收站 |
| 20D04FE0-3AEA-1069-A2D8-08002B30309D | 我的計算機 |
| 208D2C60-3AEA-1069-A2D7-08002B30309D | 網上鄰居 |
| 871C5380-42A0-1069-A2EA-08002B30309D | IE?瀏覽器 |
| 21EC2020-3AEA-1069-A2DD-08002B30309D | 控制面板 |
| 992CFFA0-F557-101A-88EC-00DD010CCC48 | 撥號連接 |
| 2227A280-3AEA-1069-A2DE-08002B30309D | 打印機 |
?
注意,在這里你可以把CLSIDs看作傳統的文件夾,并且可以使用斜線連接它們。例如要訪問打印機文件夾,它是‘我的計算機’的子文件夾,你可以使用下面的文法:
explorer ::{20D04FE0-3AEA-1069-A2D8-08002B30309D}/
::{2227A280-3AEA-1069-A2DE-08002B30309D}
?
關于rundll32.exe
?????????我們曾說過在探測器組織中命令行扮演著重要的角色。許多在文件對象上執行的功能都是通過命令行實現的。為了進一步增強,Windows9x和NT4.0以后的版本推出了rundll32.exe輔助程序。它允許你直接從命令行調用DLL輸出的函數。這個實用程序簡單地封裝了幾個需要動態執行的API調用。
?????????下面的偽代碼說明了rundll32的操作:
void DoRunDll32(LPCTSTR szDllName, LPCTSTR szFuncName, LPCTSTR szCmdLine)
{
//?裝入這個庫
HANDLE hLib = LoadLibrary(szDllName);
//?取得請求執行的函數地址
FARPROC pFunc = GetProcAddress(hLib, szFuncName);
//?使用指針執行函數
pFunc(GetFocus(), hLib, szCmdLine, SW_SHOW);
//?釋放庫
FreeLibrary(hLib);
}
在實際使用中,rundll32接收它所解析的單個字符串,從中抽取動態庫和函數名,以及選擇的函數變量。命令行有下面形式:
rundll32 dllname,funcname [arguments]
DLL名和函數名之間用逗號分割,并且不能有空格。如果調用rundll32沒有指定DLL的全路徑,它搜索所有標準路經包括應用的目錄,Windows目錄和當前目錄。
?????????這個程序的缺點是在錯誤情況下通常不能返回足夠的信息。如果你試圖調用的DLL函數缺失(比如,鍵入的名字錯),則它可以使你相當快地知道發生了什么,然而,如果這個函數可以被調用,但是在執行時失敗,此時你就必須自己猜測究竟發生了什么。
?????????rundll32的接口通常用于調用某些不熟知的系統對話框,別的用戶也可以用它來調用你的DLL,要這樣做的唯一要求是需要可調用函數的預定義原型。
?
rundll32.exe可調用的函數
?????????在上面的偽代碼中,看一下實際調用DLL函數那一行。Rundll32僅僅可以調用具有下面原型的函數—有四個參數,而僅僅有一個用戶可以設置的:
void CALLBACK FuncName(HWND hwnd, // Window handle
HINSTANCE hinst, // Instance handle
LPTSTR lpszCmdLine, // Command line
int nCmdShow); // ShowWindow() parameter
hwnd參數指定函數建立任何窗口的父窗口,也就是說,這個對話框是相對于父窗口的模式對話框。事實上,這個參數總是等價于桌面窗口(即,hwnd?是?NULL),所以,相對于任何Shell中打開的窗口,新窗口總是非模式的。第二個參數是HINSTANCE類型的Handle,是一個由LoadLibrary()返回的庫Handle。第四個參數nCmdShow確定窗口的顯示方式,而且在通過rundll32調用函數時應該總是SW_SHOW。
????唯一一個可控制參數是lpszCmdLine,通過這個參數,函數接收數據變量,例如,假設有一個函數接收兩個數字,定義如下:
void MyFunc(int iFirst, long lSecond)
{
...
}
要使這個函數可通過rundll32接口調用,它應該變形為:
void MyFunc1(HWND hwnd, HINSTANCE hinst, LPTSTR lpszCmdLine, int nShow)
{
int iFirst;
long lSecond;
//?解析命令行和截斷出參數
ParseCommandLine(lpszCmdLine, &iFirst, &lSecond);
//?用參數調用已經抽取的函數
MyFunc(iFirst, lSecond);
}
在NT下,要調用的函數首先搜索Unicode名,然后是ANSI名,最后才是實際所寫的名。也就是說,調用MyFunc()被依次轉換成調用MyFuncW(),?MyFuncA()和?MyFunc(),當然,此時的參數串也被傳遞成寬字符串,封裝為LPWSTR,而不是LPSTR。
?
rundll32.exe能做些什么
?????????rundll32(其16位祖先是rundll)主要用于顯示系統對話框和在僅能使用命令行的情況下調用DLL函數。初始它作為微軟內部使用的工具而設計,因此,故意限制其實際設置是顯然的。
?????????在測試代碼時,這個程序可以使加載和卸載DLL明顯地節省時間。有時它也能幫助你檢查DLL輸出的對話框。反過來,它也強制函數調用采用固定的格式,因而也就沒有數據返回調用者的機理。然而,它有一種特殊的功能是你想使用的。
?????????你可以使用rundll32來獲得對某些系統對話框的訪問,包括那些由于資料缺乏不容易訪問的對話框。在許多情況下訪問某些東西的唯一推薦的(和資料說明的)方法是使用rundll32。例如‘添加新打印機’操作:
??????????????
?
?
這個操作是系統通過調用sysdm.cpl庫中的一個函數實現的。擴展名說明這個庫是‘控制面板’DLL。資料中建議這樣調用:
rundll32.exe sysdm.cpl,InstallDevice_Rundll printer
而不是直接調用InstallDevice_Rundll()函數。這個函數的四個參數我們已經討論過了,使用‘printer’作為變量來確定要做什么。事實上盡管沒有資料說明,我們也能夠使用相同的語法來安裝新的調制解調器或監視器,只須簡單地替換‘printer’為‘modem’或‘monitor’即可。
?
RunDll()?函數
?????????許多系統對話框和應用大師僅支持使用rundll32的調用。這可能是因為可以適應將來的變化,其中使用rundll32可以屏蔽新的細節。還有可能是因為這些函數和對話框不是完全公共的,再有就是使用rundll32接口可以隱藏它們的許多方面。下面的清單顯示了用C++?函數模仿rundll32的可能的實現。回顧一下我們早先給出的偽代碼例子,這里加入了錯誤檢查和指定父窗口的方法:
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);
}
以這種方式封裝我們自己的函數,就可以把我們想要的任何窗口作為父窗口傳遞給子對話框,這就允許我們顯示模式對話框。
?
Rundll32.exe保護錯
?????????你當然可以用rundll32調用不是為了與rundll32一起工作而設計的函數。但是這樣做需要冒險—可能出問題。例如,如果試圖運行一個對話框,在關閉對話框時可能會有保護錯發生。試著在‘運行’對話框中運行下面代碼:
rundll32 appwiz.cpl,ConfigStartMenu
這個命令顯示一個可以修改程序菜單內容的窗口:
???????????????????????
?
?
當你關閉這個對話框時,rundll32產生這個錯誤:
??????????????????
?
?
然而我們發現,如果使用你自己的代碼在程序中輸出相同的命令,使用上面的RunDll()函數,它正好能正常地工作。
?????????這只是一個例子。如果你搜索MSDN知識庫文檔,使用‘rundll’或‘rundll32’文字搜索,你將會找到很多描述由rundll32引起的退出對話框的各種錯誤。
?
通常使用的命令
?????????在某些情況下,這些沖突來自于函數的不正確使用。如果你請求rundll32執行一個函數具有非推薦原型,將可能獲得錯誤。rundll32實際也不可能知道此處的函數是否為安全可調用函數。但是我們可以保證,如果函數有一個自解釋的RunDll前綴或后綴,它將正常工作。在深入研究了知識庫文章和新聞組等信息后,我們給出下表總結了允許訪問無資料系統對話框的調用:
?
| 對話框 | 命令行 |
| 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 |
| 添加打印機 | Rundll32 sysdm.cpl,InstallDevice_Rundll printer |
| 安裝新的調制解調器 | Rundll32 sysdm.cpl,InstallDevice_Rundll modem |
| 安裝新監視器 | Rundll32 sysdm.cpl,InstallDevice_Rundll monitor |
| 添加新硬件 | Control.exe sysdm.cpl,Add New Hardware |
?
‘internet?屬性’是已知的標簽對話框,當你在控制面板上雙擊Internet下的小程序時顯示這個對話框,或在IE的‘觀察?| Internet選項…’菜單下,或從聯系IE圖標的關聯菜單中選擇‘屬性’時也顯示這個對話框。下一個是‘刪除快捷方式?/?文件夾’對話框,而‘打開文件’對話框則是打開文件時彈出的對話框。‘連接到’對話框可以通過指定的撥號連接到Internet網,注意不需要把表示連接的串用引號括起來,即使名字中包含空格也不用。‘建立新連接’運行系統大師,并添加新連接到‘撥號網絡’文件夾。正像我們提到過的,還有一個‘添加?/?建立’對話框允許你添加新的modem?,打印機或監視器到系統硬件配置中。
?????????表中最后一項‘添加新硬件’是默認的PC搜索即插即用硬件的大師,然而,它并沒有綁定到rundll32,而是由control.exe取代,這是一個在控制面板后臺的可執行程序。我們之所以在這里引出它是因為它引出一個新課題:探測器委托對象。
????如果運行非英語班本的Windows,‘添加新硬件’的命令行不能工作,我們可以告訴你問題所在—是由于串‘添加新硬件’應該使用本地版本。
?
探測器對象
?????????下圖中顯示了Shell命名空間的一個觀察。所有你所看到的文件夾在桌面下都是形成Shell的對象‘我的計算機’和‘網上鄰居’包含了這臺PC的詳細信息和它所連接的網絡。IE節點是一個表現Internet的虛擬文?件夾。如果給出一個連接,你就可以展開它來查看任何web頁面,就像任何普通文件一樣顯示。‘回收站’和‘我的公文包’則完全是桌面對象的列表。
???????????????
?
?
?????????在第10章中我們已經談到過‘回收站’。它是局部驅動器上多個物理文件夾的狀態集合,這些文件夾中的每一個都包含了對標志為刪除文件的引用。
?????????‘我的公文包’是有趣的,但是我們很少了解它的特征,它幫助你在多臺PC上保持文件和目錄的同步。后面將詳細介紹。
?????????在‘我的計算機’節點下有一些更特殊的文件夾。從程序員的觀點看,最有趣的是:
???????????????????打印機
???????????????????控制面板
???????????????????撥號網絡
???????????????????任務調度
另一些特殊文件夾在Windows目錄下,它們包含了子描述,下載的程序文件,以及站點訪問歷史。
?
控制面板
?????????‘控制面板’是描述和配置硬軟件部件的對話框庫。系統自動提供一定數量的對話框,也可以添加你自己的。‘控制面板’文件夾是由讀取所有在‘system’目錄中找到的?.cpl?信息充填的命名空間擴展。還有一個可執行程序control.exe,功能是簡單地請求Shell打開這個文件夾,和管理用戶的活動,據此通知這些小程序(applets)。
?????????當然,你所寫的應用程序作為控制面板項,需要有一個不同于.cpl的擴展名。首先也是最重要的是這個程序必須是一個DLL,并且還要滿足附加的條件:
?????????必須輸出CPlApplet()函數
????必須適當地響應一定的消息
????必須提供圖標和對話框
控制面板小程序全都是圍繞配置對話框構建的——每個小程序,一個對話框。
?????????無論現在applets意義如何,它最常用于描述小的java模塊。在微軟資料中很長時間以來都表示為控制面板部件。
?
開發控制面板小程序
????控制面板小程序DLL必須輸出CPlApplet()函數,這個函數的原型如下:
LONG CPlApplet(HWND hwndCPl, //?對話框父窗口的Handle
UINT uMsg, //?接收的消息
LONG lParam1, //?由消息指定的頭一個變量
LONG lParam2); //?有消息指定的第二個變量
這個函數基本上是小程序的窗口過程——通過消息控制應用與小程序通訊。特別,可以請求DLL實現的多個小程序,以及請求圖標信息,名字和給定小程序的描述信息。很多.cpl文件僅實現單個小程序,然而并沒有限制你在其中實現多個小程序。下表列出了所有可能的消息:
?
| 消息 | 描述 |
| CPL_DBLCLK | 小程序的圖標已經被雙擊,所以應該顯示相關的對話框。lParam變量基于0開始的數,表示小程序在DLL的序號,這個消息在CPL_INQUIRE和CPL_NEWINQUIRE之后發送。lParam2變量包含CPLINFO結構的lData成員定義的用戶數據(見下面)。 |
| CPL_EXIT | 這個消息在CPL_STOP之后,并且在小程序卸載之后立即發生,它沒有參數。 |
| CPL_GETCOUNT | 詢問DLL返回它所實現的小程序數。它在CPL_INIT之后發送,沒有參數。 |
| CPL_INIT | 在小程序被加載時立即發送。沒有參數。 |
| CPL_INQUIRE | 用于獲取小程序的信息。它僅被調用一次,并且返回的信息由系統緩存。lParam1變量是DLL中小程序的基于0的序號。lParam2是一個指向CPLINFO結構的指針,它必須被填充。 |
| CPL_NEWINQUIRE | 與CPL_INQUIRE目的相同,它使用不同結構,會話期間可以多次發送。lParam1變量是DLL中基于0的對話框序號,lParam2是指向NEWCPLINFO結構的指針,它必須被填充。 |
| CPL_SELECT | 在所有Win32平臺上作廢的和不支持的消息。 |
| CPL_STOP | 只發送一次,表示對話框打算關閉。lParam是DLL中基于0的對話框序號,lParam2變量表示在CPLINFO結構中lData成員定義的用戶數據。 |
?
如上所述,除了沒有參數的消息,lParam1程序總是表示DLL中對話框的索引。相反,lParam2變量有兩個意義:表示一個客戶的32位緩沖,或指向數據結構的指針,這個結構是用于收集對話框信息的。
?????????有兩個消息是控制應用用來獲取給定小程序顯示的對話框信息的:CPL_INQUIRE和?CPL_NEWINQUIRE。在此情況下lParam2指向兩個不同的結構——CPLINFO和NEWCPLINFO——其定義如下:
typedef struct tagCPLINFO
{
int idIcon; //?小程序的圖標資源ID
int idName; //?對話框短名字串的資源ID
int idInfo; //?對話框描述串的資源ID
LONG lData; //?應用定義的數據
} CPLINFO;
?
typedef struct tagNEWCPLINFO
{
DWORD dwSize; //?結構尺寸
DWORD dwFlags; //?省略
DWORD dwHelpContext; //?省略
LONG lData; //?應用定義的數據
HICON hIcon; //?小程序圖標的?Handle
TCHAR szName[32]; //?對話框的短名字
TCHAR szInfo[64]; //?對話框的描述
TCHAR szHelpFile[128]; //?省略
} NEWCPLINFO;
如上所見,盡管聲明不同,但是結構包含了相同的信息。控制面板小程序應該回答至少一個相關的消息,在絕大多數情況下,因為緩存信息,所以CPL_INQUIRE消息提供較好的性能。如果有任何返回的信息在會話期間改變了,則你需要支持CPL_NEWINQUIRE消息,事實上后一個消息在控制器每次使用小程序信息時都被發送。另一個敏感的差異是CPLINFO信息要求存儲在小程序資源中的信息。而NEWCPLINFO則可以使用緩沖返回它們。
?????????可能有一種情況,你希望關聯小程序的全程狀態信息——隨沒有必要,但有時會有幫助,這就是lData起作用的地方。下面的清單顯示了一個示例CPlApplet()函數:
LONG CPlApplet(HWND hwndCPl, UINT uMsg, LONG lParam1, LONG lParam2)
{
//?保存操作對話框點索引
int iDlgIndex = lParam1;
switch(uMsg)
{
//?作需要的初始化操作
case CPL_INIT:
return 1;
//?返回DLL中小程序的數量
case CPL_GETCOUNT:
return g_iNumOfApplets;
//?充填CPLINFO?結構的字段
case CPL_INQUIRE:
LPCPLINFO pCPL = reinterpret_cast<LPCPLINFO>(lParam2);
pCPL->idIcon = g_iIconIndex;
pCPL->idName = g_pszAppName;
pCPL->idInfo = g_pszDesc;
break;
//?在接收雙擊后顯示對話框
case CPL_DBLCLK:
DialogBox(GetModuleHandle(NULL),
MAKEINTRESOURCE(g_iDlgID), hwndCPl, pfnDlgProc);
break;
}
return 1;
}
?
運行控制面板小程序
?????????我們前面說過control.exe不是控制面板文件夾后面的程序,而簡單地是一個存根,是調用探測器來顯示可以找到的所有.cpl文件內容的導引。‘控制面板’不是物理文件夾——其表現和所有管理工作都由實現這個文件夾的命名空間擴展完成。如果你想要從一個程序中運行控制面板小程序,最好的方法是使用rundll32來執行Control_RunDLL()函數,這是一個由shell32.dll庫輸出的函數:
rundll32.exe shell32.dll,Control_RunDLL applet.cpl
上面代碼通過發送CPL_DBLCLK到DLL輸出的CPlApplet()函數執行名為applet.cpl的小程序。你也可以使用我們前面給出的RunDll()函數調用Control_RunDLL():
RunDll(hDlg, "shell32.dll", "Control_RunDLL", "desk.cpl,,3");
這個命令的輸出顯示如圖所示:
??????????????????????
?
?
這個命令調用顯示小程序,并告訴它顯示第四個頁面(命令行中的3,基于0的記數,引用第四個元素)。因為通過Control_RunDLL()調用控制面板小程序時,指定的三個參數有兩個是可選的,頭一個是.cpl文件名,第二個是DLL中基于0的小程序號,默認為0,必須有前綴@。第三個變量是基于0的頁面索引,這個頁面是想要初始選擇的。當然,這個小程序僅是對頁面對話框,并且有0為默認頁面。記住,上面的串‘desk.cpl,,3’必須讀作“顯示desk.cpl中的頭一個小程序的第四個頁面”。在后面的例子中我們考慮sysdm.cpl模塊,其中包含兩個小程序:‘系統’和‘添加新硬件’。要顯示頭一個小程序的第二個頁面,它列出了系統中所安裝的設備,你可以使用下面的命令行:
RunDll(hDlg, "shell32.dll", "Control_RunDLL", "sysdm.cpl,,1");
這等價于:
RunDll(hDlg, "shell32.dll", "Control_RunDLL", "sysdm.cpl,@0,1");
反之為了啟動‘添加新硬件大師’,應該使用:
RunDll(hDlg, "shell32.dll", "Control_RunDLL", "sysdm.cpl,@1");
這個調用反映出,我們正在調用DLL的第二個小程序,它是一個沒有分頁的對話框。
?
Control_RunDLL()?與. Control.exe
?????????前面我們給出過另一種導出‘添加新硬件大師’的方法,使用control.exe程序:
control.exe sysdm.cpl,Add New Hardware
而要使它工作,有一個重要的缺點,就是命令行將根據本地位置而變化,因此它不如Control_RunDLL()方案。如果你運行的是意大利版的系統,串就變為‘Nuovo Hardware’,而不是‘Add New Hardware’。也就是說,命令行必須變為:
control.exe sysdm.cpl,Nuovo Hardware
在sysdm.cpl庫的串表中,本地化串有ID 202索引,這也是通過CPlApplet()接口返回的。
?
RunDll32.exe和RunDll()的交換
?????????rundll32.exe和我們的函數RunDll()提供了相同的功能。在二者各有自己的的特點。rundll32.exe程序是操作系統的標準部件,它可能隨版本的升級而更新,也就是說即使微軟改變了rundll32.exe的編程接口——正確調用函數所需要的原型——程序仍能正常工作。
????相反,如果你在NT上開發16位代碼,不可能有等價16位的rundll32.exe(在Windows9x上有一個rundll.exe)。更重要的是,rundll32是一個你不能直接控制的程序,并且它總是啟動新的進程。相反RunDll()則是運行在調用者空間上的。
?
打印機文件夾
?????????‘打印機’文件夾也不映射到真實的文件系統文件夾——它是虛擬文件夾,提供對系統中可用的打印設備進行訪問的能力,比如打印機和傳真機。在往PC上安裝新打印機時,在隱藏的子目錄中建立一個新文件,這個隱藏的目錄是Windows下的一個目錄。這個子目錄與‘回收站’的‘Recycled’文件夾(第10章中已經說明了)有同樣的作用。這個目錄稱為‘PrintHood’,可以使用SHGetSpecialFolderPath() API?函數恢復這個路徑。
?????????我們在第5章中檢視過怎樣瀏覽‘打印機’文件夾的內容,在下一章中我們返回到這個科目,我們將查看一個新的腳本Shell對象,它提供了許多我們先前使用自動控制構建的功能。
?????????關于打印機,我們需要查看SHInvokePrinterCommand()函數。它允許我們向打印機對象發送命令。這個函數僅在4.71以上版操作系統上支持。它的原型為:
BOOL SHInvokePrinterCommand(HWND hwnd,
UINT uAction,
LPCTSTR lpBuf1,
LPCTSTR lpBuf2,
BOOL fModal);
?
| 參數 | 描述 |
| hwnd | 由函數顯示的任何對話框或窗口的父窗口 |
| uAction | 打印機執行活動的標識代碼 |
| lpBuf1 | 包含相關活動附加信息的緩沖,總是打印機名。 |
| lpBuf2 | 包含相關活動附加信息的緩沖 |
| fModal | 如果設置為TRUE,函數返回之前必須等待活動完成。 |
?
喚醒打印機命令
?????????uAction參數可以是下面顯示的值:
?
| 命令 | 描述 |
| PRINTACTION_OPEN | 打開顯示打印機狀態的窗口 |
| PRINTACTION_PROPERTIES | 顯示打印機的屬性 |
| PRINTACTION_TESTPAGE | 打印測試頁 |
| PRINTACTION_OPENNETPRN | 與PRINTACTION_OPEN相同,但是是網絡打印機 |
| PRINTACTION_NETINSTALL | 安裝指定的網絡打印機 |
| PRINTACTION_NETINSTALLLINK | 建立指定網絡打印機的快捷方式,lpBuf2指向這個快捷方式的路徑。 |
?
在所有情況下lpBuf1都指向打印機名,可以是本地的,網絡的或共享的。如果是網絡打印機,名字必須遵循UNC格式(//server/printer)。相對的lpBuf2參數僅與PRINTACTION_NETINSTALLLINK標志一起使用。在NT下還有另外兩個標志支持網絡打印機:
PRINTACTION_SERVERPROPERTIES,?顯示服務器屬性
PRINTACTION_DOCUMENTDEFAULTS,?顯示打印機上默認文檔屬性
通常,可以使用AppWizard快速建立測試SHInvokePrinterCommand()函數的應用。
?
函數的返回
?????????資料說明函數成功返回非零值,但是經過測試,我們不能使它返回?0?值,這個函數即使在指定了不存在的打印機的情況下也頑強地返回TRUE。與執行命令相關的是可能獲得錯誤消息,告訴你發生了什么。
?
撥號網絡
?????????‘撥號網絡’是一個虛擬文件夾,它聚集了所有可用的Internet和網絡連接。相關的函數由rnaui.dll輸出,是RnaDial()(撥號連接)和RnaWizard()(建立新連接)。這兩個函數都支持rundll32接口,因而可以由前面描述過的方法運行。如果想要連接到Internet而不使用特定的連接,你可以求助于WinInetAPI。尤其是InternetAutoDial()函數可以通過定義在‘撥號網絡’文件夾中的默認連接連接上網。
?
離線瀏覽
?????????IE4.0引進了離線瀏覽——一種瀏覽模式,從本地專有緩存中瀏覽頁面。你可以通過WinInet API中的InternetQueryOption()函數感知這種狀態,而InternetGoOnline()函數則導出選擇對話框,詢問是否連接或保持離線狀態。所有這些科目都在Internet客戶SDK資料中有詳細說明,更多的關于WinInet API資料請參見在線幫助。
?
任務調度
?????????任務調度(或調度代理)是一個與Windows95和NT4.0下活動桌面一起引進的模塊,在后來的Windows98中也包含了這個模塊。它的主要用途就是提供在特殊時間或預定義的狀態出現時運行指定任務的能力。從程序員的角度上看,任務調度器是一個COM服務器,它給出定義任務和觸發任務執行的功能。基本上,任務調度是一個簡單的監視器應用程序,它花費所有時間監視一定的日期、時間、周的組合,以及標志為‘感興趣’的時間,然后執行要求的活動。
?
Windows NT支持的調度
?????????這個與Shell4.71版一起引進的調度代理是一個功能上類似于NT提供的AT命令的應用程序。它與AT的差別是采用了COM接口,而AT則是NetSchedule API。
?
調度代理
?????????Windows9x的調度代理是mstask.exe程序,而在NT下它是Schedule服務。這個代理默認情況下不啟動,除非運行的是Windows98,代理也包含一個特殊文件夾,用來保持被調度任務的軌跡。Internet客戶SDK提供了幾段代碼說明怎樣啟動和驅動這個代理。
????代理管理的對象是任務,任務基本上是可執行文件,在每一個任務上可以有一個或多個觸發條件,用以確定什么時候運行它。
????調度代理是一個服務器應用程序,它管理所有確定的任務,在正確的時間導出任務,以及返回最近執行時間和全部可執行任務數信息等。調度代理的功能完全由ITaskScheduler接口描述。
?
任務和觸發器
?????????任務由IScheduledWorkItem和ITask接口描述,而且接口輸出了與在快捷方式中看到的完全不同的方法。你可以使用諸如應用名、工作目錄、參數、優先級、最大允許執行時間,以及——最重要的——觸發器等信息設置任務。ITask從IScheduledWorkItem中導出,是這個接口的一個更特殊的版本。在將來可以定義很多不同類型的工作項,但是現在它僅僅支持任務項。任務可以是32位或16位的應用,OS2或MS-DOS應用,批處理文件(*.bat),命令文件(*.cmd),或注冊的任何文件類型的處理器應用。
????觸發器是用于辨識正確時段以運行工作項的事件。在許多情況下,觸發器是一個唯一的時間,如"12:00:00?1998/12/3"。另一些情況下,它是可重復的,如"6:00:00?在每個月的第三個星期一"。用ITaskTrigger接口和TASK_TRIGGER結構操作觸發器,它定義了任務的開始時間、重復頻率和參數。
????任務調度所涉及的所有相關接口和結構在Internet客戶SDK中都有完整的資料說明。你可以參考這個幫助文檔獲得更詳細的信息和示例。
?
我的公文包
?????????‘我的公文包’是Windows95上的一個實用程序,設計用于幫助用戶在不同計算機上維護相同文檔的多份拷貝。一旦你把文檔放入‘我的公文包’文件夾,這個軟件將注意保持公文包中的備份與初始文檔同步。當你使用桌面PC和筆記本電腦協同工作時,這個同步是有用的。通常把桌面機器中的文檔作為初始文檔,并可以用來自筆記本電腦修改過的版本替換這個文檔,只需在‘我的公文包’中點擊這個文件名,它就可以檢查和同步這個備份:
???????????????????
?
?
如果在上一次同步操作后有一個文件已經改變,Windows自動用修改過的備份替換未修改的文件。如果兩個文件都有改變,就發生合并操作。合并操作涉及到要實現某些接口來專門處理文檔的合并:
????????????????
?
實現這些接口的對象稱為協調者(reconcilers),用來確定相同文件的兩個版本文檔是否要并列。如果兩個文檔都有改變,這個模塊可以提供合并內容產生新的備份。也可以根據需要進行交互操作,和放棄殘余的文件。精確的合并操作依賴于協調者的特殊實現。
?????????關于協調者的詳細信息可以在Internet客戶SDK資料中找到。
?
零碎對象
你是否從未從微軟的Word文檔中選擇一段文字,然后移動或拷貝它到Windows的桌面上。這樣做時,鼠標的光標變成鼓勵操作的矢量而不是禁止符號。也就是說,你可以直接拖動Word文檔的文字段到桌面(或其它Windows文件夾)。此時,你建立了一個零碎對象:
???????????????????????????????????
?
?
基本上你所看到的是對象的一個連接,零碎文檔是一個具有.shs擴展名的文件,它在IDataObject實現的對象被拖動到文件夾上或桌面時自動建立。要讀出零碎文檔的內容有一個由shscrap.dll輸出的rundll32兼容的函數。這個庫在‘System’目錄下,是沒有正規資料說明的,這是因為零碎文檔的支持應該自動由建立它的完全OLE應用所提供。然而,如果你有興趣測試,shscrap.dll輸出的函數OpenScrap_RunDLL()可以用于打開任何.shs文件。
?
新快捷方式處理器
?????????到目前為止在這本書中我們討論了一定數量的課題,也討論了在應用中集成特殊特征的方法。然而在這一章中,我們開始探索Shell的組成部分,主要集中在文件夾和命令行方面。現在,我們打算介紹一個有意義的例子,它使用了我們解釋過的許多基本課題(快捷方式,圖標,和文件夾),以及未來將要涉及的Shell客戶化技術。我們打算組成一個類似于Windows標準部件的工具,然后探索怎樣用我們自己的工具替換Windows標準部件。
?????????如果試圖通過右擊在桌面或任何探測器文件夾上建立一個快捷方式,或通過選擇‘文件?|?新文件?|?快捷方式’菜單建立快捷方式,則出現一個建立大師,它允許你指定目標對象和要建立的文件名。.lnk文件的目標文件夾總是設定在進程啟動的文件夾上。不能指定描述文字,熱鍵,甚至圖標,也不能決定在哪里建立這個快捷方式。
????????????????
?
?
為了繞過這些限制,我們打算構建我們自己的快捷方式建立器,更進一步,我們還打算用它來取代Windows標準的快捷方式大師工具。用這種方法,每次右擊建立新的快捷方式時,無論在Shell的任何地方,我們的應用都替代標準對話框彈出。
?
用戶界面
?????????這個建立快捷方式的新大師將提供全部目標文件對象的輸入,如,描述,熱鍵和圖標。此外還允許用戶選擇路經和最終快捷方式的名。路經可以用絕對驅動器和目錄方式表示,也可以用指定文件夾的ID方式表示,例如,‘桌面’,‘發送到’,‘程序’,‘開始菜單’等。
??????????
?
?
上面截圖顯示了這個應用的用戶界面。頭一個編輯框包含有目標文件名,這可以瀏覽選擇。第二個編輯框是快捷方式描述文字,而第三個指定用于喚醒快捷方式的組合鍵。
?????????下一個區域可以選擇快捷方式相關的圖標,既可以選擇源文件(使用‘選擇圖標路經’按鈕)也可以選擇文件內圖標的索引,我們使用SHBrowseForIcon()函數(在第9章中定義的)實現這個功能。在‘Save as’區域,可以鍵入路徑名或從下拉框選取預定義項,預定義項是特殊文件夾,如‘桌面’,‘發送到’等。右手邊的編輯框包含了快捷方式文件名,沒有.lnk擴展名。最后,要建立快捷方式還需要按‘建立’按鈕來喚醒SHCreateShortcutEx()函數,這是在第6章中定義的函數。
????到此,我們的應用是基于AppWizard生成的對話框應用——我們稱之為NewLink。當從Shell調用這個程序時(我們將簡短地說明怎樣做),它接收一個臨時文件名,這是Shell在喚醒這個大師時自動建立的。這個名字被傳遞給WinMain()函數的lpsz變量,由于我們對它不感興趣,因此,第一個活動就是刪除這個文件。而后我們卻使用這個名字作為輸出的.lnk文件。
?
舊函數
?????????完成對話框界面布局之后,下面我們開始編碼WinMain()函數,刪除臨時文件,但是保留名字為以后使用:
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevious,LPTSTR lpsz, int iCmd)
{
//?刪除任何由Shell建立的臨時文件
if(lstrlen(lpsz))
DeleteFile(lpsz);
//?保留全程數據
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()函數
CoInitialize(NULL);
//?運行主對話框
BOOL b = DialogBox(hInstance, "DLG_MAIN", NULL, APP_DlgProc);
CoUninitialize()
//?退出
DestroyIcon(g_hIconLarge);
DestroyIcon(g_hIconSmall);
return b;
}
對話框過程調用所有新按鈕的處理器:
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;
后三個是預定義的函數,OnInitDialog()初始化所有控件:
void OnInitDialog(HWND hDlg)
{
//?設置圖標(T/F?大/小圖標)
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");
}
開始的活動是使用特殊文件夾名充填‘Save As’中的下拉框內容,這些特殊文件夾可以接收我們建立的快捷方式文件。為了使進一步的處理容易些,我們為每一個串關聯了一些項數據。接下來的工作是初始化熱鍵控件。加一個前綴Ctrl-Alt,對于快捷方式這是必須的。然后設置目標串和圖標索引的默認值,使用命令行上傳遞來的文件名作為連接名的基礎。
?
新函數
?????????第二個處理器函數,也是我們加到應用中的第一個新函數,在對話框含有的三個瀏覽按鈕中的任何一個被按下時被調用,第二個變量用于區分這三個按鈕:
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);
//?默認顯示頭一個圖標
HICON hIcon = ExtractIcon(GetModuleHandle(NULL), ofn.lpstrFile, 0);
SendDlgItemMessage(hDlg, IDI_ICON, STM_SETICON,
reinterpret_cast<WPARAM>(hIcon), 0);
}
如果正在瀏覽路經,我們可以使用SHBrowseForFolder() API函數使用戶找到路經并將其顯示在適當的編輯控件上,然后返回。如果在瀏覽文件,我們填寫適合于要查找文件類型的濾波串,然后調用GetOpenFileName(),把文件名顯示在適當的編輯控件上,并且把文件的第一個圖標作為默認圖標顯示。
?????????‘選擇圖標路經’按鈕的處理器簡單地使用SHBrowseForIcon()函數,它是第9章從文件中選擇圖標的函數:
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);
}
}
在獲得了所有數據之后,這個應用的主要工作就是‘建立’按鈕的處理器:
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));
//?取得目標和描述
GetDlgItemText(hDlg, IDC_TARGET, szTarget, MAX_PATH);
GetDlgItemText( hDlg, IDC_DESCRIPTION, szDesc, MAX_PATH);
ss.pszTarget = szTarget;
ss.pszDesc = szDesc;
//?取得圖標
TCHAR szIcon[MAX_PATH] = {0};
GetDlgItemText(hDlg, IDC_ICONPATH, szIcon, MAX_PATH);
ss.pszIconPath = szIcon;
ss.wIconIndex = 0;
//?確定快捷方式文件名
//?取得目標文件夾和最后的反斜杠
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);
}
這里做的所有事情就是收集屏幕上各個控件的信息,封裝信息到SHORTCUTSTRUCT結構,然后調用SHCreateShortCutEx()做實際建立的操作。(這里所涉及的函數和結構在第6章中已經定義了)。要編譯這段代碼,你需要添加通常的頭文件和連接庫,需要#includes?resource.h,?shlobj.h commdlg.h,?以及連接ole32.lib?和comdlg32.lib.庫。
?
怎樣替換Windows的大師
?????????如果能用我們的應用替換掉Windows標準的建立快捷方式大師程序就好了。事實上這并不困難,就像我們期望的,關鍵在于注冊表。快捷方式是.lnk類型的文件,所以頭一個要查看的地方應該是:
HKEY_CLASSES_ROOT
/.lnk
在這個鍵下,我們發現了ShellNew鍵。在從Shell建立一個給定類型的新文件時——即,通過‘新文件’菜單——探測器總是搜索文件類子樹的ShellNew鍵。進入這個鍵,‘命令’的值顯示了命令行,你應該看到,它設置為:
runDLL32 AppWiz.Cpl,NewLinkHere %2
?
???
?
?
注意,使用rundll32.exe來運行DLL函數作為命令行指令。替換標準的大師所有做的全部工作就是改變‘命令’的值,使之執行我們的程序,如下:
c:/Utility/NewLink/NewLink.exe %2
注意最后的%2是使命令行正常工作的基礎——刪除或置換都將使對話框消失。當你從桌面選擇建立新快捷方式時,將會看到我們的對話框出現。
?????????NewLink.exe程序感知和使用由Shell作為變量傳遞的文件名——在喚醒快捷方式建立器之前,Shell總是建立一個空文件并把名字傳遞給程序,然而對于我們,這個文件名的處理不是問題。
?
編輯注冊表
?????????替換默認的快捷方式大師需要編輯注冊表,這可以通過注冊表編輯器手動完成,或使用腳本文件編程實現。例如,你可以恢復初始狀態,簡單的把初始值賦給‘命令’實體就可以了:
; restore.reg
REGEDIT4
[HKEY_CLASSES_ROOT/.lnk/ShellNew]
"Command" = "RunDLL32 appwiz.cpl,NewLinkHere %2"
用相同的方法,你可以用腳本來安裝處理器到同一個地方:
; replace.reg
REGEDIT4
[HKEY_CLASSES_ROOT/.lnk/ShellNew]
"Command" = "c://utility//newlink//newlink.exe %2"
一定要保證在.reg腳本中輸入路徑時總是使用雙斜線,還要記住例子中置換的路徑應該是newlink.exe文件所在的路徑。
?
小結
?????????我們第一次Windows Shell的旅行到這里就結束了。我們已經視察了探測器的命令行,并且發現了一個有趣的實用程序rundll32.exe,這個程序允許我們以命令行的形式使用DLL函數。在討論rundll32.exe特征期間,我們還探索了怎樣編程訪問系統對話框,其編程接口是沒有說明資料的,例如‘添加打印機大師’,‘添加新硬件’,‘建立新連接’和‘打開文件’。
這一章的第二部分討論了某些特殊的虛擬文件夾,它們實現為Shell對象,例如‘打印機’,‘撥號網絡’‘任務調度’和‘我的公文包’。我們給出了這些項目的概覽,和進一步資料的來源。最后,我們給出了結合許多曾經討論過的科目的一個例子。這個快捷方式的處理器還初步說明了我們將在第14章討論的科目。概括地講,這一章包含了:
?????????探測器命令行
?????????RunDLL32編程接口
?????????訪問無資料函數顯示系統對話框
?????????回顧某些虛擬文件夾如‘打印機’和‘我的公文包’
?????????零碎對象
?????????怎樣編寫和安裝新的,客戶建立快捷方式的模塊。
在下兩章中我們繼續探索和發掘兩個方面的有用技術,一是腳本Shell對象,這使你能編程訪問任何Shell特征,從對話框到文件夾,從窗口到快捷方式。此后我們聚焦于非常有前途的子系統——Windows腳本環境(WSH),這使DOS批處理文件的概念被帶到了Windows中。
轉載于:https://www.cnblogs.com/songtzu/p/3239842.html
總結
以上是生活随笔為你收集整理的[转]Windows Shell 编程 第十一章 【来源:http://blog.csdn.net/wangqiulin123456/article/details/7987992】...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SQL Server 迁移数据到MySQ
- 下一篇: linux动态链接库---一篇讲尽