Tree Control : how to disable an item
Tree Control 不支持節(jié)點(diǎn)的禁用, 但是可以通過(guò)自繪實(shí)現(xiàn), 主要如下:
1. 標(biāo)記節(jié)點(diǎn)是否禁用. 可以用 SetItemData & GetItemData 來(lái)設(shè)置&獲取節(jié)點(diǎn)數(shù)據(jù)
2. 在禁用的節(jié)點(diǎn)上, 要過(guò)濾一些對(duì)節(jié)點(diǎn)操作, 如expanding, selecting, drag & drop 等.
TVN_SELCHANGING 節(jié)點(diǎn)選中改變時(shí)
TVN_ITEMEXPANDING 節(jié)點(diǎn)展開(kāi)時(shí)
TVN_BEGINDRAG 節(jié)點(diǎn)開(kāi)始被拖拉
TVN_BEGINLABELEDIT 節(jié)點(diǎn)被編輯
//Preventing selection: (handle TVN_SELCHANGING)
void CYourDialog::OnSelchangingTree(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*) pNMHDR;
if(((CItemStruct *) m_tree.GetItemData(pNMTreeView->iNewItem))->m_bDisabled)
{
*pResult = 1; // 設(shè)置 *pResult=1 表示TVN_SELCHANGING 這個(gè)操作不能繼續(xù)
return;
}
*pResult = 0;
}
3. 對(duì)禁用節(jié)點(diǎn)進(jìn)行自畫(huà), 用圖標(biāo),顏色將禁用節(jié)點(diǎn)和其他節(jié)點(diǎn)進(jìn)行區(qū)分
a) 直接在 WM_PAINT 中進(jìn)行自畫(huà) (或?qū)ree Control的繪圖結(jié)果進(jìn)行修改)
可以參考: 如何在樹(shù)型控件中使用背景位圖
b) 相應(yīng) WM_OWERDRAW 事件
可以參考: Outlook風(fēng)格的單列使用不同的顏色顯示新郵件數(shù)
參考:
MFC Tree Control: How to disable an item?
Setting color and font attribute for individual items
1. IClassFactory 的用途
在 http://www.80diy.com/home/20041120/19/3572410.html 看到幾段關(guān)于COM 的類(lèi)廠的話,
"""
類(lèi)廠用來(lái)抽象組件的create過(guò)程,客戶不需要知道組件的詳細(xì)情況,也不需要知道類(lèi)廠的詳細(xì)情況,只要知道CoCreateInstance可以創(chuàng)建組件即可。而CoCreateInstance內(nèi)部調(diào)用DllGetClassObject來(lái)生成該組件的類(lèi)廠,由于類(lèi)廠有組件的作者撰寫(xiě),所以對(duì)組件類(lèi)可謂知根知底,由類(lèi)廠來(lái)生成組件完全行得通,這樣客戶和組件就進(jìn)一步劃分,客戶只能查詢?cè)摻M件是否支持某借口,而對(duì)組件的其他情況一無(wú)所知,這樣的劃分可以使組件和客戶間的耦合更小。
"""
"""
組件如果將某接口的全部方法都實(shí)現(xiàn)了,就稱該組件支持某接口,com并沒(méi)有規(guī)定組件和接口之間是虛函數(shù)繼承的關(guān)系,只是在c++中以這種方法來(lái)實(shí)現(xiàn)最好而已。
IClassFactory說(shuō)穿了就是專門(mén)構(gòu)造組件的類(lèi),這樣做是為了抽象,因?yàn)榭蛻魶](méi)有必要知道組件是什么,如果由客戶直接構(gòu)造組件,客戶勢(shì)必要知道組件的信息,com就失去了它的意義了,所以,規(guī)定了一個(gè)類(lèi)廠(支持IClassFactory接口),每個(gè)組件的類(lèi)廠都很清楚并且也只清楚該組件的信息,而客戶只需要調(diào)用com庫(kù)函數(shù)CoCreateInstance就可以了。
下面是流程圖:
CoCreateInstance -> CoGetClassObject -> DllGetClassObject -> new ClassFactory -> IClassFactory::CreateInstance() -> new Component
"""
并且在 http://www.codeproject.com/com/comintro2.asp 也看到幾段話
"""
每次實(shí)現(xiàn)組件對(duì)象類(lèi)的時(shí)候,都要寫(xiě)一個(gè)旁類(lèi)負(fù)責(zé)創(chuàng)建第一個(gè)組件對(duì)象類(lèi)的實(shí)例。這個(gè)旁類(lèi)就叫這個(gè)組件對(duì)象類(lèi)的類(lèi)工廠(class factory),其唯一目的是創(chuàng)建COM對(duì)象。之所以要一個(gè)類(lèi)工廠,是因?yàn)檎Z(yǔ)言無(wú)關(guān)的緣故。COM本身并不創(chuàng)建對(duì)象,因?yàn)樗皇仟?dú)立于語(yǔ)言的也不是獨(dú)立于實(shí)現(xiàn)的。
當(dāng)某個(gè)客戶端想要?jiǎng)?chuàng)建一個(gè)COM對(duì)象時(shí),COM庫(kù)就從COM服務(wù)器請(qǐng)求類(lèi)工廠。然后類(lèi)工廠創(chuàng)建COM對(duì)象并將它返回客戶端。它們的通訊機(jī)制由函數(shù)DllGetClassObject()來(lái)提供。
"""
在<COM 技術(shù)內(nèi)幕> 中, 對(duì)類(lèi)廠的引入也有描述.
主要是:
a. 在面向?qū)ο笙到y(tǒng)中, 對(duì)象創(chuàng)建是非常重要的, 因?yàn)橐褂盟仨毾葎?chuàng)建它. 所以盡可能靈活的創(chuàng)建對(duì)象(component)
b. 在CoCreateInstance 創(chuàng)建對(duì)象過(guò)程是: 傳給一共CLSID, 然后創(chuàng)建成相應(yīng)組件, 并返回所請(qǐng)求的指針. 其弊端在于無(wú)法提供給客戶一種控制對(duì)象創(chuàng)建過(guò)程的方法. (問(wèn)題關(guān)鍵不在初始化, 而是控制創(chuàng)建對(duì)象過(guò)程)
c. IClassFactory2 成批的調(diào)用接口.
2.
參考CoCreateInstance 的實(shí)現(xiàn)過(guò)程:
CoCreateInstance -> CoGetClassObject -[系統(tǒng)|組件代碼]-> DllGetClassObject -> new ClassFactory -> IClassFactory::CreateInstance() -> new Component
因?yàn)?DllGetClassObject -> new ClassFactory -> IClassFactory::CreateInstance() -> new Component 都是組件所來(lái)實(shí)現(xiàn)的, 而系統(tǒng)調(diào)用 CoCreateInstance 所提供的參數(shù), 和通過(guò)自己使用IClassFactory 來(lái)創(chuàng)建Component 的參數(shù)是沒(méi)有變化的, 所以如果省略 ClassFactory 應(yīng)該也可以.
CoCreateInstance -> CoGetClassObject -[系統(tǒng)|組件代碼]-> DllGetClassObject -> new Component
DllGetClassObject 完全可以完成<COM 技術(shù)內(nèi)幕說(shuō)的> a. 靈活創(chuàng)建對(duì)象, b. 控制創(chuàng)建過(guò)程, c. IClassFactory2 , 而且這樣子的實(shí)現(xiàn)也與語(yǔ)言無(wú)關(guān).
所以感覺(jué)沒(méi)有必要一定要用到IClassFactory 這個(gè)接口
3.
因此在實(shí)現(xiàn)的時(shí)候, 完全可以這樣子的實(shí)現(xiàn)組件
CCoClass : public IA, public IB, public IClassFactory
{
......
}
DllGetClassObject()
{
new CCoClass
}
而不需要額外的用一個(gè)類(lèi)單獨(dú)的去實(shí)現(xiàn)IClassFactory . 好像ATL 默認(rèn)的就是這么干的, 提供了一個(gè)CComCoClass<CCoClass, &CLSID_CCoClass) 實(shí)現(xiàn)類(lèi).
這個(gè)是我的對(duì)COM 的IClassFactory 的理解, 感覺(jué)沒(méi)必要多一個(gè)這個(gè)東西.
不知道大家是如何看待這個(gè)東西的:)
1. 子類(lèi)化
改變一個(gè)已經(jīng)存在的窗口實(shí)例的性質(zhì):消息處理與其他實(shí)例屬性。
在SDK編程范疇內(nèi),子類(lèi)化就是改變一個(gè)窗口實(shí)例的窗口函數(shù)(通過(guò)GetWindowLong()和SetWindowLong()),子類(lèi)化所要做的就是為某窗口實(shí)例編寫(xiě)新的窗口函數(shù)。其操作是在實(shí)例級(jí)別上進(jìn)行的。
在MFC中子類(lèi)化的情況有所不同:所有MFC窗口有相同的窗口函數(shù),由該窗口函數(shù)根據(jù)窗口句柄查找窗口實(shí)例,在把消息映射到該窗口類(lèi)(class)得消息處理函數(shù)上。為了利用MFC的消息映射機(jī)制,不宜改變窗口函數(shù)(名),MFC也把子類(lèi)化封裝在函數(shù)SubclassWindow()中。但子類(lèi)化的本質(zhì)沒(méi)有變:在實(shí)例級(jí)別影響窗口的消息及其處理。例:
Class B :public A
{
……
}
A a;
B b;
HWND ha=a.GetSafeHwnd();
b.SubclassWindow(ha); #當(dāng)然A 和B 不一定是繼承關(guān)系。
注意:在被子類(lèi)化的窗口銷(xiāo)毀之前,必須執(zhí)行窗口的反子類(lèi)化:
b.UnSubclassWindow();
2 超類(lèi)化
窗口超類(lèi)化是在窗口類(lèi)——WNDCLASS或WNDCLASSEX(非MFC類(lèi)概念)級(jí)別進(jìn)行的改變窗口類(lèi)特征的。
使用過(guò)程:首先獲得一個(gè)已存在的窗口類(lèi),然后設(shè)置窗口類(lèi),最后注冊(cè)該窗口類(lèi)。
例:
WNDCLASSEX wc;
wc.cbSize=sizeof(wc); //Windows用來(lái)進(jìn)行版本檢查的,與窗口特征無(wú)關(guān)
GetClassInfoEx(hinst,”XXXXXX”,&wc);
// hinst—定義窗口類(lèi)XXXXXX的模塊的句柄,如為系統(tǒng)定義的窗口類(lèi)(如:EDIT、BUTTON)則hinst=NULL.。
wc.lpszClassName = “YYYYYYY”;//必須改變窗口類(lèi)的名字
wc.hbrBackGround = CreateSolidBrush(RGB(0,0.0));//改變背景刷
wc.lpfnWndProc = NewWndProc;//改變窗口函數(shù)
……
RegisterClassEx(&wc);// 注冊(cè)新窗口類(lèi)
//使用窗口類(lèi)
……
::CreateWindow(_T(“YYYYYYYY”,……);
故超類(lèi)化只能改變自己創(chuàng)建的窗口的特征,而不能用于由Windows創(chuàng)建的窗口(如對(duì)話框上的按鈕就不能進(jìn)行超類(lèi)化) 。而子類(lèi)化是實(shí)例級(jí)別上的,只要能獲得窗口的實(shí)例,就可對(duì)其子類(lèi)化,這是唯一的子類(lèi)化對(duì)于超類(lèi)化的優(yōu)勢(shì)。另外,凡是子類(lèi)化可實(shí)現(xiàn)的,超類(lèi)化都可實(shí)現(xiàn),不過(guò)超類(lèi)化用起來(lái)較麻煩。
3. 總結(jié)
(0) 子類(lèi)化修改窗口過(guò)程函數(shù), 超類(lèi)化修改窗口類(lèi)(新的窗口類(lèi)名)
(1) 子類(lèi)化是在窗口實(shí)例級(jí)別上的,超類(lèi)化是在窗口類(lèi)(WNDCLASS)級(jí)別上的。
(2) 超類(lèi)化可以完成比子類(lèi)化更復(fù)雜的功能,在SDK范疇上,可以認(rèn)為子類(lèi)化是超類(lèi)化的子集。
(3) 子類(lèi)化只能改變窗口創(chuàng)建后的性質(zhì),對(duì)于窗口創(chuàng)建期間無(wú)能為力(無(wú)法截獲ON_CREATE 事件),而超類(lèi)化可以實(shí)現(xiàn);超類(lèi)化不能用于Windows創(chuàng)建的窗口,子類(lèi)化可以。
4. 其他
在 眼見(jiàn)為實(shí)(2):介紹Windows的窗口、消息、子類(lèi)化和超類(lèi)化 這里有一個(gè)例子..
可以得出結(jié)論
a) 子類(lèi)化的classname 是不會(huì)變化的, 而超類(lèi)化使用新注冊(cè)classname
b) 子類(lèi)化 & 超類(lèi)化 描述的是一個(gè)動(dòng)作 和實(shí)現(xiàn)方法沒(méi)什么關(guān)系..... 主要是子類(lèi)化是SubclassWindow, SubclassDlgItem, 而超類(lèi)化是RegisterClassEx(&newwindowclass)
c) 感覺(jué)具體沒(méi)有必要區(qū)分這些, 實(shí)現(xiàn)功能就行了, 呵呵
zt: http://blogs.msdn.com/mikewasson/archive/2005/05/23/some-directshow-samples-break-in-visual-studio-2005.aspx
DirectX 9.0 與 VS 2005 之間存在沖突, 主要因?yàn)閂S 2005 的語(yǔ)法比VC6 & VS2003 更加嚴(yán)格, 所以一些DirectX 自帶的代碼需要更改以后才能編譯通過(guò). 本來(lái)想自己改的, 不過(guò)在網(wǎng)上發(fā)現(xiàn)了有人已經(jīng)做了這個(gè):)
[Note: This post applies to the Platform SDK for Windows Server 2003 SP1 and Server 2003 R2. These issues were fixed in the Windows SDK for Vista.]
Some of the DirectShow samples break if you install Visual Studio 2005 Beta 2. Most of the errors that I found fall into three categories:
- C4430: Missing type specifier. To conform with C++, undeclared types do not default to int. All types must be declared. Fix: Declare the type, or suppress the warning with the "/wd4430" flag.
- C4996: ' xxxx' was declared deprecated. You may be including an older version of strsafe.h from the DirectX SDK or the Platform SDK. You should include the version installed with Visual Studio. (But it's probably harmless to ignore this warning.)
- C2065: 'xxx': undeclared identifier. To conform with C++, the scope of a variable declared inside a "for" loop is restricted to the loop. Fixes: (a) Move the declaration outside the for loop. (b) Redeclare the variable in multiple scopes, if you don't need it to persist outside the loop. (c) Set the /Zc:forScope flag. (You can find this under Project, Properties, Configuration Properties, C/C++, Language, Force Conformance In For Loop Scope. Set to "No".)
Here are the specific fixes that I made. Warning: I have not thoroughly tested these, and I only tried them under the "Windows XP 32-bit Debug" environment in Platform SDK. You should use your own judgment before making any of these fixes.
- BaseClasses\ctlutil.h (278)
(LONG) operator=(LONG);
- BaseClasses\wxdebug.cpp (564)
static DWORD g_dwLastRefresh = 0;
- BaseClasses\winutil.cpp (2092)
UINT Count;
for (Count = 0;Count < Result;Count++) {
- BaseClasses\outputq.cpp (635)
long iDone = 0;
for (iDone = 0;
- Capture\AmCap\amcap.cpp (691)
for(int i = 0; i < NUMELMS(gcap.rgpmAudioMenu); i++)
- Capture\AmCap\amcap (2795)
for(int i = 0; i < NUMELMS(gcap.rgpmAudioMenu); i++)
- DMODemo\dsutil.cpp (686)
DWORD i = 0;
for( i=0; i<m_dwNumBuffers; i++ )
- dmoimpl.h (622) [In the Platform SDK headers]
for (DWORD dw = 0; dw < NUMBEROFOUTPUTS; dw++) {
- DMO\GargleDMO\MedParamBase\param.cpp (91)
for (DWORD dwIndex = 0; dwIndex < cParams; dwIndex++)
- DMO\GargleDMO\MedParamBase\param.cpp (309)
CCurveItem *pCurve = NULL;
for (pCurve = pCurveHead;
- DMO\GargleDMO\gargle.cpp (145)
for (DWORD i = 0; i < cOutputStreams && SUCCEEDED(hr); ++i)
- Filters\Dump\dump.cpp (426)
for (int Loop = 0;Loop < (DataLength % BYTES_PER_LINE);Loop++)
- Filters\Gargle\gargle.cpp (212)
static int m_nInstanceCount; // total instances
- Filters\RGBFilters\RateSource\ratesource.cpp (382)
for( int y = 0 ; y < DEFAULT_HEIGHT ; y++ )
- Filters\RGBFilters\RateSource\ratesource.cpp (387)
for( int y = 0 ; y < DEFAULT_WIDTH ; y++ )
- VMR\VMRXclBasic and VMR\Ticker: LNK1181: cannot open input file 'dxguid.lib'. This was an error in the makefile. Change to read:
DXLIB="$(DXSDK_DIR)\Lib\x86" (currently says "x32")
- VMR\VMRXcl and VMR\VMRMulti: C1083: Cannot open include file: 'd3dxmath.h': No such file or directory. This is an old DX header that is no longer included in DX or in Visual Studio. Unfortunately the only fix is to download an older version of the DirectX SDK.
- VMR9\MultiVMR9\GamePlayer\character.cpp (383)
DWORD i = 0;
for (i = 0; i < pMeshContainer->NumInfl; ++i)
- VMR9\MultiVMR9\DLL\MixerControl.h (28)
static const DWORD MultiVMR9Mixer_DefaultFVF = D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1;
- VMR9\VMRAllocator: error LNK2019: unresolved external symbol "wchar_t * __stdcall _com_util::ConvertStringToBSTR(char const *)" (etc). Add this to the makefile:
LINK32_LIBS = \
comsuppw.lib \
shell32.lib \
1. 目的
在Codeproject 中看到有人做這個(gè), 稍微做了下修改, 做成了命令行的, 使用方法是
iesnap.exe url filename
用以抓取 URL 對(duì)于的Web, 并且將Web 的截圖保存在Filename 中.
url : 要截圖的網(wǎng)址
filename: 截圖保存文件名
2. 思路
主要使用WebBrowser Control 和 MSHTML 來(lái)完成.
a. 創(chuàng)建WebBrowser control
b. 從WebBrowser Control 獲取 IWebBrowser2 接口, 用 IWebBrowser2::Navigate2 來(lái)訪問(wèn)URL
c. 獲得IHTMLElementRender (WebBrowser -> IHTMLDocument2 -> IHTMLElement -> IHTMLElementRender), 使用該接口的DrawToDC 來(lái)獲取URL 對(duì)應(yīng)的頁(yè)面截圖內(nèi)容.
d. 用GDI 的 Image 來(lái)保存截圖到Filename
3.
可以看看原文以得到更好的解答
我看的那個(gè)文章: Capture an HTML document as an image
另外一篇C# 的文章: Image Capture Whole Web Page using C#
4. 問(wèn)題
a. DocumentComplete 事件會(huì)在URL 對(duì)應(yīng)頁(yè)面的每個(gè)元素下載完畢的時(shí)候都會(huì)發(fā)生, 所以:( 還不知道哪個(gè)事件是整個(gè)頁(yè)面下載完畢的事件:/
b. 現(xiàn)在是通過(guò)MFC 來(lái)創(chuàng)建WebBrowser Control 的, 不知道怎么在Win32 Application 中創(chuàng)建這個(gè)控件.
c. 不知道如何得到整個(gè)頁(yè)面的大小, 現(xiàn)在只能保存頁(yè)面截圖的一部分:(
這個(gè)已經(jīng)知道怎么做了.
IHTMLElement2 的 scrollWidth & scrollHeight 加上 scrollLeft & scrollTop 來(lái)實(shí)現(xiàn), 但是不知道為什么, put_scrollTop & put_scrollLeft 這些函數(shù)好像不對(duì), 只能截獲到一部分頁(yè)面截圖.:( 不知道為什么
代碼可以在這里下載: http://m.shnenglu.com/Files/bigsml/iesnapshot.zip
這款瀏覽器,稱之為Yang C/C++ Compiler & Internet Browser或者YC編譯型瀏覽器..
最開(kāi)始是在程序員看到相關(guān)的文章的, 開(kāi)始以為是在吹..但是今天用他的程序的時(shí)候, 感覺(jué)真的很nb..
"""
記者在楊曉兵處觀摩了該產(chǎn)品的演示,發(fā)現(xiàn)它是由如下五個(gè)部分組成:HTML解析器;XML解析器(目前完成一部分);javascript腳本解釋器,C/C++腳本解釋器;C/C++編譯器;文本及二進(jìn)制編輯器.其中最令人矚目的功能,自然是他所內(nèi)嵌的C/C++實(shí)時(shí)編譯功能了,這樣使得C/C++的運(yùn)行效果看上去類(lèi)似動(dòng)態(tài)語(yǔ)言,它不僅使得HTML支持腳本化的標(biāo)準(zhǔn)C/C++語(yǔ)言,而且使得C/C++ 能夠與象JacaScript這樣的動(dòng)態(tài)語(yǔ)言可以互相調(diào)用.楊曉兵說(shuō)“這將是軟件開(kāi)發(fā)方式的革新.通過(guò)這個(gè)實(shí)時(shí)編譯功能,每個(gè)軟件可以同時(shí)分割成若干模塊,相互之間可以獨(dú)立運(yùn)行.”該編譯器,與主流的C/C++編譯器相比,YC++在功能進(jìn)行了一些刪減和改良,比如去掉了標(biāo)準(zhǔn)C/C++的函數(shù)重載、運(yùn)算符重載、模板等,而改良的方向,主要側(cè)重增加與網(wǎng)頁(yè)開(kāi)發(fā)環(huán)境(HTML4.0、CSS2.JavaScript)的互相支持和調(diào)用.令人頗感意外的是,該產(chǎn)品包括編譯器、HTML解析器等在內(nèi)的五個(gè)模塊的所有代碼,都與由楊曉兵獨(dú)自一個(gè)人用C語(yǔ)言開(kāi)發(fā)完成,從未借鑒參考任何其他源代碼.楊曉兵解釋說(shuō)主要是為了便于調(diào)試和控制.這是相當(dāng)大的工作量,整整花了他六年時(shí)間.從演示的運(yùn)行效果來(lái)看,這款由一人手工完成的作品,編譯速度竟然比VC++還要快一些.據(jù)透露是源自其許多算法的優(yōu)化,如專門(mén)為變量參數(shù)作了可供快速查找的字典表等.
"""
在用的時(shí)候, 發(fā)現(xiàn)整個(gè)瀏覽器沒(méi)有使用一個(gè)Windows 控件, 所有的控件都是由用GDI 級(jí)來(lái)繪制成的... 包括菜單, 輸入框, 按鈕, 樹(shù)形控件等等等
Orz... 一把
可以在這里下載試用, 解壓縮3個(gè)壓縮包就行了.
/Files/bigsml/setyc.part01.rar
/Files/bigsml/setyc.part02.rar
/Files/bigsml/setyc.part03.rar
在Web 要實(shí)現(xiàn)地圖,流程圖,涂鴉等功能的時(shí)候,可以選擇VML,SVG,Javaapplet,或者Flash。百度的map 搜索的地圖展示就是使用VML 的。
1、 VML
VML是微軟1999年9月附帶IE5.0發(fā)布的,全稱是Vector Markup Language(矢量可標(biāo)記語(yǔ)言),其實(shí)是Word和HTML結(jié)合的產(chǎn)物。可以將Word文檔另存為HTML,其中的文本和圖片可以很容易的轉(zhuǎn)換,但如果是手繪制的圖形在以往的IE里面就無(wú)法解釋了,如果都轉(zhuǎn)換成圖形文件又不太現(xiàn)實(shí)。于是微軟把Word里面的圖形控件結(jié)合到IE里面,使IE也具備了繪圖功能。
VML 的資料:
MS 的 How to Use VML on Web Pages
http://msdn.microsoft.com/library/default.asp?url=/workshop/author/VML/ref/basic1.asp
Think In VML(VML基本教程)
http://www.itlearner.com/code/vml/index.html
VML畫(huà)板
http://www.tool.la/VMLPalette/
VML編輯器
http://www.dynamicdrive.com/dynamicindex11/editor.htm
流程圖
http://cosoft.org.cn/projects/webflow
How to Implement Vector Markup Language (VML) on Web Pages?
http://www.tudjarov.hit.bg/vml/vmlbt.html
VML Chart 控件
http://dev.csdn.net/article/23/23770.shtm
Chart Demo
http://webfx.eae.net/dhtml/chart/demo.html
2、SVG
SVG,全稱為Scalable Vector Graphics(可伸縮矢量圖形)。它是W3C制定的、用矢量描述圖形的XML應(yīng)用標(biāo)準(zhǔn)。它有著許多的優(yōu)點(diǎn),比如可擴(kuò)充性(scalable),動(dòng)態(tài)的,交互性強(qiáng)。SVG支持無(wú)極放大,對(duì)SVG圖片進(jìn)行任意比例的放大都不會(huì)損害圖片的顯示(沒(méi)有太多的失真),其他諸如BMP,JPEG格式的圖片都不支持無(wú)級(jí)放大。SVG有動(dòng)畫(huà)元素,只要在SVG文件中嵌入SVG動(dòng)畫(huà)元素就可以實(shí)現(xiàn)動(dòng)畫(huà)效果了。同時(shí)SVG也定義了豐富的事件,包括鼠標(biāo)事件和鍵盤(pán)事件,只要對(duì)SVG進(jìn)行相關(guān)的腳本編程就可以實(shí)現(xiàn)SVG文件的交互操作。
相關(guān)示例:
SVG腳本編程
http://dev.csdn.net/article/26/26676.shtm
http://www.adobe.com/svg/examples.html
SVM & SVG 相關(guān)的庫(kù)和應(yīng)用有
Prototype Graphic Framework
http://prototype-graphic.xilinus.com/
Cumulate Draw 很強(qiáng)大的一個(gè)畫(huà)圖工具, 基本上實(shí)現(xiàn)了Visio 的功能
http://www.cumulatelabs.com/cumulatedraw/
RichDraw - Simple VML/SVG Editor
http://starkravingfinkle.org/blog/2006/04/richdraw-simple-vmlsvg-editor/
Mapbar 地圖
http://main.mapbar.com/
mxDraw 很強(qiáng)大的一個(gè)流程圖畫(huà)板
http://www.mxgraph.com/demo/mxgraph/editors/diagrameditor.html
Del.icio.us
http://del.icio.us/hedgerwang/VML
3、Flash
Flash的強(qiáng)大功能就不用介紹了,相信它完全可以實(shí)現(xiàn)任何流程圖的操作功能。但是,F(xiàn)lash的學(xué)習(xí)成本太高了,它的ActionScript讓我們這些寫(xiě)慣了java、js的開(kāi)發(fā)人員一頭霧水。而且相關(guān)的網(wǎng)絡(luò)資源實(shí)在是太少了,當(dāng)初我google了N久,才勉強(qiáng)找到一個(gè)通過(guò)讀取xml文件顯示流程圖的example,功能僅僅是顯示xml文件中配置的流程節(jié)點(diǎn)(有需要源碼的朋友可以mail我)。所以,要想實(shí)現(xiàn)強(qiáng)大的流程圖編輯功能,F(xiàn)lash只推薦高手使用。
商用的軟件有
Gliff http://www.gliffy.com/
DrawAnywhere http://drawanywhere.com/
Google code有一個(gè)開(kāi)源項(xiàng)目 http://code.google.com/p/flexvizgraphlib/ , 其代碼可以在
http://groups.google.com/group/flexvizgraphlib/web/FlexVisualGraph.zip 下載到
4、Java Applet
在SVM 和 SVG 出現(xiàn)以前,有網(wǎng)絡(luò)涂鴉,就是用Applet 做成的,但是現(xiàn)在好像用的不多了。
ref:
Tools for Creating Charts and Diagrams 一篇英文的文章, 對(duì)現(xiàn)有的流程圖創(chuàng)建工具有一個(gè)很好的介紹
IE7 近幾天發(fā)布了, 英文版可以通過(guò)下面的鏈接下載.
XP w/sp2
http://download.microsoft.com/download/3/8/8/38889DC1-848C-4BF2-8335-86C573AD86D9/IE7-WindowsXP-x86-enu.exe
2003
http://download.microsoft.com/download/D/1/3/D1346F12-F3A0-4AC6-8F5C-2BEA2A184957/IE7-WindowsServer2003-x86-enu.exe
xp&2003 64bit
http://download.microsoft.com/download/1/1/4/114D5B07-4DBC-42F3-96FA-2097E207D0AF/IE7-WindowsServer2003-x64-enu.exe
2003 ia64
http://download.microsoft.com/download/4/E/3/4E3E332E-4A7B-4E85-9F45-8209472F2FD2/IE7-WindowsServer2003-ia64-enu.exe
在安裝中要求正版驗(yàn)證.?不過(guò)在網(wǎng)上已經(jīng)出現(xiàn)了破解方法,?在 http://green.crsky.com/soft/4012.html?可以下載破解過(guò)的IE7.
1.先把IE7的安裝文件用WinRAR解開(kāi).
2.將破解iecustom.dll放進(jìn)Update目錄,覆蓋原有文件.
3.執(zhí)行update.exe安裝,這個(gè)時(shí)候先勾選安裝界面的"Do not restart now",先不要重啟,如下圖:

4.復(fù)制下載來(lái)的normaliz.dll到Windows的System32目錄(必須執(zhí)行,否則系統(tǒng)將故障)
5.重新啟動(dòng).
6.執(zhí)行Update目錄下的xmllitesetup.exe更新一下就可以了
一定一定要記住安裝后勾選安裝界面的"Do not restart now",先不要重啟!等復(fù)制normaliz.dll到Windows的System32目錄下再重啟!其實(shí)先復(fù)制normaliz.dll到Windows的System32目錄下也是可以的!下面2個(gè)不同版本都適合上面的方法!
Internet Explorer 7 正式版(Final)版本號(hào)7.0.5730.11;Internet Explorer 7 (RC1)版本號(hào)7.0.5700.6
BoundsChecker采用一種被稱為 Code Injection的技術(shù),來(lái)截獲對(duì)分配內(nèi)存和釋放內(nèi)存的函數(shù)的調(diào)用。簡(jiǎn)單地說(shuō),當(dāng)你的程序開(kāi)始運(yùn)行時(shí),BoundsChecker的DLL被自動(dòng)載入進(jìn)程的地址空間(這可以通過(guò)system-level的Hook實(shí)現(xiàn)),然后它會(huì)修改進(jìn)程中對(duì)內(nèi)存分配和釋放的函數(shù)調(diào)用,讓這些調(diào)用首先轉(zhuǎn)入它的代碼,然后再執(zhí)行原來(lái)的代碼。BoundsChecker在做這些動(dòng)作的時(shí),無(wú)須修改被調(diào)試程序的源代碼或工程配置文件,這使得使用它非常的簡(jiǎn)便、直接。
這里我們以malloc函數(shù)為例,截獲其他的函數(shù)方法與此類(lèi)似。
需要被截獲的函數(shù)可能在DLL中,也可能在程序的代碼里。比如,如果靜態(tài)連結(jié)C-Runtime Library,那么malloc函數(shù)的代碼會(huì)被連結(jié)到程序里。為了截獲住對(duì)這類(lèi)函數(shù)的調(diào)用,BoundsChecker會(huì)動(dòng)態(tài)修改這些函數(shù)的指令。
以下兩段匯編代碼,一段沒(méi)有BoundsChecker介入,另一段則有BoundsChecker的介入:
126:?_CRTIMP?void?*?__cdecl?malloc?(
127:?size_t?nSize
128:?)
129:?{
00403C10?push?ebp
00403C11?mov?ebp,esp
130:?return?_nh_malloc_dbg(nSize,?_newmode,?_NORMAL_BLOCK,?NULL,?0);
00403C13?push?0
00403C15?push?0
00403C17?push?1
00403C19?mov?eax,[__newmode?(0042376c)]
00403C1E?push?eax
00403C1F?mov?ecx,dword?ptr?[nSize]
00403C22?push?ecx
00403C23?call?_nh_malloc_dbg?(00403c80)
00403C28?add?esp,14h
131:?}
以下這一段代碼有BoundsChecker介入:
126:?_CRTIMP?void?*?__cdecl?malloc?(
127:?size_t?nSize
128:?)
129:?{
00403C10?jmp?01F41EC8
00403C15?push?0
00403C17?push?1
00403C19?mov?eax,[__newmode?(0042376c)]
00403C1E?push?eax
00403C1F?mov?ecx,dword?ptr?[nSize]
00403C22?push?ecx
00403C23?call?_nh_malloc_dbg?(00403c80)
00403C28?add?esp,14h
131:?}
當(dāng)BoundsChecker介入后,函數(shù)malloc的前三條匯編指令被替換成一條jmp指令,原來(lái)的三條指令被搬到地址01F41EC8處了。當(dāng)程序進(jìn)入malloc后先jmp到01F41EC8,執(zhí)行原來(lái)的三條指令,然后就是BoundsChecker的天下了。大致上它會(huì)先記錄函數(shù)的返回地址(函數(shù)的返回地址在stack上,所以很容易修改),然后把返回地址指向?qū)儆贐oundsChecker的代碼,接著跳到malloc函數(shù)原來(lái)的指令,也就是在00403c15的地方。當(dāng)malloc函數(shù)結(jié)束的時(shí)候,由于返回地址被修改,它會(huì)返回到BoundsChecker的代碼中,此時(shí)BoundsChecker會(huì)記錄由malloc分配的內(nèi)存的指針,然后再跳轉(zhuǎn)到到原來(lái)的返回地址去。
如果內(nèi)存分配/釋放函數(shù)在DLL中,BoundsChecker則采用另一種方法來(lái)截獲對(duì)這些函數(shù)的調(diào)用。BoundsChecker通過(guò)修改程序的DLL Import Table讓table中的函數(shù)地址指向自己的地址,以達(dá)到截獲的目的。
截獲住這些分配和釋放函數(shù),BoundsChecker就能記錄被分配的內(nèi)存或資源的生命周期。接下來(lái)的問(wèn)題是如何與源代碼相關(guān),也就是說(shuō)當(dāng)BoundsChecker檢測(cè)到內(nèi)存泄漏,它如何報(bào)告這塊內(nèi)存塊是哪段代碼分配的。答案是調(diào)試信息(Debug Information)。當(dāng)我們編譯一個(gè)Debug版的程序時(shí),編譯器會(huì)把源代碼和二進(jìn)制代碼之間的對(duì)應(yīng)關(guān)系記錄下來(lái),放到一個(gè)單獨(dú)的文件里(.pdb)或者直接連結(jié)進(jìn)目標(biāo)程序,通過(guò)直接讀取調(diào)試信息就能得到分配某塊內(nèi)存的源代碼在哪個(gè)文件,哪一行上。使用Code Injection和Debug Information,使BoundsChecker不但能記錄呼叫分配函數(shù)的源代碼的位置,而且還能記錄分配時(shí)的Call Stack,以及Call Stack上的函數(shù)的源代碼位置。這在使用像MFC這樣的類(lèi)庫(kù)時(shí)非常有用,以下我用一個(gè)例子來(lái)說(shuō)明:
void?ShowXItemMenu()
{
…
CMenu?menu;
menu.CreatePopupMenu();
//add?menu?items.
menu.TrackPropupMenu();
…
}
void?ShowYItemMenu(?)
{
…
CMenu?menu;
menu.CreatePopupMenu();
//add?menu?items.
menu.TrackPropupMenu();
menu.Detach();//this?will?cause?HMENU?leak
…
}
BOOL?CMenu::CreatePopupMenu()
{
…
hMenu?=?CreatePopupMenu();
…
}
當(dāng)調(diào)用ShowYItemMenu()時(shí),我們故意造成HMENU的泄漏。但是,對(duì)于BoundsChecker來(lái)說(shuō)被泄漏的HMENU是在class CMenu::CreatePopupMenu()中分配的。假設(shè)的你的程序有許多地方使用了CMenu的CreatePopupMenu()函數(shù),如CMenu::CreatePopupMenu()造成的,你依然無(wú)法確認(rèn)問(wèn)題的根結(jié)到底在哪里,在ShowXItemMenu()中還是在ShowYItemMenu()中,或者還有其它的地方也使用了CreatePopupMenu()?有了Call Stack的信息,問(wèn)題就容易了。BoundsChecker會(huì)如下報(bào)告泄漏的HMENU的信息:
Function
File
Line
CMenu::CreatePopupMenu
E:\8168\vc98\mfc\mfc\include\afxwin1.inl
1009
ShowYItemMenu
E:\testmemleak\mytest.cpp
100
這里省略了其他的函數(shù)調(diào)用
如此,我們很容易找到發(fā)生問(wèn)題的函數(shù)是ShowYItemMenu()。當(dāng)使用MFC之類(lèi)的類(lèi)庫(kù)編程時(shí),大部分的API調(diào)用都被封裝在類(lèi)庫(kù)的class里,有了Call Stack信息,我們就可以非常容易的追蹤到真正發(fā)生泄漏的代碼。
記錄Call Stack信息會(huì)使程序的運(yùn)行變得非常慢,因此默認(rèn)情況下BoundsChecker不會(huì)記錄Call Stack信息。可以按照以下的步驟打開(kāi)記錄Call Stack信息的選項(xiàng)開(kāi)關(guān):
1. 打開(kāi)菜單:BoundsChecker|Setting…
2. 在Error Detection頁(yè)中,在Error Detection Scheme的List中選擇Custom
3. 在Category的Combox中選擇 Pointer and leak error check
4. 鉤上Report Call Stack復(fù)選框
5. 點(diǎn)擊Ok
基于Code Injection,BoundsChecker還提供了API Parameter的校驗(yàn)功能,memory over run等功能。這些功能對(duì)于程序的開(kāi)發(fā)都非常有益。由于這些內(nèi)容不屬于本文的主題,所以不在此詳述了。
盡管BoundsChecker的功能如此強(qiáng)大,但是面對(duì)隱式內(nèi)存泄漏仍然顯得蒼白無(wú)力。所以接下來(lái)我們看看如何用Performance Monitor檢測(cè)內(nèi)存泄漏。
使用Performance Monitor檢測(cè)內(nèi)存泄漏
NT的內(nèi)核在設(shè)計(jì)過(guò)程中已經(jīng)加入了系統(tǒng)監(jiān)視功能,比如CPU的使用率,內(nèi)存的使用情況,I/O操作的頻繁度等都作為一個(gè)個(gè)Counter,應(yīng)用程序可以通過(guò)讀取這些Counter了解整個(gè)系統(tǒng)的或者某個(gè)進(jìn)程的運(yùn)行狀況。Performance Monitor就是這樣一個(gè)應(yīng)用程序。
為了檢測(cè)內(nèi)存泄漏,我們一般可以監(jiān)視Process對(duì)象的Handle Count,Virutal Bytes 和Working Set三個(gè)Counter。Handle Count記錄了進(jìn)程當(dāng)前打開(kāi)的HANDLE的個(gè)數(shù),監(jiān)視這個(gè)Counter有助于我們發(fā)現(xiàn)程序是否有Handle泄漏;Virtual Bytes記錄了該進(jìn)程當(dāng)前在虛地址空間上使用的虛擬內(nèi)存的大小,NT的內(nèi)存分配采用了兩步走的方法,首先,在虛地址空間上保留一段空間,這時(shí)操作系統(tǒng)并沒(méi)有分配物理內(nèi)存,只是保留了一段地址。然后,再提交這段空間,這時(shí)操作系統(tǒng)才會(huì)分配物理內(nèi)存。所以,Virtual Bytes一般總大于程序的Working Set。監(jiān)視Virutal Bytes可以幫助我們發(fā)現(xiàn)一些系統(tǒng)底層的問(wèn)題; Working Set記錄了操作系統(tǒng)為進(jìn)程已提交的內(nèi)存的總量,這個(gè)值和程序申請(qǐng)的內(nèi)存總量存在密切的關(guān)系,如果程序存在內(nèi)存的泄漏這個(gè)值會(huì)持續(xù)增加,但是Virtual Bytes卻是跳躍式增加的。
監(jiān)視這些Counter可以讓我們了解進(jìn)程使用內(nèi)存的情況,如果發(fā)生了泄漏,即使是隱式內(nèi)存泄漏,這些Counter的值也會(huì)持續(xù)增加。但是,我們知道有問(wèn)題卻不知道哪里有問(wèn)題,所以一般使用Performance Monitor來(lái)驗(yàn)證是否有內(nèi)存泄漏,而使用BoundsChecker來(lái)找到和解決。
當(dāng)Performance Monitor顯示有內(nèi)存泄漏,而B(niǎo)oundsChecker卻無(wú)法檢測(cè)到,這時(shí)有兩種可能:第一種,發(fā)生了偶發(fā)性內(nèi)存泄漏。這時(shí)你要確保使用Performance Monitor和使用BoundsChecker時(shí),程序的運(yùn)行環(huán)境和操作方法是一致的。第二種,發(fā)生了隱式的內(nèi)存泄漏。這時(shí)你要重新審查程序的設(shè)計(jì),然后仔細(xì)研究Performance Monitor記錄的Counter的值的變化圖,分析其中的變化和程序運(yùn)行邏輯的關(guān)系,找到一些可能的原因。這是一個(gè)痛苦的過(guò)程,充滿了假設(shè)、猜想、驗(yàn)證、失敗,但這也是一個(gè)積累經(jīng)驗(yàn)的絕好機(jī)會(huì)。
總結(jié)
內(nèi)存泄漏是個(gè)大而復(fù)雜的問(wèn)題,即使是Java和.Net這樣有Gabarge Collection機(jī)制的環(huán)境,也存在著泄漏的可能,比如隱式內(nèi)存泄漏。由于篇幅和能力的限制,本文只能對(duì)這個(gè)主題做一個(gè)粗淺的研究。其他的問(wèn)題,比如多模塊下的泄漏檢測(cè),如何在程序運(yùn)行時(shí)對(duì)內(nèi)存使用情況進(jìn)行分析等等,都是可以深入研究的題目。如果您有什么想法,建議或發(fā)現(xiàn)了某些錯(cuò)誤,歡迎和我交流。
我們?cè)贑語(yǔ)言編程中會(huì)遇到一些參數(shù)個(gè)數(shù)可變的函數(shù),例如printf()這個(gè)函數(shù),這里將介紹可變函數(shù)的寫(xiě)法以及原理.
* 1. 可變參數(shù)的宏
一般在調(diào)試打印Debug 信息的時(shí)候, 需要可變參數(shù)的宏. 從C99開(kāi)始可以使編譯器標(biāo)準(zhǔn)支持可變參數(shù)宏(variadic macros), 另外GCC 也支持可變參數(shù)宏, 但是兩種在細(xì)節(jié)上可能存在區(qū)別.
1. __VA_ARGS__
__VA_ARGS__ 將"..." 傳遞給宏.如
#define debug(format, ...) fprintf(stderr, fmt, __VA_ARGS__)
在GCC中也支持這類(lèi)表示, 但是在G++ 中不支持這個(gè)表示.
2. GCC 的復(fù)雜宏
GCC使用一種不同的語(yǔ)法從而可以使你可以給可變參數(shù)一個(gè)名字,如同其它參數(shù)一樣。
#define debug(format, args...) fprintf (stderr, format, args)
這和上面舉的那個(gè)定義的宏例子是完全一樣的,但是這么寫(xiě)可讀性更強(qiáng)并且更容易進(jìn)行描述。
3. ##__VA_ARGS__
上面兩個(gè)定義的宏, 如果出現(xiàn)debug("A Message") 的時(shí)候, 由于宏展開(kāi)后有個(gè)多余的逗號(hào), 所以將導(dǎo)致編譯錯(cuò)誤. 為了解決這個(gè)問(wèn)題,CPP使用一個(gè)特殊的‘##’操作。
#define debug(format, ...) fprintf (stderr, format, ## __VA_ARGS__)
這里,如果可變參數(shù)被忽略或?yàn)榭眨?#’操作將使預(yù)處理器(preprocessor)去除掉它前面的那個(gè)逗號(hào)。如果你在宏調(diào)用時(shí),確實(shí)提供了一些可變參數(shù),GNU CPP也會(huì)工作正常,它會(huì)把這些可變參數(shù)放到逗號(hào)的后面。
4. 其他方法
一種流行的技巧是用一個(gè)單獨(dú)的用括弧括起來(lái)的的 "參數(shù)" 定義和調(diào)用宏, 參數(shù)在宏擴(kuò)展的時(shí)候成為類(lèi)似 printf() 那樣的函數(shù)的整個(gè)參數(shù)列表。
#define DEBUG(args) (printf("DEBUG: "), printf(args))
* 2. 可變參數(shù)的函數(shù)
寫(xiě)可變參數(shù)的C函數(shù)要在程序中用到以下這些宏:
void va_start( va_list arg_ptr, prev_param )
type va_arg( va_list arg_ptr, type )
void va_end( va_list arg_ptr )
va在這里是variable-argument(可變參數(shù))的意思,這些宏定義在stdarg.h中.下面我們寫(xiě)一個(gè)簡(jiǎn)單的可變參數(shù)的函數(shù),該函數(shù)至少有一個(gè)整數(shù)參數(shù),第二個(gè)參數(shù)也是整數(shù),是可選的.函數(shù)只是打印這兩個(gè)參數(shù)的值.
void simple_va_fun(int i, ...)
{
?? ?va_list arg_ptr;
?? ?int j=0;
?? ?
?? ?va_start(arg_ptr, i);
?? ?j=va_arg(arg_ptr, int);
?? ?va_end(arg_ptr);
?? ?printf("%d %d\n", i, j);
?? ?return;
}
在程序中可以這樣調(diào)用:
simple_va_fun(100);
simple_va_fun(100,200);
從這個(gè)函數(shù)的實(shí)現(xiàn)可以看到,使用可變參數(shù)應(yīng)該有以下步驟:
1)首先在函數(shù)里定義一個(gè)va_list型的變量,這里是arg_ptr,這個(gè)變量是指向參數(shù)的指針.
2)然后用va_start宏初始化變量arg_ptr,這個(gè)宏的第二個(gè)參數(shù)是第一個(gè)可變參數(shù)的前一個(gè)參數(shù),是一個(gè)固定的參數(shù).
3)然后用va_arg返回可變的參數(shù),并賦值給整數(shù)j. va_arg的第二個(gè)參數(shù)是你要返回的參數(shù)的類(lèi)型,這里是int型.
4)最后用va_end宏結(jié)束可變參數(shù)的獲取.然后你就可以在函數(shù)里使用第二個(gè)參數(shù)了.如果函數(shù)有多個(gè)可變參數(shù)的,依次調(diào)用va_arg獲取各個(gè)參數(shù).
如果我們用下面三種方法調(diào)用的話,都是合法的,但結(jié)果卻不一樣:
1)simple_va_fun(100);
結(jié)果是:100 -123456789(會(huì)變的值)
2)simple_va_fun(100,200);
結(jié)果是:100 200
3)simple_va_fun(100,200,300);
結(jié)果是:100 200
我們看到第一種調(diào)用有錯(cuò)誤,第二種調(diào)用正確,第三種調(diào)用盡管結(jié)果正確,但和我們函數(shù)最初的設(shè)計(jì)有沖突.下面一節(jié)我們探討出現(xiàn)這些結(jié)果的原因和可變參數(shù)在編譯器中是如何處理的.
* 3. 可變參數(shù)函數(shù)原理
va_start,va_arg,va_end是在stdarg.h中被定義成宏的,由于硬件平臺(tái)的不同,編譯器的不同,所以定義的宏也有所不同,下面以VC++中stdarg.h里x86平臺(tái)的宏定義摘錄如下:
typedef char * va_list;
#define _INTSIZEOF(n) ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) )
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define va_end(ap) ( ap = (va_list)0 )
定義_INTSIZEOF(n)主要是為了內(nèi)存對(duì)齊,C語(yǔ)言的函數(shù)是從右向左壓入堆棧的(設(shè)數(shù)據(jù)進(jìn)棧方向?yàn)閺母叩刂废虻偷刂钒l(fā)展,即首先壓入的數(shù)據(jù)在高地址). 下圖是函數(shù)的參數(shù)在堆棧中的分布位置:
低地址?? ?|-----------------------------|<-- &v
?? ??? ?|第n-1個(gè)參數(shù)(最后一個(gè)固定參數(shù))|
?? ??? ?|-----------------------------|<--va_start后ap指向
?? ??? ?|第n個(gè)參數(shù)(第一個(gè)可變參數(shù)) |
?? ??? ?|-----------------------------|
?? ??? ?|....... |
?? ??? ?|-----------------------------|
?? ??? ?|函數(shù)返回地址 |
高地址? |-----------------------------|
1. va_list 被定義為char *
2. va_start 將地址ap定義為 &v+_INTSIZEOF(v),而&v是固定參數(shù)在堆棧的地址,所以va_start(ap, v)以后,ap指向第一個(gè)可變參數(shù)在堆棧的地址
3. va_arg 取得類(lèi)型t的可變參數(shù)值,以int型為例,va_arg取int型的返回值:
?? j= ( *(int*)((ap += _INTSIZEOF(int))-_INTSIZEOF(int)) );
4. va_end 使ap不再指向堆棧,而是跟NULL一樣.這樣編譯器不會(huì)為va_end產(chǎn)生代碼.
在不同的操作系統(tǒng)和硬件平臺(tái)的定義有些不同,但原理卻是相似的.
* 4. 小結(jié)
對(duì)于可變參數(shù)的函數(shù),因?yàn)関a_start, va_arg, va_end等定義成宏,所以它顯得很愚蠢,可變參數(shù)的類(lèi)型和個(gè)數(shù)需要在該函數(shù)中由程序代碼控制;另外,編譯器對(duì)可變參數(shù)的函數(shù)的原型檢查不夠嚴(yán)格,對(duì)編程查錯(cuò)不利.
所以我們寫(xiě)一個(gè)可變函數(shù)的C函數(shù)時(shí),有利也有弊,所以在不必要的場(chǎng)合,無(wú)需用到可變參數(shù).如果在C++里,我們應(yīng)該利用C++的多態(tài)性來(lái)實(shí)現(xiàn)可變參數(shù)的功能,盡量避免用C語(yǔ)言的方式來(lái)實(shí)現(xiàn).
* 5. 附一些代碼
#define debug(format, ...) fprintf(stderr, fmt, __VA_ARGS__)
#define debug(format, args...) fprintf (stderr, format, args)
#define debug(format, ...) fprintf (stderr, format, ## __VA_ARGS__)
// 使用va... 實(shí)現(xiàn)
void debug(const char *fmt, ...)
{
??? int nBuf;
??? char szBuffer[1024];
??? va_list args;
??? va_start(args, fmt);
??? nBuf = vsprintf(szBuffer, fmt, args) ;
??? assert(nBuf >= 0);
??? printf("QDOGC ERROR:%s\n",szBuffer);
??? va_end(args);
}