📄 hotopcserver.cpp
字号:
// 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 + -