VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/VirtualBoxClientImpl.cpp@ 69496

Last change on this file since 69496 was 69496, checked in by vboxsync, 7 years ago

*: scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.5 KB
Line 
1/* $Id: VirtualBoxClientImpl.cpp 69496 2017-10-28 14:55:58Z vboxsync $ */
2/** @file
3 * VirtualBox COM class implementation
4 */
5
6/*
7 * Copyright (C) 2010-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#define LOG_GROUP LOG_GROUP_MAIN_VIRTUALBOXCLIENT
19#include "LoggingNew.h"
20
21#include "VirtualBoxClientImpl.h"
22
23#include "AutoCaller.h"
24#include "VBoxEvents.h"
25#include "VBox/com/ErrorInfo.h"
26
27#include <iprt/asm.h>
28#include <iprt/thread.h>
29#include <iprt/critsect.h>
30#include <iprt/semaphore.h>
31#include <iprt/cpp/utils.h>
32#include <iprt/utf16.h>
33#ifdef RT_OS_WINDOWS
34# include <iprt/ldr.h>
35# include <msi.h>
36# include <WbemIdl.h>
37#endif
38
39
40/** Waiting time between probing whether VBoxSVC is alive. */
41#define VBOXCLIENT_DEFAULT_INTERVAL 30000
42
43
44/** Initialize instance counter class variable */
45uint32_t VirtualBoxClient::g_cInstances = 0;
46
47LONG VirtualBoxClient::s_cUnnecessaryAtlModuleLocks = 0;
48
49// constructor / destructor
50/////////////////////////////////////////////////////////////////////////////
51
52HRESULT VirtualBoxClient::FinalConstruct()
53{
54 HRESULT rc = init();
55 BaseFinalConstruct();
56 return rc;
57}
58
59void VirtualBoxClient::FinalRelease()
60{
61 uninit();
62 BaseFinalRelease();
63}
64
65
66// public initializer/uninitializer for internal purposes only
67/////////////////////////////////////////////////////////////////////////////
68
69#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_SDS)
70
71HRESULT CreateVirtualBoxThroughSDS(ComPtr<IVirtualBox> &aVirtualBox, ComPtr<IToken> &aToken)
72{
73 ComPtr<IVirtualBoxSDS> aVirtualBoxSDS;
74 HRESULT hrc = CoCreateInstance(CLSID_VirtualBoxSDS, /* the VirtualBoxSDS object */
75 NULL, /* no aggregation */
76 CLSCTX_LOCAL_SERVER, /* the object lives in the current process */
77 IID_IVirtualBoxSDS, /* IID of the interface */
78 (void **)aVirtualBoxSDS.asOutParam());
79 AssertComRCReturn(hrc, hrc);
80
81 hrc = aVirtualBoxSDS->GetVirtualBox(aVirtualBox.asOutParam(), aToken.asOutParam());
82 AssertComRC(hrc);
83
84 return hrc;
85}
86
87
88int VirtualBoxClient::getServiceAccount(const wchar_t *pwszServiceName, wchar_t *pwszAccountName, size_t cwcAccountName)
89{
90 AssertPtr(pwszServiceName);
91 AssertPtr(pwszAccountName);
92 Assert(cwcAccountName);
93 *pwszAccountName = '\0';
94
95 int vrc;
96
97 // Get a handle to the SCM database.
98 SC_HANDLE hSCManager = OpenSCManagerW(NULL /*pwszMachineName*/, NULL /*pwszDatabaseName*/, SC_MANAGER_ALL_ACCESS);
99 if (hSCManager != NULL)
100 {
101 SC_HANDLE hService = OpenServiceW(hSCManager, pwszServiceName, SERVICE_QUERY_CONFIG);
102 if (hService != NULL)
103 {
104 DWORD cbNeeded = sizeof(QUERY_SERVICE_CONFIGW) + 256;
105 if (!QueryServiceConfigW(hService, NULL, 0, &cbNeeded))
106 {
107 Assert(GetLastError() == ERROR_INSUFFICIENT_BUFFER);
108 LPQUERY_SERVICE_CONFIGW pSc = (LPQUERY_SERVICE_CONFIGW)RTMemTmpAllocZ(cbNeeded);
109 if (pSc)
110 {
111 DWORD cbNeeded2 = 0;
112 if (QueryServiceConfigW(hService, pSc, cbNeeded, &cbNeeded2))
113 {
114 vrc = RTUtf16Copy(pwszAccountName, cwcAccountName, pSc->lpServiceStartName);
115 if (RT_FAILURE(vrc))
116 LogRel(("Error: SDS service name is too long (%Rrc): %ls\n", vrc, pSc->lpServiceStartName));
117 }
118 else
119 {
120 int dwError = GetLastError();
121 vrc = RTErrConvertFromWin32(dwError);
122 LogRel(("Error: Failed querying service config: %Rwc (%u) -> %Rrc; cbNeeded=%d cbNeeded2=%d\n",
123 dwError, dwError, vrc, cbNeeded, cbNeeded2));
124 }
125 RTMemTmpFree(pSc);
126 }
127 else
128 {
129 LogRel(("Error: Failed allocating %#x bytes of memory for service config!\n", cbNeeded));
130 vrc = VERR_NO_TMP_MEMORY;
131 }
132 }
133 else
134 {
135 AssertLogRelMsgFailed(("Error: QueryServiceConfigW returns success with zero buffer!\n"));
136 vrc = VERR_IPE_UNEXPECTED_STATUS;
137 }
138 CloseServiceHandle(hService);
139 }
140 else
141 {
142 int dwError = GetLastError();
143 vrc = RTErrConvertFromWin32(dwError);
144 LogRel(("Error: Could not open service: %Rwc (%u) -> %Rrc\n", dwError, dwError, vrc));
145 }
146 CloseServiceHandle(hSCManager);
147 }
148 else
149 {
150 int dwError = GetLastError();
151 vrc = RTErrConvertFromWin32(dwError);
152 LogRel(("Error: Could not open SCM: %Rwc (%u) -> %Rrc\n", dwError, dwError, vrc));
153 }
154 return vrc;
155}
156
157
158HRESULT VirtualBoxClient::isServiceDisabled(const wchar_t *pwszServiceName, bool* pOutIsDisabled)
159{
160 Assert(pOutIsDisabled);
161 Assert(pwszServiceName);
162 ComPtr<IWbemLocator> aLocator;
163 ComPtr<IWbemServices> aService;
164
165 HRESULT hr = CoCreateInstance(CLSID_WbemLocator, 0,
166 CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *)aLocator.asOutParam());
167 if (FAILED(hr))
168 {
169 LogRel(("Error: Cannot instantiate WbemLocator: %Rhrc", hr));
170 return hr;
171 }
172
173 hr = aLocator->ConnectServer(
174 com::Bstr(L"ROOT\\CIMV2").raw(), // Object path of WMI namespace
175 NULL, // User name. NULL = current user
176 NULL, // User password. NULL = current
177 0, // Locale. NULL indicates current
178 NULL, // Security flags.
179 0, // Authority (for example, Kerberos)
180 0, // Context object
181 aService.asOutParam() // pointer to IWbemServices proxy
182 );
183 if (FAILED(hr))
184 {
185 LogRel(("Error: Cannot connect to Wbem Service: %Rhrc\n", hr));
186 return hr;
187 }
188
189 // query settings for VBoxSDS windows service
190 ComPtr<IEnumWbemClassObject> aEnumerator;
191 hr = aService->ExecQuery(
192 com::Bstr("WQL").raw(),
193 com::BstrFmt("SELECT * FROM Win32_Service WHERE Name='%ls'", pwszServiceName).raw(),
194 WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
195 NULL,
196 aEnumerator.asOutParam());
197 if (FAILED(hr) || aEnumerator == NULL)
198 {
199 LogRel(("Error: querying service settings from WMI: %Rhrc\n", hr));
200 return hr;
201 }
202
203 ULONG uReturn = 0;
204 ComPtr<IWbemClassObject> aVBoxSDSObj;
205 hr = aEnumerator->Next(WBEM_INFINITE, 1, aVBoxSDSObj.asOutParam(), &uReturn);
206 if (FAILED(hr))
207 {
208 LogRel(("Error: Cannot get Service WMI record: %Rhrc\n", hr));
209 return hr;
210 }
211 if (aVBoxSDSObj == NULL || uReturn == 0)
212 {
213 LogRel(("Error: Service record didn't exist in WMI: %Rhrc\n", hr));
214 return hr;
215 }
216
217 VARIANT vtProp;
218 VariantInit(&vtProp);
219
220 // Get "StartMode" property
221 hr = aVBoxSDSObj->Get(L"StartMode", 0, &vtProp, 0, 0);
222 if (FAILED(hr) || (vtProp.vt & VT_NULL) == VT_NULL)
223 {
224 LogRel(("Error: Didn't found StartMode property: %Rhrc\n", hr));
225 return hr;
226 }
227
228 Assert((vtProp.vt & VT_BSTR) == VT_BSTR);
229
230 *pOutIsDisabled = RTUtf16Cmp((RTUTF16*)vtProp.bstrVal,
231 (RTUTF16*)L"Disabled") == 0;
232
233 LogRel(("Service start mode is '%ls' \n", vtProp.bstrVal));
234
235 VariantClear(&vtProp);
236
237 return S_OK;
238}
239
240#endif
241
242/**
243 * Initializes the VirtualBoxClient object.
244 *
245 * @returns COM result indicator
246 */
247HRESULT VirtualBoxClient::init()
248{
249
250#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_SDS)
251 // setup COM Security to enable impersonation
252 // This works for console Virtual Box clients, GUI has own security settings
253 // For GUI Virtual Box it will be second call so can return TOO_LATE error
254 HRESULT hrGUICoInitializeSecurity = CoInitializeSecurity(NULL,
255 -1,
256 NULL,
257 NULL,
258 RPC_C_AUTHN_LEVEL_DEFAULT,
259 RPC_C_IMP_LEVEL_IMPERSONATE,
260 NULL,
261 EOAC_NONE,
262 NULL);
263 NOREF(hrGUICoInitializeSecurity);
264 Assert(SUCCEEDED(hrGUICoInitializeSecurity) || hrGUICoInitializeSecurity == RPC_E_TOO_LATE);
265#endif
266
267 LogFlowThisFuncEnter();
268
269 /* Enclose the state transition NotReady->InInit->Ready */
270 AutoInitSpan autoInitSpan(this);
271 AssertReturn(autoInitSpan.isOk(), E_FAIL);
272
273 /* Important: DO NOT USE any kind of "early return" (except the single
274 * one above, checking the init span success) in this method. It is vital
275 * for correct error handling that it has only one point of return, which
276 * does all the magic on COM to signal object creation success and
277 * reporting the error later for every API method. COM translates any
278 * unsuccessful object creation to REGDB_E_CLASSNOTREG errors or similar
279 * unhelpful ones which cause us a lot of grief with troubleshooting. */
280
281 HRESULT rc = S_OK;
282 try
283 {
284 if (ASMAtomicIncU32(&g_cInstances) != 1)
285 AssertFailedStmt(throw setError(E_FAIL,
286 tr("Attempted to create more than one VirtualBoxClient instance")));
287
288 mData.m_ThreadWatcher = NIL_RTTHREAD;
289 mData.m_SemEvWatcher = NIL_RTSEMEVENT;
290
291#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_SDS)
292 rc = CreateVirtualBoxThroughSDS(mData.m_pVirtualBox, mData.m_pToken);
293#else
294 rc = mData.m_pVirtualBox.createLocalObject(CLSID_VirtualBox);
295#endif
296 if (FAILED(rc))
297#ifdef RT_OS_WINDOWS
298 throw i_investigateVirtualBoxObjectCreationFailure(rc);
299#else
300 throw rc;
301#endif
302
303 /* VirtualBox error return is postponed to method calls, fetch it. */
304 ULONG rev;
305 rc = mData.m_pVirtualBox->COMGETTER(Revision)(&rev);
306 if (FAILED(rc))
307 throw rc;
308
309 rc = unconst(mData.m_pEventSource).createObject();
310 AssertComRCThrow(rc, setError(rc,
311 tr("Could not create EventSource for VirtualBoxClient")));
312 rc = mData.m_pEventSource->init();
313 AssertComRCThrow(rc, setError(rc,
314 tr("Could not initialize EventSource for VirtualBoxClient")));
315
316 /* HACK ALERT! This is for DllCanUnloadNow(). */
317 s_cUnnecessaryAtlModuleLocks++;
318 AssertMsg(s_cUnnecessaryAtlModuleLocks == 1, ("%d\n", s_cUnnecessaryAtlModuleLocks));
319
320 /* Setting up the VBoxSVC watcher thread. If anything goes wrong here it
321 * is not considered important enough to cause any sort of visible
322 * failure. The monitoring will not be done, but that's all. */
323 int vrc = RTSemEventCreate(&mData.m_SemEvWatcher);
324 if (RT_FAILURE(vrc))
325 {
326 mData.m_SemEvWatcher = NIL_RTSEMEVENT;
327 AssertRCStmt(vrc, throw setError(VBOX_E_IPRT_ERROR,
328 tr("Failed to create semaphore (rc=%Rrc)"),
329 vrc));
330 }
331
332 vrc = RTThreadCreate(&mData.m_ThreadWatcher, SVCWatcherThread, this, 0,
333 RTTHREADTYPE_INFREQUENT_POLLER, RTTHREADFLAGS_WAITABLE, "VBoxSVCWatcher");
334 if (RT_FAILURE(vrc))
335 {
336 RTSemEventDestroy(mData.m_SemEvWatcher);
337 mData.m_SemEvWatcher = NIL_RTSEMEVENT;
338 AssertRCStmt(vrc, throw setError(VBOX_E_IPRT_ERROR,
339 tr("Failed to create watcher thread (rc=%Rrc)"),
340 vrc));
341 }
342 }
343 catch (HRESULT err)
344 {
345 /* we assume that error info is set by the thrower */
346 rc = err;
347 }
348 catch (...)
349 {
350 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
351 }
352
353 /* Confirm a successful initialization when it's the case. Must be last,
354 * as on failure it will uninitialize the object. */
355 if (SUCCEEDED(rc))
356 autoInitSpan.setSucceeded();
357 else
358 autoInitSpan.setFailed(rc);
359
360 LogFlowThisFunc(("rc=%Rhrc\n", rc));
361 LogFlowThisFuncLeave();
362 /* Unconditionally return success, because the error return is delayed to
363 * the attribute/method calls through the InitFailed object state. */
364 return S_OK;
365}
366
367#ifdef RT_OS_WINDOWS
368/**
369 * Looks into why we failed to create the VirtualBox object.
370 *
371 * @returns hrcCaller thru setError.
372 * @param hrcCaller The failure status code.
373 */
374HRESULT VirtualBoxClient::i_investigateVirtualBoxObjectCreationFailure(HRESULT hrcCaller)
375{
376 /*
377 * First step is to try get an IUnknown interface of the VirtualBox object.
378 *
379 * This will succeed even when oleaut32.msm (see @bugref{8016}, @ticketref{12087})
380 * is accidentally installed and messes up COM. It may also succeed when the COM
381 * registration is partially broken (though that's unlikely to happen these days).
382 */
383 IUnknown *pUnknown = NULL;
384
385# ifdef VBOX_WITH_SDS
386 // Check the VBOXSDS service running account name is SYSTEM
387 wchar_t wszBuffer[256];
388 int vrc = getServiceAccount(L"VBoxSDS", wszBuffer, RT_ELEMENTS(wszBuffer));
389 if (RT_FAILURE(vrc))
390 return setError(hrcCaller,
391 tr("Failed to instantiate CLSID_VirtualBox using VBoxSDS: The VBoxSDS is unavailable: %Rrc"), vrc);
392
393 LogRelFunc(("VBoxSDS service is running under the '%ls' account.\n", wszBuffer));
394
395 if (RTUtf16Cmp(wszBuffer, L"LocalSystem") != 0)
396 return setError(hrcCaller,
397 tr("VBoxSDS should be run under SYSTEM account, but it started under '%ls' account:\n"
398 "Change VBoxSDS Windows Service Logon parameters in Service Control Manager. \n%Rhrc"),
399 wszBuffer, hrcCaller);
400
401 bool fIsVBoxSDSDisabled = false;
402 HRESULT hrc = isServiceDisabled(L"VBoxSDS", &fIsVBoxSDSDisabled);
403 if (FAILED(hrc))
404 {
405 LogRelFunc(("Warning: Failed to get information about VBoxSDS using WMI:: %Rhrc & %Rhrc", hrcCaller, hrc));
406 fIsVBoxSDSDisabled = false;
407 //return setError(hrcCaller, tr("Failed to get information about VBoxSDS using WMI:: %Rhrc & %Rhrc"), hrcCaller, hrc);
408 }
409 if (fIsVBoxSDSDisabled)
410 {
411 return setError(hrcCaller,
412 tr("Completely failed to instantiate CLSID_VirtualBox using VBoxSDS: "
413 "VBoxSDS windows service disabled.\n"
414 "Enable VBoxSDS Windows Service using Windows Service Management Console.\n %Rhrc"), hrcCaller);
415 }
416
417 // Check the VBoxSDS windows service is enabled
418 ComPtr<IVirtualBox> aVirtualBox;
419 ComPtr<IToken> aToken;
420 hrc = CreateVirtualBoxThroughSDS(aVirtualBox, aToken);
421 if (FAILED(hrc))
422 {
423 if (hrc == hrcCaller)
424 return setError(hrcCaller, tr("Completely failed to instantiate CLSID_VirtualBox using VBoxSDS: %Rhrc"), hrcCaller);
425 return setError(hrcCaller, tr("Completely failed to instantiate CLSID_VirtualBox using VBoxSDS: %Rhrc & %Rhrc"), hrcCaller, hrc);
426 }
427
428 hrc = aVirtualBox.queryInterfaceTo<IUnknown>(&pUnknown);
429 if (FAILED(hrc))
430 {
431 if (hrc == hrcCaller)
432 return setError(hrcCaller, tr("Completely failed to instantiate CLSID_VirtualBox using VBoxSDS: %Rhrc"), hrcCaller);
433 return setError(hrcCaller, tr("Completely failed to instantiate CLSID_VirtualBox using VBoxSDS: %Rhrc & %Rhrc"), hrcCaller, hrc);
434 }
435# else
436 HRESULT hrc = CoCreateInstance(CLSID_VirtualBox, NULL, CLSCTX_LOCAL_SERVER, IID_IUnknown, (void **)&pUnknown);
437 if (FAILED(hrc))
438 {
439 if (hrc == hrcCaller)
440 return setError(hrcCaller, tr("Completely failed to instantiate CLSID_VirtualBox: %Rhrc"), hrcCaller);
441 return setError(hrcCaller, tr("Completely failed to instantiate CLSID_VirtualBox: %Rhrc & %Rhrc"), hrcCaller, hrc);
442 }
443# endif
444
445 /*
446 * Try query the IVirtualBox interface (should fail), if it succeed we return
447 * straight away so we have more columns to spend on long messages below.
448 */
449 IVirtualBox *pVirtualBox;
450 hrc = pUnknown->QueryInterface(IID_IVirtualBox, (void **)&pVirtualBox);
451 if (SUCCEEDED(hrc))
452 {
453 pVirtualBox->Release();
454 pUnknown->Release();
455 return setError(hrcCaller,
456 tr("Failed to instantiate CLSID_VirtualBox the first time, but worked when checking out why ... weird"));
457 }
458
459 /*
460 * Check for oleaut32.msm traces in the registry.
461 */
462 HKEY hKey;
463 LSTATUS lrc = RegOpenKeyExW(HKEY_CLASSES_ROOT, L"CLSID\\{00020420-0000-0000-C000-000000000046}\\InprocServer32",
464 0 /*fFlags*/, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS | STANDARD_RIGHTS_READ, &hKey);
465 if (lrc == ERROR_SUCCESS)
466 {
467 wchar_t wszBuf[8192];
468 DWORD cbBuf = sizeof(wszBuf) - sizeof(wchar_t);
469 DWORD dwType = 0;
470 lrc = RegQueryValueExW(hKey, L"InprocServer32", NULL /*pvReserved*/, &dwType, (BYTE *)&wszBuf[0], &cbBuf);
471 if (lrc == ERROR_SUCCESS)
472 {
473 wszBuf[cbBuf / sizeof(wchar_t)] = '\0';
474 bool fSetError = false;
475
476 /*
477 * Try decode the string and improve the message.
478 */
479 typedef UINT (WINAPI *PFNMSIDECOMPOSEDESCRIPTORW)(PCWSTR pwszDescriptor,
480 LPWSTR pwszProductCode /*[40]*/,
481 LPWSTR pwszFeatureId /*[40]*/,
482 LPWSTR pwszComponentCode /*[40]*/,
483 DWORD *poffArguments);
484 PFNMSIDECOMPOSEDESCRIPTORW pfnMsiDecomposeDescriptorW;
485 pfnMsiDecomposeDescriptorW = (PFNMSIDECOMPOSEDESCRIPTORW)RTLdrGetSystemSymbol("msi.dll", "MsiDecomposeDescriptorW");
486 if ( pfnMsiDecomposeDescriptorW
487 && ( dwType == REG_SZ
488 || dwType == REG_MULTI_SZ))
489 {
490 wchar_t wszProductCode[RTUUID_STR_LENGTH + 2 + 16] = { 0 };
491 wchar_t wszFeatureId[RTUUID_STR_LENGTH + 2 + 16] = { 0 };
492 wchar_t wszComponentCode[RTUUID_STR_LENGTH + 2 + 16] = { 0 };
493 DWORD offArguments = ~(DWORD)0;
494 UINT uRc = pfnMsiDecomposeDescriptorW(wszBuf, wszProductCode, wszFeatureId, wszComponentCode, &offArguments);
495 if (uRc == 0)
496 {
497 /*
498 * Can we resolve the product code into a name?
499 */
500 typedef UINT (WINAPI *PFNMSIOPENPRODUCTW)(PCWSTR, MSIHANDLE *);
501 PFNMSIOPENPRODUCTW pfnMsiOpenProductW;
502 pfnMsiOpenProductW = (PFNMSIOPENPRODUCTW)RTLdrGetSystemSymbol("msi.dll", "MsiOpenProductW");
503
504 typedef UINT (WINAPI *PFNMSICLOSEHANDLE)(MSIHANDLE);
505 PFNMSICLOSEHANDLE pfnMsiCloseHandle;
506 pfnMsiCloseHandle = (PFNMSICLOSEHANDLE)RTLdrGetSystemSymbol("msi.dll", "MsiCloseHandle");
507
508 typedef UINT (WINAPI *PFNGETPRODUCTPROPERTYW)(MSIHANDLE, PCWSTR, PWSTR, PDWORD);
509 PFNGETPRODUCTPROPERTYW pfnMsiGetProductPropertyW;
510 pfnMsiGetProductPropertyW = (PFNGETPRODUCTPROPERTYW)RTLdrGetSystemSymbol("msi.dll", "MsiGetProductPropertyW");
511 if ( pfnMsiGetProductPropertyW
512 && pfnMsiCloseHandle
513 && pfnMsiOpenProductW)
514 {
515 MSIHANDLE hMsi = 0;
516 uRc = pfnMsiOpenProductW(wszProductCode, &hMsi);
517 if (uRc == 0)
518 {
519 static wchar_t const * const s_apwszProps[] =
520 {
521 INSTALLPROPERTY_INSTALLEDPRODUCTNAME,
522 INSTALLPROPERTY_PRODUCTNAME,
523 INSTALLPROPERTY_PACKAGENAME,
524 };
525
526 wchar_t wszProductName[1024];
527 DWORD cwcProductName;
528 unsigned i = 0;
529 do
530 {
531 cwcProductName = RT_ELEMENTS(wszProductName) - 1;
532 uRc = pfnMsiGetProductPropertyW(hMsi, s_apwszProps[i], wszProductName, &cwcProductName);
533 }
534 while ( ++i < RT_ELEMENTS(s_apwszProps)
535 && ( uRc != 0
536 || cwcProductName < 2
537 || cwcProductName >= RT_ELEMENTS(wszProductName)) );
538 uRc = pfnMsiCloseHandle(hMsi);
539 if (uRc == 0 && cwcProductName >= 2)
540 {
541 wszProductName[RT_MIN(cwcProductName, RT_ELEMENTS(wszProductName) - 1)] = '\0';
542 setError(hrcCaller,
543 tr("Failed to instantiate CLSID_VirtualBox w/ IVirtualBox, but CLSID_VirtualBox w/ IUnknown works.\n"
544 "PSDispatch looks broken by the '%ls' (%ls) program, suspecting that it features the broken oleaut32.msm module as component %ls.\n"
545 "\n"
546 "We suggest you try uninstall '%ls'.\n"
547 "\n"
548 "See also https://support.microsoft.com/en-us/kb/316911 "),
549 wszProductName, wszProductCode, wszComponentCode, wszProductName);
550 fSetError = true;
551 }
552 }
553 }
554
555 /* MSI uses COM and may mess up our stuff. So, we wait with the fallback till afterwards in this case. */
556 if (!fSetError)
557 {
558 setError(hrcCaller,
559 tr("Failed to instantiate CLSID_VirtualBox w/ IVirtualBox, CLSID_VirtualBox w/ IUnknown works.\n"
560 "PSDispatch looks broken by installer %ls featuring the broken oleaut32.msm module as component %ls.\n"
561 "\n"
562 "See also https://support.microsoft.com/en-us/kb/316911 "),
563 wszProductCode, wszComponentCode);
564 fSetError = true;
565 }
566 }
567 }
568 if (!fSetError)
569 setError(hrcCaller, tr("Failed to instantiate CLSID_VirtualBox w/ IVirtualBox, CLSID_VirtualBox w/ IUnknown works.\n"
570 "PSDispatch looks broken by some installer featuring the broken oleaut32.msm module as a component.\n"
571 "\n"
572 "See also https://support.microsoft.com/en-us/kb/316911 "));
573 }
574 else if (lrc == ERROR_FILE_NOT_FOUND)
575 setError(hrcCaller, tr("Failed to instantiate CLSID_VirtualBox w/ IVirtualBox, but CLSID_VirtualBox w/ IUnknown works.\n"
576 "PSDispatch looks fine. Weird"));
577 else
578 setError(hrcCaller, tr("Failed to instantiate CLSID_VirtualBox w/ IVirtualBox, but CLSID_VirtualBox w/ IUnknown works.\n"
579 "Checking out PSDispatch registration ended with error: %u (%#x)"), lrc, lrc);
580 RegCloseKey(hKey);
581 }
582
583 pUnknown->Release();
584 return hrcCaller;
585}
586#endif /* RT_OS_WINDOWS */
587
588/**
589 * Uninitializes the instance and sets the ready flag to FALSE.
590 * Called either from FinalRelease() or by the parent when it gets destroyed.
591 */
592void VirtualBoxClient::uninit()
593{
594 LogFlowThisFunc(("\n"));
595
596 /* Enclose the state transition Ready->InUninit->NotReady */
597 AutoUninitSpan autoUninitSpan(this);
598 if (autoUninitSpan.uninitDone())
599 return;
600
601 if (mData.m_ThreadWatcher != NIL_RTTHREAD)
602 {
603 /* Signal the event semaphore and wait for the thread to terminate.
604 * if it hangs for some reason exit anyway, this can cause a crash
605 * though as the object will no longer be available. */
606 RTSemEventSignal(mData.m_SemEvWatcher);
607 RTThreadWait(mData.m_ThreadWatcher, 30000, NULL);
608 mData.m_ThreadWatcher = NIL_RTTHREAD;
609 RTSemEventDestroy(mData.m_SemEvWatcher);
610 mData.m_SemEvWatcher = NIL_RTSEMEVENT;
611 }
612
613 mData.m_pToken.setNull();
614 mData.m_pVirtualBox.setNull();
615
616 ASMAtomicDecU32(&g_cInstances);
617}
618
619// IVirtualBoxClient properties
620/////////////////////////////////////////////////////////////////////////////
621
622/**
623 * Returns a reference to the VirtualBox object.
624 *
625 * @returns COM status code
626 * @param aVirtualBox Address of result variable.
627 */
628HRESULT VirtualBoxClient::getVirtualBox(ComPtr<IVirtualBox> &aVirtualBox)
629{
630 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
631 aVirtualBox = mData.m_pVirtualBox;
632 return S_OK;
633}
634
635/**
636 * Create a new Session object and return a reference to it.
637 *
638 * @returns COM status code
639 * @param aSession Address of result variable.
640 */
641HRESULT VirtualBoxClient::getSession(ComPtr<ISession> &aSession)
642{
643 /* this is not stored in this object, no need to lock */
644 ComPtr<ISession> pSession;
645 HRESULT rc = pSession.createInprocObject(CLSID_Session);
646 if (SUCCEEDED(rc))
647 aSession = pSession;
648 return rc;
649}
650
651/**
652 * Return reference to the EventSource associated with this object.
653 *
654 * @returns COM status code
655 * @param aEventSource Address of result variable.
656 */
657HRESULT VirtualBoxClient::getEventSource(ComPtr<IEventSource> &aEventSource)
658{
659 /* this is const, no need to lock */
660 aEventSource = mData.m_pEventSource;
661 return aEventSource.isNull() ? E_FAIL : S_OK;
662}
663
664// IVirtualBoxClient methods
665/////////////////////////////////////////////////////////////////////////////
666
667/**
668 * Checks a Machine object for any pending errors.
669 *
670 * @returns COM status code
671 * @param aMachine Machine object to check.
672 */
673HRESULT VirtualBoxClient::checkMachineError(const ComPtr<IMachine> &aMachine)
674{
675 BOOL fAccessible = FALSE;
676 HRESULT rc = aMachine->COMGETTER(Accessible)(&fAccessible);
677 if (FAILED(rc))
678 return setError(rc, tr("Could not check the accessibility status of the VM"));
679 else if (!fAccessible)
680 {
681 ComPtr<IVirtualBoxErrorInfo> pAccessError;
682 rc = aMachine->COMGETTER(AccessError)(pAccessError.asOutParam());
683 if (FAILED(rc))
684 return setError(rc, tr("Could not get the access error message of the VM"));
685 else
686 {
687 ErrorInfo info(pAccessError);
688 ErrorInfoKeeper eik(info);
689 return info.getResultCode();
690 }
691 }
692 return S_OK;
693}
694
695// private methods
696/////////////////////////////////////////////////////////////////////////////
697
698
699/// @todo AM Add pinging of VBoxSDS
700/*static*/
701DECLCALLBACK(int) VirtualBoxClient::SVCWatcherThread(RTTHREAD ThreadSelf,
702 void *pvUser)
703{
704 NOREF(ThreadSelf);
705 Assert(pvUser);
706 VirtualBoxClient *pThis = (VirtualBoxClient *)pvUser;
707 RTSEMEVENT sem = pThis->mData.m_SemEvWatcher;
708 RTMSINTERVAL cMillies = VBOXCLIENT_DEFAULT_INTERVAL;
709 int vrc;
710
711 /* The likelihood of early crashes are high, so start with a short wait. */
712 vrc = RTSemEventWait(sem, cMillies / 2);
713
714 /* As long as the waiting times out keep retrying the wait. */
715 while (RT_FAILURE(vrc))
716 {
717 {
718 HRESULT rc = S_OK;
719 ComPtr<IVirtualBox> pV;
720 {
721 AutoReadLock alock(pThis COMMA_LOCKVAL_SRC_POS);
722 pV = pThis->mData.m_pVirtualBox;
723 }
724 if (!pV.isNull())
725 {
726 ULONG rev;
727 rc = pV->COMGETTER(Revision)(&rev);
728 if (FAILED_DEAD_INTERFACE(rc))
729 {
730 LogRel(("VirtualBoxClient: detected unresponsive VBoxSVC (rc=%Rhrc)\n", rc));
731 {
732 AutoWriteLock alock(pThis COMMA_LOCKVAL_SRC_POS);
733 /* Throw away the VirtualBox reference, it's no longer
734 * usable as VBoxSVC terminated in the mean time. */
735 pThis->mData.m_pVirtualBox.setNull();
736 }
737 fireVBoxSVCAvailabilityChangedEvent(pThis->mData.m_pEventSource, FALSE);
738 }
739 }
740 else
741 {
742 /* Try to get a new VirtualBox reference straight away, and if
743 * this fails use an increased waiting time as very frequent
744 * restart attempts in some wedged config can cause high CPU
745 * and disk load. */
746 ComPtr<IVirtualBox> pVirtualBox;
747 ComPtr<IToken> pToken;
748#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_SDS)
749 rc = CreateVirtualBoxThroughSDS(pVirtualBox, pToken);
750#else
751 rc = pVirtualBox.createLocalObject(CLSID_VirtualBox);
752#endif
753 if (FAILED(rc))
754 cMillies = 3 * VBOXCLIENT_DEFAULT_INTERVAL;
755 else
756 {
757 LogRel(("VirtualBoxClient: detected working VBoxSVC (rc=%Rhrc)\n", rc));
758 {
759 AutoWriteLock alock(pThis COMMA_LOCKVAL_SRC_POS);
760 /* Update the VirtualBox reference, there's a working
761 * VBoxSVC again from now on. */
762 pThis->mData.m_pVirtualBox = pVirtualBox;
763 pThis->mData.m_pToken = pToken;
764 }
765 fireVBoxSVCAvailabilityChangedEvent(pThis->mData.m_pEventSource, TRUE);
766 cMillies = VBOXCLIENT_DEFAULT_INTERVAL;
767 }
768 }
769 }
770 vrc = RTSemEventWait(sem, cMillies);
771 }
772 return 0;
773}
774
775/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette