/**********************************************************************/ /** Microsoft Windows/NT **/ /** Copyright(c) Microsoft Corporation **/ /**********************************************************************/ /* sharesdo.cpp implement classes for sharing SdoServer among property pages for different users and snapins FILE HISTORY: */ ////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include #include "rasdial.h" #include "sharesdo.h" #include "iastrace.h" // the server pool pointer used to share SdoServer among pages and snapins CSdoServerPool* g_pSdoServerPool; #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif // DO SDO Attach -- real HRESULT ConnectToSdoServer(BSTR machine, BSTR user, BSTR passwd,ISdoMachine** ppServer) { ASSERT(ppServer); if(!ppServer) return E_INVALIDARG; else *ppServer = NULL; HRESULT hr = S_OK; // connect to the new one IASTraceString("CoCreateInstance SdoServer"); CHECK_HR(hr = CoCreateInstance( CLSID_SdoMachine, NULL, CLSCTX_INPROC_SERVER, IID_ISdoMachine, (void**)ppServer)); IASTracePrintf(" hr = %8x\r\n", hr); ASSERT(*ppServer); IASTracePrintf("SdoServer::Attach(%s, %s, %s);", machine, user, passwd); CHECK_HR(hr = (*ppServer)->Attach(machine)); IASTracePrintf(" hr = %8x\r\n", hr); L_ERR: if(FAILED(hr) && *ppServer) { (*ppServer)->Release(); *ppServer = NULL; } return hr; } // When using Single connection, get the marshaled interface from the stream HRESULT GetSharedSdoServer(LPCTSTR machine, LPCTSTR user, LPCTSTR passwd, bool* pbConnect, CMarshalSdoServer* pServer) { static CCriticalSection cs; HRESULT hr = S_OK; if(cs.Lock()) // locked { if(NULL == g_pSdoServerPool) { try{ g_pSdoServerPool = new CSdoServerPool; }catch(CMemoryException* pException) { pException->Delete(); hr = E_OUTOFMEMORY; } } cs.Unlock(); } else { IASTraceString("ERROR: GetSharedSdoServer, CS lock failed"); return E_FAIL; } if(FAILED(hr)) return hr; return g_pSdoServerPool->GetMarshalServer(machine, user, passwd, pbConnect, pServer); } //====================================================== // class CSharedSdoServerImp // implementation class of shared server CSharedSdoServerImp::CSharedSdoServerImp(LPCTSTR machine, LPCTSTR user, LPCTSTR passwd) : strMachine(machine), strUser(user), strPasswd(passwd), bConnected(false) {}; // to make this class element of collection, provide following member functions bool CSharedSdoServerImp::IsFor(LPCTSTR machine, LPCTSTR user, LPCTSTR passwd) const { // Compare order, ServerName, UserName, Passwd, and RetriveType CString strM(machine); CString strU(user); CString strP(passwd); return ( strMachine.CompareNoCase(strM) == 0 && strUser.Compare(strU) == 0 && strPasswd.Compare(strP) == 0 ); }; // CoCreate SdoServer object HRESULT CSharedSdoServerImp::CreateServer() { // cannot create twice! ASSERT(!(ISdoMachine*)spServer); if((ISdoMachine*)spServer) return S_OK; HRESULT hr = S_OK; // connect to the new one IASTraceString("CoCreateInstance SdoServer"); hr = CoCreateInstance( CLSID_SdoMachine, NULL, CLSCTX_INPROC_SERVER, IID_ISdoMachine, (void**)&spServer); IASTracePrintf(" hr = %8x\r\n", hr); threadId = ::GetCurrentThreadId(); return hr; }; // get marshal stream, can specify, if immediate connection is required. HRESULT CSharedSdoServerImp::GetMarshalStream(LPSTREAM *ppStream, bool* pbConnect /* both input and output */) { ASSERT(ppStream); DWORD tid = ::GetCurrentThreadId(); if (tid != threadId) return E_FAIL; // make sure the interface should be marshaled from the same thread HRESULT hr = S_OK; cs.Lock(); if(pbConnect) { if(*pbConnect && !bConnected) { *pbConnect = false; CHECK_HR(hr = Connect(NULL)); *pbConnect = true; } else { *pbConnect = bConnected; } } // Marshal the interface CHECK_HR(hr = CoMarshalInterThreadInterfaceInStream(IID_ISdoMachine, (ISdoMachine*)spServer, ppStream)); L_ERR: cs.Unlock(); return hr; }; // Connect the server to the a machine HRESULT CSharedSdoServerImp::Connect(ISdoMachine* pServer) // pServer, marshaled pointer, passed in from a different thread ( than the spServer in the object) { cs.Lock(); HRESULT hr = S_OK; DWORD tid = ::GetCurrentThreadId(); USES_CONVERSION; if(!bConnected) { ASSERT((ISdoMachine*)spServer); BSTR bstrM = NULL; BSTR bstrU = NULL; BSTR bstrP = NULL; if(!strMachine.IsEmpty()) bstrM = T2BSTR((LPTSTR)(LPCTSTR)strMachine); if(!strUser.IsEmpty()) bstrM = T2BSTR((LPTSTR)(LPCTSTR)strUser); if(!strPasswd.IsEmpty()) bstrP = T2BSTR((LPTSTR)(LPCTSTR)strPasswd); IASTracePrintf("SdoServer::Connect(%s, %s, %s );", bstrM, bstrU, bstrP); if(!pServer) { // this function should be called within the same thread // if the request if from a different thread, then this should NOT be NULL ASSERT(tid == threadId); pServer = (ISdoMachine*)spServer; } hr = pServer->Attach(bstrM); IASTracePrintf(" hr = %8x\r\n", hr); bConnected = (hr == S_OK); SysFreeString(bstrM); SysFreeString(bstrU); SysFreeString(bstrP); } cs.Unlock(); return hr; }; HRESULT CSharedSdoServerImp::GetServerNReleaseStream(LPSTREAM pStream, ISdoMachine** ppServer) { #ifdef _DEBUG DWORD __tid = ::GetCurrentThreadId(); #endif return CoGetInterfaceAndReleaseStream(pStream, IID_ISdoMachine, (LPVOID*) ppServer); }; // if connection is needed, should call the connec of CSharedSdoServer, rather than ISdoServer::Connect HRESULT CMarshalSdoServer::GetServer(ISdoMachine** ppServer) { HRESULT hr = S_OK; if(!(ISdoMachine*)spServer) { if((IStream*)spStm) { CHECK_HR(hr = CSharedSdoServerImp::GetServerNReleaseStream((IStream*)spStm, (ISdoMachine**)&spServer)); } spStm.p = NULL; // need to manually clean this, since the above API already Release the COM interface } else CHECK_HR(hr = E_FAIL); if((ISdoMachine*)spServer) { *ppServer = (ISdoMachine*)spServer; (*ppServer)->AddRef(); } L_ERR: return hr; // not valid to call at this point }; // make SDO connect when / if NOT already done so. Note: multiple thread safe HRESULT CMarshalSdoServer::Connect() { ASSERT(pImp); // should not happen return pImp->Connect(spServer); }; void CMarshalSdoServer::Release() { pImp = NULL; spServer.Release(); spStm.Release(); }; CMarshalSdoServer::CMarshalSdoServer(): pImp(NULL) {}; // estiblish an entry in the pool, if it's new // get marshaServer object from the thread pool HRESULT CSdoServerPool::GetMarshalServer(LPCTSTR machineName, LPCTSTR userName, LPCTSTR passwd, bool* pbConnect, CMarshalSdoServer* pServer) { ASSERT(pServer); CSharedSdoServerImp* pImp = NULL; HRESULT hr = S_OK; std::list::iterator i; // search if the server is already exist cs.Lock(); for(i = listServers.begin(); i != listServers.end(); i++) { if((*i)->IsFor( machineName, userName, passwd)) { pImp = (*i); break; } } // if not, then create one if(!pImp) { try{ pImp = new CSharedSdoServerImp( machineName, userName, passwd); }catch(...) { CHECK_HR(hr = E_OUTOFMEMORY); } ASSERT(pImp); CHECK_HR(hr = pImp->CreateServer()); listServers.push_front(pImp); } // marshal it. bConnect will be filled pServer->Release(); { CComPtr spStm; CHECK_HR(hr = pImp->GetMarshalStream(&spStm, pbConnect)); // fill the information to the provided buffer pServer->SetInfo((IStream*)spStm, pImp); } L_ERR: cs.Unlock(); return hr; }; // clean the POOL CSdoServerPool::~CSdoServerPool() { #ifdef _DEBUG DWORD __tid = ::GetCurrentThreadId(); #endif std::list::iterator i; for(i = listServers.begin(); i != listServers.end(); i++) { delete (*i); } listServers.erase(listServers.begin(), listServers.end()); };