daemon進(jìn)程是后臺(tái)守護(hù)進(jìn)程,有時(shí)候也叫精靈進(jìn)程(agent).linux 下server都是daemon進(jìn)程。相信大部分開發(fā)人員都知道如何去寫一個(gè)daemon進(jìn)程。但是另一方面,大部分人不知道為什么要這么做,不少人是從某個(gè)地方copy一個(gè)函數(shù),拿來(lái)主義。但是具體為什么這么實(shí)現(xiàn),卻不是很透徹。
見過(guò)一些面試官或被面試人。很多人解釋daemon進(jìn)程存在的理由是因?yàn)榻┧肋M(jìn)程。或者輸入輸出。其實(shí)和這些東西一毛錢關(guān)系都沒(méi)有。daemon函數(shù)存在的原因是因?yàn)榭刂平K端由于某些原因(如斷開終端鏈接)會(huì)發(fā)送一些信號(hào)的原因。而接收進(jìn)城處理這些信號(hào)缺省動(dòng)作會(huì)讓進(jìn)程退出。這些信號(hào)會(huì)由于終端上敲一些特殊按鍵而產(chǎn)生。
貼一個(gè)daemon函數(shù)常見的實(shí)現(xiàn):
1 int daemon(void)
2 {
3 pid_t pid = fork();
4
5 if( pid != 0 ) exit(0);//parent
6
7 //first children
8 if(setsid() == -1)
9 {
10 printf("setsid failed\n");
11 assert(0);
12 exit(-1);
13 }
14
15 umask(0);
16
17
18 pid = fork();
19
20 if( pid != 0) exit(0);
21
22 //second children
23 chdir ("/");
24
25 for (int i = 0; i < 3; i++)
26 {
27 close (i);
28 }
29
30
31 int stdfd = open ("/dev/null", O_RDWR);
32 dup2(stdfd, STDOUT_FILENO);
33 dup2(stdfd, STDERR_FILENO);
34
35 return 0;
36 }
37
1 、第一次fork的作用是讓shell 認(rèn)為本條命令 已經(jīng)終止,不用掛在終端輸入上。還有一個(gè)作用是為后面setsid服務(wù)。setsid的調(diào)用者不能是進(jìn)程組組長(zhǎng)(group leader). 此時(shí)父進(jìn)程是進(jìn)程組組長(zhǎng)。
2 、setsid() 是本函數(shù)最重要的一個(gè)調(diào)用。它完成了daemon函數(shù)想要做的大部分事情。調(diào)用完整個(gè)函數(shù)。子進(jìn)程是會(huì)話組長(zhǎng)(sid==pid),也是進(jìn)程組組長(zhǎng)(pgid == pid),并且脫離了原來(lái)控制終端。到了這一步,基本上不管控制終端如何怎么樣。新的進(jìn)程都不會(huì)收到那些信號(hào)。
3 、經(jīng)過(guò)前面2個(gè)步驟,基本想要做的都做了。第2次fork不是必須的。也看到很多開源服務(wù)沒(méi)有fork第二次。fork第二次主要目的是。防止進(jìn)程再次打開一個(gè)控制終端。因?yàn)榇蜷_一個(gè)控制終端的前臺(tái)條件是該進(jìn)程必須是會(huì)話組長(zhǎng)。再fork一次,子進(jìn)程ID != sid(sid是進(jìn)程父進(jìn)程的sid)。所以也無(wú)法打開新的控制終端。
daemon目的就是防止終端產(chǎn)生的一些信號(hào)讓進(jìn)程退出。上面函數(shù)并沒(méi)有直接調(diào)用signal函數(shù)去處理它。而是間接通過(guò)fork和setsid函數(shù)使用更少代碼優(yōu)雅處理。而被有些人誤以為是僵死進(jìn)程的原因需要這樣處理。
當(dāng)然,也有很多程序不是像上面函數(shù)那樣去實(shí)現(xiàn)。而是直接通過(guò)忽略信號(hào)方式處理。這樣其實(shí)也不錯(cuò),因?yàn)檫@些信號(hào)很少會(huì)有用到的價(jià)值。直接忽略基本上不存在誤殺的情況。反正達(dá)到最終目的就可以。條條大路通羅馬。
下面羅列一下控制終端會(huì)產(chǎn)生哪些信號(hào)。程序中只要處理好這些信號(hào),同樣能達(dá)到上面函數(shù)實(shí)現(xiàn)的目的。
//后臺(tái)進(jìn)程讀取/寫入終端輸入產(chǎn)生下面兩個(gè)信號(hào),或者控制終端不存在情況讀取和寫入會(huì)產(chǎn)生
signal(SIGTTOU, SIG_IGN);
signal(SIGTTIN, SIG_IGN);
//按CTRL-C ,CTRL-\ CTRL-Z會(huì)向前臺(tái)進(jìn)程組發(fā)送下面這些信號(hào)
signal(SIGINT, SIG_IGN );
signal(SIGQUIT, SIG_IGN );
signal(SIGTSTP, SIG_IGN );
//終端斷開,會(huì)給會(huì)話組長(zhǎng)或孤兒進(jìn)程組所有成員發(fā)送下面信號(hào)
signal(SIGHUP, SIG_IGN );
還有有些信號(hào)也可以由終端shell產(chǎn)生,需要關(guān)注
signal(SIGCONT, SIG_IGN );
signal(SIGSTOP, SIG_IGN );
上面這些信號(hào),應(yīng)該有些程序缺省處理(SIG_DFL)本身動(dòng)作就是忽略(SIG_IGN),不是退出進(jìn)程。不過(guò)按照上面寫也不會(huì)造成什么問(wèn)題。
摘要:
11.在COM組件中調(diào)用JavaScript函數(shù) 2// 連接點(diǎn)方式頁(yè)面javascript腳本 3<object classid="CLSID:B568F111-DFE4-4944-B67F-0728AB2AB30F" id="testCom" VIEWASTEXT&...
閱讀全文
轉(zhuǎn)自 http://www.shnenglu.com/Zezese/archive/2010/07/25/121247.html?opt=admin
1
2
3
4
5 template <class R, class P1, class P2>
6 class IDelegate
7 {
8 public:
9 virtual R Invoke(P1, P2) = 0;
10 };
11
12
13 template <class T, class R, class P1, class P2>
14 class CDelegate : public IDelegate<R, P1, P2>
15 {
16 protected:
17
18 typedef R (T::*pfnHandle)(P1, P2);
19
20 const pfnHandle m_pfn;
21
22 T* const m_pThis;
23
24 public:
25
26 CDelegate(T* const pThis, const pfnHandle pfn)
27 :m_pThis(pThis), m_pfn(pfn)
28 {
29 if (m_pThis == NULL || m_pfn == NULL)
30 {
31 throw;
32 }
33 }
34
35 virtual R Invoke(P1 p1, P2 p2)
36 {
37 return (m_pThis->*m_pfn)(p1, p2);
38 }
39
40 };
41
42 class CDelegateSource
43 {
44 public:
45 CDelegateSource()
46 : m_lpCallBack(NULL)
47 {
48 }
49
50 void SetCallBack(IDelegate<bool, int, int>* newVal)
51 {
52 m_lpCallBack = newVal;
53 }
54
55 void DoSomething()
56 {
57 for (int i = 0; i < 10; i++)
58 {
59 if (m_lpCallBack != NULL)
60 {
61 m_lpCallBack->Invoke(i, i * i);
62 }
63 }
64 }
65
66 private:
67
68 IDelegate<bool, int, int>* m_lpCallBack;
69
70 };
71
72 class CDelegateTester
73 {
74 private:
75
76 bool OnCallBack(int nParam1, int nParam2)
77 {
78 printf("OnCallBack -> nParam1:%d, nParam2:%d\r\n", nParam1, nParam2);
79
80 return true;
81 }
82
83 CDelegate<CDelegateTester, bool, int, int> m_OnCallBack;
84
85 public:
86
87 CDelegateTester()
88 : m_OnCallBack(this, OnCallBack)
89 {
90 }
91
92 void Execute()
93 {
94 CDelegateSource src;
95 src.SetCallBack(&m_OnCallBack);
96 src.DoSomething();
97 }
98 };
99
100 void main()
101 {
102 CDelegateTester Tester;
103 Tester.Execute();
104
105 getchar();
106 }
107
1
SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST+SHCNF_FLUSH, 0, 0);
轉(zhuǎn)一些從shell32.dll導(dǎo)出的函數(shù)
外殼對(duì)話框
外殼對(duì)話框的秘密
常見的Windows的通用對(duì)話框被封裝在Comdlg32.dll,這給我們的編程提供了很大的便利。但它還不夠完整,我們?cè)谙到y(tǒng)里經(jīng)常能看到大量的可重復(fù)使用的對(duì)話框,但在Windows的文檔里你卻找不到它們的調(diào)用方法。而如果我們自己去做這樣的界面是非常費(fèi)時(shí)費(fèi)力的而且也是沒(méi)有必要的,因?yàn)檫@些對(duì)話框?qū)嶋H上很容易得到。這里我要介紹一些已經(jīng)眾所周知或不為認(rèn)知的對(duì)話框,它們可以應(yīng)用在我們的程序中使程序顯得非常友好和專業(yè)。
瀏覽文件夾對(duì)話框

圖2.23
大多數(shù)Delphi程序員都知道如何使用VCL的TOpenDialog控件來(lái)讓用戶瀏覽將要打開的文件。然而有時(shí)你可能只想讓用戶選擇文件夾而不是特定的文件,windows已經(jīng)提供了一個(gè)這樣的對(duì)話框如圖2.23所示。我們可以通過(guò)公開的函數(shù)SHBrowseForFolder來(lái)調(diào)用 (這個(gè)函數(shù)定義在ShlObj單元),函數(shù)定義如下:
function SHBrowseForFolder(var BrowseInfo: TBrowseInfo): PItemIDList; stdcall;
這個(gè)函數(shù)只有一個(gè)參數(shù),但這個(gè)參數(shù)是一個(gè)比較復(fù)雜的記錄類型
TBrowseInfo = packed record
hwndOwner: HWND;
pidlRoot: PItemIDList;
pszDisplayName: PChar;
lpszTitle: PChar;
ulFlags: UINT;
lpfn: TFNBFFCallBack;
lParam: LPARAM;
iImage: Integer;
end; |
hwndOwner數(shù)據(jù)成員包含對(duì)話框的父窗體的窗口句柄,可以把它設(shè)成0。PIdlRoot數(shù)據(jù)成員指向一個(gè)PIDL的指針對(duì)應(yīng)于對(duì)話框初始化時(shí)的根目錄。指定了PIdlRoot后,就只有根目錄及它的子目錄會(huì)出現(xiàn)在對(duì)話框中。可以設(shè)定它為nil,這時(shí)缺省的根目錄是桌面,pszDisplayName 數(shù)據(jù)成員指向一個(gè)緩沖區(qū)可以用來(lái)儲(chǔ)存被用戶選中的文件名,緩沖區(qū)的大小至少為MAX_PATH 這個(gè)常數(shù)那么大,否則遇到特別長(zhǎng)的文件名會(huì)溢出。lpszTitle 數(shù)據(jù)對(duì)象指向一個(gè)以null結(jié)尾的字符串,字符串作為對(duì)話框的標(biāo)題來(lái)顯示。注意標(biāo)題不要太長(zhǎng),否則顯示時(shí)會(huì)被截?cái)唷lFlags 標(biāo)志數(shù)據(jù)對(duì)象用來(lái)限制在對(duì)話框中顯示的文件夾類型。可以設(shè)定它為0或下列值的組合:
//在對(duì)話框中會(huì)包含一個(gè)狀態(tài)區(qū),回調(diào)函數(shù)可以通過(guò)向?qū)υ捒虬l(fā)送消息來(lái)設(shè)定狀態(tài)
BIF_STATUSTEXT
//只允許選擇標(biāo)準(zhǔn)文件系統(tǒng),若選了非標(biāo)準(zhǔn)的文件夾如打印機(jī),確認(rèn)按鈕會(huì)變灰
BIF_RETURNONLYFSDIRS = $0001;
//不選擇網(wǎng)絡(luò)文件夾
BIF_DONTGOBELOWDOMAIN = $0002;
// 給狀態(tài)條留出空白
BIF_STATUSTEXT = $0004;
// 只選擇文件系統(tǒng)的上級(jí)目錄
BIF_RETURNFSANCESTORS = $0008;
//只選擇計(jì)算機(jī)
BIF_BROWSEFORCOMPUTER = $1000;
//只選擇打印機(jī)
BIF_BROWSEFORPRINTER = $2000;
//包括文件也可以選
BIF_BROWSEINCLUDEFILES = $4000; |
注意:如果你想對(duì)話框顯示lpszTitle里的用戶定制的狀態(tài)條信息,必須包括 BIF_STATUSTEXT標(biāo)識(shí)。Lpfn數(shù)據(jù)對(duì)象是一個(gè)回調(diào)函數(shù)類型的指針,函數(shù)類型如下:
TFNBFFCallBack = function(DialogHandle: HWND;
MessageID: UINT; PIDL: PItemIDList; Data: LPARAM):Integer; stdcall; |
這是一個(gè)回調(diào)函數(shù),可以用來(lái)在同用戶交互時(shí)控制和更新對(duì)話框的顯示。如果你不想控制對(duì)話框,可以把它設(shè)成nil,lParam 數(shù)據(jù)對(duì)象允許你在回調(diào)函數(shù)中以參數(shù)lpfn形式返回一個(gè)指針(通常我們用它來(lái)返回對(duì)象),當(dāng)然也可以把它設(shè)成為0。IImage數(shù)據(jù)成員不需要設(shè)置,因?yàn)樗怯脕?lái)接收系統(tǒng)中同文件夾相關(guān)的圖標(biāo)列表索引的,我們這里設(shè)它為0。
SHBrowseForFolder函數(shù)返回一個(gè)唯一的指向被選擇的文件夾的PIDL。如果文件夾是一個(gè)傳統(tǒng)的文件對(duì)象的話,可以用函數(shù)SHGetPathFromIDList把PIDL轉(zhuǎn)換為真實(shí)的目錄。同時(shí),作為調(diào)用者,必須負(fù)責(zé)釋放被返回的item identifier list,使用IMalloc COM 接口來(lái)釋放。
注意:不要用FreeMem或其他方法來(lái)釋放 PIDL ,這是因?yàn)橥鈿さ膬?nèi)存管理是獨(dú)立的,只能用IMalloc來(lái)釋放。
現(xiàn)在我們已經(jīng)可以顯示對(duì)話框了,那讓我們更深入一步看看如何能夠控制對(duì)用戶動(dòng)作的反應(yīng),這就要用到了回調(diào)函數(shù) TFNBFFCallBack。 注意回調(diào)函數(shù)的意思就是,你只是實(shí)現(xiàn)了它,系統(tǒng)就知道什么時(shí)候去調(diào)用它,就好比一個(gè)守株待兔的例子。
DialogHandle參數(shù)代表對(duì)話框窗口句柄。通常可以用這個(gè)句柄給對(duì)話框發(fā)消息,MessageID 參數(shù)并不是一個(gè)TMessage 結(jié)構(gòu)的記錄,它是對(duì)話框通過(guò)回調(diào)函數(shù)發(fā)給用戶消息的,它可以是下面兩個(gè)值:
BFFM_INITIALIZED = 1; // 對(duì)話框?qū)⒁@示
BFFM_SELCHANGED = 2; // 用戶選中了某項(xiàng) |
PIDL參數(shù)包含其他的額外信息。如果MessageID 是 BFFM_INITIALIZED,PIDL將等于nil。如果MessageID是BFFM_SELCHANGED,PIDL的值將是一個(gè)PIDL 對(duì)應(yīng)于用戶選擇的文件夾。Data 參數(shù)包含用戶付給TbrowseInfo記錄中的Lparam數(shù)據(jù)成員的值,通常可以傳遞一個(gè)對(duì)象指針。下面是一個(gè)簡(jiǎn)單的回調(diào)函數(shù)的例子:
function BrowseForFolderCallback(DialogHandle: HWND;
MessageID: UINT; PIDL: PItemIDList; Data: LPARAM):
Integer;
begin
//響應(yīng)對(duì)話框的通知消息
case (MessageID) of
BFFM_INITIALIZED:
DialogInitialized(DialogHandle, Data);
BFFM_SELCHANGED:
HandleNewSelection(DialogHandle, PIDL, Data);
end;
Result := 0; // 總返回0.
end; |
在回調(diào)函數(shù)里,可以根據(jù)用戶的輸入發(fā)送三個(gè)用戶的消息給對(duì)話框,下面是消息ID:
// 改變對(duì)話框的狀態(tài)信息
BFFM_SETSTATUSTEXT = WM_USER + 100;
//控制確定按鈕失效與否
BFFM_ENABLEOK = WM_USER + 101;
//改變選擇的文件夾
BFFM_SETSELECTION = WM_USER + 102; |
通常,這些消息發(fā)送給對(duì)話框使之根據(jù)用戶的選擇更新顯示,當(dāng)然你也可以發(fā)送其他的消息給對(duì)話框,比如可以發(fā)送WM_SETTEXT消息來(lái)改變對(duì)話框的標(biāo)題。
下面是一個(gè)發(fā)送消息的例子(見表2.11):
PostMessage(DialogHandle, BFFM_SETSELECTION, True, LPARAM(PChar (NewPath)));
表2.11
Message ID |
WParam |
LParam |
BFFM_SETSTATUSTEXT |
沒(méi)有使用 |
一個(gè)指向新的狀態(tài)信息的Pchar |
BFFM_ENABLEOK |
沒(méi)有使用 |
True使得確認(rèn)按鈕有效,F(xiàn)alse無(wú)效 |
BFFM_SETSELECTION |
如果Lparam是路徑則為True,若Lparam是PIDL則為False |
指向被選擇的文件路徑或PIDL的Pchar |
|
另外要提到的是,Delphi也提供了對(duì)這個(gè)函數(shù)的封裝,那就是SelectDirectory函數(shù)。
關(guān)于對(duì)話框
通常我們都要在自己的程序里加上一個(gè)關(guān)于對(duì)話框來(lái)顯示一些版本信息等等,Windows為我們提供了一個(gè)標(biāo)準(zhǔn)的對(duì)話框如圖2.24所示,可以在一定范圍內(nèi)對(duì)它定制,不過(guò)它只適合顯示簡(jiǎn)單的標(biāo)識(shí)和文本(我覺(jué)得用處極小)。我們可以通過(guò)函數(shù)ShellAbout來(lái)調(diào)用它(聲明在ShellAPI單元里),函數(shù)定義如下:
function ShellAbout(Owner: HWND; ApplicationName: PChar;
OtherText: PChar; IconHandle: HICON): Integer; stdcall; |
Owner參數(shù)標(biāo)識(shí)了擁有對(duì)話框的父窗體句柄,通常設(shè)為0,表明沒(méi)有父窗體。ApplicationName 參數(shù)包含對(duì)話框的標(biāo)題,字符串中可以包含“#”字符,它能起到分割符的作用。這種情況下,函數(shù)會(huì)把分割符前的字符串作為標(biāo)題欄,分割符后的部分作為 "Microsoft"字符串后的第一行。OtherText參數(shù)包含了打算顯示在Microsoft 版本和版權(quán)信息后的字符串。IconHandle 參數(shù)標(biāo)識(shí)了打算顯示在對(duì)話框上的圖標(biāo)標(biāo)識(shí),如果設(shè)為0,函數(shù)會(huì)顯示W(wǎng)indows缺省的圖標(biāo)。

圖2.24

圖2.25
格式化對(duì)話框
SHFormatDrive函數(shù)會(huì)顯示一個(gè)格式化對(duì)話框,如圖2.25所示,它是一個(gè)半公開的函數(shù)。但現(xiàn)在它不在微軟的SDK里。然而微軟承認(rèn)它的存在并把它從Shell32.dll里用名字公開聲明,Delphi中的函數(shù)定義如下:
function SHFormatDrive(Owner: HWND; Drive: UINT;
FormatID: UINT; OptionFlags: UINT): DWORD; stdcall; |
Owner參數(shù)標(biāo)識(shí)擁有對(duì)話框的窗體句柄,文檔中推薦不要設(shè)為0,但實(shí)際上好像沒(méi)什么影響。Drive參數(shù)是用來(lái)標(biāo)識(shí)打算格式化的驅(qū)動(dòng)器的數(shù)值,它是以0為底的,從A開始 A:=0,B:=1依此類推。FormatID 參數(shù)允許我們指定一個(gè)格式化的模板,通常情況下,只要賦值為SHFMT_ID_DEFAULT就可以了。OptionFlags 參數(shù)是一個(gè)選項(xiàng)掩碼,來(lái)確定格式化的選項(xiàng)。當(dāng)前有兩個(gè)選項(xiàng):
SHFMT_OPT_FULL = $0001; // 快速格式化
SHFMT_OPT_SYSONLY = $0002; // 復(fù)制系統(tǒng)文件 |
如果函數(shù)調(diào)用失敗,會(huì)返回下列錯(cuò)誤中的一種來(lái)表明錯(cuò)誤原因,錯(cuò)誤常數(shù)如下:
SHFMT_NOFORMAT = $FFFFFFFD; // 驅(qū)動(dòng)器無(wú)法格式化
SHFMT_CANCEL = $FFFFFFFE; //格式化被取消了
SHFMT_ERROR = $FFFFFFFF; //其他錯(cuò)誤 |
Windows NT 和 WideChar
在進(jìn)一步研究未公開的函數(shù)前, 我們必須清楚一點(diǎn),對(duì)于未公開的函數(shù)來(lái)說(shuō)以null結(jié)尾的字符串類型參數(shù)大多數(shù)被聲明為類型指針而不是PChar。這有點(diǎn)像陷阱,但必須承認(rèn)這是事實(shí)。在Win 9X上所有的字符串類型參數(shù)聲明為PAnsiChar,而在Windows NT上被聲明為PWideChar。如果你想你的應(yīng)用程序適應(yīng)所有平臺(tái),你必須考慮兩種情況,在運(yùn)行時(shí)要判斷平臺(tái)類型,這是很討厭的,但這也是使用未公開的API的代價(jià)。
選擇圖標(biāo)對(duì)話框

圖2.26
我們要討論的第一個(gè)完全未公開的函數(shù)是PickIconDlg。如圖2.26所示這個(gè)函數(shù)會(huì)顯示一個(gè)對(duì)話框,用戶可以用來(lái)從文件中選擇一個(gè)圖標(biāo)資源。它通常是用文件類型編輯器來(lái)關(guān)聯(lián)圖標(biāo)和某一文件類型的,也會(huì)在快捷方式對(duì)話框中被調(diào)用來(lái)修改快捷方式的圖標(biāo)。這個(gè)函數(shù)從Shell32.dll 用值62來(lái)公開出來(lái),函數(shù)定義如下:
function PickIconDlg(Owner: HWND; FileName: Pointer;
MaxFileNameChars: DWORD; var IconIndex: DWORD):LongBool; stdcall; |
Owner參數(shù)和上面的意義類似。FileName 參數(shù)指向一個(gè)緩沖區(qū),包含了被瀏覽圖標(biāo)的文件名,緩沖區(qū)要不小于MAX_PATH+1。MaxFileNameChars 指定字符數(shù)量大小。IconIndex 常數(shù)是以0為底的圖標(biāo)索引,當(dāng)對(duì)話框打開時(shí)會(huì)把焦點(diǎn)定在IconIndex對(duì)應(yīng)的圖標(biāo)上,函數(shù)返回后,IconIndex指向最后被選的圖標(biāo)索引。如果用戶點(diǎn)了取消按鈕,函數(shù)返回False。
運(yùn)行程序?qū)υ捒?/strong>

圖2.27
RunFileDlg函數(shù)是相當(dāng)靈活的,如圖2.27所示就是調(diào)用開始菜單的運(yùn)行子菜單后會(huì)顯示的對(duì)話框,我們通過(guò)值61把它從Shell32.dll暴露出來(lái)。下面是函數(shù)聲明:
procedure RunFileDlg(Owner: HWND; IconHandle: HICON;
WorkPath: Pointer; Caption: Pointer; Description: Pointer; Flags: UINT); stdcall; |
Owner參數(shù)就不用再說(shuō)了。IconHandle參數(shù)是顯示在對(duì)話框上的圖標(biāo)句柄,如果為nil,缺省的icon將會(huì)使用。WorkPath 參數(shù)指向一個(gè)字符串來(lái)指定應(yīng)用程序運(yùn)行的工作路徑。 Title 參數(shù)指向作為對(duì)話框標(biāo)題的字符串,如果為nil,就使用缺省的標(biāo)題。Description 參數(shù)指向一個(gè)描述字符串,主要是告訴用戶如何去做,可以設(shè)為nil,這時(shí)使用缺省的描述。 Flags參數(shù)用一組位掩碼來(lái)設(shè)定對(duì)話框的屬性。下面是定義:
RFF_NOBROWSE = $01; // 移去瀏覽按鈕
RFF_NODEFAULT = $02; // 無(wú)缺省的選項(xiàng)
RFF_CALCDIRECTORY = $04; // 由文件名確定工作路徑
RFF_NOLABEL = $08; // 去掉編輯框標(biāo)簽
RFF_NOSEPARATEMEM = $20; // 去掉在單獨(dú)的內(nèi)存空間運(yùn)行的復(fù)選框 (只對(duì)NT有效) |
這個(gè)對(duì)話框一個(gè)很好的特性是允許你控制用戶可以運(yùn)行的應(yīng)用程序。當(dāng)用戶選擇了確認(rèn)按鈕,對(duì)話框的父窗體會(huì)發(fā)送一個(gè)通知消息來(lái)傳遞將要運(yùn)行的程序信息。通知消息是一個(gè) WM_NOTIFY消息,它的通知代碼設(shè)定為RFN_VALIDATE (-510),然后lParam指向一個(gè) TNM_RunFileDlg記錄。定義如下:
TNM_RunFileDlg = packed record
hdr: TNMHdr;
lpFile: Pointer;
lpDirectory: Pointer;
nShow: LongBool;
end; |
hdr數(shù)據(jù)對(duì)象是TNMHdr類型,它是一種標(biāo)準(zhǔn)的Windows數(shù)據(jù)類型,每個(gè)WM_NOTIFY消息的lParam參數(shù)都會(huì)指向這個(gè)數(shù)據(jù)成分。同時(shí)根據(jù)不同的消息類型,可能一些額外的數(shù)據(jù)跟在記錄后面,標(biāo)準(zhǔn)的TNMHdr記錄定義如下:
TNMHdr = packed record
hwndFrom: HWND;
idFrom: UINT;
code: UINT;
end; |
記錄中的hwndFrom包含發(fā)送消息的窗口句柄,idFrom則包含發(fā)送消息的控件標(biāo)示符,code 中包含標(biāo)識(shí)被發(fā)送的消息的通知代碼。
在TNMHdr記錄后被打包的額外數(shù)據(jù)包含三個(gè)數(shù)據(jù)成分:LpFile指向一個(gè)包含將要運(yùn)行的文件的路徑字符串;LpDirectory指向正在運(yùn)行程序的工作目錄字符串;最后,nShow 用來(lái)指定將要運(yùn)行的應(yīng)用程序是否可見。
對(duì)于本文中特定的消息,只對(duì)TNMHdr記錄中的Code感興趣,通過(guò)檢驗(yàn)Code可以確保我們收到一個(gè)運(yùn)行文件校驗(yàn)消息,同時(shí)使我們可以存取額外的TNM_RunFileDlg數(shù)據(jù)成員。當(dāng)TNMHdr記錄中的code等于RFN_VALIDATE(-510)時(shí),可以獲得一個(gè)TNM_RunFileDlg記錄。下面是校驗(yàn)消息的代碼:
var
FileToRun: String;
...
if TheMessage.Msg = WM_NOTIFY then
if PNMHdr(TheMessage.LParam).code = RFN_VALIDATE then
WideCharToStrVar(PNM_RUNFILEDLG(
TheMessage.LParam).lpFile, FileToRun);
... |
注意只有當(dāng)我們已經(jīng)檢驗(yàn)TNMHdr的Code為RFN_VALIDATE后,才映射LParam 參數(shù)為PNM_RunFileDlg類型。
通知消息的返回值決定了應(yīng)用程序是否能夠運(yùn)行,下面是可能的值:
RF_OK = $00; //允許程序運(yùn)行
RF_CANCEL = $01; //取消操作,關(guān)閉對(duì)話框
RF_RETRY = $02; //取消操作,對(duì)話框仍然打開 |
查找文件對(duì)話框

圖2.28
調(diào)用查找文件對(duì)話框的函數(shù)是SHFindFiles,對(duì)話框如圖2.28所示。它是從Shell32.dll按索引值90公開出來(lái)的:
function SHFindFiles(SearchRoot: PItemIDList;
SavedSearchFile: PItemIDList): LongBool; stdcall; |
SearchRoot 參數(shù)允許從一個(gè)特定的文件夾開始查找,同在資源管理器中在文件夾上用右鍵點(diǎn)擊查找菜單的效果是一樣的。如果設(shè)為nil,那么查找是從桌面開始的。 SavedSearchFile 參數(shù)讓你指定一個(gè)以前查詢保存的查找策略文件(*.fnd文件),根據(jù)以前的設(shè)定來(lái)查找,若不需要的話可以設(shè)定為nil。如果你指定了一個(gè)非空值的SearchRoot PIDL,那么在調(diào)用完SHFindFiles后必須負(fù)責(zé)釋放掉。但是有點(diǎn)奇怪的是,如果你指定了一個(gè)非空的SavedSearchFile PIDL參數(shù),函數(shù)成功調(diào)用的話,你不能去釋放這個(gè) PIDL,否則會(huì)出錯(cuò),但如果調(diào)用失敗了的話,你必須釋放它。
同大多數(shù)對(duì)話框函數(shù)不一樣,這個(gè)函數(shù)是非模態(tài)的,也就是系統(tǒng)在另外一個(gè)獨(dú)立的線程中啟動(dòng)對(duì)話框,然后立即返回,對(duì)話框會(huì)在你的程序結(jié)束后自動(dòng)關(guān)閉。也就是說(shuō)你沒(méi)有任何直接的方法來(lái)告訴用戶如何使用查找到的結(jié)果,所以要想知道用戶找到的文件的話,最好是讓你的程序支持文件拖放,以便讓用戶把找到的文件拖放給你。
查找電腦對(duì)話框
同SHFindFiles比較接近的一個(gè)函數(shù)是SHFindComputer,這個(gè)函數(shù)調(diào)用的結(jié)果同開始菜單上查找電腦菜單調(diào)用的結(jié)果是一樣的。它的參數(shù)同SHFindFiles完全一樣,不同之處在于它完全忽略傳遞給它的參數(shù),很顯然是保留起來(lái)為了將來(lái)擴(kuò)展的需要。這里我們只要把參數(shù)都設(shè)成nil就可以了,另外注意這個(gè)對(duì)話框也是非模態(tài)的。 SHFindComputer 是從Shell32.dll 以索引號(hào)91公開出來(lái)的:
function SHFindComputer(Reserved1: PItemIDList;
Reserved2: PItemIDList): LongBool; stdcall; |
查找文件對(duì)話框
通過(guò)調(diào)用GetFileNameFromBrowse函數(shù)可以調(diào)出這個(gè)對(duì)話框,不過(guò)說(shuō)實(shí)在的,它實(shí)際上只是GetOpenFileName 函數(shù)的簡(jiǎn)單封裝。而我們常用的TOpenDialog控件也是對(duì)GetOpenFileName 函數(shù)封裝,這個(gè)函數(shù)我們很少會(huì)去直接用它。不過(guò)還是寫出來(lái)吧,它是從Shell32.dll里按索引值63公開出來(lái)的:
function GetFileNameFromBrowse(Owner: HWND;
FileName: Pointer; MaxFileNameChars: DWORD;
InitialDirectory: Pointer; DefaultExtension: Pointer;
Filter: Pointer; Caption: Pointer): LongBool; stdcall; |

圖2.29
大多數(shù)參數(shù)對(duì)應(yīng)于OPENFILENAME 結(jié)構(gòu)的成員。Owner參數(shù)我想就不用再重復(fù)了, FileName 參數(shù)指向一個(gè)初始化對(duì)話框編輯控制文件名的緩沖區(qū),函數(shù)返回后FileName包含被選擇的文件路徑,它的大小一般設(shè)成MAX_PATH+1那么大。MaxFileNameChars 參數(shù)用來(lái)指定FileName緩沖區(qū)的大小。 InitialDirectory參數(shù)指向?qū)υ捒虺跏蓟哪夸浢绻鸉ileName參數(shù)被指定了,InitialDirectory就會(huì)被忽略而使用FileName參數(shù)中的路徑。DefaultExtension參數(shù)指向一個(gè)包含要搜索的缺省擴(kuò)展名的字符串。Filter參數(shù)指向一個(gè)以null結(jié)尾的可以用來(lái)在下拉列表中限定文件類型的過(guò)濾字符串。Caption參數(shù)指向?qū)υ捒驑?biāo)題字符串。
如果用戶選擇了一個(gè)要打開的文件,函數(shù)返回True,當(dāng)有錯(cuò)誤發(fā)生,用戶選擇取消按鈕或關(guān)閉對(duì)話框的話會(huì)返回False。
外殼對(duì)象屬性對(duì)話框
另一個(gè)未公開的對(duì)話框函數(shù)是SHObjectProperties,它可以用來(lái)顯示外殼對(duì)象的屬性,比如驅(qū)動(dòng)器、文件夾或文件等,運(yùn)行效果如圖2.29所示。函數(shù)可以從Shell32.dll中按索引值178公開出來(lái),定義如下:
function SHObjectProperties(Owner: HWND; Flags: UINT;
ObjectName: Pointer; InitialTabName: Pointer):LongBool; stdcall; |
Flags參數(shù)用來(lái)指定ObjectName參數(shù)對(duì)應(yīng)對(duì)象的類型,它可以是下列標(biāo)識(shí):
//打印機(jī)
OPF_PRINTERNAME = $01;
//路徑
OPF_PATHNAME = $02; |
ObjectName參數(shù)指向一個(gè)包含路徑名的字符串或是要顯示屬性的打印機(jī)名。如果打印機(jī)是本地的,可以使用實(shí)際的打印機(jī)名,如果是網(wǎng)絡(luò)打印機(jī),就需要使用完整的UNC樣式名稱,比如\\COMPUTERNAME\PRINTERNAME。InitialTabName參數(shù)指向一個(gè)屬性對(duì)話框中頁(yè)面名稱字符串,用來(lái)指定要顯示的缺省頁(yè)面。如果InitialTabName參數(shù)為nil,或不匹配任何頁(yè)面的名稱,第一個(gè)屬性頁(yè)面將會(huì)被顯示。
如果函數(shù)調(diào)用成功會(huì)返回True,如果失敗會(huì)返回False。要想獲得擴(kuò)展的錯(cuò)誤信息,可以調(diào)用API函數(shù)GetLastError。要注意的是這個(gè)對(duì)話框是非模態(tài)的,類似于查找文件對(duì)話框,所以函數(shù)一被調(diào)用,就肯定會(huì)顯示一個(gè)對(duì)話框,同時(shí)我們沒(méi)有辦法知道用戶什么時(shí)候關(guān)閉了對(duì)話框。
映射網(wǎng)絡(luò)驅(qū)動(dòng)對(duì)話框

圖2.30
圖2.30顯示了映射網(wǎng)絡(luò)驅(qū)動(dòng)器的對(duì)話框,我們通過(guò)SHNetConnectionDialog函數(shù)調(diào)用它(win 9x和Win NT上都支持),它可以按索引值160從Shell32.dll暴露出來(lái),函數(shù)定義如下:
function SHNetConnectionDialog(Owner: HWND;
ResourceName: Pointer; ResourceType: DWORD): DWORD; stdcall; |
SHStartNetConnectionDialog函數(shù)也會(huì)顯示同樣的對(duì)話框,但它顯示的對(duì)話框是非模態(tài)的,同時(shí)只在NT上才支持。它可以按索引值215從Shell32.dll中公開出來(lái),函數(shù)定義如下:
function SHStartNetConnectionDialog(Owner: HWND;
ResourceName: PWideChar; ResourceType: DWORD):DWORD; stdcall; |
上面兩個(gè)函數(shù)的參數(shù)完全相同。其中ResourceName參數(shù)指向一個(gè)要連接的網(wǎng)絡(luò)資源UNC路徑名。指定了這個(gè)參數(shù)的話,顯示的對(duì)話框中被預(yù)設(shè)的連接資源就不可改變了。如果這個(gè)參數(shù)為nil,則在對(duì)話框中用戶可以指定要連接的資源。ResourceType參數(shù)可以是下面的值之一:RESOURCETYPE_DISK或RESOURCETYPE_PRINT。它的不同將會(huì)生成不同的對(duì)話框。參數(shù)為RESOURCETYPE_DISK允許我們?yōu)榫W(wǎng)絡(luò)驅(qū)動(dòng)資源指定一個(gè)盤符,另一個(gè)參數(shù)允許我們映射一個(gè)并行口名比如LPT2為一個(gè)網(wǎng)絡(luò)打印機(jī)。然而,不知道為什么RESOURCETYPE_PRINT參數(shù)在NT上無(wú)效。

圖2.31
如果函數(shù)調(diào)用成功的話,返回值是NO_ERROR,如果用戶取消的對(duì)話框,則返回 -1($FFFFFFFF),如果調(diào)用失敗則返回其他的錯(cuò)誤代碼,具體錯(cuò)誤信息可以用GetLastError API調(diào)用獲得。
關(guān)閉系統(tǒng)對(duì)話框
ExitWindowsDialog和RestartDialog函數(shù)可以用來(lái)顯示關(guān)閉和重啟系統(tǒng)對(duì)話框(如圖2.31),它們同公開的ExitWindowsEx API函數(shù)沒(méi)有什么太大的不同,但在其過(guò)程中都會(huì)產(chǎn)生一個(gè)對(duì)話框。ExitWindowsDialog函數(shù)可以按索引值60從Shell32.dll中公開出來(lái),RestartDialog函數(shù)的在Shell32.dll中的索引值則是59,兩個(gè)函數(shù)的定義如下:
procedure ExitWindowsDialog(Owner: HWND); stdcall;
function RestartDialog(Owner: HWND; Reason: Pointer; ExitType: UINT): DWORD; stdcall; |
對(duì)ExitWindowsDialog函數(shù)來(lái)說(shuō),對(duì)話框好像并不使用Owner參數(shù)作為父窗口,在Windows 95上,當(dāng)操作成功的話owner窗口會(huì)收到一個(gè)WM_QUIT消息。在Windows NT上,owner 窗口根本不被使用。同時(shí)這個(gè)函數(shù)沒(méi)有返回值,所以沒(méi)有辦法知道用戶選擇了什么操作以及操作是否被取消了。
RestartDialog函數(shù)更有用一些,當(dāng)我們修改了系統(tǒng)的設(shè)置,并希望重新啟動(dòng)系統(tǒng)使修改生效的時(shí)候可以使用這個(gè)函數(shù)。Reason參數(shù)指向一個(gè)要顯示在對(duì)話框中的字符串,用來(lái)解釋關(guān)閉系統(tǒng)的原因。ExitType參數(shù)指定關(guān)閉類型,可以使用ExitWindowsEX函數(shù)使用值的一個(gè)子集及額外的幾個(gè)新值,下面是它們的完全列表:
EWX_LOGOFF = $00;
EWX_SHUTDOWN = $01;
EWX_REBOOT = $02;
EW_RESTARTWINDOWS = $42;
EW_REBOOTSYSTEM = $43;
EW_EXITANDEXECAPP = $44; |
如果用戶選擇執(zhí)行關(guān)閉操作,函數(shù)返回IDYES,否則返回IDNO。
要注意的是顯示在對(duì)話框中的原因字符串后總會(huì)跟著一個(gè)系統(tǒng)缺省提供的字符串用來(lái)顯示確認(rèn)信息,所以應(yīng)該在我們的Reason字符串后附上空格或回車換行字符。另外返回值不能用于確定操作的成功性,它只表明用戶的選擇,如果重啟操作由于某些原因失敗了,返回值仍然是IDYES。同時(shí)要注意的是要想調(diào)用成功,用戶還必須有SE_SHUTDOWN_NAME權(quán)限(在NT上)。
缺少內(nèi)存對(duì)話框
SHOutOfMemoryMessageBox是一個(gè)未公開的函數(shù),當(dāng)系統(tǒng)內(nèi)存不足時(shí)可以用來(lái)顯示標(biāo)準(zhǔn)的外殼信息對(duì)話框,它在Shell32.dll中的索引值是126,函數(shù)定義如下:
function SHOutOfMemoryMessageBox(Owner: HWND;
Caption: Pointer; Style: UINT): Integer; stdcall; |
它會(huì)調(diào)用MessageBox API,同時(shí)傳遞3個(gè)標(biāo)準(zhǔn)的參數(shù)和ERROR_OUTOFMEMORY錯(cuò)誤消息。Caption參數(shù)指向?qū)υ捒驑?biāo)題字符串。如果Caption為nil,父窗口的標(biāo)題就會(huì)被使用。Style參數(shù)可以被設(shè)置為任意MessageBox函數(shù)使用的MB_XXX常數(shù)的組合,通常設(shè)置它為MB_OK或MB_ICONHAND。函數(shù)調(diào)用返回值參見SDK中MessageBox函數(shù)說(shuō)明。
當(dāng)MessageBox函數(shù)被調(diào)用時(shí),MB_SETFOREGROUND標(biāo)識(shí)會(huì)被添加到Style參數(shù)中,但如果第一次調(diào)用失敗了的話,MessageBox函數(shù)會(huì)被再次調(diào)用,這次MB_SYSTEMMODAL 標(biāo)識(shí)會(huì)被添加到Style參數(shù)中。MB_SYSTEMMODAL同MB_ICONHAND標(biāo)識(shí)結(jié)合后會(huì)忽略內(nèi)存狀況來(lái)顯示消息對(duì)話框。當(dāng)內(nèi)存確實(shí)不足時(shí),函數(shù)不會(huì)顯示任何東西,然而它仍然會(huì)返回MessageBox函數(shù)調(diào)用結(jié)果。所以我們可以根據(jù)返回值判斷函數(shù)是否調(diào)用成功了。
空間不足對(duì)話框

圖2.32
另一個(gè)資源相關(guān)的函數(shù)是SHHandleDiskFull,它會(huì)顯示磁盤不足的信息對(duì)話框(如圖2.32)。我們可以在由于沒(méi)有足夠磁盤空間時(shí)導(dǎo)致程序無(wú)法運(yùn)行的條件下調(diào)用這個(gè)函數(shù),調(diào)用后,如果回收站中有什么東西沒(méi)有刪除的話,對(duì)話框允許用戶清空回收站來(lái)釋放磁盤空間。它在Shell32.dll中的索引值為185,函數(shù)的定義如下:
procedure SHHandleDiskFull(Owner: HWND; Drive: UINT); stdcall; |
Drive參數(shù)用于指定以0為底的驅(qū)動(dòng)器盤符。0代表A:\,1代表B:\,依此類推。這個(gè)函數(shù)的應(yīng)用比較困難,因?yàn)楫?dāng)回收站中沒(méi)有任何東西時(shí)對(duì)話框不會(huì)顯示,同時(shí)也沒(méi)有任何返回值表示對(duì)話框是否顯示,還無(wú)法知道用戶的操作,比如它是否真的清空了。看起來(lái)比較可行的應(yīng)用只能是程序自行監(jiān)視磁盤剩余空間,只是使用這個(gè)對(duì)話框作為一個(gè)快速修復(fù)的工具。
一般外殼消息對(duì)話框
ShellMessageBox函數(shù)僅僅是一個(gè)對(duì)MessageBox函數(shù)的簡(jiǎn)單封裝函數(shù),它允許使用字符串資源標(biāo)識(shí)符或標(biāo)準(zhǔn)的以null結(jié)尾的字符串,同時(shí)還允許加入支持格式化ForamtMessage函數(shù)的控制符。ShellMessageBox函數(shù)在Shell32.dll中的索引值為183:
function ShellMessageBoxA(Module: THandle; Owner: HWND;
Text: PChar; Caption: PChar; Style: UINT;
Parameters: array of Pointer): Integer; cdecl; |
更確切地說(shuō)這個(gè)函數(shù)應(yīng)該叫ShellMessageBoxA因?yàn)樗恢С諥NSI字符串,還有一個(gè)UNICODE的版本的函數(shù)ShellMessageBoxW,它的索引值為182,但它只在Windows NT上才有,函數(shù)定義如下:
function ShellMessageBoxW(Module: THandle; Owner: HWND;
Text: PWideChar; Caption: PWideChar; Style: UINT;
Parameters: array of Pointer): Integer; cdecl; |
Module參數(shù)是提供字符串資源的模塊句柄,句柄可以用GetModuleHandle函數(shù)獲得。顧名思義Text 參數(shù)指向一個(gè)要顯示在對(duì)話框中的文本,它也可以是資源字符串ID,文本中可以包括格式控制序列,它將會(huì)被在Parameters中提供的額外字符串替代。控制符格式為“%#”,其中“#”是額外字符串在參數(shù)中的位置,比如“%1”將被第一個(gè)Parameters數(shù)組中的字符串元素替代,“%3”將會(huì)被第三個(gè)元素替代,依此類推。Caption參數(shù)指向?qū)υ捒驑?biāo)題文本,同樣它也可以是資源ID,如果參數(shù)為nil,Owner指定的窗口標(biāo)題將被用于對(duì)話框標(biāo)題。Style參數(shù)是由位掩碼標(biāo)識(shí)組成的,可以設(shè)置成MessageBox函數(shù)支持的MB_XXX常數(shù)的組合。返回值同MessageBox完全一樣。
對(duì)于這個(gè)函數(shù)很重要的一點(diǎn)是微軟公司使用cdecl來(lái)輸出這個(gè)函數(shù)而不是通常的 stdcall。此外,Parameters參數(shù)使用了C語(yǔ)言中的可變參數(shù)列表,這意味著這個(gè)函數(shù)不是語(yǔ)言無(wú)關(guān)的,這使得調(diào)用起來(lái)非常麻煩,因?yàn)镈elphi并不直接支持cdecl和可變參數(shù)列表。為了解決這個(gè)問(wèn)題,Parameters參數(shù)被映射為一個(gè)動(dòng)態(tài)指針列表。同時(shí)我們還需要使用嵌入式匯編來(lái)建立cdecl樣式的堆棧。由于動(dòng)態(tài)指針列表的性質(zhì),我們必須至少指定一個(gè)指針值。如果不想指定要替代的字符串,簡(jiǎn)單設(shè)置為nil就可以了。
首先,先簡(jiǎn)單介紹一下MD5
MD5的全稱是message-digest algorithm 5(信息-摘要算法,在90年代初由mit laboratory for computer science和rsa data security inc的ronald l. rivest開發(fā)出來(lái), 經(jīng)md2、md3和md4發(fā)展而來(lái)。
MD5具有很好的安全性(因?yàn)樗哂胁豢赡娴奶卣?加過(guò)密的密文經(jīng)過(guò)解密后和加密前的東東相同的可能性極小)
引用
using System.Security.Cryptography;
using System.Text;
具體代碼如下(寫在按鈕的Click事件里):
byte[] result = Encoding.Default.GetBytes(this.tbPass.Text.Trim()); //tbPass為輸入密碼的文本框
MD5 md5 = new MD5CryptoServiceProvider();
byte[] output = md5.ComputeHash(result);
this.tbMd5pass.Text = BitConverter.ToString(output).Replace("-",""); //tbMd5pass為輸出加密文本的文本框
本文來(lái)自CSDN博客,轉(zhuǎn)載請(qǐng)標(biāo)明出處:http://blog.csdn.net/lykycs/archive/2006/06/16/802873.aspx
再談異常――談C++與Object Pascal中的構(gòu)造函數(shù)與異常
作者:Nicrosoft(nicrosoft@sunistudio.com) 2001.9.15
個(gè)人主頁(yè):http://www.sunistudio.com/nicrosoft/
東日文檔:http://www.sunistudio.com/asp/sunidoc.asp
我們知道,類的構(gòu)造函數(shù)是沒(méi)有返回值的,如果構(gòu)造函數(shù)構(gòu)造對(duì)象失敗,不可能依靠返回錯(cuò)誤代碼。那么,在程序中如何標(biāo)識(shí)構(gòu)造函數(shù)的失敗呢?最“標(biāo)準(zhǔn)”的方法就是:拋出一個(gè)異常。
構(gòu)造函數(shù)失敗,意味著對(duì)象的構(gòu)造失敗,那么拋出異常之后,這個(gè)“半死不活”的對(duì)象會(huì)被如何處理呢?這就是本文的主題。
在C++中,構(gòu)造函數(shù)拋出異常后,析構(gòu)函數(shù)不會(huì)被調(diào)用。這是合理的,因?yàn)榇藭r(shí)對(duì)象并沒(méi)有被完整構(gòu)造。也就是說(shuō),如果構(gòu)造函數(shù)已經(jīng)做了一些諸如分配內(nèi)存、
打開文件等操作的話,那么類需要有自己的成員來(lái)記住做過(guò)哪些動(dòng)作。在C++中,經(jīng)典的解決方案是使用STL的標(biāo)準(zhǔn)類auto_ptr,這在每一本經(jīng)典
C++著作中都有介紹,我在這里就不多說(shuō)了。在這里,我想再介紹一種“非常規(guī)”的方式,其思想就是避免在構(gòu)造函數(shù)中拋出異常。我們可以在類中增加一個(gè)
Init(); 以及 UnInit();成員函數(shù)用于進(jìn)行容易產(chǎn)生錯(cuò)誤的資源分配工作,而真正的構(gòu)造函數(shù)中先將所有成員置為NULL,然后調(diào)用
Init(); 并判斷其返回值(或者捕捉 Init()拋出的異常),如果Init();失敗了,則在構(gòu)造函數(shù)中調(diào)用 UnInit();
并設(shè)置一個(gè)標(biāo)志位表明構(gòu)造失敗。UnInit()中按照成員是否為NULL進(jìn)行資源的釋放工作。示例代碼如下:
class A
{
private:
char* str;
int failed;
public:
A();
~A();
int Init();
int UnInit();
int Failed();
};
A::A()
{
str = NULL;
try
{
Init();
failed = 0;
}
catch(...)
{
failed = 1;
UnInit();
}
}
A::~A()
{
UnInit();
}
int A::Init()
{
str = new char[10];
strcpy(str, "ABCDEFGHI");
throw 10;
return 1;
}
int A::UnInit()
{
if (!str)
{
delete []str;
str = NULL;
}
printf("Free Resource
");
return 1;
}
int A::Failed()
{
return failed;
}
int main(int argc, char* argv[])
{
A* a = new A;
if ( a->Failed() )
printf("failed
");
else
printf("succeeded
");
delete a;
getchar();
return 0;
}
你會(huì)發(fā)現(xiàn),在int A::Init()中包含了throw 10;的代碼(產(chǎn)生一個(gè)異常,模擬錯(cuò)誤的發(fā)生),執(zhí)行結(jié)果是:
Free Resource
failed
Free Resource
雖然 UnInit();被調(diào)用了兩次,但是由于UnInit();中做了判斷(if (!str)),因此不會(huì)發(fā)生錯(cuò)誤。而如果沒(méi)有發(fā)生異常(去掉 int A::Init()中的throw 10;代碼),執(zhí)行結(jié)果是:
Succeeded
Free Resource
和正常的流程沒(méi)有任何區(qū)別。
在Object Pascal(Delphi/VCL)中,這個(gè)問(wèn)題就變得非常的簡(jiǎn)單了,因?yàn)?OP
對(duì)構(gòu)造函數(shù)的異常的處理與C++不同,在Create時(shí)拋出異常后,編譯器會(huì)自動(dòng)調(diào)用析構(gòu)函數(shù)Destroy,并且會(huì)判斷哪些資源被分配了,實(shí)行自動(dòng)回
收。因此,其代碼也變得非常簡(jiǎn)潔,如下:
type
A = class
private
str : PChar;
public
constructor Create();
destructor Destroy(); override;
end;
constructor A.Create();
begin
str := StrAlloc(10);
StrCopy(str, 'ABCDEFGHI');
raise Exception.Create('error');
end;
destructor A.Destroy();
begin
StrDispose(str);
WriteLn('Free Resource');
end;
var oa : A;
i : integer;
begin
try
oa := A.Create();
WriteLn('Succeeded');
oa.Free();
except
oa := nil;
WriteLn('Failed');
end;
Read(i);
end.
在這段代碼中,如果構(gòu)造函數(shù)拋出異常(即Create中含有raise Exception.Create('error');),執(zhí)行的結(jié)果是:
Free Resource
Failed
此時(shí)的“Free Resource”輸出是由編譯器自動(dòng)調(diào)用析構(gòu)函數(shù)所產(chǎn)生的。而如果構(gòu)造函數(shù)正常返回(即不拋出異常),則執(zhí)行結(jié)果是:
Succeeded
Free Resource
此時(shí)的“Free Resource”輸出是由 oa.Free()的調(diào)用產(chǎn)生的。
綜上,C++與Object
Pascal對(duì)于構(gòu)造函數(shù)拋出異常后的不同處理方式,其實(shí)正是兩種語(yǔ)言的設(shè)計(jì)思想的體現(xiàn)。C++秉承C的風(fēng)格,注重效率,一切交給程序員來(lái)掌握,編譯器不
作多余動(dòng)作。Object
Pascal繼承Pascal的風(fēng)格,注重程序的美學(xué)意義(不可否認(rèn),Pascal代碼是全世界最優(yōu)美的代碼),編譯器幫助程序員完成復(fù)雜的工作。兩種語(yǔ)
言都有存在的理由,都有存在的必要!而掌握它們之間的差別,能讓你更好地控制它們,達(dá)到自由的理想王國(guó)。
串口通訊中的DCB結(jié)構(gòu)
typedef struct _DCB {// dcb
DWORD DCBlength; // sizeof(DCB)
DORD BaudRate; // current baud rate 指定當(dāng)前的波特率
DWORD fBinary: 1; // binary mode, no EOF check 指定是否允許二進(jìn)制模式WIN95中須為TRUE
DWORD fParity: 1; // enable parity checking 指定奇偶校驗(yàn)是否允許
DWORD fOutxCtsFlow:1; // CTS output flow control 指定CTS是否用于檢測(cè)發(fā)送控制。當(dāng)為TRUE是CTS為OFF,發(fā)送將被掛起。
DWORD fOutxDsrFlow:1; // DSR output flow control 指定CTS是否用于檢測(cè)發(fā)送控制。當(dāng)為TRUE是CTS為OFF,發(fā)送將被掛起。
DWORD fDtrControl:2; // DTR flow control type
DTR_CONTROL_DISABLE 值將DTR置為OFF,DTR_CONTROL_ENABLE值將DTR置為ON,
DTR_CONTROL_HANDSHAKE 允許DTR"握手",
DWORD fDsrSensitivity:1;// DSR sensitivity 當(dāng)該值為TRUE時(shí)DSR為OFF時(shí)接收的字節(jié)被忽略
DWORD fTXContinueOnXoff:1; // XOFF continues Tx
指定當(dāng)接收緩沖區(qū)已滿,并且驅(qū)動(dòng)程序已經(jīng)發(fā)送出XoffChar字符時(shí)發(fā)送是否停止。
TRUE時(shí),在接收緩沖區(qū)接收到緩沖區(qū)已滿的字節(jié)XoffLim且驅(qū)動(dòng)程序已經(jīng)發(fā)送出XoffChar字 符中止接收字節(jié)之后,發(fā)送繼續(xù)進(jìn)行。
FALSE時(shí),在接收緩沖區(qū)接收到代表緩沖區(qū)已空的字節(jié)XonChar且驅(qū)動(dòng)程序已經(jīng)發(fā)送出恢復(fù)發(fā)送的XonChar之后,發(fā)送繼續(xù)進(jìn)行。
DWORD fOutX: 1; // XON/XOFF out flow control TRUE時(shí),接收到XoffChar之后便停止發(fā)送接收到XonChar之后將重新開始
DWORD fInX: 1; // XON/XOFF in flow control
TRUE時(shí),接收緩沖區(qū)接收到代表緩沖區(qū)滿的XoffLim之后,XoffChar發(fā)送出去接收緩沖區(qū)接收到代表緩沖區(qū)空的XonLim之后,XonChar發(fā)送出去
DWORD fErrorChar: 1; // enable error replacement
該值為TRUE且fParity為TRUE時(shí),用ErrorChar 成員指定的字符代替奇偶校驗(yàn)錯(cuò)誤的接收字符
DWORD fNull: 1; // enable null stripping TRUE時(shí),接收時(shí)去掉空(0值)字節(jié)
DWORD fRtsControl:2; // RTS flow control
DWORD fAbortOnError:1; // abort reads/writes on error TRUE時(shí),有錯(cuò)誤發(fā)生時(shí)中止讀和寫操作RTS_CONTROL_DISABLE時(shí),RTS置為OFFRTS_CONTROL_ENABLE時(shí), RTS置為ON
RTS_CONTROL_HANDSHAKE時(shí),當(dāng)接收緩沖區(qū)小于半滿時(shí)RTS為ON當(dāng)接收緩沖區(qū)超過(guò)四分之三滿時(shí)RTS為OFF
RTS_CONTROL_TOGGLE時(shí),當(dāng)接收緩沖區(qū)仍有剩余字節(jié)時(shí)RTS為ON ,否則缺省為OFF
DWORD fDummy2:17; // reserved 未使用
WORD wReserved; // not currently used 未使用,必須為0
WORD XonLim; // transmit XON threshold 指定在XON字符發(fā)送這前接收緩沖區(qū)中可允許的最小字節(jié)數(shù)
WORD XoffLim; // transmit XOFF threshold 指定在XOFF字符發(fā)送這前接收緩沖區(qū)中可允許的最小字節(jié)數(shù)
BYTE ByteSize; // number of bits/byte, 4-8 指定端口當(dāng)前使用的數(shù)據(jù)位
BYTE Parity; // 0-4=no,odd,even,mark,space 指定端口當(dāng)前使用的奇偶校驗(yàn)方法,可能為:
EVENPARITY,MARKPARITY,NOPARITY,ODDPARITY BYTE
StopBits; 0,1,2 = 1, 1.5, 2 指定端口當(dāng)前使用的停止位數(shù),可能為:
ONESTOPBIT,ONE5STOPBITS,TWOSTOPBITS
char XonChar; // Tx and Rx XON character 指定用于發(fā)送和接收字符XON的值
char XoffChar; // Tx and Rx XOFF character 指定用于發(fā)送和接收字符XOFF值
char ErrorChar; // error replacement character本字符用來(lái)代替接收到的奇偶校驗(yàn)發(fā)生錯(cuò)誤時(shí)的值
char EofChar; // end of input character 當(dāng)沒(méi)有使用二進(jìn)制模式時(shí),本字符可用來(lái)指示數(shù)據(jù)的結(jié)束
char EvtChar; // received event character 當(dāng)接收到此字符時(shí),會(huì)產(chǎn)生一個(gè)事件
WORD wReserved1; // reserved; do not use 未使用
} DCB;
CSS切換皮膚切換代碼:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "<html xmlns=" <head>
<meta http-equiv="content-type" content="text/html;charset=gb2312" />
<meta name="generator" content="" />
<meta name="author" content="" />
<meta name="keywords" content="" />
<meta name="robots" content="index, follow" />
<meta name="googlebot" content="index, follow" />
<title> </title>
<link rel="stylesheet" type="text/css" href="code/changestyle/styles1.css" title="styles1" media="screen" />
<link rel="alternate stylesheet" type="text/css" href="code/changestyle/styles2.css" title="styles2" media="screen" />
<link rel="alternate stylesheet" type="text/css" href="code/changestyle/styles3.css" title="styles3" media="screen" />
<script type="text/javascript">
window.onload=function(){ var c = readCookie('style');
if (c) switchStylestyle(c);
};
function switchStylestyle(styleName)
{ aa=document.styleSheets;
for(i=0;i<aa.length;i++)
{
aa[i].disabled = true;
if (aa[i].title== styleName){aa[i].disabled = false;}
};
createCookie('style', styleName, 365);
}
function createCookie(name,value,days)
{
if (days)
{
var date = new Date();
date.setTime(date.getTime()+(days*24*60*60*1000));
var expires = "; expires="+date.toGMTString();
}
else var expires = "";
document.cookie = name+"="+value+expires+"; path=/";
}
function readCookie(name)
{
var nameEQ = name + "=";
var ca = document.cookie.split(';');
for(var i=0;i < ca.length;i++)
{
var c = ca[i];
while (c.charAt(0)==' ') c = c.substring(1,c.length);
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
}
return null;
}
function eraseCookie(name)
{
createCookie(name,"",-1);
}
// /cookie functions
</script>
</head>
<body id="">
<div id="wrapper">
<ul>
<li><a href="javascript:void(0);" onclick="switchStylestyle('styles1')">styles1</a></li>
<li><a href="javascript:void(0);" onclick="switchStylestyle('styles2')">styles2</a></li>
<li><a href="javascript:void(0);" onclick="switchStylestyle('styles3')">styles3</a></li>
</ul
</div>
</body>
</html>
DLL注入程序的一般步驟:
(1)取得宿主進(jìn)程(即要注入木馬的進(jìn)程)的進(jìn)程ID dwRemoteProcessId;
(2)取得DLL的完全路徑,并將其轉(zhuǎn)換為寬字符模式pszLibFileName;
(3)利用Windows API OpenProcess打開宿主進(jìn)程,應(yīng)該開啟下列選項(xiàng):
a.PROCESS_CREATE_THREAD:允許在宿主進(jìn)程中創(chuàng)建線程;b.PROCESS_VM_OPERATION:允許對(duì)宿主進(jìn)程中進(jìn)行VM操作;
c.PROCESS_VM_WRITE:允許對(duì)宿主進(jìn)程進(jìn)行VM寫。
(4)利用Windows API VirtualAllocEx函數(shù)在遠(yuǎn)程線程的VM中分配DLL完整路徑寬字符所需的存儲(chǔ)空間,并利用Windows API WriteProcessMemory函數(shù)將完整路徑寫入該存儲(chǔ)空間;(5)利用Windows API GetProcAddress取得Kernel32模塊中LoadLibraryW函數(shù)的地址,這個(gè)函數(shù)將作為隨后將啟動(dòng)的遠(yuǎn)程線程的入口函數(shù);
(6)利用Windows API CreateRemoteThread啟動(dòng)遠(yuǎn)程線程,將LoadLibraryW的地址作為遠(yuǎn)程線程的入口函數(shù)地址,將宿主進(jìn)程里被分配空間中存儲(chǔ)的完整DLL路徑作為線程入口函數(shù)的參數(shù)以另其啟動(dòng)指定的DLL;(7)清理現(xiàn)場(chǎng)。