目标:基于單文檔程式生成一個程式架構來開發BSOPCServer。
為了友善管理和配置,我們的程式将會基于單文檔類型,支援MFC,然後添加ATL支援。添加ATL是為了友善實作OPCServer的接口。
1.1 應用程式類型
這裡用的開發環境是VS2008 + SP1。打開建立工程向導,建立基于單文檔的應用程式:
由向導生成的檔案清單:
1.2 添加ATL支援
我們可以通過添加一個ATL簡單對象的辦法讓開發環境為我們自動添加支援ATL必須的代碼。
因為在我們的程式中要有一個server對象實作IOPCServer等接口,這裡我們不妨就直接添加一個類COPCServer,繼承于IOPCServer:
1) 選擇“AddClass …”:
2) 添加“ATL Simple Object”:
各項設定如圖所示:
3) 設定線程模型:
線程模型:選擇“Apartment”, 即套間模型,表示我們的COPCServer對象運作在套間裡保證線程安全;
接口:選擇“Custom”, 即定制接口。如果選擇“Dual”則表示該對象支援自動化接口,将會繼承IDispatch 接口。
聚合: 選擇預設值,“允許聚合”,實際上不會用到。
連接配接點:COPCServer對象需要支援連接配接點(實作IConnectionPoint接口),但在這裡我們不選,我們以後手動加進去。
現在來看看向導為我們添加的幾個檔案:
1) OPCServer.h
COPCServer類定義的頭檔案;
2) OPCServer.cpp
COPCServer 類的實作檔案;
目前為空檔案。
3) OPCServer.rgs
COPCServer 類的注冊檔案;
應用程式注冊時會把該類的相關資訊注冊到系統資料庫中,這樣OPC client就可以根據注冊的資訊通路到我們的server了。下面我們會專門介紹server的注冊。
4) BSOPCServer.idl
接口定義檔案;
因為在向導裡我們設定COPCServer是繼承于IOPCServer的,IOPCServer的定義就在該檔案裡。我們來看看它的内容:
1: import "oaidl.idl"; 2: import "ocidl.idl"; 3: 4: [ 5: object, 6: uuid(3418B5C9-878F-436B-BFDA-6EB5EEFF4DF2), 7: helpstring("IOPCServer Interface"), 8: pointer_default(unique) 9: ] 10: interface IOPCServer : IUnknown{ 11: }; 12: 13: [ 14: uuid(B34FC1D6-0D49-4352-B48C-C6C56E856ACA), 15: version(1.0), 16: helpstring("BSOPCServer 1.0 Type Library") 17: ] 18: library BSOPCServerLib 19: { 20: importlib("stdole2.tlb"); 21: [ 22: uuid(2C03FF28-FD3C-4B18-9149-5BFFD190BD38), 23: helpstring("OPCServer Class") 24: ] 25: coclass OPCServer 26: { 27: [default] interface IOPCServer; 28: }; 29: }; |
檔案定義了IOPCServer 接口和BSOPCServerLib類型庫。
值得注意的是,因為我們最終要實作OPC标準裡定義的IOPCServer接口而不是我們上面自己定義的接口,是以我們一會會把這部分code做一下修改。
編譯一下,應該沒有錯誤。
1.3 添加OPCGroup 對象
以同樣的方法添加OPCGroup類:
向導生成的檔案:
OPCGroup.h —— COPCGroup 類定義頭檔案
OPCGroup.cpp ——COPCGroup 類實作檔案
OPCGroup.rgs ——COPCGroup 類注冊檔案
同時,相關代碼會加到BSOPCServer.idl裡面,我們看看都有哪些:
1: import "oaidl.idl"; 2: import "ocidl.idl"; 3: 4: [ 5: object, 6: uuid(3418B5C9-878F-436B-BFDA-6EB5EEFF4DF2), 7: helpstring("IOPCServer Interface"), 8: pointer_default(unique) 9: ] 10: interface IOPCServer : IUnknown{ 11: }; 12: [ 13: object, 14: uuid(60948577-318D-46F9-948C-838DB34EF8EC), 15: helpstring("IOPCGroup Interface"), 16: pointer_default(unique) 17: ] 18: interface IOPCGroup : IUnknown{ 19: }; 20: [ 21: uuid(B34FC1D6-0D49-4352-B48C-C6C56E856ACA), 22: version(1.0), 23: helpstring("BSOPCServer 1.0 Type Library") 24: ] 25: library BSOPCServerLib 26: { 27: importlib("stdole2.tlb"); 28: [ 29: uuid(2C03FF28-FD3C-4B18-9149-5BFFD190BD38), 30: helpstring("OPCServer Class") 31: ] 32: coclass OPCServer 33: { 34: [default] interface IOPCServer; 35: }; 36: [ 37: uuid(7E67188C-27CA-47E8-BB92-C22BD88272E2), 38: helpstring("OPCGroup Class") 39: ] 40: coclass OPCGroup 41: { 42: [default] interface IOPCGroup; 43: }; 44: }; |
可以看到多了IOPCGroup 和 coclass OPCGroup的定義。因為我們将不對其他程式暴露OPCGroup對象,是以删掉OPCGroup.rgs并注釋掉BSOPCServer.idl中關于OPCGroup的代碼:
[
object,
uuid(60948577-318D-46F9-948C-838DB34EF8EC),
helpstring("IOPCGroup Interface"),
pointer_default(unique)
]
interface IOPCGroup : IUnknown{
};
[
uuid(7E67188C-27CA-47E8-BB92-C22BD88272E2),
helpstring("OPCGroup Class")
]
coclass OPCGroup
{
[default] interface IOPCGroup;
};
|
也就是說,我們雖然用向導生成了CCOPCGroup類的架構,但我們隻保留了OPCGroup.h 和OPCGroup.cpp。
我們還需要對CCOPCGroup類定義進行修改:
在OPCGroup.h檔案中注釋掉:
1) public CComCoClass<COPCGroup, &CLSID_OPCGroup>,
2) public IOPCGroup
3) DECLARE_REGISTRY_RESOURCEID(IDR_OPCGROUP)
4) COM_INTERFACE_ENTRY(IOPCGroup)
5) OBJECT_ENTRY_AUTO(__uuidof(OPCGroup), COPCGroup)
1.4 繼承IOPCServer
上面我們提到雖然我們自己利用向導生成了IOPCServer的定義,但是最終我們用到的是OPC标準裡面定義的IOPCServer, 是以我們要把它替換掉。
1) 添加OPC 标準的include檔案
OPC基金會為我們提供了一些開發OPC server的公共頭檔案和idl接口定義檔案,為了便于使用,我們把它們拷貝到我們的工程目錄下面。 上面提到的IOPCServer接口就在opcda.idl裡面定義:
2) 修改BSOPCServer.idl檔案
a. 添加import “opcda.idl”;
b. 注釋掉IOPCServer的定義:
[
object,
uuid(3418B5C9-878F-436B-BFDA-6EB5EEFF4DF2),
helpstring("IOPCServer Interface"),
pointer_default(unique)
]
interface IOPCServer : IUnknown{
};
|
c. 設定編譯BSOPCServer.idl包含的路徑:
(opcda.idl 跟BSOPCServer.idl 不在一個檔案夾下面。)
重新編譯,在編譯OPCServer.cpp時會得到如下錯誤提示:
c:/program files/microsoft visual studio 9.0/vc/atlmfc/include/atlcom.h(1867) : error C2259: 'ATL::CComObject<Base>' : cannot instantiate abstract class
這個錯誤主要是因為我們把COPCServer從IOPCServer繼承,但是沒有實作IOPCServer定義的方法。
在OPCServer.h裡添加IOPCServer接口函數定義:
// IOPCServer implementation methods
STDMETHODIMP AddGroup(
LPCWSTR szName,
BOOL bActive,
DWORD dwRequestedUpdateRate,
OPCHANDLE hClientGroup,
LONG* pTimeBias,
FLOAT* pPercentDeadband,
DWORD dwLCID,
OPCHANDLE* phServerGroup,
DWORD* pRevisedUpdateRate,
REFIID riid,
LPUNKNOWN* ppUnk);
STDMETHODIMP GetErrorString(HRESULT dwError, LCID dwLocale, LPWSTR* ppString);
STDMETHODIMP GetGroupByName(LPCWSTR szName, REFIID riid, LPUNKNOWN* ppUnk);
STDMETHODIMP GetStatus(OPCSERVERSTATUS** ppServerStatus);
STDMETHODIMP RemoveGroup(OPCHANDLE hServerGroup, BOOL bForce);
STDMETHODIMP CreateGroupEnumerator(OPCENUMSCOPE dwScope, REFIID riid, LPUNKNOWN* ppUnk);
|
在OPCServer.CPP檔案添加接口函數實作,這裡我們沒有具體實作每個函數,隻是設定傳回值:
// IOPCServer implementation methods
STDMETHODIMP COPCServer::AddGroup(
LPCWSTR szName,
BOOL bActive,
DWORD dwRequestedUpdateRate,
OPCHANDLE hClientGroup,
LONG* pTimeBias,
FLOAT* pPercentDeadband,
DWORD dwLCID,
OPCHANDLE* phServerGroup,
DWORD* pRevisedUpdateRate,
REFIID riid,
LPUNKNOWN* ppUnk)
{
return S_OK;
}
STDMETHODIMP COPCServer::GetErrorString(HRESULT dwError, LCID dwLocale, LPWSTR* ppString)
{
return S_OK;
}
STDMETHODIMP COPCServer::GetGroupByName(LPCWSTR szName, REFIID riid, LPUNKNOWN* ppUnk)
{
return S_OK;
}
STDMETHODIMP COPCServer::GetStatus(OPCSERVERSTATUS** ppServerStatus)
{
return S_OK;
}
STDMETHODIMP COPCServer::RemoveGroup(OPCHANDLE hServerGroup, BOOL bForce)
{
return S_OK;
}
STDMETHODIMP COPCServer::CreateGroupEnumerator(OPCENUMSCOPE dwScope, REFIID riid, LPUNKNOWN* ppUnk)
{
return S_OK;
}
|
重新build成功。
到此為止,應用程式架構已經生成,接下來我們來探讨OPC server的注冊問題。