OPC服务器是如何被找到的?

上篇谈了OPC和COM的渊源,现在继续谈下如何找到服务器。

今天要说的是在每个服务器上必装的一个来自OPC基金会的应用,OpcEnum.exe。当一个客户端程序要和服务器沟通时第一步就是要问,“嘿,你那有我要的东西吗?有装OPC服务器吗?”。这个程序就负责回答这个问题。

看一下OPCEnum.exe中IDL定义的唯一一个类OpcServerList。它执行了opccomn.idl中的两个接口,IOpcServerList2和IOpcServerList。 

[
	uuid(13486D43-4821-11D2-A494-3CB306C10000),
	version(1.1),
	helpstring("OpcEnum 1.1 Type Library")
]
library OpcEnumLib
{
	importlib("stdole32.tlb");
	importlib("stdole2.tlb");

	[
		uuid(13486D51-4821-11D2-A494-3CB306C10000),
		helpstring("OpcEnum Class")
	]
	coclass OpcServerList
	{
		[default] interface IOpcServerList2;
		          interface IOpcServerList;
	};
};

在这个类的具体执行中,这二个接口都被照顾到了,见OpcSeLst.cpp如下(相关非全部),

class ATL_NO_VTABLE COpcServerList : 
	public CComObjectRootEx<CComSingleThreadModel>,
	public CComCoClass<COpcServerList, &CLSID_OpcServerList>,
	public IOPCServerList,
	public IOPCServerList2
{
// IOpcServerList
public:
	STDMETHOD(EnumClassesOfCategories)(
			/*[in]*/ ULONG cImplemented,
			/*[in,size_is(cImplemented)]*/ CATID rgcatidImpl[],
			/*[in]*/ ULONG cRequired,
			/*[in,size_is(cRequired)]*/ CATID rgcatidReq[],
			/*[out]*/ IEnumCLSID** ppenumClsid);
// IOpcServerList2
public:
	STDMETHOD(EnumClassesOfCategories)(
			/*[in]*/ ULONG cImplemented,
			/*[in,size_is(cImplemented)]*/ CATID rgcatidImpl[],
			/*[in]*/ ULONG cRequired,
			/*[in,size_is(cRequired)]*/ CATID rgcatidReq[],
			/*[out]*/ IOPCEnumGUID** ppenumClsid);

至此便明白了,只要能获得OpcServerList的实例,必然有IOpcServerList2和IOpcServerList的接口,下面只要根据需要调用其中一个EnumClassesOfCategories就行了。那么话又说回来,EnumClassesOfCategories是如何帮我找到OPC服务器的呢?继续看,相关非全部的程序,

STDMETHODIMP COpcServerList::EnumClassesOfCategories2(
			/*[in]*/ ULONG cImplemented,
			/*[in,size_is(cImplemented)]*/ CATID rgcatidImpl[],
			/*[in]*/ ULONG cRequired,
			/*[in,size_is(cRequired)]*/ CATID rgcatidReq[],
			/*[out]*/ IEnumCLSID** ppenumClsid)
{
	ICatInformationPtr pCatInfo;
	HRESULT hr = pCatInfo.CreateInstance(CLSID_StdComponentCategoriesMgr);
	
	return pCatInfo->EnumClassesOfCategories(cImplemented, rgcatidImpl, 
		cRequired, rgcatidReq, ppenumClsid);
}

看到这里就明白了,先获得个微软COM提供的关于CLSID_StdComponentCategoriesMgr的实例,然后根据输入的CATID返回一个枚举实例,你所要的OPC服务器实例尽在该枚举中了。注意CATID可以是多个,如基金会定义的CATID_OPCDAServer10/20/30(相应的uuid:{63D5F430-CFE4-11D1-B2C8-0060083BA1FB}, {63D5F432-CFE4-11D1-B2C8-0060083BA1FB} 和{CC603642-66D7-48F1-B69A-B625E73652D7})。

下面看下Matrikon的实例,很明显地,上述三个CATID都出现了,也就是说该服务器注册时注册了三个CATID,也说是表明本程序执行了基金会定义的DA规范1.0到3.0的接口函数。


这里还发现了另外二个CATID,{7DE5B060-E089-11D2-A5E6-000086339399}和{58E13251-AC87-11D1-84D5-00608CB8A7E9}。这是谁的CATID?查一下IDL文件,在opc_ae.idl中找到如下的,

//==============================================================================
// Category ID declaration (defined as an interface to ensure they show up in the typelib).

[uuid(58E13251-AC87-11d1-84D5-00608CB8A7E9)] interface OPCEventServerCATID : IUnknown {}

cpp_quote("#define CATID_OPCAEServer10 IID_OPCEventServerCATID")

在opchda.idl中找到了,

//==============================================================================
// Category ID declarations (defined as interfaces to ensure they show up in the typelib).

[uuid(7DE5B060-E089-11d2-A5E6-000086339399)] interface CATID_OPCHDAServer10 : IUnknown {}

cpp_quote("#define CATID_OPCHDAServer10 IID_CATID_OPCHDAServer10")

至此五个CATID都清楚了,也就是Matrikon实现了DA,HDA和AE的规范,如下图所见和我们的分析一样,遗憾的是它没有执行security方面的IDL,是要你出银子的。这方面可以找我进一步咨询也行。


最后来点干货,看看是如何用到我刚才的分析,

HRESULT enumServerList(wchar_t hostname[], MULTI_QI MultiQI[], DWORD count)
{
	// initialize server security info here if required.
	COSERVERINFO ServerInfo = { NULL };
	ServerInfo.pwszName = hostname;

	HRESULT hr = CoCreateInstanceEx(CLSID_OpcServerList, NULL, CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER, &ServerInfo, count, MultiQI); 

	if (FAILED(hr))
	{
		OLECHAR *bstrGuid;
		StringFromCLSID(CLSID_OpcServerList, &bstrGuid);
		_com_error err(hr);
		LPCTSTR errMsg = err.ErrorMessage();

		_tprintf(_T("enumServerList failed: %s for interface %S\n"), errMsg, bstrGuid);
		CoTaskMemFree(bstrGuid);
	}

	return hr;
}

这段主要就是获得一个在服务器端的关于CLSID_OpcServerList的实例,CLSID_OpcServerList是定义在OpcEnum_i.c中的,

MIDL_DEFINE_GUID(CLSID, CLSID_OpcServerList,0x13486D51,0x4821,0x11D2,0xA4,0x94,0x3C,0xB3,0x06,0xC1,0x00,0x00);

数值和OpcEnum.idl中一模一样,说白了就是要获得一个OpcServerList的实例,从OpcEnum.exe中。

        [
		uuid(13486D51-4821-11D2-A494-3CB306C10000),
		helpstring("OpcEnum Class")
	]
	coclass OpcServerList

下篇给出个自产的用C和COM写的获取OPC服务器的示例,望有所收获!

  • 5
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值