/* ======================================================================== * Copyright (c) 2005-2019 The OPC Foundation, Inc. All rights reserved. * * OPC Foundation MIT License 1.00 * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * The complete license agreement can be found here: * http://opcfoundation.org/License/MIT/1.00/ * ======================================================================*/ #include "StdAfx.h" #include "COpcComModule.h" #include "COpcTextReader.h" #include "OpcRegistry.h" #include "COpcSecurity.h" #include "COpcList.h" #include "COpcMap.h" #include "COpcXmlDocument.h" //============================================================================== // Static Data static COpcComModule* g_pModule = NULL; //============================================================================== // Local Declarations #define OPCREG_CONFIG_INFO _T("ConfigInfo") #define OPCREG_SELFREG_INFO _T("SelfRegInfo") #define OPCREG_VENDOR_NAME _T("VendorName") #define OPCREG_APPLICATION_NAME _T("ApplicationName") #define OPCREG_APPLICATION_DESCRIPTION _T("ApplicationDescription") #define OPCREG_APPID _T("AppID") #define OPCREG_CLASS_NAME _T("ClassName") #define OPCREG_CLASS_DESCRIPTION _T("ClassDescription") #define OPCREG_CLASS_VERSION _T("ClassVersion") #define OPCREG_CLSID _T("CLSID") #define OPCREG_ALLOW_EVERYONE_ACCESS _T("AllowEveryoneAccess") #define OPCREG_SERVER_WRAPPER _T("ServerWrapper") //============================================================================== // ServiceMain static void WINAPI ServiceMain(DWORD dwArgc, LPTSTR* lpszArgv) { OPC_ASSERT(g_pModule != NULL); g_pModule->ServiceMain(dwArgc, lpszArgv); } //============================================================================== // EventHandler static void WINAPI EventHandler(DWORD fdwControl) { OPC_ASSERT(g_pModule != NULL); g_pModule->EventHandler(fdwControl); } //============================================================================== // ExitProcess void COpcComModule::ExitProcess(DWORD dwExitCode) { OPC_ASSERT(g_pModule != NULL); g_pModule->m_cServiceStatus.dwWin32ExitCode = dwExitCode; PostThreadMessage(g_pModule->m_dwThread, WM_QUIT, 0, 0); } //============================================================================== // OpcWinMain OPCUTILS_API DWORD OpcWinMain( COpcComModule* pModule, HINSTANCE hInstance, LPTSTR lpCmdLine ) { OPC_ASSERT(pModule != NULL); g_pModule = pModule; DWORD dwResult = g_pModule->WinMain(hInstance, lpCmdLine); g_pModule = NULL; return dwResult; } //============================================================================== // OpcExtractSelfRegInfo HRESULT OpcExtractSelfRegInfo(IXMLDOMElement* ipRoot, IXMLDOMElement** ippInfo) { BSTR bstrName = NULL; IXMLDOMElement* ipElement = NULL; *ippInfo = NULL; HRESULT hResult = S_OK; TRY { hResult = ipRoot->get_firstChild((IXMLDOMNode**)&ipElement); if (FAILED(hResult)) { THROW(); } hResult = ipElement->get_nodeName(&bstrName); if (FAILED(hResult)) { THROW(); } if (((COpcString)bstrName) == OPCREG_SELFREG_INFO) { hResult = ipRoot->removeChild(ipElement, (IXMLDOMNode**)ippInfo); if (FAILED(hResult)) { THROW(); } } } CATCH_FINALLY { if (bstrName != NULL) SysFreeString(bstrName); if (ipElement != NULL) ipElement->Release(); } return hResult; } //============================================================================== // OpcInsertSelfRegInfo HRESULT OpcInsertSelfRegInfo(IXMLDOMElement* ipRoot, IXMLDOMElement* ipInfo) { IXMLDOMElement* ipElement = NULL; IXMLDOMElement* ipResult = NULL; HRESULT hResult = S_OK; TRY { VARIANT_BOOL bHasChildren = VARIANT_FALSE; hResult = ipRoot->hasChildNodes(&bHasChildren); if (FAILED(hResult)) { THROW(); } if (bHasChildren == VARIANT_TRUE) { hResult = ipRoot->get_firstChild((IXMLDOMNode**)&ipElement); if (FAILED(hResult)) { THROW(); } VARIANT vRefChild; vRefChild.vt = VT_UNKNOWN; vRefChild.punkVal = ipElement; hResult = ipRoot->insertBefore(ipInfo, vRefChild, (IXMLDOMNode**)&ipResult); if (FAILED(hResult)) { THROW(); } } else { hResult = ipRoot->appendChild(ipInfo, (IXMLDOMNode**)&ipResult); if (FAILED(hResult)) { THROW(); } } } CATCH_FINALLY { if (ipElement != NULL) ipElement->Release(); if (ipResult != NULL) ipResult->Release(); } return hResult; } //============================================================================== // COpcComModule // Constructor COpcComModule::COpcComModule( const COpcString& cVendorName, const COpcString& cApplicationName, const COpcString& cApplicationDescription, const GUID& cAppID, const TOpcClassTableEntry* pClasses, const TClassCategories* pCategories, bool bIsWrapper ) : m_cAppID(cAppID) { m_cVendorName = cVendorName; m_cApplicationName = cApplicationName; m_cApplicationDescription = cApplicationDescription; m_pClasses = (TOpcClassTableEntry*)pClasses; m_pCategories = (TClassCategories*)pCategories; m_bRegFromFile = false; m_bWrapper = bIsWrapper; m_pWrappedClasses = NULL; m_hModule = NULL; m_dwThread = NULL; m_hServiceStatus = NULL; memset(&m_cServiceStatus, 0, sizeof(m_cServiceStatus)); } // Destructor COpcComModule::~COpcComModule() { if (m_bRegFromFile) { FreeClassTable(m_pClasses); OpcFree(m_pCategories); CoUninitialize(); } } // GetConfigParam bool COpcComModule::GetConfigParam(const COpcString& cName, COpcString& cValue) { return m_cParameters.Lookup(cName, cValue); } // WinMain DWORD COpcComModule::WinMain( HINSTANCE hInstance, LPTSTR lpCmdLine ) { m_hModule = hInstance; // need to extract the executable name from the command line. WCHAR* pchCommandLine = GetCommandLine(); BOOL isQuoted = FALSE; if (*pchCommandLine == '"') { isQuoted = TRUE; pchCommandLine++; } while (*pchCommandLine != '\0') { // check for quotes. if (isQuoted) { if (*pchCommandLine == '"') { pchCommandLine++; break; } } // check for whitespace. else { if (iswspace(*pchCommandLine)) { pchCommandLine++; break; } } pchCommandLine++; } // skip trailing whitespace. while (iswspace(*pchCommandLine)) { pchCommandLine++; } // parse command line arguments. COpcText cText; COpcTextReader cReader(pchCommandLine); // skip until first command flag - if any. cText.SetType(COpcText::Delimited); cText.SetDelims(L"-/"); if (cReader.GetNext(cText)) { // read first command flag. cText.SetType(COpcText::NonWhitespace); cText.SetEofDelim(); if (cReader.GetNext(cText)) { COpcString cFlags = ((COpcString&)cText).ToLower(); // register module as local server. if (cFlags== _T("regserver")) return RegisterServer(false); // register module as service. if (cFlags== _T("service")) return RegisterServer(true); // unregister module. if (cFlags == _T("unregserver")) return UnregisterServer(); } } // check for service key in registry. LPTSTR tsValue; bool bService = OpcRegGetValue( HKEY_CLASSES_ROOT, _T("AppID\\") + (COpcString)m_cAppID, _T("LocalService"), &tsValue); // enter the main execution loop when running as an EXE server. if (!bService) { Run(); } // start the service main thread when running as a service. else { OpcFree(tsValue); // initialize status structure. m_cServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; m_cServiceStatus.dwCurrentState = SERVICE_STOPPED; m_cServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; m_cServiceStatus.dwWin32ExitCode = 0; m_cServiceStatus.dwServiceSpecificExitCode = 0; m_cServiceStatus.dwCheckPoint = 0; m_cServiceStatus.dwWaitHint = 0; SERVICE_TABLE_ENTRY* pServices = new SERVICE_TABLE_ENTRY[2]; pServices[0].lpServiceName = (LPTSTR)(LPCTSTR)m_cApplicationName; pServices[0].lpServiceProc = ::ServiceMain; pServices[1].lpServiceName = NULL; pServices[1].lpServiceProc = NULL; // notify the server control manager. if (!StartServiceCtrlDispatcher(pServices)) { delete [] pServices; return -1; } delete [] pServices; } return m_cServiceStatus.dwWin32ExitCode; } // ServiceMain void COpcComModule::ServiceMain(DWORD dwArgc, LPTSTR* lpszArgv) { // register handler for service control events. m_hServiceStatus = RegisterServiceCtrlHandler(m_cApplicationName, ::EventHandler); if (m_hServiceStatus == NULL) { return; } // update service state. m_cServiceStatus.dwCurrentState = SERVICE_START_PENDING; SetServiceStatus(m_hServiceStatus, &m_cServiceStatus); m_cServiceStatus.dwWin32ExitCode = S_OK; m_cServiceStatus.dwCheckPoint = 0; m_cServiceStatus.dwWaitHint = 0; // enter the main execution loop. Run(); // update service state. m_cServiceStatus.dwCurrentState = SERVICE_STOPPED; SetServiceStatus(m_hServiceStatus, &m_cServiceStatus); } // EventHandler void COpcComModule::EventHandler(DWORD fdwControl) { switch (fdwControl) { case SERVICE_CONTROL_STOP: { // update service state. m_cServiceStatus.dwCurrentState = SERVICE_STOP_PENDING; SetServiceStatus(m_hServiceStatus, &m_cServiceStatus); // post quit message. PostThreadMessage(m_dwThread, WM_QUIT, 0, 0); break; } default: { // do nothing. break; } } } // Run void COpcComModule::Run() { HRESULT hResult = S_OK; // record service control thread. m_dwThread = GetCurrentThreadId(); // intialize thread as free threaded service. hResult = CoInitializeEx(NULL, COINIT_MULTITHREADED); if (FAILED(hResult)) { return; } // create a NULL DACL which will allow access to everyone. COpcSecurity cSecurity; hResult = cSecurity.InitializeFromThreadToken(); if (FAILED(hResult)) { return; } // initialize security. hResult = CoInitializeSecurity( cSecurity, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL); if (FAILED(hResult)) { // security may have already been initialized when registering classes from config file. if (hResult != RPC_E_TOO_LATE) { return; } } DWORD pdwRegister[256]; // register class objects. hResult = OpcRegisterClassObjects( m_pClasses, CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER, REGCLS_MULTIPLEUSE, pdwRegister); if (FAILED(hResult)) { return; } // check if a wrapper process. if (m_bWrapper) { // load classes from registry. m_pWrappedClasses = GetClassesFromRegistry(m_pClasses); // register class objects for wrapped classes. if (m_pWrappedClasses != NULL) { OpcRegisterClassObjects( m_pWrappedClasses, CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER, REGCLS_MULTIPLEUSE, pdwRegister); } } // update service state. m_cServiceStatus.dwCurrentState = SERVICE_RUNNING; if (m_hServiceStatus != NULL) { SetServiceStatus(m_hServiceStatus, &m_cServiceStatus); } TRY { // enter message loop. MSG cMsg; while (GetMessage(&cMsg, 0, 0, 0)) DispatchMessage(&cMsg); } CATCH { } // revoke wrapped classes. if (m_pWrappedClasses != NULL) { OpcRevokeClassObjects(m_pWrappedClasses, pdwRegister); } // unregister class objects. OpcRevokeClassObjects(m_pClasses, pdwRegister); // unregister com libraries. CoUninitialize(); } // RegisterServer HRESULT COpcComModule::RegisterServer(bool bService) { COpcString cValue; bool bEveryone = GetConfigParam(OPCREG_ALLOW_EVERYONE_ACCESS, cValue); return OpcRegisterServer( m_hModule, m_cVendorName, m_cApplicationName, m_cApplicationDescription, m_cAppID, bService, m_pClasses, m_pCategories, bEveryone); } // UnregisterServer HRESULT COpcComModule::UnregisterServer() { return OpcUnregisterServer( m_hModule, m_cVendorName, m_cApplicationName, m_cAppID, m_pClasses, m_pCategories); } // RegisterFromFiles HRESULT COpcComModule::RegisterFromFiles(HINSTANCE hModule) { CoInitializeEx(NULL, COINIT_MULTITHREADED); // create a NULL DACL which will allow access to everyone. COpcSecurity cSecurity; HRESULT hResult = cSecurity.InitializeFromThreadToken(); if (FAILED(hResult)) { return hResult; } // initialize security. hResult = CoInitializeSecurity( cSecurity, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL); if (FAILED(hResult)) { return hResult; } // ================= // TJF - skip GetClassesFromFiles and just use the hard-coded configuration // defined by macros 'OPC_CLASS_TABLE_ENTRY' & 'OPC_CATEGORY_TABLE_ENTRY'. Not clear // at this point how to support multiple CoClasses from XML file m_bRegFromFile = false; return S_OK; // ================= COpcString cVendorName; COpcString cApplicationName; COpcString cApplicationDescription; GUID cAppID; // get classes from config file(s). TOpcClassTableEntry* pClassesFromFile = GetClassesFromFiles( hModule, m_pClasses, cVendorName, cApplicationName, cApplicationDescription, cAppID ); // check for error reading from file - use defaults. if (pClassesFromFile == NULL) { m_bRegFromFile = false; return S_OK; } m_cVendorName = cVendorName; m_cApplicationName = cApplicationName; m_cApplicationDescription = cApplicationDescription; m_cAppID = cAppID; // check if the server wraps other servers. COpcString cWrapper; if (GetConfigParam(OPCREG_SERVER_WRAPPER, cWrapper)) { m_bWrapper = (cWrapper.ToLower() == _T("true")); } // replace the class and category tables with the auto generated ones. for (UINT uClassCount = 0; pClassesFromFile[uClassCount].tsClassName != NULL; uClassCount++); for (UINT uCategoryCount = 0; m_pCategories[uCategoryCount].pClsid != NULL; uCategoryCount++); TClassCategories* pCategoriesFromFile = OpcArrayAlloc(TClassCategories, uClassCount*uCategoryCount+1); memset(pCategoriesFromFile, 0, (uClassCount*uCategoryCount+1)*sizeof(TClassCategories)); UINT uIndex = 0; for (UINT ii = 0; pClassesFromFile[ii].tsClassName != NULL; ii++) { for (UINT jj = 0; m_pCategories[jj].pClsid != NULL; jj++) { pCategoriesFromFile[uIndex].pClsid = pClassesFromFile[ii].pClsid; pCategoriesFromFile[uIndex].pCategory = m_pCategories[jj].pCategory; uIndex++; } } m_pClasses = pClassesFromFile; m_pCategories = pCategoriesFromFile; m_bRegFromFile = true; return S_OK; } // InsertSelfRegInfo HRESULT COpcComModule::InsertSelfRegInfo( COpcXmlElement& cRoot, const TOpcClassTableEntry* pClasses ) { BSTR bstrName = NULL; BSTR bstrNamespace = NULL; IXMLDOMElement* ipInfo = NULL; IXMLDOMDocument* ipDocument = NULL; HRESULT hResult = S_OK; TRY { IXMLDOMElement* ipRoot = cRoot; HRESULT hResult = ipRoot->get_ownerDocument(&ipDocument); if (FAILED(hResult)) { THROW(); } VARIANT vNodeType; vNodeType.vt = VT_I4; vNodeType.lVal = NODE_ELEMENT; BSTR bstrName = SysAllocString((COpcString)OPCREG_SELFREG_INFO); BSTR bstrNamespace = SysAllocString((COpcString)OPCXML_NS_OPC); hResult = ipDocument->createNode(vNodeType, bstrName, bstrNamespace, (IXMLDOMNode**)&ipInfo); if (FAILED(hResult)) { THROW(); } COpcXmlElement cElement = ipInfo; // save the default information. WRITE_ELEMENT(OPCREG_VENDOR_NAME, m_cVendorName); WRITE_ELEMENT(OPCREG_APPLICATION_NAME, m_cApplicationName); WRITE_ELEMENT(OPCREG_APPLICATION_DESCRIPTION, m_cApplicationDescription); WRITE_ELEMENT(OPCREG_APPID, m_cAppID); WRITE_ELEMENT(OPCREG_CLASS_NAME, (COpcString)pClasses[0].tsClassName); WRITE_ELEMENT(OPCREG_CLASS_DESCRIPTION, (COpcString)pClasses[0].tsClassDescription); WRITE_ELEMENT(OPCREG_CLASS_VERSION, (COpcString)pClasses[0].tsClassVersion); WRITE_ELEMENT(OPCREG_CLSID, *(pClasses[0].pClsid)); OpcInsertSelfRegInfo(ipRoot, ipInfo); } CATCH_FINALLY { SysFreeString(bstrName); SysFreeString(bstrNamespace); if (ipInfo != NULL) ipInfo->Release(); if (ipDocument != NULL) ipDocument->Release(); } return hResult; } // GetClassesFromFiles TOpcClassTableEntry* COpcComModule::GetClassesFromFiles( HINSTANCE hModule, const TOpcClassTableEntry* pClasses, COpcString& cVendorName, COpcString& cApplicationName, COpcString& cApplicationDescription, GUID& cAppID ) { // lookup the module path. TCHAR tsPath[MAX_PATH+1]; memset(tsPath, 0, sizeof(tsPath)); GetModuleFileName(hModule, tsPath, MAX_PATH); // split into components. TCHAR tsDrive[_MAX_DRIVE]; TCHAR tsDir[_MAX_DIR]; TCHAR tsName[_MAX_FNAME]; TCHAR tsExtention[_MAX_EXT]; _tsplitpath(tsPath, tsDrive, tsDir, tsName, tsExtention); // construct config file parh. COpcString cConfigFile; cConfigFile += tsDrive; cConfigFile += tsDir; cConfigFile += tsName; cConfigFile += OPCREG_CONFIG_FILE_SUFFIX; // load the XML document. COpcXmlDocument cDocument; if (!cDocument.Load(cConfigFile)) { return NULL; } COpcXmlElement cRoot = cDocument.GetRoot(); // nothing more to do document is not valid. if (cRoot == NULL) { return NULL; } // populate the configuration parameters table. cRoot.GetAttributes(m_cParameters); // find the registration element. COpcXmlElement cElement = cRoot.GetChild(OPCREG_SELFREG_INFO); if (cElement == NULL) { // insert default self registration info. HRESULT hResult = InsertSelfRegInfo(cRoot, pClasses); // persist the configuration with the self registration info. if (SUCCEEDED(hResult)) { cDocument.Save(); }; } // nothing more to do if self reg info was not found. if (cElement == NULL) { return NULL; } // read registration information from the XML document. COpcString cClassName; COpcString cClassDescription; COpcString cClassVersion; CLSID cClsid = GUID_NULL; READ_ELEMENT(OPCREG_VENDOR_NAME, cVendorName); READ_ELEMENT(OPCREG_APPLICATION_NAME, cApplicationName); READ_ELEMENT(OPCREG_APPLICATION_DESCRIPTION, cApplicationDescription); READ_ELEMENT(OPCREG_APPID, cAppID); READ_ELEMENT(OPCREG_CLASS_NAME, cClassName); READ_ELEMENT(OPCREG_CLASS_DESCRIPTION, cClassDescription); READ_ELEMENT(OPCREG_CLASS_VERSION, cClassVersion); READ_ELEMENT(OPCREG_CLSID, cClsid); // check that all require information has been provided. if ( cVendorName.IsEmpty() || cAppID == GUID_NULL || cClassName.IsEmpty() || cClsid == GUID_NULL ) { return NULL; } // default application name as class name. if (cApplicationName.IsEmpty()) { cApplicationName = cClassName; } // default application description as class description. if (cApplicationDescription.IsEmpty()) { cApplicationDescription = cClassDescription; } // re-allocate class table dynamically, TOpcClassTableEntry* pClassesFromFile = OpcArrayAlloc(TOpcClassTableEntry, 2); memset(pClassesFromFile, 0, sizeof(TOpcClassTableEntry)*2); pClassesFromFile[0].tsClassName = OpcStrDup((LPCTSTR)cClassName); pClassesFromFile[0].tsClassDescription = OpcStrDup((LPCTSTR)cClassDescription); pClassesFromFile[0].tsClassVersion = OpcStrDup((LPCTSTR)cClassVersion); pClassesFromFile[0].pClsid = (CLSID*)OpcAlloc(sizeof(CLSID)); *((CLSID*)(pClassesFromFile[0].pClsid)) = cClsid; pClassesFromFile[0].pfnCreateInstance = pClasses[0].pfnCreateInstance; return pClassesFromFile; } // 899A3076-F94E-4695-9DF8-0ED25B02BDBA static const GUID CATID_PseudoComServers = {0x899A3076, 0xF94E, 0x4695, { 0x9D, 0xF8, 0x0E, 0xD2, 0x5B, 0x02, 0xBD, 0xBA } }; // GetClassesFromRegistry map TOpcClassTableEntry* COpcComModule::GetClassesFromRegistry( const TOpcClassTableEntry* pClasses ) { // read registration information from the XML document. COpcString cClassName; COpcString cClassDescription; COpcString cClassVersion; CLSID cClsid = GUID_NULL; CreatorsMap creatorsByCategory; HRESULT hResult = GetHostCreatorsByCategory (pClasses, creatorsByCategory); if (FAILED(hResult)) { return NULL; } COpcList cClsids; hResult = OpcEnumServersInCategory(NULL, CATID_PseudoComServers, &cClsids); if (FAILED(hResult)) { return NULL; } UINT uCount = cClsids.GetCount(); if (uCount == 0) { return NULL; } // re-allocate class table dynamically, TOpcClassTableEntry* pClassesFromFile = OpcArrayAlloc(TOpcClassTableEntry, uCount+1); memset(pClassesFromFile, 0, sizeof(TOpcClassTableEntry)*(uCount+1)); OPC_POS pos = cClsids.GetHeadPosition(); for (UINT ii = 0; pos != NULL; ii++) { CLSID cClsid = cClsids.GetNext(pos); PfnOpcCreateInstance* pCreator = NULL; COpcString cSubKey(_T("CLSID\\")); cSubKey += (COpcString)cClsid; cSubKey += _T("\\ProgID"); LPTSTR tsProgID = NULL; bool bResult = OpcRegGetValue(HKEY_CLASSES_ROOT, cSubKey, NULL, &tsProgID); if (bResult) { pCreator = FindHostCreatorByCategory(creatorsByCategory, cClsid); pClassesFromFile[ii].tsClassName = OpcStrDup(tsProgID); pClassesFromFile[ii].tsClassDescription = OpcStrDup(tsProgID); pClassesFromFile[ii].tsClassVersion = NULL; pClassesFromFile[ii].pClsid = (CLSID*)OpcAlloc(sizeof(CLSID)); *((CLSID*)(pClassesFromFile[ii].pClsid)) = cClsid; if (pCreator) pClassesFromFile[ii].pfnCreateInstance = pCreator; else pClassesFromFile[ii].pfnCreateInstance = pClasses[0].pfnCreateInstance; OpcFree(tsProgID); } } return pClassesFromFile; } // FreeClassTable void COpcComModule::FreeClassTable(TOpcClassTableEntry* ppClassesFromFile) { for (UINT ii = 0; ppClassesFromFile[ii].tsClassName != NULL; ii++) { OpcFree((LPTSTR)ppClassesFromFile[ii].tsClassName); OpcFree((LPTSTR)ppClassesFromFile[ii].tsClassDescription); OpcFree((LPTSTR)ppClassesFromFile[ii].tsClassVersion); OpcFree((CLSID*)ppClassesFromFile[ii].pClsid); } OpcFree(ppClassesFromFile); } //============================================================================== // OpcServerRegInfo HRESULT OpcServerRegInfo( const COpcString& cSrcFile, COpcString& cVendorName, COpcString& cApplicationName, COpcString& cApplicationDescription, GUID& cAppID, COpcString& cClassName, COpcString& cClassDescription, COpcString& cClassVersion, GUID& cClsid ) { HRESULT hResult = S_OK; TRY { // load the configuration file. COpcXmlDocument cConfigDocument; if (!cConfigDocument.Load(cSrcFile)) { THROW_(hResult, E_FAIL); } // find the existing self registration element. COpcXmlElement cElement = cConfigDocument.GetRoot().GetChild(OPCREG_SELFREG_INFO); if (cElement == NULL) { THROW_(hResult, E_FAIL); } READ_ELEMENT(OPCREG_VENDOR_NAME, cVendorName); READ_ELEMENT(OPCREG_APPLICATION_NAME, cApplicationName); READ_ELEMENT(OPCREG_APPLICATION_DESCRIPTION, cApplicationDescription); READ_ELEMENT(OPCREG_APPID, cAppID); READ_ELEMENT(OPCREG_CLASS_NAME, cClassName); READ_ELEMENT(OPCREG_CLASS_DESCRIPTION, cClassDescription); READ_ELEMENT(OPCREG_CLASS_VERSION, cClassVersion); READ_ELEMENT(OPCREG_CLSID, cClsid); } CATCH { } return hResult; } //============================================================================== // OpcCopyServer HRESULT OpcCopyServer( const COpcString& cSrcPath, const COpcString& cDstPath, const COpcString& cVendorName, const COpcString& cApplicationName, const COpcString& cApplicationDescription, const GUID& cAppID, const COpcString& cClassName, const COpcString& cClassDescription, const COpcString& cClassVersion, const GUID& cClsid ) { // parse the source path. COpcString cSrcBase = cSrcPath; int iIndex = cSrcBase.ReverseFind(OPCREG_CONFIG_FILE_SUFFIX); if (iIndex == -1) { return E_INVALIDARG; } cSrcBase = cSrcBase.SubStr(0, iIndex); // parse the destination path. COpcString cDstBase = cDstPath; iIndex = cDstBase.ReverseFind(OPCREG_CONFIG_FILE_SUFFIX); if (iIndex != -1) { cDstBase = cDstBase.SubStr(0, iIndex); } HRESULT hResult = S_OK; TRY { // load the configuration file. COpcString cConfigFile; cConfigFile += cSrcBase; cConfigFile += OPCREG_CONFIG_FILE_SUFFIX; // parse the document. COpcXmlDocument cConfigDocument; if (!cConfigDocument.Load(cConfigFile)) { THROW_(hResult, E_FAIL); } // find the existing self registration element. COpcXmlElement cElement = cConfigDocument.GetRoot().GetChild(OPCREG_SELFREG_INFO); if (cElement == NULL) { cElement = cConfigDocument.GetRoot().AddChild(OPCREG_SELFREG_INFO); } WRITE_ELEMENT(OPCREG_VENDOR_NAME, cVendorName); WRITE_ELEMENT(OPCREG_APPLICATION_NAME, cApplicationName); WRITE_ELEMENT(OPCREG_APPLICATION_DESCRIPTION, cApplicationDescription); WRITE_ELEMENT(OPCREG_APPID, cAppID); WRITE_ELEMENT(OPCREG_CLASS_NAME, cClassName); WRITE_ELEMENT(OPCREG_CLASS_DESCRIPTION, cClassDescription); WRITE_ELEMENT(OPCREG_CLASS_VERSION, cClassVersion); WRITE_ELEMENT(OPCREG_CLSID, cClsid); // save the document. cConfigFile = cDstBase; cConfigFile += OPCREG_CONFIG_FILE_SUFFIX; if (!cConfigDocument.Save(cConfigFile)) { THROW_(hResult, E_FAIL); } // copy the COM server executable binary file. CopyFile((COpcString)(cSrcBase + _T(".exe")), (COpcString)(cDstBase + _T(".exe")), FALSE); } CATCH { } return hResult; } // OpcGetModuleName COpcString OpcGetModuleName() { COpcString cConfigPath; // get executable path. TCHAR tsPath[MAX_PATH+1]; memset(tsPath, 0, sizeof(tsPath)); DWORD dwResult = GetModuleFileName(NULL, tsPath, MAX_PATH); if (dwResult == 0) { return cConfigPath; } cConfigPath = tsPath; // remove directory. int iIndex = cConfigPath.ReverseFind(_T("\\")); if (iIndex != -1) { cConfigPath = cConfigPath.SubStr(iIndex+1); } // remove extension. iIndex = cConfigPath.ReverseFind(_T(".")); if (iIndex != -1) { cConfigPath = cConfigPath.SubStr(0, iIndex); } return cConfigPath; } // OpcGetModulePath COpcString OpcGetModulePath() { COpcString cConfigPath; // get executable path. TCHAR tsPath[MAX_PATH+1]; memset(tsPath, 0, sizeof(tsPath)); DWORD dwResult = GetModuleFileName(NULL, tsPath, MAX_PATH); if (dwResult == 0) { return cConfigPath; } cConfigPath = tsPath; // remove file name. int iIndex = cConfigPath.ReverseFind(_T("\\")); if (iIndex != -1) { cConfigPath = cConfigPath.SubStr(0, iIndex); } return cConfigPath; } // OpcGetModuleVersion bool OpcGetModuleVersion(OpcVersionInfo& cInfo) { // initialize output parameters. cInfo.cFileDescription.Empty(); cInfo.wMajorVersion = 0; cInfo.wMinorVersion = 0; cInfo.wBuildNumber = 0; cInfo.wRevisionNumber = 0; // get module path. TCHAR tsModuleName[MAX_PATH+1]; memset(tsModuleName, 0, sizeof(tsModuleName)); DWORD dwResult = GetModuleFileName(NULL, tsModuleName, sizeof(tsModuleName)); if (dwResult == 0) { return false; } // get the size of the version info blob. DWORD dwSize = GetFileVersionInfoSize(tsModuleName, NULL); if (dwSize == 0) { return false; } // allocate space for the version info blob. BYTE* pBuffer = OpcArrayAlloc(BYTE, dwSize); memset(pBuffer, 0, dwSize); // get the version info blob. if (GetFileVersionInfo(tsModuleName, NULL, dwSize, pBuffer)) { // get the base version info. UINT uInfoSize = 0; VS_FIXEDFILEINFO* pInfo = NULL; if (VerQueryValue(pBuffer, _T("\\"), (void**)&pInfo, &uInfoSize)) { cInfo.wMajorVersion = (WORD)((pInfo->dwFileVersionMS>>16)&0x00FF); cInfo.wMinorVersion = (WORD)(pInfo->dwFileVersionMS&0x00FF); cInfo.wBuildNumber = (WORD)((pInfo->dwFileVersionLS>>16)&0x00FF); cInfo.wRevisionNumber = (WORD)(pInfo->dwFileVersionLS&0x00FF); } UINT uTranslateSize = 0; struct LANGANDCODEPAGE { WORD wLanguage; WORD wCodePage; }* pTranslate = NULL; // read the list of languages and code pages. if (VerQueryValue(pBuffer, _T("\\VarFileInfo\\Translation"), (void**)&pTranslate, &uTranslateSize)) { TCHAR tsSubBlock[MAX_PATH+1]; memset(tsSubBlock, 0, sizeof(tsSubBlock)); _stprintf( tsSubBlock, _T("\\StringFileInfo\\%04x%04x\\FileDescription"), pTranslate->wLanguage, pTranslate->wCodePage ); TCHAR* pBlock = NULL; UINT uBlockSize = 0; if (VerQueryValue(pBuffer, tsSubBlock, (void**)&pBlock, &uTranslateSize)) { cInfo.cFileDescription = pBlock; } } } // free the buffer. OpcFree(pBuffer); return true; } // 132B3E2B-0E92-4816-972B-E42AA9532529 static const GUID CATID_ComDllServerHost = {0x132B3E2B, 0x0E92, 0x4816, { 0x97, 0x2B, 0xE4, 0x2A, 0xA9, 0x53, 0x25, 0x29 } }; // GetHostCreatorsByCategory HRESULT GetHostCreatorsByCategory (const TOpcClassTableEntry* pClasses, CreatorsMap& creatorsByCategory) { HRESULT hResult = S_OK; ICatInformation* ipInfo = NULL; IEnumCATID* penumCATID = NULL; TRY { // create component category manager. hResult = CoCreateInstance( CLSID_StdComponentCategoriesMgr, NULL, CLSCTX_INPROC_SERVER, IID_ICatInformation, (void**)&ipInfo ); if (FAILED(hResult)) { THROW(); } creatorsByCategory.RemoveAll(); for (UINT i = 0; pClasses[i].tsClassName != NULL; i++) { hResult = ipInfo->EnumImplCategoriesOfClass(*(pClasses[i].pClsid), &penumCATID); if (FAILED(hResult)) { THROW(); } do { ULONG ulFetched = 0; CATID catid = GUID_NULL; hResult = penumCATID->Next(1, &catid, &ulFetched); if (ulFetched == 1 && (catid != CATID_ComDllServerHost)) { creatorsByCategory[catid] = pClasses[i].pfnCreateInstance; } } while (hResult == S_OK); if (FAILED(hResult)) { THROW(); } } // error while enumerating clsids. if (FAILED(hResult)) { THROW(); } } CATCH { creatorsByCategory.RemoveAll(); } FINALLY { if (penumCATID != NULL) penumCATID->Release(); if (ipInfo != NULL) ipInfo->Release(); } return hResult; } // FindHostCreatorByCategory PfnOpcCreateInstance* FindHostCreatorByCategory(const CreatorsMap& creatorsByCategory, const GUID& cClsid) { HRESULT hResult = S_OK; ICatInformation* ipInfo = NULL; IEnumCATID* penumCATID = NULL; PfnOpcCreateInstance* pCreator = NULL; TRY { // create component category manager. hResult = CoCreateInstance( CLSID_StdComponentCategoriesMgr, NULL, CLSCTX_INPROC_SERVER, IID_ICatInformation, (void**)&ipInfo ); if (FAILED(hResult)) { THROW(); } hResult = ipInfo->EnumImplCategoriesOfClass(cClsid, &penumCATID); do { ULONG ulFetched = 0; CATID catid = GUID_NULL; hResult = penumCATID->Next(1, &catid, &ulFetched); if (ulFetched == 1 && (catid != CATID_PseudoComServers)) { if (creatorsByCategory.Lookup(catid, pCreator)) { break; } } } while (hResult == S_OK); if (FAILED(hResult)) { THROW(); } // error while enumerating clsids. if (FAILED(hResult)) { THROW(); } } CATCH { pCreator = NULL; } FINALLY { if (penumCATID != NULL) penumCATID->Release(); if (ipInfo != NULL) ipInfo->Release(); } return pCreator; }