創(chuàng)建ActiveX工程: 可以使用VC建立MFC工程或者ATL工程來(lái)進(jìn)行ActiveX控件開(kāi)發(fā),。使用MFC方式建立,,系統(tǒng)會(huì)自動(dòng)實(shí)現(xiàn)一些ActiveX控件必須的接口,開(kāi)發(fā)人員只需關(guān)注業(yè)務(wù)就可以了,;使用ATL方式建立,,開(kāi)發(fā)人員需要實(shí)現(xiàn)十幾個(gè)COM接口(ActiveX實(shí)際上是COM的一種),需要對(duì)COM有一定的了解,。但是使用MFC方式,,最后發(fā)布的安裝包要包含引入了的MFC的DLL(視頻控件引入了兩個(gè),總共3M左右),,會(huì)導(dǎo)致安裝包會(huì)比ATL方式大一點(diǎn)。 1,、MFC方式 使用MFC方式創(chuàng)建ActiveX工程比較簡(jiǎn)單,,只要選擇了MFC ActiveX工程,之后都按系統(tǒng)下一步就可以了,。
2,、ATL方式 使用ATL方式創(chuàng)建,沒(méi)有仔細(xì)研究過(guò),,待補(bǔ)充,。
調(diào)試ActiveX工程 可以使用兩種方式調(diào)試ActiveX控件:ActiveX測(cè)試容器或者IE,建議使用IE作為調(diào)試工具,,因?yàn)?/span>ActiveX測(cè)試容器有些地方和IE不太一樣,,而且實(shí)際使用是嵌入到IE中使用的。 1,、使用ActiveX控件測(cè)試容器 可以使用系統(tǒng)自帶的ActiveX測(cè)試容器來(lái)進(jìn)行調(diào)試,,調(diào)試時(shí)選擇自己的ActiveX控件名,,即可打開(kāi)創(chuàng)建的工程。
2,、使用IE 也可以使用IE來(lái)進(jìn)行調(diào)試,,需要自己先創(chuàng)建一個(gè)包含了此ActiveX控件(通過(guò)CLSID指定)的HTML文件,調(diào)試時(shí)指定參數(shù)為此文件,。
HTML文件內(nèi)容如: <BODY> <OBJECT ID="NVS_VAE" CLASSID="CLSID:c8cd5ebc-817b-401f-ab06-05cc55c8d9ee"> </OBJECT> </BODY> 其中ID可以隨便定義,,CLASSID是與創(chuàng)建的ActiveX工程中自動(dòng)生成的ID保持一致(在創(chuàng)建工程的×××Ctrl.cpp中) // 初始化類(lèi)工廠和guid
IMPLEMENT_OLECREATE_EX(CNVS_VAECtrl,"NVS_VAE.NVS_VAECtrl.1", 0xc8cd5ebc, 0x817b, 0x401f, 0xab, 0x6, 0x5, 0xcc, 0x55, 0xc8, 0xd9, 0xee) 注:使用IE8的話,默認(rèn)是多線程,,不支持ActiveX控件的調(diào)試,,可以修改如下注冊(cè)表,來(lái)支持調(diào)試 Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Main] "TabProcGrowth"=dword:00000000
JavaScrip調(diào)用ActiveX控件中的對(duì)外方法 頁(yè)面可以通過(guò)JavaScript調(diào)用ActiveX控件中的方法與控件進(jìn)行交互: 1,、傳入?yún)?shù) <BODY> <OBJECT ID="NVS_VAE" CLASSID="CLSID:c8cd5ebc-817b-401f-ab06-05cc55c8d9ee"> <PARAM NAME="lInitFacePara" VALUE="0"> </OBJECT> </BODY>
2,、調(diào)用方法 通過(guò)getElementById 方法,傳入控件ID(比如前面的MyPostItCtrl),,來(lái)調(diào)用ActiveX控件中提供的接口(假定StartRealVideo為ActiveX中提供的對(duì)外接口) <SCRIPT language=JScript> function test() { document.getElementById("MyPostItCtrl").StartRealVideo (“攝像機(jī)ID”,”碼流ID”); } </SCRIPT>
ActiveX控件的打包安裝 可以把ActiveX控件打成CAB包,,在頁(yè)面中指定該CAB包的位置,如果目標(biāo)機(jī)器訪問(wèn)頁(yè)面時(shí),,沒(méi)有安裝過(guò)此控件,,則IE會(huì)自動(dòng)下載此CAB包到目標(biāo)機(jī)器,并進(jìn)行安裝 1,、 確認(rèn)ActiveX控件依賴于哪些DLL 使用vc的DEPENDS.EXE(Microsoft Visual Studio\Common\Tools目錄下)打開(kāi)ocx/dll文件,,即可看到其依賴的dll文件
2、 創(chuàng)建INF文件,,用記事本編輯以下信息
[version] signature="$CHINA$" AdvancedINF=1.0
[Add.Code] evS1300.ocx=evS1300.ocx msvcr71.dll=msvcr71.dll mfc71.dll=mfc71.dll msvcp71.dll=msvcp71.dll
[evS1300.ocx] file=thiscab clsid={0440906E-9BD6-4F3E-B65A-39E1B339D9DA} FileVersion=1,0,0,0 RegisterServer=yes
[msvcr71.dll] file-win32-x86=thiscab RegisterServer=no DestDir=11 FileVersion=7,10,3052,4
[mfc71.dll] file-win32-x86=thiscab RegisterServer=no DestDir=11 FileVersion=7,10,3077,0
[msvcp71.dll] file-win32-x86=thiscab RegisterServer=no DestDir=11 FileVersion=7,10,3077,0 在evS1300.inf的內(nèi)容里,,[version]和[Add.Code]項(xiàng)是必須的,[Add.Code]的鍵值項(xiàng)的多少取決于以下你所配制項(xiàng)的多少,。[msvcr71.dll],、[mfc71.dll]、[msvcp71.dll]就是上面我所說(shuō)不是必須的項(xiàng),,只要你想把msvcr71.dll,、mfc71.dll、msvcp71.dll包括在發(fā)布包里,,那這么三項(xiàng)就必須寫(xiě)在inf里,,而這三項(xiàng)的具體內(nèi)容是固定的,可復(fù)制過(guò)去即可,。最為關(guān)鍵的就是[evS1300.ocx]項(xiàng),,其中有clsid和FileVersion就是evS1300.ocx的classId和version,這要求必須一至,,否我們發(fā)布出去的CAB包時(shí)不能在客戶端自動(dòng)更新下載安裝,。說(shuō)到這里,,那我們?nèi)绾尾拍苤?/span>evS1300.ocx里面的classId和version呢?我在上面的必備條件里介紹到有一個(gè)用于查看ocx控件的工具ActvxDoc,,對(duì),,就是用它,我們雙擊這個(gè)文件運(yùn)行它,,此時(shí)可以看到圖13所示的界面:
在圖13的界面里,,點(diǎn)擊“File”à“Open…”,打開(kāi)您所要查看的OCX控件,,如圖14所示:
打開(kāi)了控件之后,,我們?cè)诮缑娴挠疫叢课弧?span style="font-family:Calibri">Class”的下拉框里選擇“”就可以看到我們想要查找的FileVersion和classId,如圖15所示:
3,、 使用iexpress進(jìn)行打包 可以使用系統(tǒng)自帶的iexpress.exe(C:\WINDOWS\system32目錄下)工具進(jìn)行打包,。工具默認(rèn)是8.3命名格式,會(huì)把不符合命名規(guī)則的文件名截?cái)?,可以在打包過(guò)程中選擇長(zhǎng)命名,。
之后都下一步即可。 4,、 對(duì)CAB包進(jìn)行簽名 1,、 制作(購(gòu)買(mǎi))證書(shū) 正式的證書(shū)需要向相關(guān)機(jī)構(gòu)購(gòu)買(mǎi),可以制作測(cè)試的證書(shū),,步驟如下: 在Visual Studio的安裝目錄下,,有制作證書(shū)和簽名的相關(guān)工具 E:\Microsoft Visual Studio 8\SDK\v2.0\Bin\ makecert.exe 命令行中執(zhí)行如下命令來(lái)創(chuàng)建證書(shū)和私鑰文件: E:\Microsoft Visual Studio 8\SDK\v2.0\Bin>makecert.exe -sv D:VAE.pvk -ss VAE -n "CN=huawei" -$ commercial -r D:VAE.cer 在彈出的窗口中輸入證書(shū)密鑰密碼
再次確認(rèn)密碼
證書(shū)文件就生成在指定位置了。 2,、 對(duì)CAB包進(jìn)行簽名 在Visual Studio的安裝目錄下,,有制作證書(shū)和簽名的相關(guān)工具 E:\Microsoft Visual Studio 8\SDK\v2.0\Bin\ signtool.exe 在命令行執(zhí)行:signtool.exe signwizard命令,會(huì)啟動(dòng)簽名向?qū)?/span>
簽名向?qū)В?/span>
下一步,,選擇要打包的CAB文件
下一步選擇自定義
下一步選擇“從文件選擇”,,選擇前面生成的證書(shū)文件
下一步選擇“磁盤(pán)上的私鑰文件”,選擇前面生成的私鑰文件
下一步輸入密碼
確定后選擇MD5加密
之后一直下一步(根據(jù)需要填寫(xiě)內(nèi)容,,也可都默認(rèn)) 最后再輸入一次密碼,,就成功簽名了
5,、 頁(yè)面指定路徑 頁(yè)面中指定CAB包位置和版本號(hào) <BODY> <OBJECT ID="NVS_VAE" CLASSID="CLSID:c8cd5ebc-817b-401f-ab06-05cc55c8d9ee"codeBase="NVS_VAE.CAB#version=1,0,0,1"> </OBJECT> </BODY> 訪問(wèn)到此頁(yè)面,,IE自動(dòng)判斷CLSID在目標(biāo)機(jī)器上是否有注冊(cè),沒(méi)有注冊(cè)重新下載,,并注冊(cè),,有注冊(cè),比較版本號(hào),,有更新則取根據(jù)setup.ini中的版本號(hào)進(jìn)行更新,??梢缘?/span>C:\WINDOWS\Downloaded Program Files目錄查看CAB包是否有下載安裝,。 6,、 使用安裝程序打包 另外,也可以制作安裝程序(可以使用常用的安裝程序制作工具,,也可以使用VS自帶的建立安裝部署功能來(lái)進(jìn)行),,把安裝程序打到CAB包中,這樣當(dāng)IE下載CAB包的客戶機(jī)上后,,會(huì)根據(jù)setup.ini中的指定執(zhí)行命令,,執(zhí)行安裝程序,由客戶自己選擇安裝路徑 腳本如下: ;//這里注意了,發(fā)行包安裝的信息 [setup.exe] fileVersion=8.0.50727.42 hook=InstallerHook ;//這項(xiàng)并沒(méi)有設(shè)置file-win32-x86的值,,因?yàn)樗⒉辉?/span>CAB里面,,這里設(shè)置了一HOOK,讓HOOK ;//來(lái)處理
//發(fā)行包的安裝來(lái)源 [InstallerHook] ;//這里安裝默認(rèn)是在同X.CAB的目錄,,如果是在網(wǎng)站,,可以更改為URL路徑 ;//如:http://www./vcredist.cab
file-win32-x86=NVS_VAE.CAB ;//這里就是讓CAB自動(dòng)解壓過(guò)后,運(yùn)行CAB包里面的 setup.exe run=%EXTRACT_DIR%\setup.exe
開(kāi)發(fā)過(guò)程中一些經(jīng)驗(yàn)總結(jié) 以下總結(jié)主要由李俊峰(lijunfeng 00165774/huawei,),、李偉(liwei 00165242/huawei,),、姜川(j00132245)總結(jié) 1.自定義CListCtrl,使用自定義列表頭CHeaderCtrl 在自定義CListCtrl的列表頭時(shí),,需要替換系統(tǒng)自帶的CHeaderCtrl,,網(wǎng)上代碼的處理方式通常是在自定義的MyListCtrl中重寫(xiě)PreSubclassWindow,并在其中調(diào)用(其中m_MyHeaderCtrl是自定義的CHeaderCtrl,,作為MyListCtrl的成員變量) if(GetHeaderCtrl()) m_MyHeaderCtrl.SubclassWindow(GetHeaderCtrl()->m_hWnd); 這種方法在MFC桌面程序中可以奏效,。但是放到ActiveX中時(shí),此處的GetHeaderCtrl()==NULL,,所以無(wú)法達(dá)到替換表頭的效果(具體原理未知,,有興趣的同學(xué)可以研究)。解決辦法是在創(chuàng)建自定義的MyListCtrl之后手動(dòng)調(diào)用 m_MyHeaderCtrl.SubclassWindow(GetHeaderCtrl()->m_hWnd); 當(dāng)然需要把這個(gè)包裝成一個(gè)InitMyHeader之類(lèi)的public方法放到MyListCtrl中,。 完整代碼請(qǐng)參考VAEListCtrl和VideoListCtrl及其使用,。
2.為ActiveX窗口上的自定義控件添加ToolTip 為自定義控件添加ToolTip時(shí),一般做法是在PreTranslateMessage中添加如下代碼: if (NULL != m_pToolTipCtrl) m_pToolTipCtrl->RelayEvent(pMsg); 希望CToolTipCtrl能夠捕獲鼠標(biāo)移動(dòng)等消息,,但是結(jié)果PreTranslateMessage方法在ActiveX程序中不會(huì)執(zhí)行(經(jīng)驗(yàn)證在ProjectName+Ctrl(即整個(gè)ActiveX獲得輸入焦點(diǎn))時(shí)PreTranslateMessage方法會(huì)執(zhí)行),。 解決辦法是為控件添加OnMouseMove消息響應(yīng),在OnMouseMove(UINT nFlags, CPoint point)方法中添加代碼: //構(gòu)造一個(gè)MSG MSG msg; msg.hwnd = m_hWnd; msg.message = WM_MOUSEMOVE; msg.wParam = LOWORD(point.x); msg.lParam = LOWORD(point.y); msg.time = 0; msg.pt.x = LOWORD(point.y); msg.pt.y = HIWORD(point.y); m_ToolTip.RelayEvent(&msg); 這樣才會(huì)使鼠標(biāo)在控件上移動(dòng)時(shí)顯示ToolTip,,但是要求要先點(diǎn)選過(guò)該控件才行,。如果想要更進(jìn)一步去掉這個(gè)先點(diǎn)選控件的限制,則需要在自定義控件的父窗口中響應(yīng)OnMouseMove消息,并構(gòu)造MSG關(guān)聯(lián)到ToolTip,。此時(shí)需要注意point的坐標(biāo)轉(zhuǎn)換,。 完整的相關(guān)代碼(PTZControlWnd和BitmapSlider)如下:
父窗口PTZControlWnd: //成員變量聲明 CBitmapSlider m_bsSpeedOrStepsize; voidPTZControlWnd::OnMouseMove(UINTnFlags,CPointpoint) { (void)nFlags; UpdateToolTipText();//更新ToolTip文字 m_bsSpeedOrStepsize->RelayToolTipEventFromParent(point);//此處的point的坐標(biāo)是相對(duì)PTZControlWnd的 VAEBaseWnd::OnMouseMove(nFlags,point); }
控件CBitmapSlider //成員變量聲明 CToolTipCtrl m_ToolTip; voidCBitmapSlider::RelayToolTipEventFromParent(CPoint &point) { //將相對(duì)父窗口的坐標(biāo)轉(zhuǎn)換成屏幕坐標(biāo) GetParent()->ClientToScreen(&point);
//從屏幕坐標(biāo)轉(zhuǎn)換成相對(duì)控件自身CBitmapSlider的坐標(biāo) ScreenToClient(&point);
//因?yàn)樵?/span>CBitmapSlider::OnMouseMove中也要用到,所以提取成函數(shù) RelayToolTipEvent(point); }
voidCBitmapSlider::RelayToolTipEvent(constCPoint &point ) { if (m_ToolTip.m_hWnd !=NULL) { //構(gòu)造一個(gè)MSG MSGmsg;
msg.hwnd =m_hWnd; msg.message =WM_MOUSEMOVE; msg.wParam =LOWORD(point.x); msg.lParam =LOWORD(point.y); msg.time = 0; msg.pt.x = LOWORD(point.y); msg.pt.y = HIWORD(point.y);
m_ToolTip.RelayEvent(&msg); } }
為整個(gè)ActiveX添加ToolTip的方法可以參考MSDN,,同樣沒(méi)有使用PreTranslateMessage方法 http://support.microsoft.com/kb/141871/zh-cn
3.MFC ActiveX控件添加對(duì)外接口 打開(kāi)類(lèi)視圖,,找到ProjectName+Lib,此處為NVS_VAELib
在_D+ProjectName(此處為_DNVS_VAE)上右鍵彈出菜單,,選擇添加方法
彈出窗口如下所示,,填寫(xiě)相關(guān)內(nèi)容,注意字符串類(lèi)型參數(shù)需要選擇BSTR,。 填寫(xiě)完畢點(diǎn)擊『完成』,,如果出現(xiàn)錯(cuò)誤提示,可以關(guān)閉VisualStudio,,刪除解決方案文件夾下的“.ncb”文件,,然后重新打開(kāi)解決方案再添加方法。 修改已經(jīng)添加的對(duì)外接口簽名時(shí)注意除了聲明和實(shí)現(xiàn)外還需要在“.idl”文件中更改相應(yīng)的調(diào)度接口,。
4.對(duì)外接口BSTR參數(shù)轉(zhuǎn)換為char * 生成的方法簽名中的對(duì)應(yīng)BSTR類(lèi)型的是LPCTSTR,。 將javascript傳進(jìn)來(lái)的Unicode字符串轉(zhuǎn)換為內(nèi)部接口使用的char*,需要使用MFC宏,, USES_CONVERSION; char *pszCameraID =W2A(bstrCameraID);
5.關(guān)于網(wǎng)頁(yè)的刷新 當(dāng)ocx加載在網(wǎng)頁(yè)上時(shí),,如果F5刷新,ocx控件會(huì)銷(xiāo)毀ocx的窗口類(lèi),,但是ocx的app類(lèi)是不銷(xiāo)毀的,, 只有當(dāng)網(wǎng)頁(yè)關(guān)閉時(shí),才銷(xiāo)毀app類(lèi),。 1 刷新引起的問(wèn)題 當(dāng)app類(lèi)中有成員變量時(shí),,請(qǐng)注意刷新回來(lái)后變量的值還是刷新前的值。 2 利用刷新app類(lèi)的不析構(gòu)恢復(fù)刷新前的狀態(tài) 可在控件的APP類(lèi)中保存刷新前的值,,刷新后恢復(fù)刷新前的狀態(tài)
6.一個(gè)網(wǎng)頁(yè)中加載兩次(或者多次)OCX控件 同一進(jìn)程加載兩次控件時(shí),,app類(lèi)調(diào)用一次,ocx窗口類(lèi)調(diào)用兩次,。也就是說(shuō)兩個(gè)控件實(shí)例使用的是同一個(gè)app類(lèi)的實(shí)例,,只是有各自的窗口。這時(shí)如果app類(lèi)中有成員變量,,值得注意,。
7.當(dāng)將UNICODE字符串轉(zhuǎn)化為多字符集字符串時(shí)注意的問(wèn)題 當(dāng)unicode字符串中含有漢字時(shí),注意轉(zhuǎn)化前后的字符串長(zhǎng)度,。 例如:TCHAR* pUnicde=_T("abc例子"); // pUnicde長(zhǎng)度5 char* pMutiBtye=T2A(pUnicde); // pMutiBtye長(zhǎng)度是7,,一個(gè)漢字占兩個(gè)字節(jié)
8.開(kāi)發(fā)DLL等控件時(shí),加載字符串/圖片等資源失敗的原因 MFC的對(duì)話框裝載資源是通過(guò)獲取當(dāng)前線程對(duì)應(yīng)的ModuleState保存的ResourceHandler來(lái)裝載資源的,。 所以,,DLL里的代碼,需要在函數(shù)的入口,,首先把當(dāng)前執(zhí)行線程的ModuleState換成該Dll的State,,這樣才能裝載該dll的資源 即:使用AFX_MANAGE_STATE(AfxGetStaticModuleState());
9.SetEvent與PulseEvent的區(qū)別 SetEvent為設(shè)置事件對(duì)象為有信號(hào)狀態(tài);而PulseEvent也是將指定的事件設(shè)為有信號(hào)狀態(tài),,不同的是如果是一個(gè)人工重設(shè)事件,, 正在等候事件的、被掛起的所有線程都會(huì)進(jìn)入活動(dòng)狀態(tài),,函數(shù)隨后將事件設(shè)回,,并返回;如果是一個(gè)自動(dòng)重設(shè)事件,, 則正在等候事件的,、被掛起的單個(gè)線程會(huì)進(jìn)入活動(dòng)狀態(tài),事件隨后設(shè)回?zé)o信號(hào),,并且函數(shù)返回,。 也就是說(shuō)在自動(dòng)重置模式下PulseEvent和SetEvent的作用沒(méi)有什么區(qū)別,但在手動(dòng)模式下PulseEvent就有明顯的不同,, 可以比較容易的控制程序是單步走,,還是連續(xù)走。如果讓循環(huán)按要求執(zhí)行一次就用PulseEvent,,如果想讓循環(huán)連續(xù)不停的運(yùn)轉(zhuǎn)就用SetEvent,, 在要求停止的地方發(fā)個(gè)ResetEvent就OK了。
|
|