天天看點

BSOPCServer Development - 建立應用程式架構

目标:基于單文檔程式生成一個程式架構來開發BSOPCServer。

為了友善管理和配置,我們的程式将會基于單文檔類型,支援MFC,然後添加ATL支援。添加ATL是為了友善實作OPCServer的接口。

1.1 應用程式類型

這裡用的開發環境是VS2008 + SP1。打開建立工程向導,建立基于單文檔的應用程式:

BSOPCServer Development - 建立應用程式架構

由向導生成的檔案清單:

BSOPCServer Development - 建立應用程式架構
1.2 添加ATL支援

我們可以通過添加一個ATL簡單對象的辦法讓開發環境為我們自動添加支援ATL必須的代碼。

因為在我們的程式中要有一個server對象實作IOPCServer等接口,這裡我們不妨就直接添加一個類COPCServer,繼承于IOPCServer:

1) 選擇“AddClass …”:

BSOPCServer Development - 建立應用程式架構

2) 添加“ATL Simple Object”:

各項設定如圖所示:

BSOPCServer Development - 建立應用程式架構

3) 設定線程模型:

線程模型:選擇“Apartment”, 即套間模型,表示我們的COPCServer對象運作在套間裡保證線程安全;

接口:選擇“Custom”, 即定制接口。如果選擇“Dual”則表示該對象支援自動化接口,将會繼承IDispatch 接口。

聚合: 選擇預設值,“允許聚合”,實際上不會用到。

連接配接點:COPCServer對象需要支援連接配接點(實作IConnectionPoint接口),但在這裡我們不選,我們以後手動加進去。

BSOPCServer Development - 建立應用程式架構

現在來看看向導為我們添加的幾個檔案:

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類:

BSOPCServer Development - 建立應用程式架構
BSOPCServer Development - 建立應用程式架構

向導生成的檔案:

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裡面定義:

BSOPCServer Development - 建立應用程式架構

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 不在一個檔案夾下面。)

BSOPCServer Development - 建立應用程式架構

重新編譯,在編譯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的注冊問題。

繼續閱讀