討論區快速選單
知識庫快速選單
網路投保旅行平安險 政府補助!學嵌入式+物聯網 掌握Salesforce雲端管理秘訣
[ 回上頁 ] [ 討論區發言規則 ]
[教學]ActiveX控制項
更改我的閱讀文章字型大小
作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/1/19 上午 03:10:03
最近稍微研究了一下ActiveX控制項, 寫成了教學文件, 供各位參考. 不敢保證百分百正確, 但至少個人都有親自試過 (除了最後的簽署外) .如果文中有任何錯誤, 請告知更改.
作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/1/19 上午 03:11:00
ActiveX控制項 -- 邱奕南,2011/01/18

一. 基礎知識

OLE技術(Object Linking and Embedding,物件連結與嵌入),為微軟1990年所提出,最初係為改良DDE(Dynamic Data Exchange,動態資料交換)所設,以便提供複合文件,亦即文件中可嵌入其他文件。在OLE 1.0版時,係利用VTBL(virtual function Table,虛擬函數表),做為兩文件之間的溝通界面。

1993年,微軟提出COM架構(Component Object Model,元件物件模式),做為元件間溝通的界面,並改變OLE技術,以基植於此一架構上,稱為OLE 2.0版。這個COM架構,便是目前所有COM元件的基礎。所有的界面均從IUnkown開始,利用IUnkown::QueryInterface查找該元件有支援的界面並加以運用。當時要實作OLE元件,都必須使用C++透過ATL(Active template Library)來撰寫,過程相當繁複。且在撰寫前,多半必須研讀”Inside OLE 2.0”這本書半年或一年以上,才算稍有基礎,使得大部份的程式設計師都視撰寫OLE元件為畏途。

OCX控制項(OLE custom control),係1994年VB 4.0版時所提出,以取代早期VBX控制項(Visual Basic Extension control)。它是基植於OLE 2.0的界面,再加上新提出的界面,以便與使用者進行互動。就如同它的名字,作用起來就是一個控制項,簡單可像是一個按鈕或Edit Box,複雜也可以像是一個檔案總管。

由於OLE 2.0的界面種類與交互關係過份複雜,同時又想解決OCX控制項只能用VB設計的問題,微軟於1996年開始進行必要界面的重新界定,並提出自動化技術(automation),以解決跨程式語言的問題,然後將整個封裝技術定名為ActiveX技術,於是OCX控制項也跟著改稱為ActiveX控制項。同年IE開始在網頁中支援ActiveX控制項,以便與使用者進行顯示與互動控制,這項變革大幅提高ActiveX控制項的實用性。像現今的Adobe Flash Player,便是一個相當常見的ActiveX控制項。之後VC++開始支援ActiveX技術,將大部份的實作細節加以封裝、自動處理,於是撰寫COM元件與ActiveX控制項變得相當簡便,不必再像以前那麼地辛苦。

所謂automation技術,便是IDispatch界面的支援。目前用VC++開COM元件的Project,內定都有支援這個界面。只是有這個界面,VB或ASP,甚至.NET都可以直接引用並呼叫元件內的函數,存取元件內的屬性。凡是不支援IDispatch界面的COM元件,則稱為純COM元件,只能被C++所引用,因此目前已很少再見到這類型的COM元件了。

作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/1/19 上午 03:11:47
二. 創建ActiveX控制項專案

以下先說明如何利用VC++ 6.0來創建一個基本的ActiveX控制項(使用VS2008,過程亦差不多)。首先創建一個MFC ActiveX Control Wizard的Project:

1.控制項的數目,一般都是1個,除非專案內要同時處理多個控制項。
2.Runtime license是當寫出的ActiveX控制項是要銷售給軟體設計人員使用時,提供相關的授權控管支援,若不是要用來賣的,便不必勾選。關於runtime license,後面會再詳細說明。
3.Activate when visible一般都維持勾選,若不勾選,則使用者必須自行去點選該控制項才會啟動。其實產生程式碼後,可到xxxCtl.cpp檔裡找OLEMISC_ACTIVATEWHENVISIBLE,將它拿掉,作用和不勾選這個選項相同。
4.若控制項的外觀與作用,已和現成的控制項很像時,可在Subclass裡直接選擇要繼承的控制項,以節省開發的時間,否則要選none。

其餘選項一般都不必選(請自行研究),About Box也多不勾選。最後按下Finish,讓VC++ Wizard自動產生程式碼即可。

*** 關於runtime license

當選擇這個選項後,產生的程式碼xxxCtl.cpp裡會多出VerifyUserLicense和GetLicenseKey這兩個函數,同時多出一個.lic檔,以便進行授權控管。事實上,ActiveX控制項的授權控管,主要是透過IClassFactory2這個界面,但因為MFC已將這些界面封裝起來,因此這裡我們只要管前述兩個函數即可。

當軟體設計人員要引用(嵌入)ActiveX控制項時,Container會先呼叫VerifyUserLicense函數,確認該元件是有授權的,然後再呼叫GetLicenseKey函數取得授權字串,並儲存在Container的引用資訊裡。當設計出來的軟體銷售給實際客戶使用時,Container會先呼叫GetLicenseKey函數取得授權字串,並比對引用資訊裡的授權字串是否一致,若一致才會產生控制項實體(Instance)。

VC++ Wizard自動產生的程式碼如下:

static const Tchar BASED_CODE _szLicFileName[] = _T("MyActiveX.lic");
static const Wchar BASED_CODE _szLicString[] = L"Copyright (c) 2010 workgroup";

BOOL CMyActiveXCtrl::CMyActiveXCtrlFactory::VerifyUserLicense()
{
return AfxVerifyLicFile(AfxGetInstanceHandle(), _szLicFileName, _szLicString);
}

BOOL CMyActiveXCtrl::CMyActiveXCtrlFactory::GetLicenseKey(DWORD dwReserved, BSTR FAR* pbstrKey)
{
if (pbstrKey == NULL) return false;
*pbstrKey = SysAllocString(_szLicString);
return (*pbstrKey != NULL);
}

VerifyUserLicense核驗元件是否授權的方式,是檢查.lic檔一開頭的字串,是否和_szLicString相同,而這個字串便是授權字串,在GetLicenseKey裡只是將它複製後往外傳。亦即內定的授權控管方式,是利用.lic這個授權檔(純文字檔)來控管,銷售給軟體設計人員時,才附上去,但因此寫出的軟體在銷售時,則不附上這個授權檔。由於終端的使用者沒有授權檔,便無法進行引用,只能由軟體來運用,達到元件保護的目的。

當然我們也可以依照實際需要來變更這些控管方式,例如變更授權檔內容格式,予以加密等等。

作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/1/19 上午 03:12:20
三. ActiveX控制項的基本測試

現在按下F5進行Compile & Debug,選擇ActiveX Control Test Container做為偵錯主程式。執行後,選Edit下的Insert new Control,找到控制項的名稱加以插入,便可以看到一個橢圓形,這是內定程式碼的顯示方式,後面會再說明如何進行修改:

void CMyActiveXCtrl::OnDraw(CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid)
{
pdc->FillRect(rcBounds, CBrush::FromHandle((HBRUSH)GetStockObject(WHITE_BRUSH)));
pdc->Ellipse(rcBounds);
}

如果不知道控制項的名稱,可以到Resource View,找String Table裡的IDS_MYACTIVEX值。當然你也可以修改裡面的字串,以變更插入控制項的名稱。記得也要到Version資訊裡,變更一下公司名稱(CompanyName)與檔案描述(FileDescription)的名稱,這些是在IE上所會顯示的資訊。

現在來測試一下IE上的顯示。首先寫一個html網頁,裡面加入使用控制項的tag,這部份可利用ActiveX Control Pad來輔助(軟體可從微軟網站下載)。其實這個Tag也可以直接用Text Editor自行加入,大抵為:

<OBJECT WIDTH=137 HEIGHT=93 CLASSID="CLSID:0126ADC4-989D-4CED-93AD-F66BAB2E3D71">
</OBJECT>

CLSID:後的字串,可從程式裡的.odl檔最後面的uuid裡找到:

[ uuid(0126ADC4-989D-4CED-93AD-F66BAB2E3D71), helpstring("MyActiveX Control"), control ]

將裡面的uuid複製貼過來即可。接著用IE顯示該網頁,會發現沒有顯示該控制項。這是因為我們寫的控制項尚屬於不安全的控制項,內定是不啟動的,因此必須變更IE的安全設定(安全性問題,後面再來說明)。由於localhost屬近端內部網路區域,只需改變這個區域的安全設定,將”起始不標示安全的ActiveX控制項”改為提示。然後再顯示前述網頁,出現警告時,選擇允許執行,便會看到控制項的橢圓形顯示了。如果出現無法顯示的圖案,那就是uuid字串打錯了。

反註冊控制項的方法,可利用ActiveX Control Test Container,選File下的Register Controls,然後點選我們註冊的控制項名稱,再按下Unregister。注意註冊的控制項名稱,和插入的控制項名稱並不同,可到xxxCtl.cpp檔裡找:

IMPLEMENT_OLECREATE_EX(CMyActiveXCtrl, "MYACTIVEX.MyActiveXCtrl.1",

裡面的字串便是註冊的控制項名稱,當然也可加以修改。

作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/1/19 上午 03:13:18
四. 實作ActiveX控制項

(一) 顯示、列印與互動

前面已提到,ActiveX控制項的顯示,主要在OnDraw這個函數,不過有些觀念需要再進一步說明:

1. 顯示時,有分active和inactive兩種狀態。
2. 進入active狀態時,Container會為控制項創建一個子視窗,做為顯示/操作使用。顯示是透過該子視窗的WM_PAINT訊息轉送至OnDraw函數,故左上角座標固定為(0,0)。
3. 在inactive狀態時,控制項的子視窗可能尚未建立,若有的話,則會被隱藏,此時顯示是由Container直接呼叫進OnDraw函數,此時左上角座標依控制項的位置而定。
4. 列印時,先經由OnDrawMetafile函數再轉呼叫OnDraw函數。

前述三種情況,最後都會呼叫到OnDraw函數。其中列印使用的DC,單位並非pixel,因此一般都會在OnDrawMetafile函數裡攔截,另行加以繪製,這部份我們可先將之排除不管。而勾選Activate when visible之後,可使得要顯示時,必定在active狀態,因此inactive狀態的情況,也可不管。

由於在active狀態時,控制項本身是一個子視窗,和其他視窗一樣,可透過各種訊息(滑鼠、鍵盤等)來進行互動,顯示亦同OnPaint一樣,在OnDraw裡直接繪製即可。這裡我們假設是要做一個像是對話視窗一樣的控制項,那麼直接建立對話視窗,再嵌入控制項的子視窗裡,會比較方便些。

首先建立一個對話視窗,裡面含一個固定文字、EditBox、按鈕,按鈕按下的結果是要顯示EditBox裡的內容:

CString cs;
m_Edit.GetWindowText(cs);
MessageBox(cs);

其中比較不同的是對話視窗的屬性。由於它是附著在控制項視窗裡,而不是Popup視窗,故Style要選Child;在顯示時也不必顯示標題與選單,故Border也要選None。最後,因為它不是經由DoModal啟動,因此初始狀態的Visible也要勾選起來(否則會被隱藏)。

接著在控制項物件(xxxCtrl)的header file裡加入該對話視窗的變數,假設是m_Main_Dialog。由於這個對話視窗必須在控制項的視窗建立時,便要跟著一起建立,因此進class Wizard的Message Maps,替xxxCtrl選擇WM_CREATE,然後加入下列程式:

int CMyActiveXCtrl::OnCreate(LPCREATEstruct lpCreateStruct)
{
if (COleControl::OnCreate(lpCreateStruct) == -1) return -1;
m_Main_Dialog.Create(IDD_MAINDIALOG, this);
return 0;
}

因為對話視窗會自己顯示,故OnDraw裡已不必再繪製,可將裡面的程式碼清掉。現在可利用ActiveX Control Test Container來測試看看結果,控制項確實已可顯示,也可以互動,只是超出對話視窗的部份會殘留影像未清除。這裡有兩種做法(都加在OnDraw函數裡),一是清除對話視窗外的背景,一是將對話視窗調整成與控制項同樣的大小。

清除對話視窗外的背景,程式如下:

COLORREF c = TranslateColor(AmbientBackColor());
CBrush brBackGnd(c);
pdc->FillRect(rcBounds,&brBackGnd);

其中AmbientBackColor函數,是用來取得控制項所在環境的背景顏色,這樣會更讓控制項看起來和Container是一體的。變更對話視窗大小的方式為:

m_Main_Dialog.MoveWindow(rcBounds,true);

至於對話視窗大小改變後,裡面的子控制項大小與位置是否要重新調整,便看實際需要來決定了。

建議是兩者一起併用,前者在列印時,至少還有清背景(如果沒攔截OnDrawMetafile函數的話),後者則是為了Container的背景顏色等屬性變更時,可讓對話視窗重繪(後面會再提及)。

現在可以用網頁來測試看看,會發現對話視窗的灰色背景相對於網頁的白色背景,很明顯的有區分。為了讓控制項與Container看起來沒有什麼區別,我們必須將AmbientBackColor函數取到的背景顏色,做為對話視窗與控制項(尤其是靜態文字)的背景顏色。這點無關乎ActiveX控制項,而是VC++對話視窗的實作技巧,我們仍詳細說明如下。

作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/1/19 上午 03:14:24
對話視窗要變更背景顏色,主要透過WM_ERASEBKGND來自行塗上。由於這個訊息無法透過ClassWizard來加入,故必須自行加入。首先在Message Map裡加入:

ON_WM_ERASEBKGND()

對話視窗物件宣告裡加入:

afx_msg BOOL OnEraseBkgnd(CDC* pDC);

然後再實作該函數:

CRect client_rect;
GetClientRect(client_rect);
COLORREF c = 要塗上的背景顏色;
CBrush br_backgnd(c);
pDC->FillRect(client_rect,&br_backgnd);
return true;

控制項要變更背景顏色,則是透過WM_CTLCOLOR_REFLECT來處理。以下以靜態文字(static)為例做說明。首先透過ClassWizard建立一個Mystatic class,並繼承自Cstatic。然後在Message Map裡加入:

ON_WM_CTLCOLOR_REFLECT()

並在物件宣告裡加入:

afx_msg HBRUSH CtlColor(CDC* pDC, Uint nCtlColor);

並加入兩個變數:

COLORREF m_Back_Color;
CBrush* m_Brush;

記得要初始化與解構釋放。然後在變更背景顏色時:

m_Back_Color = 要塗上的背景顏色;
delete m_Brush;
m_Brush = new CBrush();
if (m_Brush != NULL) m_Brush->CreateSolidBrush(m_Back_Color);

最後實作CtlColor函數:

pDC->SetBkColor(m_Back_Color);
if (m_Brush == NULL) return NULL;
return *m_Brush;

除了背景顏色外,Container使用的前景顏色、字形也可以透過AmbientForeColor與AmbientFont取得,然後再於CtlColor裡,呼叫SetTextColor與SetFont來處理。這部份便不再說明下去。注意變更字形大小,可能意味著必須重新調整對話視窗內,各控制項的位置與大小。

作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/1/19 上午 03:15:39
(二) 屬性與方法

由於ActiveX控制項本身亦支援IDispatch界面(automation技術),故和COM元件一樣,可提供外界進行屬性的存取與方法的呼叫。屬性(Property)與方法(Method)的加入方式有兩種,一種和COM元件相同,從ClassView裡選界面,按滑鼠右鍵,執行對應的選單功能加入;一種是從ClassWizard裡的automation加入。

不過和COM元件不同的是,對於ActiveX控制項,MFC已提供了許多的內定屬性(Stock Property),需要的話,直接啟用便可以,不必再自行撰寫程式碼(但MFC只負責記錄屬性值,實際的用途仍由程式設計師自行決定與處理)。這些內定屬性為:

1. Appearance:外觀型式,0表Flat,1表3D
2. BackColor:背景顏色
3. BorderStyle:外框型式,0表無外框,1表有外框
4. Caption:文字內容
5. Enabled:是否啟用
6. Font:字型
7. ForeColor:前景顏色
8. hWnd:控制項的視窗Handle
9. ReadyState:控制項的現行狀態,請參閱COleControl::GetReadyState裡的說明
10. Text:文字內容,和Caption相同,只是使用不同名稱罷了

例如前面的例子,若背景顏色交給使用者來設定,而不是直接使用Container的背景色,那麼可進入Add Property的對話視窗,在external name裡選擇BackColor(Implementation維持Stock),並修改程式,將背景色的使用改為GetBackColor函數。使用ActiveX Control Test Container來測試,在Control的Invoke Methods裡,便可看到兩個BackColor方法:PropGet與PropPut,分別表示讀取與設定BackColor的屬性值。

現在先選PropPut,由於BackColor屬性值是OLE_COLOR,它是一個4 byte長整數,故要選擇VT_I4,值的格式為0x00BBGGRR,可自行填入,選擇Set Value後,再按下Invoke,便可看到控制項的背景顏色變更了。若未立即變更,檢查一下對話視窗或控制項在變更背景顏色後,是否有呼叫Invalidate(true)進行重繪動作。

現在來試著自己增加一個屬性,以存取EditBox裡的字串。一樣進入Add Property的對話視窗,Implementation選擇Get/Set methods,建立一個EditBoxData屬性,型態為BSTR(字串)。然後在對應的存取函數裡,存取對話視窗裡的EditBox內容:

BSTR CMyActiveXCtrl::GetEditBoxData()
{
CString strResult;
m_Main_Dialog.m_Edit.GetWindowText(strResult);
return strResult.AllocSysString();
}

void CMyActiveXCtrl::SetEditBoxData(LPCTSTR lpszNewValue)
{
m_Main_Dialog.m_Edit.SetWindowText(lpszNewValue);
SetModifiedFlag();
}

注意在SetEditBoxData裡呼叫了SetModifiedFlag函數,表示這個控制項已經被修改(提示Container應加以儲存)。

現在可以用ActiveX Control Test Container來測試看看這個EditBoxData屬性是否正常運作(EditBox應該會變更字串)。設好屬性後,將它存檔,然後再載入,會發現原本設好的字串不見了,沒有顯示在EditBox裡,但背景顏色卻有變更。這是因為我們尚未實作非內定屬性的檔案讀寫(Serialize)所致,這部份主要是在DoPropExchange函數中處理,內定的寫法為:

void CMyActiveXCtrl::DoPropExchange(CPropExchange* pPX)
{
ExchangeVersion(pPX, MAKELONG(_wVerMinor, _wVerMajor));
COleControl::DoPropExchange(pPX);
}

第一行是讀寫版本,第二行是讀寫內定屬性值(所以BackColor有作用)。現在我們要加入EditBoxData屬性值的讀寫,不過在加之前,有些情況要注意一下:

作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/1/19 上午 03:16:12
1. 屬性在讀檔時,控制項尚未建立視窗,故讀到的資料無法直接設進EditBox裡,必須另建立一個變數(假設為m_String)記錄,而在OnInitDialog時,將之設入。
2. EditBox內容在存檔時,應重新讀出到一變數,然後再進行存檔。

以下的程式碼便可以完成前述的兩種情況(加在COleControl::DoPropExchange之後):

if (!pPX->IsLoading()) m_Main_Dialog.m_Edit.GetWindowText(m_Main_Dialog.m_String);
PX_String(pPX, _T("EditBoxData"), m_Main_Dialog.m_String, _T(""));

其中IsLoading用來判斷目前是在讀檔還是寫檔,PX_String則是用來存取字串型態的屬性值。現在重新再測試,EditBoxData屬性已能正確存檔讀檔了。但如果去讀之前的存檔,便會發生錯誤,這是因為我們未做版本控制。在主cpp檔裡有兩個變數:

const WORD _wVerMajor = 1;
const WORD _wVerMinor = 0;

這兩個便是用來控制版本,因為我們有新增屬性的存檔,故版本要增加(假設變成1.1版),然後在DoPropExchange裡,依照檔案的版本別,來決定是否讀取該屬性值:

if (pPX->GetVersion() >= (DWORD)MAKELONG(1,1)) 讀取EditBoxData屬性值

另外,使用者在EditBox有更改字串時,EditBoxData的屬性值已變更,應呼叫SetModifiedFlag設定該控制項已變更。由於前面在讀檔時,是讀取到m_String變數裡,存檔後,亦重新更新m_String變數,故在EditBox的OnChange函數裡,可加上下列程式碼來判斷處理:

CString cs;
m_Edit.GetWindowText(cs);
if (cs != m_String) ((COleControl*)GetParent())->SetModifiedFlag();

現在可以測試看看網頁ActiveX控制項帶參數的結果:

<OBJECT WIDTH=637 HEIGHT=293 CLASSID="CLSID:0126ADC4-989D-4CED-93AD-F66BAB2E3D71">
    <PARAM NAME="EditBoxData" VALUE="test">
    <PARAM NAME="BackColor" VALUE="255">
</OBJECT>

裡面PARAM tag便是用來定義控制項的參數值,於是背景變紅色,EditBox裡初始字串為”test”。

方法的加入比屬性值簡單很多,把它看成是一個函數(副程式)就對了。進入Add Method的對話視窗,輸入方法的名稱,決定它的傳回值,以及該有的參數,然後再去實作這個方法所需的程式即可。外界在引用時,便如同呼叫函數一樣,給定參數呼叫,然後取得傳回值做進一步處理。

當然MFC也有一些內定的方法可直接啟用:

1. DoClick:發送一個Click的事件(Event,請見後述)。
2. Refresh:重新顯示控制項。

作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/1/19 上午 03:16:41
(三) 屬性頁

ActiveX控制項的屬性值,除了利用函數呼叫的方法來取得與設定外,也可以透過屬性頁來變更。當然,MFC也有一些內定的屬性頁可使用,但必須透過CLSID來引用:

1. CLSID_CFontPropPage:字型
2. CLSID_CColorPropPage:色彩
3. CLSID_CPicturePropPage:圖片

例如我們有啟用BackColor屬性,因此可以啟動色彩的屬性頁來設定,方式為:

BEGIN_PROPPAGEIDS(CMyActiveXCtrl, 2)
PROPPAGEID(CMyActiveXPropPage::guid)
PROPPAGEID(CLSID_CColorPropPage)
END_PROPPAGEIDS(CMyActiveXCtrl)

注意CMyActiveXCtrl後面的數字是表示有幾個屬性頁,出現的次序亦按裡面的排列順序而定。內定屬性頁的呈現方式,隨著MFC版本而異,例如VC++ 6.0和VS2008的固定屬性頁,呈現的方式與內容就不太一樣。

如果我們自己加入的屬性,其型態是字型、色彩或圖片時,也可以利用前述的內定屬性頁來加以設定。假設我們新增一個無作用的色彩屬性值,稱為OptionColor,那麼進ClassWizard的Message Maps,選控制項的OnMapPropertyToPage,然後在裡面加入(在return之前):

switch(dispid)
{
case dispidOptionColor:
*lpclsid = CLSID_CColorPropPage;
*pbPageOptional = true;
return true;
}

亦即OptionColor是使用色彩屬性頁(CLSID_CColorPropPage)來設定。測試後,便可看到色彩屬性頁可選擇BackColor或OptionColor來變更。

非前述三種型態的屬性,只能自己做出屬性頁來設定,但即使是前述三種型態的屬性,當然也可以自己寫屬性頁來設定。當我們創建ActiveX控制項的專案時,便會自動產生一個IDD_PROPPAGE_xxx的屬性頁對話視窗,若有需要自設屬性值者,便可以在這個對話視窗完成。若不需此屬性頁,也可從前述PROPPAGEIDS裡去除。

現在在裡面做一個EditBox,然後透過ClassWizard,設定一個變數給它(Category要選Value),並記得Optional property name要輸入EditBoxData,以便與該屬性做連結。測試後,便可看到該EditBox可設定EditBoxData的值。

若要新增一個自寫的屬性頁,和一般的對話視窗新增程序一樣,但Style要選Child、Border要選None,同時視窗的Class要繼承自COlePropertyPage。寫好後,將該Class的guid加入PROPPAGEIDS裡即可。

作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/1/19 上午 03:17:22
(四) 事件

方法與屬性主要是提供給Container,以便控制ActiveX控制項的行為;事件(Event)則是ActiveX控制項主動通知Container進行溝通的一種方式(可帶參數)。加入的方式可透過ClassWizard的ActiveX Event,或是從ClassView裡選界面,按滑鼠右鍵,執行對應的選單功能加入。

同樣的,MFC也已提供了不少的內定事件可直接啟用:

1. Click:滑鼠單擊
2. DblClick:滑鼠雙擊
3. Error:發生錯誤
4. KeyDown:鍵盤按下
5. KeyPress:字元輸入
6. KeyUp:鍵盤放開
7. MouseDown:滑鼠按下
8. MouseMove:滑鼠移動
9. MouseUp:滑鼠放開
10. ReadyStateChange:Ready狀態變更

這裡我們新增一個自己的事件,通知對話視窗滑鼠左鍵按下時的座標。首先增加一個ClickIn事件,參數表裡加入x和y,型態分別為OLE_XPOS_PIXELS和OLE_YPOS_PIXELS。然後用ClassWizard,替對話視窗加入WM_LBUTTONDOWN的訊息處理:

((COleControl*)GetParent())->MyClickIn(point.x, point.y);

並在控制項Class裡實作MyClickIn函數:

void CMyActiveXCtrl::MyClickIn(int x, int y)
{
FireClickIn(x,y);
}

利用ActiveX Control Test Container測試,當滑鼠在控制項裡按下,底下便會顯示出ClickIn的事件與參數值。當然網頁也可對控制項的Event作出回應,不過引用的物件要先給個ID:

<OBJECT ID="MyActiveX1" ...> … </OBJECT>
<SCRIPT FOR=MyActiveX1 EVENT=ClickIn(x,y)>
<!--
   alert("ActiveX is clicked in (" + x + "," + y + ")\n" + "Edit Data: " + MyActiveX1.EditBoxData)
-->
</SCRIPT>

其他與ActiveX控制項相關的知識,皆可查閱MSDN “Visual C++ Programmer's Guide”/“Adding User-Interface Features”/“Details”/“ActiveX Control Topics”來進一步了解。
作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/1/19 上午 03:18:02
五. IE安全性

當我們第一次在IE上使用自己寫的ActiveX控制項,或是在引用時加上參數,IE都會出現安全性的警告,以下便是要說明如何去除這項警告。至於為何會出現這項警告,以及其他詳細的做法與說明,可參考MSDN “Safe Initialization and Scripting for ActiveX Controls”一文。

IE的安全性警告,主要有初始安全性(Initialization Security)與腳本安全性(Scripting Security),解決方法有兩種,一種是透過Component Categories Manager,將控制項登錄成安全的,一種是實作IObjectSafety界面,告知IE控制項是安全的。這裡要介紹的,便是第一種方法(第二種方法較麻煩)。

首先在主cpp檔裡(有DllRegisterServer函數的),定義一個CLSID_SafeItem的常數如下:

#include "comcat.h"
#include "objsafe.h"

const CATID CLSID_SafeItem =
{ 0x126adc4, 0x989d, 0x4ced, { 0x93, 0xad, 0xf6, 0x6b, 0xab, 0x2e, 0x3d, 0x71 }};

裡面的值係由xxxCtl.cpp裡的內容拷貝過來,然後再加上括號而成:

IMPLEMENT_OLECREATE_EX(CMyActiveXCtrl, "MYACTIVEX.MyActiveXCtrl.1",
0x126adc4, 0x989d, 0x4ced, 0x93, 0xad, 0xf6, 0x6b, 0xab, 0x2e, 0x3d, 0x71)

接著實作下列函數(直接拷貝貼上即可):

HRESULT CreateComponentCategory(CATID catid, Wchar *catDescription)
{
ICatRegister *pcr = NULL;
HRESULT hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr, NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister, (void**)&pcr);
if (FAILED(hr)) return hr;
CATEGORYINFO catinfo;
catinfo.catid = catid;
catinfo.lcid = 0x0409 ; // english
size_t len = wcslen(catDescription);
if (len > 127) len = 127;
memcpy(catinfo.szDescription,catDescription,len*sizeof(WCHAR));
catinfo.szDescription[len] = 0;
hr = pcr->RegisterCategories(1,&catinfo);
pcr->Release();
return hr;
}

HRESULT RegisterCLSIDInCategory(REFCLSID clsid, CATID catid)
{
ICatRegister *pcr = NULL;
HRESULT hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr, NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister, (void**)&pcr);
if (SUCCEEDED(hr))
{
CATID rgcatid[1];
rgcatid[0] = catid;
hr = pcr->RegisterClassImplCategories(clsid,1,rgcatid);
}
if (pcr != NULL) pcr->Release();
return hr;
}
 
HRESULT UnRegisterCLSIDInCategory(REFCLSID clsid, CATID catid)
{
ICatRegister *pcr = NULL;
HRESULT hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr, NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister, (void**)&pcr);
if (SUCCEEDED(hr))
{
CATID rgcatid[1];
rgcatid[0] = catid;
hr = pcr->UnRegisterClassImplCategories(clsid, 1, rgcatid);
}
if (pcr != NULL) pcr->Release();
return hr;
}

作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/1/19 上午 03:18:24
接著在註冊的DllRegisterServer函數裡,加上(在其他的註冊之後):

HRESULT hr = CreateComponentCategory(CATID_SafeForInitializing, L"Controls safely initializable from persistent data!");
if (FAILED(hr)) return hr;
hr = RegisterCLSIDInCategory(CLSID_SafeItem, CATID_SafeForInitializing);
if (FAILED(hr)) return hr;
hr = CreateComponentCategory(CATID_SafeForScripting, L"Controls safely scriptable!");
if (FAILED(hr)) return hr;
hr = RegisterCLSIDInCategory(CLSID_SafeItem, CATID_SafeForScripting);
if (FAILED(hr)) return hr;

並在反註冊的DllUnregisterServer函數裡,加上(在其他的註冊之前,MSDN與網路上的範例次序都是錯的,會產生0x80070002的錯誤碼):

HRESULT hr = UnRegisterCLSIDInCategory(CLSID_SafeItem, CATID_SafeForInitializing);
if (FAILED(hr)) return hr;
hr = UnRegisterCLSIDInCategory(CLSID_SafeItem, CATID_SafeForScripting);
if (FAILED(hr)) return hr;

現在試著在網頁中引用,並加上參數,已不會再出現安全性的警告,即使將安全性等級調到中高級,也能正常顯示。

作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/1/19 上午 03:19:08
六. 封裝

若ActiveX控制項需要能夠直接從網路下載安裝給IE使用,必須要有兩個步驟,第一是確認控制項能自我登錄(Self-Registration),第二則是封裝成CAB檔以供安裝。

首先用文字編輯器開啟rc檔,檢查VERSIONINFO裡的StringFileInfo Block是否有下列的值:

VALUE "OLESelfRegister", "\0"

如果沒有的話,便要自己加入,以啟動自我登錄,並加入DllRegisterServer與DllUnregisterServer兩個函數。一般VC++創建的COM元件或ActiveX控制項專案,都會自動加入。

接下來便是將ActiveX控制項封裝成CAB檔,這部份需要Microsoft Cabinet Software Development Kit來處理,不過微軟網站已不提供該軟體的下載,故必須找其他的下載點,例如:

http://web.archive.org/web/20070403215326/http://download.microsoft.com/download/platformsdk/cab/2.0/w98nt42kmexp/en-us/cabsdk.exe

或是

http://www.pixelsplasher.com/_downloads/software/Microsoft-Cabinet-SDK/

有了工具軟體後,我們必須撰寫一個安裝ActiveX控制項用的INF檔(就像一般安裝程式都會有的INF檔一樣),以列出應該安裝那些檔案。關於INF檔的詳細格式與說明,可參考MSDN裡的”About INF File Architecture”。以下簡單說明相關的部份。

INF檔裡,必須在Add.Code的Section裡標示出所有要安裝的檔案,格式為:

[Add.Code]
filename1=section-name1
filename2=section-name2

注意安裝的次序是由後往前,因此通常OCX檔會放在第一個。所有相依的dll檔都要列出來,以免無法正確安裝。建議是將MFC library採用static的方式連結,以省去條列dll的麻煩。之後對每個檔案進行Section描述,格式為:

[section-name1]
key1=value1
key2=value2

以下為可用的key名稱與值:

1. File-%opersys%-%cpu%=[url | ignore | thiscab]
描述指定作業系統與CPU所適用的檔案所在,可有多個不同的key。%opersys%固定為win32,CPU則可為x86、ia64、amd64之一。thiscab表示檔案在CAB裡,ignore表示指定的作業系統與CPU不需要該檔案,url則標示檔案應下載的網址。
2. File=[url | thiscab]
和前述key作用相同,但不限作業系統與CPU。安裝時,會先處理前述key,找不到符合的作業系統與CPU後,最後才會處理本key。
3. FileVersion=a,b,c,d
描述安裝時,該檔案所需的最小檔案版本(可對檔案按滑鼠右鍵,選內容來查看目前的檔案版本)。如果省略,則任何版本皆可。
4. Clsid={nnnnnnnn-nnnn-nnnn-nnnn-nnnnnnnnnnnn}
描述元件的CLSID。對於ActiveX控制項而言,便是HTML裡Object tag中,CLASSID所描述的CLSID。
5. DestDir=[10 | 11]
描述檔案應安裝的位置。10表示Windows目錄,11表示System32目錄。如果省略的話,則安裝在Downloaded Program Files目錄。
6. RegisterServer=[yes | no]
描述是否加以註冊。省略的話,則視有否OleSelfRegister,決定是否進行自我註冊。

以下便是一個簡單的範例(假設檔名是MyActiveX.inf):

[Add.Code]
MyActiveX.ocx=MyActiveX.ocx

[MyActiveX.ocx]
File=thiscab
FileVersion=1,0,0,1
Clsid={0126ADC4-989D-4CED-93AD-F66BAB2E3D71}
RegisterServer=yes

作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/1/19 上午 03:19:32
接下來便用Microsoft Cabinet Software Development Kit裡的CABARC.EXE封裝成CAB檔(記得要將所有要封裝的檔案放在同一目錄裡):

cabarc -s 6144 N MyActiveX.cab MyActiveX.ocx MyActiveX.inf

執行參數可直接執行cabarc得知,例如-s 6144便是保留6KB空間做為簽署之用(後述)。

接著準備測試是否能成功下載。首先反註冊原本的元件(方法前面已提過),然後在html的Object tag加上CODEBASE屬性,以標示控制項下載的網址:

<OBJECT WIDTH=637 HEIGHT=293 CLASSID="CLSID:0126ADC4-989D-4CED-93AD-F66BAB2E3D71" CODEBASE="http://localhost/MyActiveX.cab#version=1,0,0,1">
</OBJECT>

檔案後面也可不加version指示,這樣的話,只要Client端有下載安裝,便不會再次下載安裝。若有標示,則當安裝的版本較舊時,便會自動再次下載安裝。

將CAB檔拷貝到正確的地方,並修改IE的安全性,將”下載未簽署的ActiveX控制項”設為提示,然後顯示網頁,會發現IE阻擋了它的安裝。選擇安裝此附加元件,會看到發行者不明,這是因為我們做的CAB檔尚未經過簽署。不過安裝後,便能正常執行ActiveX控制項。

作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/1/19 上午 03:20:07
七. 簽署

由於ActiveX控制項的控制權很大,為了避免使用者誤安裝帶有病毒或後門程式的ActiveX元件,IE要求安裝檔必須經過簽署,凡是未簽署的ActiveX控制項,IE內定都是不下載安裝的,即便是在中低等級的情況下。

所謂簽署,就是在安裝檔加上開發者的憑證(包含了認證單位資訊、開發者資訊、開發者公鑰)、以及安裝檔的數位簽章與時間戳記(以開發者私鑰加密,確認安裝檔未被修改)。當IE要下載安裝元件前,會先將安裝檔裡的數位簽章與時間戳記傳送給認證單位,確認資訊無誤後,才提示使用者是否要安裝。

常見的認證單位有Verisign、Thawte等,前者最知名但也最貴(費用每年US$499,據說微軟某些服務僅能接受其協力廠商Verisign所發的憑證),後者則較便宜(費用每年US$299)。兩者的申請流程都差不多,必須填寫申請者的名字、電話、住址、Email、公司資訊,認證單位會再連絡並確認申請者的身份後,才會簽發憑證檔與私鑰檔。如果是國內公家單位的開發商,其實也可跟公家單位的認證中心申請憑證與私鑰,效果也是一樣的。

拿到憑證檔(假設為mycert.spc)和私鑰檔(假設為mykey.pvk)後,我們還需要檔案簽署工具Signcode.exe,這個程式可從微軟網站下載,使用方法則參考MSDN “Signing and Checking Code with Authenticode”一文。以下為一個例子:

SignCode -spc mycert.spc -v mykey.pvk -n "MyActiveX" -i http://www.my.com -t http://timestamp.verisign.com/scripts/timstamp.dll MyActiveX.cab

各參數意義如下:

1. -spc:指定含有軟體發行憑證的SPC檔案
2. -v:指定含有私密金鑰的PVK檔
3. -n:指定用以代表要簽名的檔案內容之文字
4. -i:指定可進一步得知元件詳細說明的網址(開發者自己提供的網頁)
5. -t:指示時間戳記伺服器的網址

除了經由公開的認證單位簽證外,也可以自己架設Windows Server 2003認證伺服器(安裝時勾選Certificates Services),自已簽發憑證與私鑰。不過這部份個人並未試過,無法得知其優缺點。

 板主 : 青衫 , Raymond
 > Visual C++ - 討論區
 - 最近熱門問答精華集
 - 全部歷史問答精華集
 - Visual C++ - 知識庫
  ■ 全站最新Post列表
  ■ 我的文章收藏
  ■ 我最愛的作者
  ■ 全站文章收藏排行榜
  ■ 全站最愛作者排行榜
  ■  月熱門主題
  ■  季熱門主題
  ■  熱門主題Top 20
  ■  本區Post排行榜
  ■  本區評價排行榜
  ■  全站專家名人榜
  ■  全站Post排行榜
  ■  全站評價排行榜
  ■  全站人氣排行榜
 請輸入關鍵字 
  開始搜尋
 
Top 10
評價排行
Visual C++
1 青衫 11070 
2 Raymond 10090 
3 Clier 7630 
4 小約翰 2500 
5 Cog 2030 
6 coco 1870 
7 aming 1410 
8 牧童哥 1400 
9 r2109 1380 
10 Akira 1350 
Visual C++
  專家等級 評價  
  一代宗師 10000  
  曠世奇才 5000  
  頂尖高手 3000  
  卓越專家 1500  
  優秀好手 750  
Microsoft Internet Explorer 6.0. Screen 1024x768 pixel. High Color (16 bit).
2000-2019 程式設計俱樂部 http://www.programmer-club.com.tw/
0.21875