ZZ From: http://blog.csdn.net/tomcui/archive/2008/01/22/2058502.aspx
Windows 控制台編程
說明:本文章為本人在做項目時查閱相關文章而寫成,如有不當之處,請指出。
mail: tomcui60000520@163.com
關鍵字:
控制台,控制台應用程式,
一、 什麼是控制台
開啟Windows的控制台會看到類似的映像
圖一
雙擊其中的一個表徵圖,會顯示對話方塊,讓使用者來完成相應的軟硬體設定工作。這就是我們看到的控制台。那麼如何開發控制台程式呢?帶著疑問在MSDN和google裡搜尋索引鍵“Control Panel”,就會找到相關的技術文章。這是我工作的方法:借鑒已有的資源。但實際情況是那樣嗎?我們可以跟著MSDN的講述來一步一步深入下去。
經過挖掘,發現並不是exe檔案(Windows Vista下支援exe的控制台應用程式,並且微軟建議做成exe檔案),而是有著cpl尾碼名的檔案,在windows->system32下可以找到這樣的檔案。如果藉助工具,Dependency Walker for Win32 (x86) 或dumpbin等就可以看到該檔案匯出了一些函數。
圖二
多觀察幾個這樣的檔案,發現匯出的函數雖有差異,但其中都有CPLApplet函數被匯出。這些特徵與DLL的特徵吻合。去MSDN上查閱CPLApplet函數的說明證明我們的猜測是正確的。可以說控制台應該程式就是以CPL為尾碼名並且一定要匯出CPLApplet函數的dll檔案。
對於具體的描述可以參考:
http://msdn2.microsoft.com/en-us/library/bb776838(VS.85).aspx
二、 明確幾個概念
l 控制台管理程式:用於管理控制台的程式,在案頭windows版本是CONTROL.EXE,在windows CE版本是CTLPNL.EXE,它們負責管理控制台裡的控制台條目。簡單的說,我們開啟控制台時,這些管理程式就在運行了。只不過我們看到的是掛上了Shell外觀而已(註:這是我的猜測,還沒有找到依據)。
l 控制台條目(Control Panel Item):在控制台裡看到的每個表徵圖所對應的就是一個控制台條目。
l 控制台應用程式(Control Panel Application):就是最終看到的CPL檔案,一個控制台應用程式可以實現幾個控制台條目。
三、 控制台應用程式的編寫
編寫控制台應用程式,就是編寫dll檔案,在該檔案中實現控制所需要的功能。這就涉及到一個不得不說的函數,沒有它就無法完成控制台程式的實現。該函數為CPLApplet。下面就該函數的參數等知識做些介紹。
函數:LONG CPLApplet(HWND hwndCPl,UINT msg, LPARAM lParam1, LPARAM lParam2)
函數CPLApplet是控制台應用程式(Control Panel application)的進入點,它被控制台管理程式(control.exe 或Ctlpnl.exe)自動調用,它是個回呼函數(Callback),注意:CPL檔案一定要把函數CPLApplet匯出,這樣控制台才能找到程式的進入點。
當啟動控制台時,它會搜尋Windows或System32或註冊表的相應條目目錄下的檔案,並把以CPL作為副檔名的檔案載入,它調用CPL檔案的匯出函數CPLApplet(),發送訊息給該函數。所以,控制台應用程式要處理控制台發送過來的訊息,即在函數CPLApplet中進行處理,該函數沒有預設的行為。如果一個CPL檔案中實現了多個控制台程式,那麼只會有一個CPLApplet函數,它負責所有的控制台應用程式。
參數說明:
hwndCPl:控制台管理程式或稱為控制台的視窗控制代碼,即為control.exe的視窗控制代碼。如果控制台應用程式或其它視窗需要傳遞父視窗控制代碼,可以使用該參數。
Msg:發送到控制台應用程式的訊息,由控制台管理程式發送。
lParam1:訊息參數
lParam2:訊息參數
函數的傳回值依據訊息的不同而不同。
應用程式要使用該函數需要包含標頭檔:cpl.h
訊息名稱
描述
CPL_INIT
控制台應用程式收到的第一個訊息,通常在此處理全域初始化和記憶體配置。成功返回非0,否則返回0,此時控制台管理程式終止和該應用程式的通訊,並釋放相應的CPL檔案。
CPL_GETCOUNT
該訊息緊接在CPL_INIT訊息之後被發送,它返回控制台管理程式所能看到該CPL檔案中所包含的控制台組件的數目,即該CPL檔案可以出現在控制台中的表徵圖的數目。
CPL_INQUIRE
於CPL_GETCOUNT之後被發送,為指定的控制台條目提供資訊。
CPL_NEWINQUIRE
於CPL_GETCOUNT之後被發送,與訊息CPL_INQUIRE完成的功能類似,只不過其實現要求TNewCPLInfo結構指標,所包含的資源不提供緩衝,所以控制台啟動的較慢,一般不建議處理該訊息,除非特別必要,如要根據一定的條件動態改變控制台條目的表徵圖、字串等。
CPL_DBLCLK
表明使用者選定了一個控制台條目,程式應該顯示相應的對話方塊以便使用者完成相應的任務。成功返回0,否則,返回非0.
CPL_STOP
控制台管理程式關閉時被發送,控制台應用程式在此時處理記憶體釋放等動作。成功處理,返回0.
CPL_SELECT
目前不被使用。只有Windows 95 和Microsoft Windows NT 4.0之前的系統使用。
CPL_STARTWPARMS
該訊息與CPL_DBLCLK類似,但lParam2指向LPCTSTR,該訊息在shell32.dll version 5.0 (Windows 2000 ,Windows Millennium Edition (Windows Me))及以後版本有效
CPL_EXIT
於CPL_STOP訊息之後被發送,這是控制台應用程式在釋放資源的最後機會。成功處理返回0.
CPL_INQUIRE:lParam1是以0為起點的整數,它是該CPL檔案中所包含的控制台條目的索引,lParam2參數要求一個CPLINFO結構的指標,用來填充所需的表徵圖、字串等資訊。如果成功處理了該訊息,應該返回0。
CPL_NEWINQUIRE:該訊息與CPL_INQUIRE都是CPL_GETCOUNT之後被發送的訊息,但並沒有明確的先後順序。所以程式裡不要依賴它們的順序來處理不同的事務。
編寫控制台應用程式的步驟:
1 選擇適當的開發工具(如:Visual Studio 2008),建立DLL項目;
2 匯出函數CPLApplet;
3 在函數CPLApplet的訊息處理過程中完成你需要的工作;
一個簡單的例子
開發工具:Microsoft Visual Studio 2008
作業系統:Windows XP SP2
步驟:
1 建立Win32 Project,工程名為CPLTest;
2 應用程式類型選擇DLL(CPL檔案本質上是DLL);
3 在項目中新增或匯入一個表徵圖檔案和兩個字串資源,用於在控制台管理程式中顯示表徵圖和提示;
在Resource Files 上右鍵選擇Add->Resource,然後選擇Icon或String Table
以下為resource.h 的部分內容
#define IDI_ICON1 101 //表徵圖標識
#define IDS_STRING102 102 //字串tom
#define IDS_STRING103 103//字串cui
4 在dllmain.cpp檔案中增加函數的匯出CPLApplet;
extern "C" __declspec(dllexport) LONG APIENTRY CPlApplet(HWND hwndCPL, UINT uMsg, LPARAM lParam1, LPARAM lParam2);
原則上可以按照上面的方式匯出就可以了,但是請注意CPlApplet的調用方式是APIENTRY,通過這樣方式匯出的函數會被改名,通過多次實驗也不可行。你可能會上去掉APIENTRY,但這樣編出來的CPL檔案無法運行,查閱了相關文檔,在Windows Mobile Version 5.0 SDK的文檔裡指明了該函數的調用方式,windows CE 5.0 和Windows Shell and Controls沒有指明這種調用方式。所以,只有加上APIENTRY。
現在的問題是如何匯出該函數?看來要通過DEF檔案了,如果你的項目裡沒有產生DEF檔案,可以通過Project->Properties->Linker->Module Definition File來指定或自己用記事本建立這樣的檔案,輸入如下內容。
; CPLTest.def : Declares the module parameters for the DLL.
LIBRARY "CPLTest"
EXPORTS
; Explicit exports can go here
CPlApplet
5 在dllmain.cpp檔案中增加函數CPLApplet的訊息處理函數來完成指定的功能;
在dllmain.cpp中包含以上兩個標頭檔
#include "resource.h" //資源標識
#include <Cpl.h> //CPLApplet函數要求的標頭檔
我的例子完成顯示一個MessageBox的功能。
dllmain.cpp的完整代碼:
// dllmain.cpp : Defines the entry point for the DLL application.
#include "stdafx.h"
#include "resource.h"
#include <Cpl.h>
LONG APIENTRY CPlApplet(HWND hwndCPL, UINT uMsg, LPARAM lParam1, LPARAM lParam2);
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
LONG APIENTRY CPlApplet(HWND hwndCPL, UINT uMsg, LPARAM lParam1, LPARAM lParam2)
{
int i;
LPCPLINFO lpCPlInfo;
i = (int) lParam1;
switch (uMsg) {
case CPL_INIT: // first message, sent once
return TRUE;
case CPL_GETCOUNT: // second message, sent once
return 1;
break;
case CPL_INQUIRE: // third message, sent once per application
lpCPlInfo = (LPCPLINFO) lParam2;
lpCPlInfo->lData = 0;
lpCPlInfo->idIcon = IDI_ICON1;
lpCPlInfo->idName = IDS_STRING102;
lpCPlInfo->idInfo = IDS_STRING103;
break;
case CPL_DBLCLK: // application icon double-clicked
MessageBox(NULL, TEXT("Tom66"), TEXT("Cuei666"), MB_OK);
break;
case CPL_STOP: // sent once per application before CPL_EXIT
break;
case CPL_EXIT: // sent once before FreeLibrary is called
break;
default:
break;
}
return 0;
}
6 編譯
Project->Properties->Linker->Output File修改輸出檔案的尾碼名為CPL,也可以不修改,到最後把dll改為cpl也可以的。
四、 控制台應用程式的安裝與運行
l 將cpl檔案拷貝到Windows(Windows CE)或Windows/system32(案頭版本Windows),可以在這裡雙擊運行,也可以開啟控制台就可以看到該CPL檔案所包含的控制台條目,表徵圖和檔案就是你在CPLApplet裡指定的,雙擊也可運行。
2 在命令列下運行rundll32 shell32.dll,Control_RunDLL CPLTest.cpl(CPL檔案名稱)@1(數字指定運行第幾個控制台條目,一個CPL檔案可以包含幾個控制台條目)。在windows CE下,在命令列輸入ctlpnl.exe /windows/cplmain.cpl,5,與案頭版本有些差異。
3 在windows的註冊表[HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Windows/CurrentVersion/Control Panel/Cpls] 下建立字串,並指定cpl所在的完整路徑,然後就可以在控制台裡看到新增加的控制台條目。通過寫註冊表的方式,是一些應用軟體慣用的方式,安裝時可以通過InstallShield等安裝製作工具將其添加到註冊表,卸載時,刪除註冊表中相關的項。
4 通過拷貝的方式,直接刪除相應的CPL檔案就可以了。至於有沒有更好的方式,我還沒有發現。
五、參考資料:
1 http://msdn2.microsoft.com/en-us/library/aa926276.aspx
2 http://msdn2.microsoft.com/en-us/library/bb776392.aspx
Add by myself
通過修改註冊表的方式,來安裝 控制台應用程式(Control panel application),必須在CPlApplet中響應CPL_INQUIRE訊息,並在其中載入表徵圖。(LPCPLINFO->IdIcon),否則開啟控制台時,所安裝應用程式的表徵圖無法載入,無法顯示。
而通過拷貝檔案至System32目錄,則可以響應CPL_INQUIRE或者CPL_NEWINQUIRE,可以在其中的任意一個響應函數中load表徵圖。
本文來自CSDN部落格,轉載請標明出處:http://blog.csdn.net/jtujtujtu/archive/2008/12/12/3503878.aspx