Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

861 lines
20 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1992 - 1995
  5. //
  6. // File: machine.cxx
  7. //
  8. // Contents: Implementation of the CMachine class
  9. //
  10. //----------------------------------------------------------------------------
  11. #include "headers.hxx"
  12. DeclareTag(tagMachine, "MTScript", "Monitor IConnectedMachine");
  13. // ***********************************************************************
  14. //
  15. // CMachConnectPoint
  16. //
  17. // ***********************************************************************
  18. CMachConnectPoint::CMachConnectPoint(CMachine *pMach)
  19. {
  20. _ulRefs = 1;
  21. _pMachine = pMach;
  22. _pMachine->AddRef();
  23. }
  24. CMachConnectPoint::~CMachConnectPoint()
  25. {
  26. _pMachine->Release();
  27. }
  28. HRESULT
  29. CMachConnectPoint::QueryInterface(REFIID iid, void **ppv)
  30. {
  31. if (iid == IID_IUnknown || iid == IID_IConnectionPoint)
  32. {
  33. *ppv = (IConnectionPoint *)this;
  34. }
  35. else
  36. {
  37. *ppv = NULL;
  38. return E_NOINTERFACE;
  39. }
  40. ((IUnknown *)*ppv)->AddRef();
  41. return S_OK;
  42. }
  43. HRESULT
  44. CMachConnectPoint::GetConnectionInterface(IID * pIID)
  45. {
  46. *pIID = DIID_DRemoteMTScriptEvents;
  47. return S_OK;
  48. }
  49. HRESULT
  50. CMachConnectPoint::GetConnectionPointContainer(IConnectionPointContainer ** ppCPC)
  51. {
  52. *ppCPC = _pMachine;
  53. (*ppCPC)->AddRef();
  54. return S_OK;
  55. }
  56. //+---------------------------------------------------------------------------
  57. //
  58. // Member: CMachConnectPoint::Advise, public
  59. //
  60. // Synopsis: Remembers interface pointers that we want to fire events
  61. // through.
  62. //
  63. // Arguments: [pUnkSink] -- Pointer to remember
  64. // [pdwCookie] -- Place to put cookie for Unadvise
  65. //
  66. // Returns: HRESULT
  67. //
  68. //----------------------------------------------------------------------------
  69. HRESULT
  70. CMachConnectPoint::Advise(IUnknown *pUnkSink, DWORD *pdwCookie)
  71. {
  72. IDispatch *pDisp;
  73. HRESULT hr;
  74. TraceTag((tagMachine, "Advising new machine sink: %p", pUnkSink));
  75. hr = pUnkSink->QueryInterface(IID_IDispatch, (LPVOID*)&pDisp);
  76. if (hr)
  77. {
  78. TraceTag((tagMachine, "Could not get IDispatch pointer on sink! (%x)", hr));
  79. return hr;
  80. }
  81. CMachine::LOCK_MACH_LOCALS(_pMachine);
  82. hr = _pMachine->_aryDispSink.Append(pDisp);
  83. if (hr)
  84. {
  85. TraceTag((tagMachine, "Error appending sink to array!"));
  86. RRETURN(hr);
  87. }
  88. *pdwCookie = (DWORD)pDisp;
  89. return S_OK;
  90. }
  91. //+---------------------------------------------------------------------------
  92. //
  93. // Member: CMachConnectPoint::Unadvise, public
  94. //
  95. // Synopsis: Forgets a pointer we remembered during Advise.
  96. //
  97. // Arguments: [dwCookie] -- Cookie returned from Advise
  98. //
  99. // Returns: HRESULT
  100. //
  101. //----------------------------------------------------------------------------
  102. HRESULT
  103. CMachConnectPoint::Unadvise(DWORD dwCookie)
  104. {
  105. int i;
  106. TraceTag((tagMachine, "Unadvising machine sink: %p", dwCookie));
  107. CMachine::LOCK_MACH_LOCALS(_pMachine);
  108. i = _pMachine->_aryDispSink.Find((IDispatch*)dwCookie);
  109. if (i != -1)
  110. {
  111. _pMachine->_aryDispSink.ReleaseAndDelete(i);
  112. }
  113. else
  114. return E_INVALIDARG;
  115. return S_OK;
  116. }
  117. HRESULT
  118. CMachConnectPoint::EnumConnections(LPENUMCONNECTIONS * ppEnum)
  119. {
  120. *ppEnum = NULL;
  121. RRETURN(E_NOTIMPL);
  122. }
  123. // ***********************************************************************
  124. //
  125. // CMachine
  126. //
  127. // ***********************************************************************
  128. CMachine::CMachine(CMTScript *pMT, ITypeInfo *pTIMachine)
  129. {
  130. _ulRefs = 1;
  131. _pMT = pMT;
  132. TraceTag((tagMachine, "%p: CMachine object being constructed", this));
  133. Assert(pTIMachine);
  134. _pTypeInfoIMachine = pTIMachine;
  135. _pTypeInfoIMachine->AddRef();
  136. InitializeCriticalSection(&_cs);
  137. }
  138. CMachine::~CMachine()
  139. {
  140. ReleaseInterface(_pTypeInfoIMachine);
  141. DeleteCriticalSection(&_cs);
  142. }
  143. HRESULT
  144. CMachine::QueryInterface(REFIID iid, void **ppvObj)
  145. {
  146. if (iid == IID_IConnectedMachine || iid == IID_IUnknown || iid == IID_IDispatch)
  147. {
  148. *ppvObj = (IConnectedMachine *)this;
  149. }
  150. else if (iid == IID_IConnectionPointContainer)
  151. {
  152. *ppvObj = (IConnectionPointContainer *)this;
  153. }
  154. else
  155. {
  156. *ppvObj = NULL;
  157. return E_NOINTERFACE;
  158. }
  159. ((IUnknown *)*ppvObj)->AddRef();
  160. return S_OK;
  161. }
  162. //+---------------------------------------------------------------------------
  163. //
  164. // Member: CMachine::Init, public
  165. //
  166. // Synopsis: Used to do initialization that may fail
  167. //
  168. //----------------------------------------------------------------------------
  169. BOOL
  170. CMachine::Init()
  171. {
  172. return CThreadComm::Init();
  173. }
  174. //+---------------------------------------------------------------------------
  175. //
  176. // Member: CMachine::ThreadMain, public
  177. //
  178. // Synopsis: Main loop for this thread. Handles messages coming from other
  179. // threads.
  180. //
  181. //----------------------------------------------------------------------------
  182. DWORD
  183. CMachine::ThreadMain()
  184. {
  185. DWORD dwRet;
  186. BOOL fExit = FALSE;
  187. SetName("CMachine");
  188. ThreadStarted(S_OK);
  189. TraceTag((tagMachine, "CMachine thread started"));
  190. while (!fExit)
  191. {
  192. dwRet = WaitForSingleObject(_hCommEvent, INFINITE);
  193. if (dwRet == WAIT_OBJECT_0)
  194. {
  195. fExit = HandleThreadMessage();
  196. }
  197. else
  198. {
  199. AssertSz(FALSE, "FATAL: WaitForSingleObject failed!");
  200. fExit = TRUE;
  201. }
  202. }
  203. TraceTag((tagMachine, "CMachine thread exiting"));
  204. return 0;
  205. }
  206. //+---------------------------------------------------------------------------
  207. //
  208. // Member: CMachine::HandleThreadMessage, public
  209. //
  210. // Synopsis: Handles messages from other threads.
  211. //
  212. //----------------------------------------------------------------------------
  213. BOOL
  214. CMachine::HandleThreadMessage()
  215. {
  216. VERIFY_THREAD();
  217. THREADMSG tm;
  218. BYTE bData[MSGDATABUFSIZE];
  219. DWORD cbData;
  220. BOOL fRet = FALSE;
  221. while (GetNextMsg(&tm, (void *)bData, &cbData))
  222. {
  223. switch (tm)
  224. {
  225. case MD_NOTIFYSCRIPT:
  226. {
  227. VARIANT *pvar = *(VARIANT**)bData;
  228. Assert(V_VT(&pvar[0]) == VT_BSTR);
  229. FireScriptNotify(V_BSTR(&pvar[0]), pvar[1]);
  230. VariantClear(&pvar[0]);
  231. VariantClear(&pvar[1]);
  232. delete [] pvar;
  233. }
  234. break;
  235. case MD_PLEASEEXIT:
  236. fRet = TRUE;
  237. break;
  238. default:
  239. AssertSz(FALSE, "CMachine got a message it couldn't handle!");
  240. break;
  241. }
  242. }
  243. return fRet;
  244. }
  245. //---------------------------------------------------------------------------
  246. //
  247. // Member: CMachine::EnumConnectionPoints, IConnectionPointContainer
  248. //
  249. //---------------------------------------------------------------------------
  250. HRESULT
  251. CMachine::EnumConnectionPoints(LPENUMCONNECTIONPOINTS *)
  252. {
  253. return E_NOTIMPL;
  254. }
  255. //---------------------------------------------------------------------------
  256. //
  257. // Member: CMachine::FindConnectionPoint, IConnectionPointContainer
  258. //
  259. //---------------------------------------------------------------------------
  260. HRESULT
  261. CMachine::FindConnectionPoint(REFIID iid, LPCONNECTIONPOINT* ppCpOut)
  262. {
  263. HRESULT hr;
  264. if (iid == DIID_DRemoteMTScriptEvents || iid == IID_IDispatch)
  265. {
  266. *ppCpOut = new CMachConnectPoint(this);
  267. hr = *ppCpOut ? S_OK : E_OUTOFMEMORY;
  268. }
  269. else
  270. {
  271. hr = E_NOINTERFACE;
  272. }
  273. RRETURN(hr);
  274. }
  275. //---------------------------------------------------------------------------
  276. //
  277. // Member: CMachine::GetTypeInfo, IDispatch
  278. //
  279. //---------------------------------------------------------------------------
  280. HRESULT
  281. CMachine::GetTypeInfo(UINT itinfo, ULONG lcid, ITypeInfo ** pptinfo)
  282. {
  283. *pptinfo = _pTypeInfoIMachine;
  284. (*pptinfo)->AddRef();
  285. return S_OK;
  286. }
  287. //---------------------------------------------------------------------------
  288. //
  289. // Member: CMachine::GetTypeInfoCount, IDispatch
  290. //
  291. //---------------------------------------------------------------------------
  292. HRESULT
  293. CMachine::GetTypeInfoCount(UINT * pctinfo)
  294. {
  295. *pctinfo = 1;
  296. return S_OK;
  297. }
  298. //---------------------------------------------------------------------------
  299. //
  300. // Member: CMachine::GetIDsOfNames, IDispatch
  301. //
  302. //---------------------------------------------------------------------------
  303. HRESULT
  304. CMachine::GetIDsOfNames(REFIID riid, LPOLESTR * rgszNames, UINT cNames, LCID lcid, DISPID * rgdispid)
  305. {
  306. return _pTypeInfoIMachine->GetIDsOfNames(rgszNames, cNames, rgdispid);
  307. }
  308. //---------------------------------------------------------------------------
  309. //
  310. // Member: CMachine::Invoke, IDispatch
  311. //
  312. //---------------------------------------------------------------------------
  313. HRESULT
  314. CMachine::Invoke(DISPID dispidMember,
  315. REFIID riid,
  316. LCID lcid,
  317. WORD wFlags,
  318. DISPPARAMS * pdispparams,
  319. VARIANT * pvarResult,
  320. EXCEPINFO * pexcepinfo,
  321. UINT * puArgErr)
  322. {
  323. return _pTypeInfoIMachine->Invoke((IConnectedMachine *)this, dispidMember, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr);
  324. }
  325. // *************************************************************************
  326. //+---------------------------------------------------------------------------
  327. //
  328. // Member: CMachine::FireScriptNotify, public
  329. //
  330. // Synopsis: Fires the script notify event on all objects connected to
  331. // our IConnectedMachine object that have requested to receive
  332. // events (through IConnectionPoint::Advise).
  333. //
  334. // Arguments: [bstrIdent] -- Parameter of event
  335. // [vInfo] -- Parameter of event
  336. //
  337. // Returns: HRESULT
  338. //
  339. // Notes: This method is thread-safe and can be called from any thread.
  340. //
  341. //----------------------------------------------------------------------------
  342. HRESULT
  343. CMachine::FireScriptNotify(BSTR bstrIdent, VARIANT vInfo)
  344. {
  345. HRESULT hr;
  346. IDispatch **ppDisp;
  347. int i;
  348. DISPPARAMS dp;
  349. EXCEPINFO ei;
  350. UINT uArgErr = 0;
  351. VARIANT varg[2];
  352. CStackPtrAry<IDispatch*, 5> arySinks;
  353. // Since it may take some time to fire the events, and we don't want
  354. // to keep the array locked that whole time, we make a copy of the array.
  355. // This will also allow a sink to unadvise itself while handling the event
  356. // without deadlocking.
  357. {
  358. LOCK_MACH_LOCALS(this);
  359. // Check for no sinks. No use going to all this work if there's no one
  360. // listening.
  361. if (_aryDispSink.Size() == 0)
  362. return S_OK;
  363. hr = arySinks.Copy(_aryDispSink, TRUE);
  364. if (hr)
  365. RRETURN(hr);
  366. }
  367. // Set up the event parameters
  368. VariantInit(&varg[0]);
  369. VariantInit(&varg[1]);
  370. // Params are in order from last to first
  371. hr = VariantCopy(&varg[0], &vInfo);
  372. if (hr)
  373. return hr;
  374. V_VT(&varg[1]) = VT_BSTR;
  375. V_BSTR(&varg[1]) = bstrIdent;
  376. dp.rgvarg = varg;
  377. dp.cArgs = 2;
  378. dp.rgdispidNamedArgs = NULL;
  379. dp.cNamedArgs = 0;
  380. // We don't use the same critical section here so _aryDispSink can be
  381. // manipulated while we're firing events. However, we still don't want
  382. // more than one thread firing events at the same time.
  383. TraceTag((tagMachine, "About to fire OnScriptNotify(%ls) on %d sinks...", bstrIdent, arySinks.Size()));
  384. for (i = arySinks.Size(), ppDisp = arySinks;
  385. i > 0;
  386. i--, ppDisp++)
  387. {
  388. hr = (*ppDisp)->Invoke(
  389. DISPID_RemoteMTScript_OnScriptNotify,
  390. IID_NULL,
  391. 0,
  392. DISPATCH_METHOD,
  393. &dp,
  394. NULL,
  395. &ei,
  396. &uArgErr);
  397. if (hr)
  398. {
  399. // If the call failed, unadvise so we don't keep trying.
  400. TraceTag((tagError, "OnScriptNotify event call returned %x! Unadvising...", hr));
  401. // If the connection went down temporarily, don't unadvise.
  402. if (hr != HRESULT_FROM_WIN32(RPC_X_BAD_STUB_DATA) &&
  403. hr != HRESULT_FROM_WIN32(RPC_S_COMM_FAILURE))
  404. {
  405. LOCK_MACH_LOCALS(this);
  406. int index = _aryDispSink.Find(*ppDisp);
  407. Assert(index != -1);
  408. _aryDispSink.ReleaseAndDelete(index);
  409. }
  410. }
  411. }
  412. TraceTag((tagMachine, "Done firing OnScriptNotify(%ls).", bstrIdent));
  413. return S_OK;
  414. }
  415. // *************************************************************************
  416. STDMETHODIMP
  417. CMachine::Exec(BSTR bstrCmd, BSTR bstrParams, VARIANT *pvData)
  418. {
  419. // We create an event object for each call on this method. While this
  420. // may have a cost, it makes this method thread-safe. If we cached an
  421. // event object then we would have to synchronize access to that event
  422. // object which could be even more expensive.
  423. MACHPROC_EVENT_DATA med;
  424. MACHPROC_EVENT_DATA * pmed;
  425. HRESULT hr = S_OK;
  426. if (!pvData)
  427. {
  428. return E_INVALIDARG;
  429. }
  430. TraceTag((tagMachine, "Exec call received: (%ls, %ls)", bstrCmd, bstrParams));
  431. VariantInit(pvData);
  432. med.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  433. if (med.hEvent == NULL)
  434. {
  435. return HRESULT_FROM_WIN32(GetLastError());
  436. }
  437. med.bstrCmd = bstrCmd;
  438. med.bstrParams = bstrParams;
  439. med.dwProcId = 0;
  440. med.pvReturn = pvData;
  441. med.dwGITCookie = 0;
  442. med.hrReturn = S_OK;
  443. pmed = &med;
  444. HANDLE ahEvents[2];
  445. ahEvents[0] = med.hEvent;
  446. CScriptHost *pScript = _pMT->GetPrimaryScript();
  447. if (!pScript)
  448. {
  449. med.hrReturn = E_FAIL;
  450. goto Cleanup;
  451. }
  452. ahEvents[1] = pScript->hThread();
  453. _pMT->PostToThread(pScript,
  454. MD_MACHEVENTCALL,
  455. (LPVOID)&pmed,
  456. sizeof(MACHPROC_EVENT_DATA*));
  457. // We can do WaitForSingleObject because we are in OLE's multi-threaded
  458. // apartment and don't need to handle messages from our event loop.
  459. DWORD dwWait;
  460. dwWait = WaitForMultipleObjects(2, ahEvents, FALSE, INFINITE);
  461. if (dwWait != WAIT_OBJECT_0) // Thread exit
  462. {
  463. med.hrReturn = E_FAIL;
  464. goto Cleanup;
  465. }
  466. if (med.hrReturn != S_OK)
  467. {
  468. hr = med.hrReturn;
  469. goto Cleanup;
  470. }
  471. // See if the return value was an IDispatch ptr. If so, grab the pointer
  472. // out of the GlobalInterfaceTable.
  473. if (V_VT(pvData) == VT_DISPATCH)
  474. {
  475. IDispatch *pDisp;
  476. AssertSz(med.dwGITCookie != 0, "FATAL: Itf pointer improperly marshalled");
  477. hr = _pMT->_pGIT->GetInterfaceFromGlobal(med.dwGITCookie,
  478. IID_IDispatch,
  479. (LPVOID*)&pDisp);
  480. if (!hr)
  481. {
  482. V_VT(pvData) = VT_DISPATCH;
  483. V_DISPATCH(pvData) = pDisp;
  484. }
  485. _pMT->_pGIT->RevokeInterfaceFromGlobal(med.dwGITCookie);
  486. }
  487. Cleanup:
  488. CloseHandle(med.hEvent);
  489. TraceTag((tagMachine, "Exec call returning %x", hr));
  490. return hr;
  491. }
  492. STDMETHODIMP
  493. CMachine::get_PublicData(VARIANT *pvData)
  494. {
  495. HRESULT hr = S_OK;
  496. VariantInit(pvData);
  497. TraceTag((tagMachine, "Remote machine asking for PublicData"));
  498. LOCK_LOCALS(_pMT);
  499. if (V_VT(&_pMT->_vPublicData) == VT_DISPATCH)
  500. {
  501. IDispatch *pDisp;
  502. Assert(_pMT->_dwPublicDataCookie != 0);
  503. hr = _pMT->_pGIT->GetInterfaceFromGlobal(_pMT->_dwPublicDataCookie,
  504. IID_IDispatch,
  505. (LPVOID*)&pDisp);
  506. if (!hr)
  507. {
  508. V_VT(pvData) = VT_DISPATCH;
  509. V_DISPATCH(pvData) = pDisp;
  510. }
  511. }
  512. else
  513. {
  514. hr = VariantCopy(pvData, &_pMT->_vPublicData);
  515. }
  516. return hr;
  517. }
  518. STDMETHODIMP
  519. CMachine::get_Name(BSTR *pbstrName)
  520. {
  521. TCHAR achBuf[MAX_COMPUTERNAME_LENGTH + 1];
  522. DWORD dwLength = ARRAY_SIZE(achBuf);
  523. GetComputerName(achBuf, &dwLength);
  524. *pbstrName = SysAllocString(achBuf);
  525. return (pbstrName) ? S_OK : E_OUTOFMEMORY;
  526. }
  527. STDMETHODIMP
  528. CMachine::get_Platform(BSTR *pbstrPlatform)
  529. {
  530. SYSTEM_INFO si;
  531. GetSystemInfo(&si);
  532. TCHAR *pchPlatform;
  533. if (!pbstrPlatform)
  534. return E_POINTER;
  535. switch (si.wProcessorArchitecture)
  536. {
  537. case PROCESSOR_ARCHITECTURE_INTEL:
  538. pchPlatform = L"x86";
  539. break;
  540. case PROCESSOR_ARCHITECTURE_AMD64:
  541. pchPlatform = L"amd64";
  542. break;
  543. case PROCESSOR_ARCHITECTURE_IA64:
  544. pchPlatform = L"ia64";
  545. break;
  546. case PROCESSOR_ARCHITECTURE_UNKNOWN:
  547. default:
  548. pchPlatform = L"unknown";
  549. break;
  550. }
  551. *pbstrPlatform = SysAllocString(pchPlatform);
  552. if (!*pbstrPlatform)
  553. return E_OUTOFMEMORY;
  554. return S_OK;
  555. }
  556. STDMETHODIMP
  557. CMachine::get_OS(BSTR *pbstrOS)
  558. {
  559. OSVERSIONINFO os;
  560. WCHAR * pchOS = L"Unknown";
  561. os.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  562. if (!pbstrOS)
  563. return E_POINTER;
  564. GetVersionEx(&os);
  565. switch (os.dwPlatformId)
  566. {
  567. case VER_PLATFORM_WIN32_WINDOWS:
  568. if ( os.dwMajorVersion > 4
  569. || ( os.dwMajorVersion == 4
  570. && os.dwMinorVersion > 0))
  571. {
  572. pchOS = L"Win98";
  573. }
  574. else
  575. {
  576. pchOS = L"Win95";
  577. }
  578. break;
  579. case VER_PLATFORM_WIN32_NT:
  580. if (os.dwMajorVersion == 4)
  581. {
  582. pchOS = L"NT4";
  583. }
  584. else
  585. {
  586. pchOS = L"Win2000";
  587. }
  588. break;
  589. }
  590. *pbstrOS = SysAllocString(pchOS);
  591. if (!*pbstrOS)
  592. return E_OUTOFMEMORY;
  593. return S_OK;
  594. }
  595. STDMETHODIMP
  596. CMachine::get_MajorVer(long *plMajorVer)
  597. {
  598. if (!plMajorVer)
  599. return E_POINTER;
  600. OSVERSIONINFO os;
  601. os.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  602. GetVersionEx(&os);
  603. *plMajorVer = os.dwMajorVersion;
  604. return S_OK;
  605. }
  606. STDMETHODIMP
  607. CMachine::get_MinorVer(long *plMinorVer)
  608. {
  609. if (!plMinorVer)
  610. return E_POINTER;
  611. OSVERSIONINFO os;
  612. os.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  613. GetVersionEx(&os);
  614. *plMinorVer = os.dwMinorVersion;
  615. return S_OK;
  616. }
  617. STDMETHODIMP
  618. CMachine::get_BuildNum(long *plBuildNum)
  619. {
  620. if (!plBuildNum)
  621. return E_POINTER;
  622. OSVERSIONINFO os;
  623. os.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  624. GetVersionEx(&os);
  625. *plBuildNum = os.dwBuildNumber;
  626. return S_OK;
  627. }
  628. STDMETHODIMP
  629. CMachine::get_PlatformIsNT(VARIANT_BOOL *pfIsNT)
  630. {
  631. if (!pfIsNT)
  632. return E_POINTER;
  633. OSVERSIONINFO os;
  634. os.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  635. GetVersionEx(&os);
  636. *pfIsNT = (os.dwPlatformId == VER_PLATFORM_WIN32_NT)
  637. ? VB_TRUE
  638. : VB_FALSE;
  639. return S_OK;
  640. }
  641. STDMETHODIMP
  642. CMachine::get_ServicePack(BSTR *pbstrServicePack)
  643. {
  644. if (!pbstrServicePack)
  645. return E_POINTER;
  646. OSVERSIONINFO os;
  647. os.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  648. GetVersionEx(&os);
  649. *pbstrServicePack = SysAllocString(os.szCSDVersion);
  650. return (pbstrServicePack) ? S_OK : E_OUTOFMEMORY;
  651. }
  652. STDMETHODIMP
  653. CMachine::get_HostMajorVer(long *plMajorVer)
  654. {
  655. if (!plMajorVer)
  656. return E_POINTER;
  657. *plMajorVer = IConnectedMachine_lVersionMajor;
  658. return S_OK;
  659. }
  660. STDMETHODIMP
  661. CMachine::get_HostMinorVer(long *plMinorVer)
  662. {
  663. if (!plMinorVer)
  664. return E_POINTER;
  665. *plMinorVer = IConnectedMachine_lVersionMinor;
  666. return S_OK;
  667. }
  668. STDMETHODIMP
  669. CMachine::get_StatusValue(long nIndex, long *pnStatus)
  670. {
  671. if (!pnStatus)
  672. return E_POINTER;
  673. return _pMT->get_StatusValue(nIndex, pnStatus);
  674. }