⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 hotopcserver.cpp

📁 VC 编写的OPC客户端
💻 CPP
📖 第 1 页 / 共 5 页
字号:
// HotOpcServer.cpp : 实现文件
//

#include "stdafx.h"
#include "SuperOPC.h"
#include "HotOpcServer.h"

// Clipboard formats for data access 1.0 streams:
UINT CF_DATA_CHANGE = RegisterClipboardFormat (_T("OPCSTMFORMATDATA"));
UINT CF_DATA_CHANGE_TIME	= RegisterClipboardFormat (_T("OPCSTMFORMATDATATIME"));
UINT CF_WRITE_COMPLETE = RegisterClipboardFormat (_T("OPCSTMFORMATWRITECOMPLETE"));

// Clipboard formats for cut/copy/paste:
UINT CF_SERVER = RegisterClipboardFormat (_T("QCOpcClientServer"));
UINT CF_GROUP = RegisterClipboardFormat (_T("QCOpcClientGroup"));
UINT CF_ITEM = RegisterClipboardFormat (_T("QCOpcClientItem"));
void LogMsg (UINT nResID, ...)
{
	/*EVENTTYPE eType;

	// Get pointer to additional argument list (which starts after nResID):
	va_list args;
	va_start (args, nResID);

	// Allocate a buffer for message text:
	int nBuf;
	TCHAR szBuffer [512];

	// Load the specified string resource.  It should contain format 
	// specifiers if additional arguments are given.
	CString strFmt;
	strFmt.LoadString (nResID);

	// Contruct the message string:
	nBuf = _vsntprintf (szBuffer, sizeof (szBuffer) / sizeof (szBuffer[0]), (LPCTSTR)strFmt, args);

	// Chect to see if there was there an error, indicated by nBuf=0.  Debug only.
	// (was the expanded string too long?)
	ASSERT (nBuf >= 0);

	// Determine the type of message implied by string resource number.
	// (see range in function header above)
	if (nResID >= EVENT_FIRSTINFO && nResID <= EVENT_LASTINFO)
		eType = tEventInformation;
	else if (nResID >= EVENT_FIRSTERROR && nResID <= EVENT_LASTERROR)
		eType = tEventError;
	else
	{
		// String resource ID not in range.  Programmer error.
		ASSERT (FALSE);
	}

	// Add the message to the queue.

	// First get pointer to the application's main window:
	CKMainWnd *pMainWnd = (CKMainWnd *)AfxGetMainWnd ();

	// If pointer looks valid, call LogMsg() function to place message
	// in queue.
	if (pMainWnd && ::IsWindow (pMainWnd->m_hWnd))
		pMainWnd->LogMsg (eType, szBuffer);

	// Reset additional arguments pointer:
	va_end (args);*/
}

VARTYPE VartypeFromString (LPCTSTR lpszType)
{
	VARTYPE vtType;
	
	// Compare input type string with supported types and return the
	// corresponding variant type.  (A match is found when lstrcmpi returns
	// zero.)  If specified type is not supported, then return VT_EMPTY.
	// These strings must match those used below in StringFromVartype().
	if (lstrcmpi (lpszType, _T("Boolean")) == 0)
		vtType = VT_BOOL;
	else if (lstrcmpi (lpszType, _T("Byte")) == 0)
		vtType = VT_UI1;
	else if (lstrcmpi (lpszType, _T("Byte Array")) == 0)
		vtType = VT_UI1 | VT_ARRAY;
	else if (lstrcmpi (lpszType, _T("Char")) == 0)
		vtType = VT_I1;
	else if (lstrcmpi (lpszType, _T("Char Array")) == 0)
		vtType = VT_I1 | VT_ARRAY;
	else if (lstrcmpi (lpszType, _T("Word")) == 0)
		vtType = VT_UI2;
	else if (lstrcmpi (lpszType, _T("Word Array")) == 0)
		vtType = VT_UI2 | VT_ARRAY;
	else if (lstrcmpi (lpszType, _T("Short")) == 0)
		vtType = VT_I2;
	else if (lstrcmpi (lpszType, _T("Short Array")) == 0)
		vtType = VT_I2 | VT_ARRAY;
	else if (lstrcmpi (lpszType, _T("DWord")) == 0)
		vtType = VT_UI4;
	else if (lstrcmpi (lpszType, _T("DWord Array")) == 0)
		vtType = VT_UI4 | VT_ARRAY;
	else if (lstrcmpi (lpszType, _T("Long")) == 0)
		vtType = VT_I4;
	else if (lstrcmpi (lpszType, _T("Long Array")) == 0)
		vtType = VT_I4 | VT_ARRAY;
	else if (lstrcmpi (lpszType, _T("Float")) == 0)
		vtType = VT_R4;
	else if (lstrcmpi (lpszType, _T("Float Array")) == 0)
		vtType = VT_R4 | VT_ARRAY;
	else if (lstrcmpi (lpszType, _T("Double")) == 0)
		vtType = VT_R8;
	else if (lstrcmpi (lpszType, _T("Double Array")) == 0)
		vtType = VT_R8 | VT_ARRAY;
	else if (lstrcmpi (lpszType, _T("String")) == 0)
		vtType = VT_BSTR;
	else
		vtType = VT_EMPTY;
	
	// Return variant type:
	return (vtType);
}

void StringFromVartype (VARTYPE vtType, CString &strType)
{
	// Return a string describing the specified variant type.  These strings
	// must match those used above in VartypeFromString().
	switch (vtType & ~VT_ARRAY)
	{
	case VT_BOOL:	strType = _T("Boolean");	break;
	case VT_UI1:	strType = _T("Byte");		break;
	case VT_I1:		strType = _T("Char");		break;
	case VT_UI2:	strType = _T("Word");		break;
	case VT_I2:		strType = _T("Short");		break;
	case VT_UI4:	strType = _T("DWord");		break;
	case VT_I4:		strType = _T("Long");		break;
	case VT_R4:		strType = _T("Float");		break;
	case VT_R8:		strType = _T("Double");		break;
	case VT_BSTR:	strType = _T("String");		break;
	default:		strType = _T("Native");		break;
	}

	// Append " Array" if a variant array type:
	if ((vtType & VT_ARRAY) != 0)
		strType += _T(" Array");
}

//#pragma comment (lib,"Strmiids.lib")
#pragma comment (lib, "ole32.lib")
#pragma comment (lib, "oleaut32.lib") 

// CHotOpcServer

CHotOpcServer::CHotOpcServer()
{
	m_strServerName    = _T("");
	m_strRemoteMachine = _T("");
	InitInterface();
}
CHotOpcServer::CHotOpcServer(CString &strSerName,CString strRemote)
{
	ASSERT(!strSerName.IsEmpty());
	m_strServerName    = strSerName;
	m_strRemoteMachine = strRemote;
	InitInterface();
}


CHotOpcServer::~CHotOpcServer()
{
	Disconnect ();
}


// CHotOpcServer 成员函数

void CHotOpcServer::InitInterface(void)
{
	m_pServerList = NULL;
	ZeroMemory (&m_bfFlags, sizeof (m_bfFlags));

	m_pPrev = NULL;
	m_pNext = NULL;

	m_pGroupHead = NULL;
	m_cdwGroups = 0;
	// 初始化多查询接口结构
	for (int i = 0; i < sizeof (m_arrMultiQI) / sizeof (MULTI_QI); i++)
	{
		m_arrMultiQI[i].pItf = NULL;
		m_arrMultiQI[i].hr = 0;
	}

	m_pIServer = NULL;
	m_pICommon = NULL;
	m_pIConnPtContainer		= NULL;
	m_pIItemProps			= NULL;
	m_pIBrowse				= NULL;
	m_pIPublicGroups		= NULL;
	m_pIPersistFile			= NULL;	

	m_pIShutdownSink		= NULL;
	m_dwCookieShutdown		= 0;

	m_bConnected = false;
}

//如果bOpc2 = FALSE,说明使用1.0版本OPC
//默认为TRUE
INT CHotOpcServer::DisplayComponent(BOOL bOpc2 /*= TRUE*/)
{
	if(!m_pServerList)
		m_pServerList = new CStringList();
	else
		m_pServerList->RemoveAll();
	ASSERT(m_pServerList);

	HRESULT hr;
	//判断所选用的OPC版本
	CATID catid = bOpc2?CATID_OPCDAServer20:CATID_OPCDAServer10;
	// 确保COM被初始化:
	/*hr = CoInitializeEx (NULL, COINIT_SPEED_OVER_MEMORY );

	if (FAILED(hr))
		return -1;*/
	ICatInformation *pCat = NULL;

	// 得到组件类别管理
	hr = CoCreateInstance (CLSID_StdComponentCategoriesMgr, NULL,CLSCTX_SERVER, 
		IID_ICatInformation,(void **)&pCat);

	// 如果失败就反回-1
	if (FAILED(hr))
		return -1;
	IEnumCLSID *pEnum = NULL;

	CATID arrcatid [1];
	arrcatid [0] = catid;

	// 列出基于clsid注册的组件
	hr = pCat->EnumClassesOfCategories (sizeof (arrcatid) / sizeof (CATID),	
		arrcatid,0,NULL,&pEnum);

	if (SUCCEEDED (hr))
	{
		GUID guid;
		ULONG fetched;
		//循环列举组件,调用pEnum->Next方法得到下一组件的GUID

		while ((hr = pEnum->Next (1, &guid, &fetched)) == S_OK)
		{
			//取得服务器名从GUID
			WCHAR *wszProgID;
			hr = ProgIDFromCLSID (guid, &wszProgID);
			// 如果成功,加组件到列表中
			if (SUCCEEDED (hr))
			{
				//服务器名子中是UNICODE格式. 如果程序使用ANSI编码就转换成
				// ANSI络式。然后将组件插入到列表中
#ifdef _UNICODE
				m_pServerList->AddTail(CString(wszProgID));

#else
				TCHAR szProgID [MAX_PATH];
				//将WCHAR转成TCHAR
				_wcstombsz (szProgID, wszProgID, sizeof (szProgID) / sizeof (TCHAR));
				m_pServerList->AddTail(CString(szProgID));
#endif
				//装服务名变量从内存中释放
				CoTaskMemFree (wszProgID);
			}
		}
		pEnum->Release ();
	}
	pCat->Release ();
	//CoUninitialize ();
	return (INT)(m_pServerList->GetCount());
}

INT CHotOpcServer::GetServerCount(void)
{
	/*
	根据COM规范,系统中所注册的COM都会在注册表中写下其相应的GUID
	*/
	HKEY hKey = HKEY_CLASSES_ROOT;		//在此节点下进行搜询
	TCHAR szKey [MAX_PATH];			// 为键分配空间
	DWORD dwLength = MAX_PATH;

	if(!m_pServerList)
		m_pServerList = new CStringList();
	else
		m_pServerList->RemoveAll();

	//查找已注册的OPC服务.
	for (DWORD dwIndex = 0;
		RegEnumKey (hKey, dwIndex, szKey, dwLength) == ERROR_SUCCESS; dwIndex++)
	{
		HKEY hSubKey;

		// 打开注册表中的键:
		if (RegOpenKey (hKey, szKey, &hSubKey) == ERROR_SUCCESS)
		{
			//查找OPC子键:
			if (RegQueryValue (hSubKey, _T("OPC"), NULL, NULL) == ERROR_SUCCESS)
			{
				// 将找到的服务名放到m_pServerList中
				ASSERT(m_pServerList);
				m_pServerList->AddTail(CString(szKey));
			}
			//关闭注册表键:
			RegCloseKey (hSubKey);
		}

		// 重新设置长度:
		dwLength = MAX_PATH;
	}
	return (INT)(m_pServerList->GetCount());
}

CString CHotOpcServer::GetServerNameByIndex(int nIndex)
{
	if(m_pServerList == NULL || nIndex < 0 ||
		m_pServerList->GetCount() <= nIndex)
		return NULL;
	//找到指定位置的服务名
	CString strName = m_pServerList->GetAt(m_pServerList->FindIndex(nIndex));

	//LPCTSTR strRetrun = strName.GetBuffer(strName.GetLength());
	return strName;
}

bool CHotOpcServer::Connect(CString &strSerName, CString &strRemoteMachine)
{
	ASSERT (m_bConnected == FALSE);

	// 设置服务器名和地址
	SetServerName (strSerName);
	SetRemoteMachine (strRemoteMachine);

	// Connect:
	return (Connect ());
}

bool CHotOpcServer::Connect(void)
{
	// OPC服务的名子不能为空
	ASSERT (!m_strServerName.IsEmpty ());

	// 假定现在没有联接到服务器
	m_bfFlags.bIsKepServerEx = false;

	// 完成和清除上次连接
	Disconnect ();

	// 包括了OPC服务的Class ID.  (GetCLSID()函数需要OPC服务的名称,
	//在上面已经进行了检查)
	CLSID clsid;
	if (SUCCEEDED (GetCLSID (clsid)))
	{
		HRESULT hr;

		// 被化查询接口数组:
		for (int i = 0; i < sizeof (m_arrMultiQI) / sizeof (MULTI_QI); i++)
		{
			m_arrMultiQI [i].pItf = NULL;
			m_arrMultiQI [i].hr = 0;
		}

		// 查找接口ID,当我们调用CoCreateInstanceEx()时会得到接口指针
		m_arrMultiQI[MQI_IOPCSERVER].pIID		= &IID_IOPCServer;
		m_arrMultiQI[MQI_IOPCCOMMON].pIID		= &IID_IOPCCommon;
		m_arrMultiQI[MQI_IOPCCONNPT].pIID		= &IID_IConnectionPointContainer;
		m_arrMultiQI[MQI_IOPCITEMPROP].pIID	= &IID_IOPCItemProperties;
		m_arrMultiQI[MQI_IOPCBROWSE].pIID		= &IID_IOPCBrowseServerAddressSpace;
		m_arrMultiQI[MQI_IOPCPUBLIC].pIID		= &IID_IOPCServerPublicGroups;
		m_arrMultiQI[MQI_IOPCPERSIST].pIID		= &IID_IPersistFile;

		//连接到OPC服务器,查找所有或能存在的接口
		if (m_strRemoteMachine.IsEmpty ())
		{
			// 若m_strRemoteMachine是空,将试着实例化本地OPC服务

			// 如果必须CoCreateInstanceEx将开启OPC服务
			// 并调用它们的 QueryInterface 
			hr = CoCreateInstanceEx (
				clsid,										// CLSID
				NULL,										
				CLSCTX_SERVER,								// 本地连接,进程内
				NULL,										// 远方机器名
				sizeof (m_arrMultiQI) / sizeof (MULTI_QI),	// 查找到IIDS的数量
				m_arrMultiQI);								// 去找到IID的指针数组
		}
		else
		{
			// 既然m_strRemoteMachine 不为空, 假定他是一个有效的远程机器名
			// 将试着在指定名的机器上初始化OPC服务

			// 首先我们需要初始化服务信息结构
			COSERVERINFO tCoServerInfo;
			ZeroMemory (&tCoServerInfo, sizeof (tCoServerInfo));

			// 为机器名分配内存
			int nSize = m_strRemoteMachine.GetLength () * sizeof (WCHAR);
			tCoServerInfo.pwszName = new WCHAR [nSize];

			// 检察指针,如果无效将不能继续
			if (tCoServerInfo.pwszName)
			{
				ASSERT (FALSE);
				return (false);
			}

			// 复制机器名到服务信息结构中.要进行编码转换如果没用Unicode
#ifdef _UNICODE
			lstrcpyn (tCoServerInfo.pwszName, m_strRemoteMachine, nSize);
#else 
			mbstowcs (tCoServerInfo.pwszName, m_strRemoteMachine, nSize);
#endif//_UNICODE
			//进行接口实例化
			hr = CoCreateInstanceEx (
				clsid,										// CLSID
				NULL,										
				CLSCTX_SERVER,								
				&tCoServerInfo,								// 远地机器名 
				sizeof (m_arrMultiQI) / sizeof (MULTI_QI),	
				m_arrMultiQI);								

			// COM要求释放申请的内存
			delete [] tCoServerInfo.pwszName;
		}

		// 如果CoCreateInstanceEx调用成功我们要检查反回的接品

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -