// // MODULE: TSHOOTCtrl.cpp // // PURPOSE: Implementation of CTSHOOTCtrl: Interface for the component // // PROJECT: Troubleshooter 99 // // COMPANY: Saltmine Creative, Inc. (206)-284-7511 support@saltmine.com // // AUTHOR: Oleg Kalosha // // ORIGINAL DATE: 12.23.98 // // NOTES: // // Version Date By Comments //-------------------------------------------------------------------- // V3.1 12/23/98 OK #include "stdafx.h" #include "TSHOOT.h" #include "TSHOOTCtrl.h" #include "LocalECB.h" #include "apgts.h" #include "apgtsinf.h" #define APGTS_COUNTER_OWNER 1 #include "ApgtsCounters.h" #include "apgtsinf.h" #include "apgtspl.h" #include "apgtscfg.h" #include "apgtslog.h" #include "event.h" #include "apgtsinf.h" #include "apgtscls.h" #include "apgtsevt.h" #include "VariantBuilder.h" // Launcher integration #include "LaunchServ.h" #include "LaunchServ_i.c" #include "CHMFileReader.h" bool g_nLaunched = false; extern HANDLE ghModule; // Error codes for end user. Previously we gave verbose error messages. Microsoft // decided 8/98 that they do not want to tell the end user about presumably internal problems. // Hence these codes. DWORD k_ServErrDuringInit = 1000; // Error(s) During Initialization: m_dwErr number follows DWORD k_ServErrLimitedRequests = 1001; // The server has limited the number of requests DWORD k_ServErrThreadTokenFail = 1002; // Failed to open thread token (impersonation token) DWORD k_ServErrShuttingDown = 1003; // Server Shutting Down DWORD k_ServErrOutOfMemory = 1005; // Out of memory (probably never will occur) // Since VC++ v5.0 does not throw exceptions upon memory failure, we force the behavior. _PNH APGST_New_Handler( size_t ) { throw std::bad_alloc(); return( 0 ); } ///////////////////////////////////////////////////////////////////////////// // CTSHOOTCtrl CTSHOOTCtrl::CTSHOOTCtrl() : m_bInitialized(false), m_bFirstCall(true), m_pThreadPool(NULL), m_poolctl(NULL), m_pConf(NULL), m_pLog(NULL), m_dwErr(0), m_bShutdown(false), m_dwRollover(0), m_bStartedFromLauncher(false), m_pVariantBuilder(NULL), m_bRequestToSetLocale(false), m_bCanRegisterGlobal(true) { // Set a new handler that throws bad_alloc exceptions (unlike VC++ v5.0). m_SetNewHandlerPtr= _set_new_handler( (_PNH)APGST_New_Handler ); // Have malloc call the _set_new_handler upon failure to allocate memory. m_SetNewMode= _set_new_mode( 1 ); if (RUNNING_APARTMENT_THREADED()) { CoCreateInstance(CLSID_StdGlobalInterfaceTable, NULL, CLSCTX_INPROC_SERVER, IID_IGlobalInterfaceTable, reinterpret_cast(&m_pGIT)); } } CTSHOOTCtrl::~CTSHOOTCtrl() { if (RUNNING_APARTMENT_THREADED()) { for(vector::iterator it = m_vecCookies.begin(); it != m_vecCookies.end(); it++) m_pGIT->RevokeInterfaceFromGlobal(*it); m_pGIT->Release(); } Destroy(); // Restore the initial set_new_handler and set_new_mode. _set_new_handler( m_SetNewHandlerPtr ); // Restore the malloc handling as it was previously. _set_new_mode( m_SetNewMode ); } bool CTSHOOTCtrl::Init(HMODULE hModule) { try { //[BC-03022001] - added check for NULL ptr to satisfy MS code analysis tool after // all new operations in this function. m_poolctl= new CPoolQueue(); if(!m_poolctl) throw bad_alloc(); if ((m_dwErr = m_poolctl->GetStatus()) != 0) return false; m_pThreadPool = new CThreadPool(m_poolctl, dynamic_cast(this)); if(!m_pThreadPool) throw bad_alloc(); if ((m_dwErr = m_pThreadPool->GetStatus()) != 0) return false; // open log m_pLog = new CHTMLLog( DEF_LOGFILEDIRECTORY ); if(!m_pLog) throw bad_alloc(); if ((m_dwErr = m_pLog->GetStatus()) != 0) return false; m_pConf= new CDBLoadConfiguration(hModule, m_pThreadPool, m_strTopicName, m_pLog ); if(!m_pConf) throw bad_alloc(); } catch (bad_alloc&) { m_dwErr= EV_GTS_CANT_ALLOC; CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ ); CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(), SrcLoc.GetSrcFileLineStr(), _T(""), _T(""), m_dwErr ); return false; } return true; } void CTSHOOTCtrl::Destroy() { if (m_pThreadPool) { // >>>(ignore for V3.0) The following is not great encapsulation, but as of 9/22/98 we // don't see a way around it. StartRequest falls naturally in APGTSExtension, so // APGTSExtension ends up with responsibility to tell the pool threads to exit. bool bAllSuccess = true; // signal all working threads to quit DWORD dwWorkingThreadCount = m_pThreadPool->GetWorkingThreadCount(); for (DWORD i = 0; i < dwWorkingThreadCount; i++) if (StartRequest(NULL, NULL) != HSE_STATUS_PENDING) bAllSuccess = false; if (bAllSuccess == false) { CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ ); CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(), SrcLoc.GetSrcFileLineStr(), _T(""), _T(""), EV_GTS_USER_BAD_THRD_REQ ); } } if (m_pConf) delete m_pConf; if (m_pThreadPool) delete m_pThreadPool; if (m_poolctl) { // [BC-022701] - Removed Unlock call here. Not matched with preceeding Lock() call. // This never caused a problem until run on Debug build of WindowsXP. In this environment // this Unlock call causes crash. //m_poolctl->Unlock(); delete m_poolctl; } if (m_pLog) delete m_pLog; } // coded on analogy with Online troubleshooter DWORD CTSHOOTCtrl::HttpExtensionProc(CLocalECB* pECB) { bool fRet = false, bChange = false; DWORD dwRet = HSE_STATUS_PENDING; //HANDLE hImpersonationToken; CString strTemp; CLocalECB *pLocalECB = pECB; if (m_dwErr) { strTemp.Format(_T("

Error %d:%d"), k_ServErrDuringInit, m_dwErr); fRet = SendError( pLocalECB, _T("500 Try again later"), // 500 is from HTTP spec strTemp); pLocalECB->SetHttpStatusCode(500); return fRet ? HSE_STATUS_SUCCESS : HSE_STATUS_ERROR; } // Is the request queue (items requested by this fn to be serviced by working threads) // too long? If so, tell the user to come back later // if ( m_poolctl->GetTotalQueueItems() + 1 > m_pConf->GetMaxWQItems() ) { // // Send a message back to client indicating we're too busy, they // should try again later. // CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ ); CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(), SrcLoc.GetSrcFileLineStr(), _T(""), _T(""), EV_GTS_SERVER_BUSY ); strTemp.Format(_T("

Error %d"), k_ServErrLimitedRequests); fRet = SendError( pLocalECB, _T("503 Try again later"), strTemp ); pLocalECB->SetHttpStatusCode(503); return fRet ? HSE_STATUS_SUCCESS : HSE_STATUS_ERROR; } // // Capture the current impersonation token (IIS Security) so we can impersonate this // user in the other thread. Limit permissions. // /* if ( !::OpenThreadToken(::GetCurrentThread(), TOKEN_QUERY | TOKEN_IMPERSONATE, false, // Open in unimpersonated context &hImpersonationToken )) { DWORD err = ::GetLastError(); CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ ); CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(), SrcLoc.GetSrcFileLineStr(), _T(""), _T(""), EV_GTS_ERROR_THREAD_TOKEN ); strTemp.Format(_T("

Error %d"), k_ServErrThreadTokenFail); fRet = SendError( pLocalECB, _T("500 Try again later"), strTemp ); pLocalECB->SetHttpStatusCode(500); return fRet ? HSE_STATUS_SUCCESS : HSE_STATUS_ERROR; } */ dwRet = StartRequest(pLocalECB, NULL/*hImpersonationToken*/); return (dwRet); } // Thread-safe. // NOTE TWO ROLES depending on INPUT pLocalECB. // INPUT pLocalECB - NULL if shutting down // Otherwise, EXTENSION_CONTROL_BLOCK is ISAPI's way of passing in the sort of // stuff you'd get from CGI. We've abstracted from that. // INPUT hImpersonationToken obtained via prior call to OpenThreadToken // Not relevant if pLocalECB == NULL // RETURNS HSE_STATUS_SUCCESS, HSE_STATUS_ERROR, HSE_STATUS_PENDING // (or HSE_REQ_DONE_WITH_SESSION in single-threaded debugging version) DWORD CTSHOOTCtrl::StartRequest(CLocalECB *pLocalECB, HANDLE hImpersonationToken) { WORK_QUEUE_ITEM * pwqi; bool fRet = false; CString strTemp; // // Take the queue lock, get a queue item and put it on the queue // if (pLocalECB && m_bShutdown) { CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ ); CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(), SrcLoc.GetSrcFileLineStr(), _T(""), _T(""), EV_GTS_CANT_PROC_REQ_SS ); strTemp.Format(_T("

Error %d"), k_ServErrShuttingDown); fRet = SendError( pLocalECB, _T("500 Try again later"), strTemp ); pLocalECB->SetHttpStatusCode(500); ::CloseHandle( hImpersonationToken ); return fRet ? HSE_STATUS_SUCCESS : HSE_STATUS_ERROR; } m_poolctl->Lock(); // 9/23/98 JM got rid of a constraint here which was too tight a constraint // on size of queue try { // bundle up pointers the worker thread will need pwqi = new WORK_QUEUE_ITEM ( hImpersonationToken, pLocalECB, // may be null as a signal m_pConf, m_pLog); } catch (bad_alloc&) { m_poolctl->Unlock(); if (pLocalECB) { CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ ); CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(), SrcLoc.GetSrcFileLineStr(), _T(""), _T(""), EV_GTS_ERROR_WORK_ITEM ); strTemp.Format(_T("

Error %d"), k_ServErrOutOfMemory); fRet = SendError( pLocalECB, _T("500 Not enough memory"), strTemp); pLocalECB->SetHttpStatusCode(500); ::CloseHandle( hImpersonationToken ); } return fRet ? HSE_STATUS_SUCCESS : HSE_STATUS_ERROR; } if (!pLocalECB) m_bShutdown = true; // Some data passed to thread just for statistical purposes // Thread can pass this info back over web; we can't. pwqi->GTSStat.dwRollover = m_dwRollover++; // put it at the tail of the queue & signal the pool threads there is work to be done m_poolctl->PushBack(pwqi); m_poolctl->Unlock(); return HSE_STATUS_PENDING; } // Build an HTTP response in the case of an error. // INPUT *pszStatus short status (e.g. "503 Server too busy"). // INPUT str - entire content of the page. // RETURNS true on success // NOTE that this actually uses no member variables. /*static*/ bool CTSHOOTCtrl::SendSimpleHtmlPage( CLocalECB *pLocalECB, LPCTSTR pszStatus, const CString & str) { BOOL fRet; DWORD cb; TCHAR pszTemp[200]; // safely large to copy pszStatus. pLocalECB->ServerSupportFunction // doesn't want pszStatus in a const array _tcscpy(pszTemp, pszStatus); // Send the headers // fRet = pLocalECB->ServerSupportFunction( HSE_REQ_SEND_RESPONSE_HEADER, pszTemp, NULL, (LPDWORD) _T("Content-Type: text/html\r\n\r\n") ); // // If that succeeded, send the message // if ( fRet ) { cb = str.GetLength(); // (LPCTSTR) cast gives us the underlying text bytes. // >>> $UNICODE Actually, this would screw up under Unicode compile, because for HTML, // this must be SBCS. Should really be a conversion to LPCSTR, which is non-trivial // in a Unicode compile. JM 1/7/99 fRet = pLocalECB->WriteClient((LPCTSTR)str, &cb); } return fRet ? true : false; } // Build an HTTP response in the case of an error. // INPUT pLocalECB - EXTENSION_CONTROL_BLOCK is ISAPI's way of passing in the sort of // stuff you'd get from CGI. We've abstracted from that. pLocalECB should never be null. // INPUT *pszStatus short status (e.g. "503 Try again later"). // INPUT *pszMessage - typically just an error number, e.g. "1004" or // "1000:123" // RETURNS true on success /*static*/ bool CTSHOOTCtrl::SendError( CDBLoadConfiguration *pConf, CLocalECB *pLocalECB, LPCTSTR pszStatus, const CString & strMessage) { CString str; pConf->CreateErrorPage(strMessage, str); return SendSimpleHtmlPage( pLocalECB, pszStatus, str); } /*static*/ bool CTSHOOTCtrl::RemoveStartOverButton(CString& strWriteClient) { int left = 0, right = 0; if (-1 != (left = strWriteClient.Find(SZ_INPUT_TAG_STARTOVER))) { right = left; while (strWriteClient[++right] && strWriteClient[right] != _T('>')) ; if (strWriteClient[right]) strWriteClient = strWriteClient.Left(left) + strWriteClient.Right(strWriteClient.GetLength() - right - 1); else return false; return true; } return false; } /*static*/ bool CTSHOOTCtrl::RemoveBackButton(CString& strWriteClient) { int left = 0, right = 0; if (-1 != (left = strWriteClient.Find(SZ_INPUT_TAG_BACK))) { right = left; while (strWriteClient[++right] && strWriteClient[right] != _T('>')) ; if (strWriteClient[right]) strWriteClient = strWriteClient.Left(left) + strWriteClient.Right(strWriteClient.GetLength() - right - 1); else return false; return true; } return false; } bool CTSHOOTCtrl::SendError(CLocalECB *pLocalECB, LPCTSTR pszStatus, const CString & strMessage) const { return SendError(m_pConf, pLocalECB, pszStatus, strMessage); } bool CTSHOOTCtrl::ReadStaticPageFile(const CString& strTopicName, CString& strContent) { CString strPath; if (!m_pConf->GetRegistryMonitor().GetStringInfo(CAPGTSRegConnector::eResourcePath, strPath)) return false; CString strFullPath = strPath + strTopicName + LOCALTS_SUFFIX_RESULT + LOCALTS_EXTENSION_HTM; CFileReader fileResult( CPhysicalFileReader::makeReader( strFullPath ) ); if (!fileResult.Read()) return false; strContent = _T(""); fileResult.GetContent(strContent); return true; } bool CTSHOOTCtrl::ExtractLauncherData(CString& error) { HRESULT hRes = S_OK; DWORD dwResult = 0; OLECHAR *poleShooter = NULL; OLECHAR *poleProblem = NULL; OLECHAR *poleNode = NULL; OLECHAR *poleState = NULL; OLECHAR *poleMachine = NULL; OLECHAR *polePNPDevice = NULL; OLECHAR *poleGuidClass = NULL; OLECHAR *poleDeviceInstance = NULL; short i = 0; CNameValue name_value; ILaunchTS *pILaunchTS = NULL; CLSID clsidLaunchTS = CLSID_LaunchTS; IID iidLaunchTS = IID_ILaunchTS; // Get an interface on the launch server hRes = ::CoCreateInstance(clsidLaunchTS, NULL, CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER | CLSCTX_INPROC_SERVER, iidLaunchTS, (void **) &pILaunchTS); if (FAILED(hRes)) { error = _T("LaunchServ interface not found."); return false; } // Get all of the query values. hRes = pILaunchTS->GetShooterStates(&dwResult); if (S_FALSE == hRes || FAILED(hRes)) { error.Format(_T("GetShooterStates Failed. %ld"), dwResult); pILaunchTS->Release(); return false; } // clear container m_arrNameValueFromLauncher.clear(); // get tshooter name hRes = pILaunchTS->GetTroubleShooter(&poleShooter); if (S_FALSE == hRes || FAILED(hRes)) { error = _T("GetTroubleShooter Failed."); pILaunchTS->Release(); return false; } name_value.strName = C_TOPIC; name_value.strValue = poleShooter; m_arrNameValueFromLauncher.push_back(name_value); SysFreeString(poleShooter); // get problem hRes = pILaunchTS->GetProblem(&poleProblem); if (S_FALSE != hRes && !FAILED(hRes)) { name_value.strName = NODE_PROBLEM_ASK; name_value.strValue = poleProblem; m_arrNameValueFromLauncher.push_back(name_value); SysFreeString(poleProblem); // get name - value pairs for nodes set by the user do { hRes = pILaunchTS->GetNode(i, &poleNode); if (FAILED(hRes) || S_FALSE == hRes) break; name_value.strName = poleNode; SysFreeString(poleNode); hRes = pILaunchTS->GetState(i, &poleState); if (FAILED(hRes) || S_FALSE == hRes) break; name_value.strValue = poleState; SysFreeString(poleState); m_arrNameValueFromLauncher.push_back(name_value); i++; } while (true); } /////////////////////////////////////////////////////////// // obtaining Machine, PNPDevice, GuidClass, DeviceInstance hRes = pILaunchTS->GetMachine(&poleMachine); if (S_FALSE == hRes || FAILED(hRes)) { error = _T("GetMachine Failed."); pILaunchTS->Release(); return false; } m_strMachineID = poleMachine; ::SysFreeString(poleMachine); hRes = pILaunchTS->GetPNPDevice(&polePNPDevice); if (S_FALSE == hRes || FAILED(hRes)) { error = _T("GetPNPDevice Failed."); pILaunchTS->Release(); return false; } m_strPNPDeviceID = polePNPDevice; ::SysFreeString(polePNPDevice); hRes = pILaunchTS->GetGuidClass(&poleGuidClass); if (S_FALSE == hRes || FAILED(hRes)) { error = _T("GetGuidClass Failed."); pILaunchTS->Release(); return false; } m_strGuidClass = poleGuidClass; ::SysFreeString(poleGuidClass); hRes = pILaunchTS->GetDeviceInstance(&poleDeviceInstance); if (S_FALSE == hRes || FAILED(hRes)) { error = _T("GetDeviceInstance Failed."); pILaunchTS->Release(); return false; } m_strDeviceInstanceID = poleDeviceInstance; ::SysFreeString(poleDeviceInstance); //////////////////////////////////////////////////////////// pILaunchTS->Release(); return true; } // STDMETHODIMP CTSHOOTCtrl::RunQuery(VARIANT varCmds, VARIANT varVals, short size, BSTR *pbstrPage) { USES_CONVERSION; if (GetLocked()) return S_OK; if (RUNNING_APARTMENT_THREADED()) { if (m_bCanRegisterGlobal) { RegisterGlobal(); m_bCanRegisterGlobal = false; } } //!!!!!!!!!!!!! Check for size < 1 !!!!!!!!!!!!!!!!!!!!! if (size < 1) { *pbstrPage = T2BSTR(" Troubleshooter

Error in RunQuery parameter: size is less one.

"); return S_OK; } //!!!!!!!!!! detect the way we were stated !!!!!!!!!!!!!! { CString strStub; CLocalECB ECB(varCmds, varVals, 1, NULL, &strStub, NULL, m_bRequestToSetLocale, m_strRequestedLocale ); CString strFirstName = ECB.GetNameValue(0).strName; if (strFirstName == NODE_LIBRARY_ASK) { if (g_nLaunched) { *pbstrPage = T2BSTR(" Troubleshooter

Error in RunQuery: launched for the second time.

"); return S_OK; } CString strError; m_bStartedFromLauncher = true; g_nLaunched = true; if (!ExtractLauncherData(strError)) { CString strOut; strOut.Format(_T(" Troubleshooter

%s.

"), strError); *pbstrPage = T2BSTR(strOut); m_bStartedFromLauncher = false; return S_OK; } } } ///////////////////////////////////////////////////////// // automatic variable declaration HANDLE event = ::CreateEvent(NULL, false, false, NULL); CString strWriteClient; CLocalECB* pECB; if (RUNNING_APARTMENT_THREADED()) pECB = !m_bStartedFromLauncher ? new CLocalECB(varCmds, varVals, size, NULL, &strWriteClient, dynamic_cast(this), m_bRequestToSetLocale, m_strRequestedLocale) : new CLocalECB(m_arrNameValueFromLauncher, NULL, &strWriteClient, dynamic_cast(this), m_bRequestToSetLocale, m_strRequestedLocale); if (RUNNING_FREE_THREADED()) pECB = !m_bStartedFromLauncher ? new CLocalECB(varCmds, varVals, size, event, &strWriteClient, NULL, m_bRequestToSetLocale, m_strRequestedLocale) : new CLocalECB(m_arrNameValueFromLauncher, event, &strWriteClient, NULL, m_bRequestToSetLocale, m_strRequestedLocale); m_bRequestToSetLocale= false; // Deactivate locale setting after it has been passed into the ECB. SetLocked(true); bool bSaveFirstPage = false; ///////////////////////////////////////////////////////// // initialize if (!m_bInitialized) { // extract topic name first if (!m_bStartedFromLauncher) { CString strStub; CLocalECB ECB(varCmds, varVals, 1, NULL, &strStub, NULL, m_bRequestToSetLocale, m_strRequestedLocale ); m_strTopicName = ECB.GetNameValue(0).strValue; } else m_strTopicName = (*m_arrNameValueFromLauncher.begin()).strValue; if (Init((HINSTANCE)::ghModule)) { m_bInitialized = true; } else { *pbstrPage = T2BSTR(_T(" Troubleshooter

Error of initialization in RunQuery.

")); m_bStartedFromLauncher = false; return S_OK; } } ////////////////////////////////////////////////////////// // save first page when started from static page if (m_strFirstPage.IsEmpty() && !m_bStartedFromLauncher) { CString strStaticPage; if (size == 2 && // RunQuery was started from static (since !m_bStartedFromLauncher) Problem Page(since size == 2) ReadStaticPageFile(m_strTopicName, strStaticPage) ) { m_strFirstPage = strStaticPage; } else { bSaveFirstPage = true; } } HttpExtensionProc(pECB); if (RUNNING_FREE_THREADED()) ::WaitForSingleObject(event, INFINITE); ::CloseHandle(event); if (bSaveFirstPage) m_strFirstPage = strWriteClient; ///////////////////////////////////////////////////////// // first RunQuery when started from Launcher if (m_bStartedFromLauncher && m_bFirstCall) { RemoveStartOverButton(strWriteClient); RemoveBackButton(strWriteClient); } ///////////////////////////////////////////////////////// // save first page when started from Launcher if (m_strFirstPage.IsEmpty() && m_bStartedFromLauncher) m_strFirstPage = strWriteClient; *pbstrPage = T2BSTR(strWriteClient); /* ////////////////////////////////////////////////////////////////////////// // >>> $TEST HANDLE hFile = ::CreateFile(_T("D:\\TShooter Projects\\Troubleshooter\\Local\\http\\Test\\first_step.htm"), GENERIC_WRITE, 0, NULL, // no security attributes CREATE_ALWAYS, FILE_FLAG_RANDOM_ACCESS, NULL // handle to template file ); if (hFile != INVALID_HANDLE_VALUE) { DWORD read = 0; ::WriteFile(hFile, (LPCTSTR)strWriteClient, strWriteClient.GetLength(), &read, NULL); } /////////////////////////////////////////////////////////////////////////// */ m_bStartedFromLauncher = false; m_bFirstCall = false; return S_OK; } STDMETHODIMP CTSHOOTCtrl::SetSniffResult(VARIANT varNodeName, VARIANT varState, BOOL *bResult) { // >>> No sniffing is used. Oleg 03.26.99 *bResult = 1; return S_OK; } STDMETHODIMP CTSHOOTCtrl::PreLoadURL(BSTR bstrRoot, BSTR *pbstrPage) { USES_CONVERSION; // >>> This feature is not used. Oleg. 03.26.99 *pbstrPage = A2BSTR("PreLoadURL results"); return S_OK; } STDMETHODIMP CTSHOOTCtrl::Restart(BSTR *pbstrPage) { USES_CONVERSION; if (GetLocked()) return S_OK; *pbstrPage = T2BSTR(m_strFirstPage); return S_OK; } // The same as Restart(...). // Implemented for compatibility with Win98's JScript STDMETHODIMP CTSHOOTCtrl::ProblemPage(BSTR *pbstrFirstPage) { USES_CONVERSION; if (GetLocked()) return S_OK; *pbstrFirstPage = T2BSTR(m_strFirstPage); return S_OK; } STDMETHODIMP CTSHOOTCtrl::SetPair(BSTR bstrCmd, BSTR bstrVal) { USES_CONVERSION; if (GetLocked()) return S_OK; if (!m_pVariantBuilder) m_pVariantBuilder = new CVariantBuilder; // check if we've started new sequence, but // array of name - value pairs is not empty CString type = W2T(bstrCmd); if (type == C_TYPE || type == C_PRELOAD || type == C_TOPIC) { if (m_pVariantBuilder->GetSize()) { delete m_pVariantBuilder; m_pVariantBuilder = new CVariantBuilder; } } m_pVariantBuilder->SetPair(bstrCmd, bstrVal); return S_OK; } // The arguments are ignored. They are just for backward compatibility to V1.0.1.2121 & its // successors STDMETHODIMP CTSHOOTCtrl::RunQuery2(BSTR, BSTR, BSTR, BSTR *pbstrPage) { if (GetLocked()) return S_OK; if (m_pVariantBuilder) { RunQuery(m_pVariantBuilder->GetCommands(), m_pVariantBuilder->GetValues(), m_pVariantBuilder->GetSize(), pbstrPage); delete m_pVariantBuilder; m_pVariantBuilder = NULL; } return S_OK; } STDMETHODIMP CTSHOOTCtrl::NotifyNothingChecked(BSTR bstrMessage) { USES_CONVERSION; if (GetLocked()) return S_OK; CString message = W2T(bstrMessage); ::MessageBox(::GetForegroundWindow(), message != _T("") ? message : _T("Please choose a button and then press Next"), _T("Error"), MB_OK); return S_OK; } long CTSHOOTCtrl::PerformSniffingInternal(CString strNodeName, CString strLaunchBasis, CString strAdditionalArgs) { USES_CONVERSION; return Fire_Sniffing(T2BSTR((LPCTSTR)strNodeName), T2BSTR((LPCTSTR)strLaunchBasis), T2BSTR((LPCTSTR)strAdditionalArgs)); } void CTSHOOTCtrl::RenderInternal(CString strPage) { USES_CONVERSION; Fire_Render(T2BSTR((LPCTSTR)strPage)); } void CTSHOOTCtrl::RegisterGlobal() { int nConnections = CProxy_ITSHOOTCtrlEvents< CTSHOOTCtrl >::m_vec.GetSize(); for (int nConnectionIndex = 0; nConnectionIndex < nConnections; nConnectionIndex++) { Lock(); CComPtr sp = CProxy_ITSHOOTCtrlEvents< CTSHOOTCtrl >::m_vec.GetAt(nConnectionIndex); Unlock(); IDispatch* pDispatch = reinterpret_cast(sp.p); if (pDispatch != NULL) { DWORD dwCookie; m_pGIT->RegisterInterfaceInGlobal(pDispatch, IID_IDispatch, &dwCookie); m_vecCookies.push_back(dwCookie); } } } STDMETHODIMP CTSHOOTCtrl::IsLocked(BOOL *pbResult) { *pbResult = GetLocked() ? TRUE : FALSE; return S_OK; } // Set the locale. // Parameter bstrNewLocale should be of the form: // "lang[_country[.code_page]]" // | ".code_page" // | "" // | NULL STDMETHODIMP CTSHOOTCtrl::setLocale2( BSTR bstrNewLocale ) { USES_CONVERSION; if (GetLocked()) return S_OK; m_strRequestedLocale= W2T( bstrNewLocale ); m_bRequestToSetLocale= true; return S_OK; }