diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 16:49:04 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 16:49:04 +0000 |
commit | 16f504a9dca3fe3b70568f67b7d41241ae485288 (patch) | |
tree | c60f36ada0496ba928b7161059ba5ab1ab224f9d /src/VBox/Main/src-all/win | |
parent | Initial commit. (diff) | |
download | virtualbox-upstream.tar.xz virtualbox-upstream.zip |
Adding upstream version 7.0.6-dfsg.upstream/7.0.6-dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Main/src-all/win')
-rw-r--r-- | src/VBox/Main/src-all/win/Makefile.kup | 0 | ||||
-rw-r--r-- | src/VBox/Main/src-all/win/VBoxAPIWrap-precomp_vcc.h | 50 | ||||
-rw-r--r-- | src/VBox/Main/src-all/win/VBoxProxyStub-x86.rc | 34 | ||||
-rw-r--r-- | src/VBox/Main/src-all/win/VBoxProxyStub.c | 2599 | ||||
-rw-r--r-- | src/VBox/Main/src-all/win/VBoxProxyStub.def | 36 | ||||
-rw-r--r-- | src/VBox/Main/src-all/win/VBoxProxyStub.rc | 34 | ||||
-rw-r--r-- | src/VBox/Main/src-all/win/VBoxProxyStubLegacy.rc | 34 | ||||
-rw-r--r-- | src/VBox/Main/src-all/win/VirtualBox_rgs.xsl | 196 | ||||
-rw-r--r-- | src/VBox/Main/src-all/win/comregister.cmd | 212 |
9 files changed, 3195 insertions, 0 deletions
diff --git a/src/VBox/Main/src-all/win/Makefile.kup b/src/VBox/Main/src-all/win/Makefile.kup new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/VBox/Main/src-all/win/Makefile.kup diff --git a/src/VBox/Main/src-all/win/VBoxAPIWrap-precomp_vcc.h b/src/VBox/Main/src-all/win/VBoxAPIWrap-precomp_vcc.h new file mode 100644 index 00000000..5c5c2f62 --- /dev/null +++ b/src/VBox/Main/src-all/win/VBoxAPIWrap-precomp_vcc.h @@ -0,0 +1,50 @@ +/* $Id: VBoxAPIWrap-precomp_vcc.h $ */ +/** @file + * VirtualBox COM - Visual C++ precompiled header for the API wrappers. + */ + +/* + * Copyright (C) 2006-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +#include <iprt/cdefs.h> +#include <iprt/win/windows.h> +#include <VBox/cdefs.h> +#include <iprt/types.h> +#include <iprt/cpp/list.h> +#include <iprt/cpp/meta.h> +#include <iprt/cpp/ministring.h> +#include <VBox/com/microatl.h> +#include <VBox/com/com.h> +#include <VBox/com/array.h> +#include <VBox/com/Guid.h> +#include <VBox/com/string.h> + +#include "VBox/com/VirtualBox.h" + +#include "VirtualBoxBase.h" +#include "Wrapper.h" + +#if defined(Log) || defined(LogIsEnabled) +# error "Log() from iprt/log.h cannot be defined in the precompiled header!" +#endif + diff --git a/src/VBox/Main/src-all/win/VBoxProxyStub-x86.rc b/src/VBox/Main/src-all/win/VBoxProxyStub-x86.rc new file mode 100644 index 00000000..3aceef6e --- /dev/null +++ b/src/VBox/Main/src-all/win/VBoxProxyStub-x86.rc @@ -0,0 +1,34 @@ +/* $Id: VBoxProxyStub-x86.rc $ */ +/** @file + * VBoxProxyStub - Resource file containing version info, icon and typelib. + */ + +/* + * Copyright (C) 2006-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#define IN_FILE_DESCRIPTION "VirtualBox COM Proxy Stub and Typelib" +#define IN_FILE_BASENAME "VBoxProxyStub-x86" + +#include "../../../Artwork/win/TemplateDll.rc" + +1 TYPELIB "VirtualBox-x86.tlb" + diff --git a/src/VBox/Main/src-all/win/VBoxProxyStub.c b/src/VBox/Main/src-all/win/VBoxProxyStub.c new file mode 100644 index 00000000..44b9cd84 --- /dev/null +++ b/src/VBox/Main/src-all/win/VBoxProxyStub.c @@ -0,0 +1,2599 @@ +/* $Id: VBoxProxyStub.c $ */ +/** @file + * VBoxProxyStub - Proxy Stub and Typelib, COM DLL exports and DLL init/term. + * + * @remarks This is a C file and not C++ because rpcproxy.h isn't C++ clean, + * at least not in SDK v7.1. + */ + +/* + * Copyright (C) 2006-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP LOG_GROUP_MAIN +#define PROXY_DELEGATION /* see generated dlldata.c */ +#include <iprt/nt/nt-and-windows.h> +#include <rpcproxy.h> +#include <iprt/win/shlwapi.h> +#include <stdio.h> + +#include "VirtualBox.h" +#include <VBox/cdefs.h> /* for VBOX_STRICT */ +#include <VBox/log.h> +#include <iprt/alloca.h> +#include <iprt/assert.h> +#include <iprt/ctype.h> +#include <iprt/initterm.h> +#include <iprt/path.h> +#include <iprt/string.h> +#include <iprt/uuid.h> +#include <iprt/utf16.h> + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +#ifdef DEBUG_bird +# define VBSP_LOG_ENABLED +#endif + +#ifdef VBSP_LOG_ENABLED +# define VBSP_LOG_VALUE_CHANGE(a) RTAssertMsg2 a +#else +# define VBSP_LOG_VALUE_CHANGE(a) do { } while (0) +#endif + +#ifdef VBSP_LOG_ENABLED +# define VBSP_LOG_SET_VALUE(a) RTAssertMsg2 a +#else +# define VBSP_LOG_SET_VALUE(a) do { } while (0) +#endif + +#ifdef VBSP_LOG_ENABLED +# define VBSP_LOG_NEW_KEY(a) RTAssertMsg2 a +#else +# define VBSP_LOG_NEW_KEY(a) do { } while (0) +#endif + +#ifdef VBSP_LOG_ENABLED +# define VBSP_LOG_DEL_KEY(a) RTAssertMsg2 a +#else +# define VBSP_LOG_DEL_KEY(a) do { } while (0) +#endif + +/** + * Selects the proxy stub DLL based on 32-on-64-bit and host OS version. + * + * The legacy DLL covers 64-bit pre Windows 7 versions of Windows. W2K3-amd64 + * has trouble parsing the result when MIDL /target NT51 or higher. Vista and + * windows server 2008 seems to have trouble with newer IDL compilers. + */ +#if ARCH_BITS == 64 || defined(VBOX_IN_32_ON_64_MAIN_API) +# define VBPS_PROXY_STUB_FILE(a_fIs32On64) ( (a_fIs32On64) ? "x86\\VBoxProxyStub-x86.dll" : VBPS_PROXY_STUB_FILE_SUB() ) +#else +# define VBPS_PROXY_STUB_FILE(a_fIs32On64) VBPS_PROXY_STUB_FILE_SUB() +#endif +#define VBPS_PROXY_STUB_FILE_SUB() \ + ( RT_MAKE_U64(((PKUSER_SHARED_DATA)MM_SHARED_USER_DATA_VA)->NtMinorVersion, \ + ((PKUSER_SHARED_DATA)MM_SHARED_USER_DATA_VA)->NtMajorVersion) >= RT_MAKE_U64(1/*Lo*/,6/*Hi*/) \ + ? "VBoxProxyStub.dll" : "VBoxProxyStubLegacy.dll" ) + +/** For use with AssertLogRel except a_Expr1 from assertions but not LogRel. */ +#ifdef RT_STRICT +# define VBPS_LOGREL_NO_ASSERT(a_Expr) (a_Expr) +#else +# define VBPS_LOGREL_NO_ASSERT(a_Expr) false +#endif + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** For NdrXxx. */ +CStdPSFactoryBuffer g_ProxyStubFactory = /* see generated dlldata.c */ +{ + NULL, + 0, + NULL, + 0 +}; +/** Reference to VirtualBox_p.c structure. */ +EXTERN_PROXY_FILE(VirtualBox) /* see generated dlldata.c */ +/** For NdrXxx and for returning. */ +static const ProxyFileInfo *g_apProxyFiles[] = +{ + REFERENCE_PROXY_FILE(VirtualBox), + NULL /* terminator */ +}; +/** The class ID for this proxy stub factory (see Makefile). */ +static const CLSID g_ProxyClsId = PROXY_CLSID_IS; +/** The instance handle of this DLL. For use in registration routines. */ +static HINSTANCE g_hDllSelf; + + +/** Type library GUIDs to clean up manually. + * Must be upper case! */ +static PCRTUTF16 const g_apwszTypeLibIds[] = +{ + L"{46137EEC-703B-4FE5-AFD4-7C9BBBBA0259}", + L"{D7569351-1750-46F0-936E-BD127D5BC264}", +}; + +/** Type library version to clean up manually. */ +static PCRTUTF16 const g_apwszTypelibVersions[] = +{ + L"1.0", + L"1.3", +}; + +/** Proxy stub class IDs we wish to clean up manually. + * Must be upper case! */ +static PCRTUTF16 const g_apwszProxyStubClsIds[] = +{ + L"{0BB3B78C-1807-4249-5BA5-EA42D66AF0BF}", + L"{327E3C00-EE61-462F-AED3-0DFF6CBF9904}", +}; + + +/** + * DLL main function. + * + * @returns TRUE (/ FALSE). + * @param hInstance The DLL handle. + * @param dwReason The rason for the call (DLL_XXX). + * @param lpReserved Reserved. + */ +BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) +{ + switch (dwReason) + { + case DLL_PROCESS_ATTACH: + /* Save the DLL handle so we can get the path to this DLL during + registration and updating. */ + g_hDllSelf = hInstance; + + /* We don't need callbacks for thread creation and destruction. */ + DisableThreadLibraryCalls(hInstance); + + /* Init IPRT. */ + RTR3InitDll(RTR3INIT_FLAGS_UNOBTRUSIVE); + Log12(("VBoxProxyStub[%u]/DllMain: DLL_PROCESS_ATTACH\n", GetCurrentProcessId())); + +#ifdef VBOX_STRICT + { + /* + * Check that no interface has more than 256 methods in the stub vtable. + */ + const ProxyFileInfo **ppProxyFile = &g_apProxyFiles[0]; + const ProxyFileInfo *pProxyFile; + while ((pProxyFile = *ppProxyFile++) != NULL) + { + const PCInterfaceStubVtblList * const papStubVtbls = pProxyFile->pStubVtblList; + const char * const *papszNames = pProxyFile->pNamesArray; + unsigned iIf = pProxyFile->TableSize; + AssertStmt(iIf < 1024, iIf = 0); + Assert(pProxyFile->TableVersion == 2); + + while (iIf-- > 0) + AssertMsg(papStubVtbls[iIf]->header.DispatchTableCount <= 256, + ("%s: DispatchTableCount=%d\n", papszNames[iIf], papStubVtbls[iIf]->header.DispatchTableCount)); + } + } +#endif + break; + + case DLL_PROCESS_DETACH: + Log12(("VBoxProxyStub[%u]/DllMain: DLL_PROCESS_DETACH\n", GetCurrentProcessId())); + break; + } + + NOREF(lpReserved); + return TRUE; +} + + +/** + * RPC entry point returning info about the proxy. + */ +void RPC_ENTRY GetProxyDllInfo(const ProxyFileInfo ***ppapInfo, const CLSID **ppClsid) +{ + *ppapInfo = &g_apProxyFiles[0]; + *ppClsid = &g_ProxyClsId; + Log12(("VBoxProxyStub[%u]/GetProxyDllInfo:\n", GetCurrentProcessId())); +} + + +/** + * Instantiate the proxy stub class object. + * + * @returns COM status code + * @param rclsid Reference to the ID of the call to instantiate (our + * g_ProxyClsId). + * @param riid The interface ID to return (IID_IPSFactoryBuffer). + * @param ppv Where to return the interface pointer on success. + */ +HRESULT STDAPICALLTYPE DllGetClassObject(REFCLSID rclsid, REFIID riid, void **ppv) +{ + HRESULT hrc; + Assert(memcmp(rclsid, &g_ProxyClsId, sizeof(g_ProxyClsId)) == 0); + + hrc = NdrDllGetClassObject(rclsid, riid, ppv, /* see DLLGETCLASSOBJECTROUTINE in RpcProxy.h */ + g_apProxyFiles, &g_ProxyClsId, &g_ProxyStubFactory); + + /* + * This may fail if the IDL compiler generates code that is incompatible + * with older windows releases. Like for instance 64-bit W2K8 SP1 not + * liking the output of MIDL 7.00.0555 (from the v7.1 SDK), despite + * /target being set to NT51. + */ + AssertLogRelMsg(hrc == S_OK, ("%Rhrc\n", hrc)); + Log12(("VBoxProxyStub[%u]/DllGetClassObject(%RTuuid, %RTuuid, %p): %#x + *ppv=%p\n", + GetCurrentProcessId(), rclsid, riid, ppv, hrc, ppv ? *ppv : NULL)); + return hrc; +} + + +/** + * Checks whether the DLL can be unloaded or not. + * + * @returns S_OK if it can be unloaded, S_FALSE if not. + */ +HRESULT STDAPICALLTYPE DllCanUnloadNow(void) +{ + HRESULT hrc = NdrDllCanUnloadNow(&g_ProxyStubFactory); /* see DLLCANUNLOADNOW in RpcProxy.h */ + Log12(("VBoxProxyStub[%u]/DllCanUnloadNow: %Rhrc\n", GetCurrentProcessId(), hrc)); + return hrc; +} + + + +/** + * Release call that could be referenced by VirtualBox_p.c via + * CStdStubBuffer_METHODS. + * + * @returns New reference count. + * @param pThis Buffer to release. + */ +ULONG STDMETHODCALLTYPE CStdStubBuffer_Release(IRpcStubBuffer *pThis) /* see CSTDSTUBBUFFERRELEASE in RpcProxy.h */ +{ + ULONG cRefs = NdrCStdStubBuffer_Release(pThis, (IPSFactoryBuffer *)&g_ProxyStubFactory); + Log12(("VBoxProxyStub[%u]/CStdStubBuffer_Release: %p -> %#x\n", GetCurrentProcessId(), pThis, cRefs)); + return cRefs; +} + + +/** + * Release call referenced by VirtualBox_p.c via + * CStdStubBuffer_DELEGATING_METHODS. + * + * @returns New reference count. + * @param pThis Buffer to release. + */ +ULONG WINAPI CStdStubBuffer2_Release(IRpcStubBuffer *pThis) /* see CSTDSTUBBUFFER2RELEASE in RpcProxy.h */ +{ + ULONG cRefs = NdrCStdStubBuffer2_Release(pThis, (IPSFactoryBuffer *)&g_ProxyStubFactory); + Log12(("VBoxProxyStub[%u]/CStdStubBuffer2_Release: %p -> %#x\n", GetCurrentProcessId(), pThis, cRefs)); + return cRefs; +} + + +/** + * Pure virtual method implementation referenced by VirtualBox_p.c + */ +void __cdecl _purecall(void) /* see DLLDUMMYPURECALL in RpcProxy.h */ +{ + AssertFailed(); +} + + +#ifdef VBSP_LOG_ENABLED +# include <iprt/asm.h> + +/** For logging full key names. */ +static PCRTUTF16 vbpsDebugKeyToWSZ(HKEY hKey) +{ + static union + { + KEY_NAME_INFORMATION NameInfo; + WCHAR awchPadding[260]; + } s_aBufs[4]; + static uint32_t volatile iNext = 0; + uint32_t i = ASMAtomicIncU32(&iNext) % RT_ELEMENTS(s_aBufs); + ULONG cbRet = 0; + NTSTATUS rcNt; + + memset(&s_aBufs[i], 0, sizeof(s_aBufs[i])); + rcNt = NtQueryKey(hKey, KeyNameInformation, &s_aBufs[i], sizeof(s_aBufs[i]) - sizeof(WCHAR), &cbRet); + if (!NT_SUCCESS(rcNt)) + s_aBufs[i].NameInfo.NameLength = 0; + s_aBufs[i].NameInfo.Name[s_aBufs[i].NameInfo.NameLength] = '\0'; + return s_aBufs[i].NameInfo.Name; +} +#endif + +/** + * Registry modifier state. + */ +typedef struct VBPSREGSTATE +{ + /** Where the classes and stuff are to be registered. */ + HKEY hkeyClassesRootDst; + /** The handle to the CLSID key under hkeyClassesRootDst. */ + HKEY hkeyClsidRootDst; + /** The handle to the Interface key under hkeyClassesRootDst. */ + HKEY hkeyInterfaceRootDst; + + /** Alternative locations where data needs to be deleted, but never updated. */ + struct + { + /** The classes root key handle. */ + HKEY hkeyClasses; + /** The classes/CLSID key handle. */ + HKEY hkeyClsid; + /** The classes/Interface key handle. */ + HKEY hkeyInterface; + } aAltDeletes[3]; + /** Alternative delete locations. */ + uint32_t cAltDeletes; + + /** The current total result. */ + LSTATUS rc; + + /** KEY_WOW64_32KEY, KEY_WOW64_64KEY or 0 (for default). Allows doing all + * almost the work from one process (at least W7+ due to aliases). */ + DWORD fSamWow; + /** Desired key access when only deleting. */ + DWORD fSamDelete; + /** Desired key access when only doing updates. */ + DWORD fSamUpdate; + /** Desired key access when both deleting and updating. */ + DWORD fSamBoth; + /** Whether to delete registrations first. */ + bool fDelete; + /** Whether to update registry value and keys. */ + bool fUpdate; + +} VBPSREGSTATE; + + +/** + * Initializes a registry modification job state. + * + * Always call vbpsRegTerm! + * + * @returns Windows error code (ERROR_SUCCESS on success). + * @param pState The state to init. + * @param hkeyRoot The registry root tree constant. + * @param pszSubRoot The path to the where the classes are registered, + * NULL if @a hkeyRoot. + * @param fDelete Whether to delete registrations first. + * @param fUpdate Whether to update registrations. + * @param fSamWow KEY_WOW64_32KEY or 0. + */ +static LSTATUS vbpsRegInit(VBPSREGSTATE *pState, HKEY hkeyRoot, const char *pszSubRoot, bool fDelete, bool fUpdate, DWORD fSamWow) +{ + LSTATUS rc; + unsigned i = 0; + + /* + * Initialize the whole structure first so we can safely call vbpsRegTerm on failure. + */ + pState->hkeyClassesRootDst = NULL; + pState->hkeyClsidRootDst = NULL; + pState->hkeyInterfaceRootDst = NULL; + for (i = 0; i < RT_ELEMENTS(pState->aAltDeletes); i++) + { + pState->aAltDeletes[i].hkeyClasses = NULL; + pState->aAltDeletes[i].hkeyClsid = NULL; + pState->aAltDeletes[i].hkeyInterface = NULL; + } + pState->cAltDeletes = 0; + pState->rc = ERROR_SUCCESS; + pState->fDelete = fDelete; + pState->fUpdate = fUpdate; + pState->fSamWow = fSamWow; + pState->fSamDelete = 0; + if (fDelete) + pState->fSamDelete = pState->fSamWow | DELETE | KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE + | STANDARD_RIGHTS_READ | STANDARD_RIGHTS_WRITE; + pState->fSamUpdate = 0; + if (fUpdate) + pState->fSamUpdate = pState->fSamWow | KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE | KEY_SET_VALUE | KEY_CREATE_SUB_KEY + | STANDARD_RIGHTS_READ | STANDARD_RIGHTS_WRITE; + pState->fSamBoth = pState->fSamDelete | pState->fSamUpdate; + + /* + * Open the root keys. + */ + rc = RegOpenKeyExA(hkeyRoot, pszSubRoot, 0 /*fOptions*/, pState->fSamBoth, &pState->hkeyClassesRootDst); + if (rc == ERROR_SUCCESS) + { + rc = RegCreateKeyExW(pState->hkeyClassesRootDst, L"CLSID", 0 /*Reserved*/, NULL /*pszClass*/, 0 /*fOptions*/, + pState->fSamBoth, NULL /*pSecAttr*/, &pState->hkeyClsidRootDst, NULL /*pdwDisposition*/); + if (rc == ERROR_SUCCESS) + return ERROR_SUCCESS; + + /* Ignore access denied errors as these may easily happen for + non-admin users. Just give up when this happens */ + AssertLogRelMsgReturn(rc == ERROR_ACCESS_DENIED, ("%u\n", rc), pState->rc = rc); + } + else + AssertLogRelMsgReturn(rc == ERROR_ACCESS_DENIED, ("%u\n", rc), pState->rc = rc); + return pState->rc = rc; +} + + +/** + * Terminates the state, closing all open keys. + * + * @param pState The state to clean up. + */ +static void vbpsRegTerm(VBPSREGSTATE *pState) +{ + LSTATUS rc; + if (pState->hkeyClassesRootDst) + { + rc = RegCloseKey(pState->hkeyClassesRootDst); + Assert(rc == ERROR_SUCCESS); + pState->hkeyClassesRootDst = NULL; + } + if (pState->hkeyClsidRootDst) + { + rc = RegCloseKey(pState->hkeyClsidRootDst); + Assert(rc == ERROR_SUCCESS); + pState->hkeyClsidRootDst = NULL; + } + if (pState->hkeyInterfaceRootDst) + { + rc = RegCloseKey(pState->hkeyInterfaceRootDst); + Assert(rc == ERROR_SUCCESS); + pState->hkeyInterfaceRootDst = NULL; + } + + while (pState->cAltDeletes > 0 && pState->cAltDeletes <= RT_ELEMENTS(pState->aAltDeletes)) + { + unsigned i = --pState->cAltDeletes; + if (pState->aAltDeletes[i].hkeyClasses) + { + rc = RegCloseKey(pState->aAltDeletes[i].hkeyClasses); + Assert(rc == ERROR_SUCCESS); + pState->aAltDeletes[i].hkeyClasses = NULL; + } + if (pState->aAltDeletes[i].hkeyClsid) + { + rc = RegCloseKey(pState->aAltDeletes[i].hkeyClsid); + Assert(rc == ERROR_SUCCESS); + pState->aAltDeletes[i].hkeyClsid = NULL; + } + if (pState->aAltDeletes[i].hkeyInterface) + { + rc = RegCloseKey(pState->aAltDeletes[i].hkeyInterface); + Assert(rc == ERROR_SUCCESS); + pState->aAltDeletes[i].hkeyInterface = NULL; + } + } +} + + +/** + * Add an alternative registry classes tree from which to remove keys. + * + * @returns ERROR_SUCCESS if we successfully opened the destination root, other + * wise windows error code (remebered). + * @param pState The registry modifier state. + * @param hkeyAltRoot The root of the alternate registry classes + * location. + * @param pszAltSubRoot The path to the 'classes' sub-key, or NULL if + * hkeyAltRoot is it. + */ +static LSTATUS vbpsRegAddAltDelete(VBPSREGSTATE *pState, HKEY hkeyAltRoot, const char *pszAltSubRoot) +{ + unsigned i; + LSTATUS rc; + + /* Ignore call if not in delete mode. */ + if (!pState->fDelete) + return ERROR_SUCCESS; + + /* Check that there is space in the state. */ + i = pState->cAltDeletes; + AssertReturn(i < RT_ELEMENTS(pState->aAltDeletes), pState->rc = ERROR_TOO_MANY_NAMES); + + + /* Open the root. */ + rc = RegOpenKeyExA(hkeyAltRoot, pszAltSubRoot, 0 /*fOptions*/, pState->fSamDelete, + &pState->aAltDeletes[i].hkeyClasses); + if (rc == ERROR_SUCCESS) + { + /* Try open the CLSID subkey, it's fine if it doesn't exists. */ + rc = RegOpenKeyExW(pState->aAltDeletes[i].hkeyClasses, L"CLSID", 0 /*fOptions*/, pState->fSamDelete, + &pState->aAltDeletes[i].hkeyClsid); + if (rc == ERROR_SUCCESS || rc == ERROR_FILE_NOT_FOUND) + { + if (rc == ERROR_FILE_NOT_FOUND) + pState->aAltDeletes[i].hkeyClsid = NULL; + pState->cAltDeletes = i + 1; + return ERROR_SUCCESS; + } + AssertLogRelMsgFailed(("%u\n", rc)); + RegCloseKey(pState->aAltDeletes[i].hkeyClasses); + } + /* No need to add non-existing alternative roots, nothing to delete in the void. */ + else if (rc == ERROR_FILE_NOT_FOUND) + rc = ERROR_SUCCESS; + else + { + AssertLogRelMsgFailed(("%u (%#x %s)\n", rc)); + pState->rc = rc; + } + + pState->aAltDeletes[i].hkeyClasses = NULL; + pState->aAltDeletes[i].hkeyClsid = NULL; + return rc; +} + + +/** + * Open the 'Interface' keys under the current classes roots. + * + * We don't do this during vbpsRegInit as it's only needed for updating. + * + * @returns ERROR_SUCCESS if we successfully opened the destination root, other + * wise windows error code (remebered). + * @param pState The registry modifier state. + */ +static LSTATUS vbpsRegOpenInterfaceKeys(VBPSREGSTATE *pState) +{ + unsigned i; + LSTATUS rc; + + /* + * Under the root destination. + */ + if (pState->hkeyInterfaceRootDst == NULL) + { + if (pState->fSamUpdate) + rc = RegCreateKeyExW(pState->hkeyClassesRootDst, L"Interface", 0 /*Reserved*/, NULL /*pszClass*/, 0 /*fOptions*/, + pState->fSamBoth, NULL /*pSecAttr*/, &pState->hkeyInterfaceRootDst, NULL /*pdwDisposition*/); + else + rc = RegOpenKeyExW(pState->hkeyClassesRootDst, L"Interface", 0 /*fOptions*/, pState->fSamBoth, + &pState->hkeyClsidRootDst); + if (rc == ERROR_ACCESS_DENIED) + { + pState->hkeyInterfaceRootDst = NULL; + return pState->rc = rc; + } + AssertLogRelMsgReturnStmt(rc == ERROR_SUCCESS, ("%u\n", rc), pState->hkeyInterfaceRootDst = NULL, pState->rc = rc); + } + + /* + * Under the alternative delete locations. + */ + i = pState->cAltDeletes; + while (i-- > 0) + if (pState->aAltDeletes[i].hkeyInterface == NULL) + { + rc = RegOpenKeyExW(pState->aAltDeletes[i].hkeyClasses, L"Interface", 0 /*fOptions*/, pState->fSamDelete, + &pState->aAltDeletes[i].hkeyInterface); + if (rc != ERROR_SUCCESS) + { + AssertMsgStmt(rc == ERROR_FILE_NOT_FOUND || rc == ERROR_ACCESS_DENIED, ("%u\n", rc), pState->rc = rc); + pState->aAltDeletes[i].hkeyInterface = NULL; + } + } + + return ERROR_SUCCESS; +} + + +/** The destination buffer size required by vbpsFormatUuidInCurly. */ +#define CURLY_UUID_STR_BUF_SIZE 40 + +/** + * Formats a UUID to a string, inside curly braces. + * + * @returns @a pszString + * @param pszString Output buffer of size CURLY_UUID_STR_BUF_SIZE. + * @param pUuidIn The UUID to format. + */ +static const char *vbpsFormatUuidInCurly(char pszString[CURLY_UUID_STR_BUF_SIZE], const CLSID *pUuidIn) +{ + static const char s_achDigits[17] = "0123456789abcdef"; + PCRTUUID pUuid = (PCRTUUID)pUuidIn; + uint32_t u32TimeLow; + unsigned u; + + pszString[ 0] = '{'; + u32TimeLow = RT_H2LE_U32(pUuid->Gen.u32TimeLow); + pszString[ 1] = s_achDigits[(u32TimeLow >> 28)/*& 0xf*/]; + pszString[ 2] = s_achDigits[(u32TimeLow >> 24) & 0xf]; + pszString[ 3] = s_achDigits[(u32TimeLow >> 20) & 0xf]; + pszString[ 4] = s_achDigits[(u32TimeLow >> 16) & 0xf]; + pszString[ 5] = s_achDigits[(u32TimeLow >> 12) & 0xf]; + pszString[ 6] = s_achDigits[(u32TimeLow >> 8) & 0xf]; + pszString[ 7] = s_achDigits[(u32TimeLow >> 4) & 0xf]; + pszString[ 8] = s_achDigits[(u32TimeLow/*>>0*/)& 0xf]; + pszString[ 9] = '-'; + u = RT_H2LE_U16(pUuid->Gen.u16TimeMid); + pszString[10] = s_achDigits[(u >> 12)/*& 0xf*/]; + pszString[11] = s_achDigits[(u >> 8) & 0xf]; + pszString[12] = s_achDigits[(u >> 4) & 0xf]; + pszString[13] = s_achDigits[(u/*>>0*/)& 0xf]; + pszString[14] = '-'; + u = RT_H2LE_U16(pUuid->Gen.u16TimeHiAndVersion); + pszString[15] = s_achDigits[(u >> 12)/*& 0xf*/]; + pszString[16] = s_achDigits[(u >> 8) & 0xf]; + pszString[17] = s_achDigits[(u >> 4) & 0xf]; + pszString[18] = s_achDigits[(u/*>>0*/)& 0xf]; + pszString[19] = '-'; + pszString[20] = s_achDigits[pUuid->Gen.u8ClockSeqHiAndReserved >> 4]; + pszString[21] = s_achDigits[pUuid->Gen.u8ClockSeqHiAndReserved & 0xf]; + pszString[22] = s_achDigits[pUuid->Gen.u8ClockSeqLow >> 4]; + pszString[23] = s_achDigits[pUuid->Gen.u8ClockSeqLow & 0xf]; + pszString[24] = '-'; + pszString[25] = s_achDigits[pUuid->Gen.au8Node[0] >> 4]; + pszString[26] = s_achDigits[pUuid->Gen.au8Node[0] & 0xf]; + pszString[27] = s_achDigits[pUuid->Gen.au8Node[1] >> 4]; + pszString[28] = s_achDigits[pUuid->Gen.au8Node[1] & 0xf]; + pszString[29] = s_achDigits[pUuid->Gen.au8Node[2] >> 4]; + pszString[30] = s_achDigits[pUuid->Gen.au8Node[2] & 0xf]; + pszString[31] = s_achDigits[pUuid->Gen.au8Node[3] >> 4]; + pszString[32] = s_achDigits[pUuid->Gen.au8Node[3] & 0xf]; + pszString[33] = s_achDigits[pUuid->Gen.au8Node[4] >> 4]; + pszString[34] = s_achDigits[pUuid->Gen.au8Node[4] & 0xf]; + pszString[35] = s_achDigits[pUuid->Gen.au8Node[5] >> 4]; + pszString[36] = s_achDigits[pUuid->Gen.au8Node[5] & 0xf]; + pszString[37] = '}'; + pszString[38] = '\0'; + + return pszString; + +} + + +/** + * Sets a registry string value, wide char variant. + * + * @returns See RegSetValueExA (errors are remembered in the state). + * @param pState The registry modifier state. + * @param hkey The key to add the value to. + * @param pwszValueNm The value name. NULL for setting the default. + * @param pwszValue The value string. + * @param uLine The line we're called from. + */ +static LSTATUS vbpsSetRegValueWW(VBPSREGSTATE *pState, HKEY hkey, PCRTUTF16 pwszValueNm, PCRTUTF16 pwszValue, unsigned uLine) +{ + DWORD const cbValue = (DWORD)((RTUtf16Len(pwszValue) + 1) * sizeof(RTUTF16)); + LSTATUS rc; + Assert(pState->fUpdate); + + /* + * If we're not deleting the key prior to updating, we're in gentle update + * mode where we will query if the existing value matches the incoming one. + */ + if (!pState->fDelete) + { + DWORD cbExistingData = cbValue + 128; + PRTUTF16 pwszExistingData = (PRTUTF16)alloca(cbExistingData); + DWORD dwExistingType; + rc = RegQueryValueExW(hkey, pwszValueNm, 0 /*Reserved*/, &dwExistingType, (BYTE *)pwszExistingData, &cbExistingData); + if (rc == ERROR_SUCCESS) + { + if ( dwExistingType == REG_SZ + && cbExistingData == cbValue) + { + if (memcmp(pwszValue, pwszExistingData, cbValue) == 0) + return ERROR_SUCCESS; + } + VBSP_LOG_VALUE_CHANGE(("vbpsSetRegValueWW: Value difference: dwExistingType=%d cbExistingData=%#x cbValue=%#x\n" + " hkey=%#x %ls; value name=%ls\n" + "existing: %.*Rhxs (%.*ls)\n" + " new: %.*Rhxs (%ls)\n", + dwExistingType, cbExistingData, cbValue, + hkey, vbpsDebugKeyToWSZ(hkey), pwszValueNm ? pwszValueNm : L"(default)", + cbExistingData, pwszExistingData, cbExistingData / sizeof(RTUTF16), pwszExistingData, + cbValue, pwszValue, pwszValue)); + } + else + Assert(rc == ERROR_FILE_NOT_FOUND || rc == ERROR_MORE_DATA); + } + + /* + * Set the value. + */ + rc = RegSetValueExW(hkey, pwszValueNm, 0 /*Reserved*/, REG_SZ, (const BYTE *)pwszValue, cbValue); + if (rc == ERROR_SUCCESS) + { + VBSP_LOG_SET_VALUE(("vbpsSetRegValueWW: %ls/%ls=%ls (at %d)\n", + vbpsDebugKeyToWSZ(hkey), pwszValueNm ? pwszValueNm : L"(Default)", pwszValue, uLine)); + return ERROR_SUCCESS; + } + + AssertLogRelMsg(VBPS_LOGREL_NO_ASSERT(rc == ERROR_ACCESS_DENIED), + ("%d: '%ls'='%ls' -> %u\n", uLine, pwszValueNm, pwszValue, rc)); + pState->rc = rc; + return rc; +} + + +/** + * Sets a registry string value. + * + * @returns See RegSetValueExA (errors are remembered in the state). + * @param pState The registry modifier state. + * @param hkey The key to add the value to. + * @param pszValueNm The value name. NULL for setting the default. + * @param pszValue The value string. + * @param uLine The line we're called from. + */ +static LSTATUS vbpsSetRegValueAA(VBPSREGSTATE *pState, HKEY hkey, const char *pszValueNm, const char *pszValue, unsigned uLine) +{ + DWORD const cbValue = (DWORD)strlen(pszValue) + 1; + LSTATUS rc; + Assert(pState->fUpdate); + + /* + * If we're not deleting the key prior to updating, we're in gentle update + * mode where we will query if the existing value matches the incoming one. + */ + if (!pState->fDelete) + { + DWORD cbExistingData = cbValue + 128; + char *pszExistingData = alloca(cbExistingData); + DWORD dwExistingType; + rc = RegQueryValueExA(hkey, pszValueNm, 0 /*Reserved*/, &dwExistingType, (PBYTE)pszExistingData, &cbExistingData); + if (rc == ERROR_SUCCESS) + { + if ( dwExistingType == REG_SZ + && cbExistingData == cbValue) + { + if (memcmp(pszValue, pszExistingData, cbValue) == 0) + return ERROR_SUCCESS; + if (memicmp(pszValue, pszExistingData, cbValue) == 0) + return ERROR_SUCCESS; + } + VBSP_LOG_VALUE_CHANGE(("vbpsSetRegValueAA: Value difference: dwExistingType=%d cbExistingData=%#x cbValue=%#x\n" + " hkey=%#x %ls; value name=%s\n" + "existing: %.*Rhxs (%.*s)\n" + " new: %.*Rhxs (%s)\n", + dwExistingType, cbExistingData, cbValue, + hkey, vbpsDebugKeyToWSZ(hkey), pszValueNm ? pszValueNm : "(default)", + cbExistingData, pszExistingData, cbExistingData, pszExistingData, + cbValue, pszValue, pszValue)); + } + else + Assert(rc == ERROR_FILE_NOT_FOUND || rc == ERROR_MORE_DATA); + } + + /* + * Set the value. + */ + rc = RegSetValueExA(hkey, pszValueNm, 0 /*Reserved*/, REG_SZ, (PBYTE)pszValue, cbValue); + if (rc == ERROR_SUCCESS) + { + VBSP_LOG_SET_VALUE(("vbpsSetRegValueAA: %ls/%s=%s (at %d)\n", + vbpsDebugKeyToWSZ(hkey), pszValueNm ? pszValueNm : "(Default)", pszValue, uLine)); + return ERROR_SUCCESS; + } + + AssertLogRelMsg(VBPS_LOGREL_NO_ASSERT(rc == ERROR_ACCESS_DENIED), + ("%d: '%s'='%s' -> %u\n", uLine, pszValueNm, pszValue, rc)); + pState->rc = rc; + return rc; +} + + +/** + * Closes a registry key. + * + * @returns See RegCloseKey (errors are remembered in the state). + * @param pState The registry modifier state. + * @param hkey The key to close. + * @param uLine The line we're called from. + */ +static LSTATUS vbpsCloseKey(VBPSREGSTATE *pState, HKEY hkey, unsigned uLine) +{ + LSTATUS rc = RegCloseKey(hkey); + if (rc == ERROR_SUCCESS) + return ERROR_SUCCESS; + + AssertLogRelMsgFailed(("%d: close key -> %u\n", uLine, rc)); + pState->rc = rc; + return rc; +} + + +/** + * Creates a registry key. + * + * @returns See RegCreateKeyA and RegSetValueExA (errors are remembered in the + * state). + * @param pState The registry modifier state. + * @param hkeyParent The parent key. + * @param pszKey The new key under @a hkeyParent. + * @param phkey Where to return the handle to the new key. + * @param uLine The line we're called from. + */ +static LSTATUS vbpsCreateRegKeyA(VBPSREGSTATE *pState, HKEY hkeyParent, const char *pszKey, PHKEY phkey, unsigned uLine) +{ + /* + * This will open if it exists and create if new, which is exactly what we want. + */ + HKEY hNewKey; + DWORD dwDisposition = 0; + LSTATUS rc = RegCreateKeyExA(hkeyParent, pszKey, 0 /*Reserved*/, NULL /*pszClass*/, 0 /*fOptions*/, + pState->fSamBoth, NULL /*pSecAttr*/, &hNewKey, &dwDisposition); + if (rc == ERROR_SUCCESS) + { + *phkey = hNewKey; + if (dwDisposition == REG_CREATED_NEW_KEY) + VBSP_LOG_NEW_KEY(("vbpsCreateRegKeyA: %ls/%s (at %d)\n", vbpsDebugKeyToWSZ(hkeyParent), pszKey, uLine)); + } + else + { + AssertLogRelMsg(VBPS_LOGREL_NO_ASSERT(rc == ERROR_ACCESS_DENIED), + ("%d: create key '%s' -> %u\n", uLine, pszKey, rc)); + pState->rc = rc; + *phkey = NULL; + } + return rc; +} + + +/** + * Creates a registry key with a default string value. + * + * @returns See RegCreateKeyA and RegSetValueExA (errors are remembered in the + * state). + * @param pState The registry modifier state. + * @param hkeyParent The parent key. + * @param pszKey The new key under @a hkeyParent. + * @param pszValue The value string. + * @param uLine The line we're called from. + */ +static LSTATUS vbpsCreateRegKeyWithDefaultValueAA(VBPSREGSTATE *pState, HKEY hkeyParent, const char *pszKey, + const char *pszValue, unsigned uLine) +{ + HKEY hNewKey; + LSTATUS rc = vbpsCreateRegKeyA(pState, hkeyParent, pszKey, &hNewKey, uLine); + if (rc == ERROR_SUCCESS) + { + rc = vbpsSetRegValueAA(pState, hNewKey, NULL /*pszValueNm*/, pszValue, uLine); + vbpsCloseKey(pState, hNewKey, uLine); + } + else + { + AssertLogRelMsg(VBPS_LOGREL_NO_ASSERT(rc == ERROR_ACCESS_DENIED), + ("%d: create key '%s'(/Default='%s') -> %u\n", uLine, pszKey, pszValue, rc)); + pState->rc = rc; + } + return rc; +} + + +/** + * Creates a registry key with a default wide string value. + * + * @returns See RegCreateKeyA and RegSetValueExA (errors are remembered in the + * state). + * @param pState The registry modifier state. + * @param hkeyParent The parent key. + * @param pszKey The new key under @a hkeyParent. + * @param pwszValue The value string. + * @param uLine The line we're called from. + */ +static LSTATUS vbpsCreateRegKeyWithDefaultValueAW(VBPSREGSTATE *pState, HKEY hkeyParent, const char *pszKey, + PCRTUTF16 pwszValue, unsigned uLine) +{ + HKEY hNewKey; + LSTATUS rc = vbpsCreateRegKeyA(pState, hkeyParent, pszKey, &hNewKey, uLine); + if (rc == ERROR_SUCCESS) + { + rc = vbpsSetRegValueWW(pState, hNewKey, NULL /*pwszValueNm*/, pwszValue, uLine); + vbpsCloseKey(pState, hNewKey, uLine); + } + else + { + AssertLogRelMsg(VBPS_LOGREL_NO_ASSERT(rc == ERROR_ACCESS_DENIED), + ("%d: create key '%s'(/Default='%ls') -> %u\n", uLine, pszKey, pwszValue, rc)); + pState->rc = rc; + } + return rc; +} + + +/** + * Creates a registry key with a default string value, return the key. + * + * @returns See RegCreateKeyA and RegSetValueExA (errors are remembered in the + * state). + * @param pState The registry modifier state. + * @param hkeyParent The parent key. + * @param pszKey The new key under @a hkeyParent. + * @param pszValue The value string. + * @param phkey Where to return the handle to the new key. + * @param uLine The line we're called from. + */ +static LSTATUS vbpsCreateRegKeyWithDefaultValueAAEx(VBPSREGSTATE *pState, HKEY hkeyParent, const char *pszKey, + const char *pszValue, PHKEY phkey, unsigned uLine) +{ + HKEY hNewKey; + LSTATUS rc = vbpsCreateRegKeyA(pState, hkeyParent, pszKey, &hNewKey, uLine); + if (rc == ERROR_SUCCESS) + { + rc = vbpsSetRegValueAA(pState, hNewKey, NULL /*pszValueNm*/, pszValue, uLine); + *phkey = hNewKey; + } + else + { + AssertLogRelMsg(VBPS_LOGREL_NO_ASSERT(rc == ERROR_ACCESS_DENIED), + ("%d: create key '%s'(/Default='%s') -> %u\n", uLine, pszKey, pszValue, rc)); + pState->rc = rc; + *phkey = NULL; + } + return rc; +} + + +/** + * Recursively deletes a registry key. + * + * @returns See SHDeleteKeyA (errors are remembered in the state). + * @param pState The registry modifier state. + * @param hkeyParent The parent key. + * @param pszKey The key under @a hkeyParent that should be + * deleted. + * @param uLine The line we're called from. + */ +static LSTATUS vbpsDeleteKeyRecursiveA(VBPSREGSTATE *pState, HKEY hkeyParent, const char *pszKey, unsigned uLine) +{ + LSTATUS rc; + + Assert(pState->fDelete); + Assert(pszKey); + AssertReturn(*pszKey != '\0', pState->rc = ERROR_INVALID_PARAMETER); + +#ifdef VBSP_LOG_ENABLED + { + HKEY hkeyLog; + rc = RegOpenKeyExA(hkeyParent, pszKey, 0 /*fOptions*/, pState->fSamDelete, &hkeyLog); + if (rc != ERROR_FILE_NOT_FOUND) + VBSP_LOG_DEL_KEY(("vbpsDeleteKeyRecursiveA: %ls/%s (at %d)\n", vbpsDebugKeyToWSZ(hkeyParent), pszKey, uLine)); + if (rc == ERROR_SUCCESS) + RegCloseKey(hkeyLog); + } +#endif + + rc = SHDeleteKeyA(hkeyParent, pszKey); + if (rc == ERROR_SUCCESS || rc == ERROR_FILE_NOT_FOUND) + return ERROR_SUCCESS; + + AssertLogRelMsg(VBPS_LOGREL_NO_ASSERT(rc == ERROR_ACCESS_DENIED), + ("%d: delete key '%s' -> %u\n", uLine, pszKey, rc)); + pState->rc = rc; + return rc; +} + + +/** + * Recursively deletes a registry key, wide char version. + * + * @returns See SHDeleteKeyW (errors are remembered in the state). + * @param pState The registry modifier state. + * @param hkeyParent The parent key. + * @param pwszKey The key under @a hkeyParent that should be + * deleted. + * @param uLine The line we're called from. + */ +static LSTATUS vbpsDeleteKeyRecursiveW(VBPSREGSTATE *pState, HKEY hkeyParent, PCRTUTF16 pwszKey, unsigned uLine) +{ + LSTATUS rc; + + Assert(pState->fDelete); + Assert(pwszKey); + AssertReturn(*pwszKey != '\0', pState->rc = ERROR_INVALID_PARAMETER); + +#ifdef VBSP_LOG_ENABLED + { + HKEY hkeyLog; + rc = RegOpenKeyExW(hkeyParent, pwszKey, 0 /*fOptions*/, pState->fSamDelete, &hkeyLog); + if (rc != ERROR_FILE_NOT_FOUND) + VBSP_LOG_DEL_KEY(("vbpsDeleteKeyRecursiveW: %ls/%ls (at %d)\n", vbpsDebugKeyToWSZ(hkeyParent), pwszKey, uLine)); + if (rc == ERROR_SUCCESS) + RegCloseKey(hkeyLog); + } +#endif + + rc = SHDeleteKeyW(hkeyParent, pwszKey); + if (rc == ERROR_SUCCESS || rc == ERROR_FILE_NOT_FOUND) + return ERROR_SUCCESS; + + AssertLogRelMsg(VBPS_LOGREL_NO_ASSERT(rc == ERROR_ACCESS_DENIED), + ("%d: delete key '%ls' -> %u\n", uLine, pwszKey, rc)); + pState->rc = rc; + return rc; +} + + +/** + * Register an application id. + * + * @returns Windows error code (errors are rememberd in the state). + * @param pState The registry modifier state. + * @param pszModuleName The module name. + * @param pszAppId The application UUID string. + * @param pszDescription The description string. + * @param pszServiceName The window service name if the application is a + * service, otherwise this must be NULL. + */ +LSTATUS VbpsRegisterAppId(VBPSREGSTATE *pState, const char *pszModuleName, const char *pszAppId, + const char *pszDescription, const char *pszServiceName) +{ + LSTATUS rc; + HKEY hkeyAppIds; + Assert(*pszAppId == '{'); + + /* + * Delete. + */ + if (pState->fDelete) + { + unsigned i = pState->cAltDeletes; + while (i-- > 0) + { + rc = RegOpenKeyExW(pState->aAltDeletes[i].hkeyClasses, L"AppID", 0 /*fOptions*/, pState->fSamDelete, &hkeyAppIds); + AssertLogRelMsgStmt(rc == ERROR_SUCCESS || rc == ERROR_FILE_NOT_FOUND, ("%u\n", rc), pState->rc = rc); + if (rc == ERROR_SUCCESS) + { + vbpsDeleteKeyRecursiveA(pState, hkeyAppIds, pszAppId, __LINE__); + vbpsCloseKey(pState, hkeyAppIds, __LINE__); + } + } + } + + if (pState->fUpdate) + { + rc = RegCreateKeyExW(pState->hkeyClassesRootDst, L"AppID", 0 /*Reserved*/, NULL /*pszClass*/, 0 /*fOptions*/, + pState->fSamBoth, NULL /*pSecAttr*/, &hkeyAppIds, NULL /*pdwDisposition*/); + if (rc == ERROR_ACCESS_DENIED) + return ERROR_SUCCESS; + } + else + { + rc = RegOpenKeyExW(pState->hkeyClassesRootDst, L"AppID", 0 /*fOptions*/, pState->fSamBoth, &hkeyAppIds); + if (rc == ERROR_FILE_NOT_FOUND || rc == ERROR_ACCESS_DENIED) + return ERROR_SUCCESS; + } + if (rc == ERROR_ACCESS_DENIED) + return pState->rc = rc; + AssertLogRelMsgReturn(rc == ERROR_SUCCESS, ("%u\n", rc), pState->rc = rc); + + if (pState->fDelete) + { + vbpsDeleteKeyRecursiveA(pState, hkeyAppIds, pszAppId, __LINE__); + vbpsDeleteKeyRecursiveA(pState, hkeyAppIds, pszModuleName, __LINE__); + } + + /* + * Register / update. + */ + if (pState->fUpdate) + { + HKEY hkey; + rc = vbpsCreateRegKeyA(pState, hkeyAppIds, pszAppId, &hkey, __LINE__); + if (rc == ERROR_SUCCESS) + { + vbpsSetRegValueAA(pState, hkey, NULL /*pszValueNm*/, pszDescription, __LINE__); + if (pszServiceName) + vbpsSetRegValueAA(pState, hkey, "LocalService", pszServiceName, __LINE__); + vbpsCloseKey(pState, hkey, __LINE__); + } + + rc = vbpsCreateRegKeyA(pState, hkeyAppIds, pszModuleName, &hkey, __LINE__); + if (rc == ERROR_SUCCESS) + { + vbpsSetRegValueAA(pState, hkey, NULL /*pszValueNm*/, "", __LINE__); + vbpsSetRegValueAA(pState, hkey, "AppID", pszAppId, __LINE__); + vbpsCloseKey(pState, hkey, __LINE__); + } + } + + vbpsCloseKey(pState, hkeyAppIds, __LINE__); + + return pState->rc; +} + + +/** + * Register an class name. + * + * @returns Windows error code (errors are rememberd in the state). + * @param pState The registry modifier state. + * @param pszClassName The name of the class. + * @param pszDescription The description string + * @param pClsId The UUID for the class. + * @param pszCurVerSuffIfRootName This is the current version suffix to + * append to @a pszClassName when + * registering the version idependent name. + */ +LSTATUS VbpsRegisterClassName(VBPSREGSTATE *pState, const char *pszClassName, const char *pszDescription, + const CLSID *pClsId, const char *pszCurVerSuffIfRootName) +{ + LSTATUS rc; + + /* + * Delete. + */ + if (pState->fDelete) + { + unsigned i = pState->cAltDeletes; + while (i-- > 0) + vbpsDeleteKeyRecursiveA(pState, pState->aAltDeletes[i].hkeyClasses, pszClassName, __LINE__); + vbpsDeleteKeyRecursiveA(pState, pState->hkeyClassesRootDst, pszClassName, __LINE__); + } + + /* + * Update. + */ + if (pState->fUpdate) + { + /* pszClassName/Default = description. */ + HKEY hkeyClass; + rc = vbpsCreateRegKeyWithDefaultValueAAEx(pState, pState->hkeyClassesRootDst, pszClassName, pszDescription, + &hkeyClass, __LINE__); + if (rc == ERROR_SUCCESS) + { + char szClsId[CURLY_UUID_STR_BUF_SIZE]; + + /* CLSID/Default = pClsId. */ + vbpsCreateRegKeyWithDefaultValueAA(pState, hkeyClass, "CLSID", vbpsFormatUuidInCurly(szClsId, pClsId), __LINE__); + + /* CurVer/Default = pszClassName+Suffix. */ + if (pszCurVerSuffIfRootName != NULL) + { + char szCurClassNameVer[128]; + rc = RTStrCopy(szCurClassNameVer, sizeof(szCurClassNameVer), pszClassName); + if (RT_SUCCESS(rc)) + rc = RTStrCat(szCurClassNameVer, sizeof(szCurClassNameVer), pszCurVerSuffIfRootName); + AssertStmt(RT_SUCCESS(rc), pState->rc = rc = ERROR_INVALID_DATA); + if (rc == ERROR_SUCCESS) + vbpsCreateRegKeyWithDefaultValueAA(pState, hkeyClass, "CurVer", szCurClassNameVer, __LINE__); + } + + vbpsCloseKey(pState, hkeyClass, __LINE__); + } + } + + return pState->rc; +} + + +/** + * Registers a class ID. + * + * @returns Windows error code (errors are rememberd in the state). + * @param pState The registry modifier state. + * @param pClsId The UUID for the class. + * @param pszDescription The description string. + * @param pszAppId The application ID. + * @param pszClassName The version idependent class name. + * @param pszCurClassNameVerSuffix The suffix to add to @a pszClassName for + * the current version. + * @param pTypeLibId The UUID for the typelib this class + * belongs to. + * @param pszServerType The server type (InprocServer32 or + * LocalServer32). + * @param pwszVBoxDir The VirtualBox install directory + * (unicode), trailing slash. + * @param pszServerSubPath What to append to @a pwszVBoxDir to + * construct the server module name. + * @param pszThreadingModel The threading model for inproc servers, + * NULL for local servers. + */ +LSTATUS VbpsRegisterClassId(VBPSREGSTATE *pState, const CLSID *pClsId, const char *pszDescription, const char *pszAppId, + const char *pszClassName, const char *pszCurClassNameVerSuffix, const CLSID *pTypeLibId, + const char *pszServerType, PCRTUTF16 pwszVBoxDir, const char *pszServerSubPath, + const char *pszThreadingModel) +{ + LSTATUS rc; + char szClsId[CURLY_UUID_STR_BUF_SIZE]; + RT_NOREF(pszAppId); + + Assert(!pszAppId || *pszAppId == '{'); + Assert((pwszVBoxDir == NULL && !pState->fUpdate) || pwszVBoxDir[RTUtf16Len(pwszVBoxDir) - 1] == '\\'); + + /* + * We need this, whatever we end up having to do. + */ + vbpsFormatUuidInCurly(szClsId, pClsId); + + /* + * Delete. + */ + if (pState->fDelete) + { + unsigned i = pState->cAltDeletes; + while (i-- > 0) + if (pState->aAltDeletes[i].hkeyClsid != NULL) + vbpsDeleteKeyRecursiveA(pState, pState->aAltDeletes[i].hkeyClsid, szClsId, __LINE__); + vbpsDeleteKeyRecursiveA(pState, pState->hkeyClsidRootDst, szClsId, __LINE__); + } + + /* + * Update. + */ + if (pState->fUpdate) + { + HKEY hkeyClass; + rc = vbpsCreateRegKeyWithDefaultValueAAEx(pState, pState->hkeyClsidRootDst, szClsId, pszDescription, + &hkeyClass, __LINE__); + if (rc == ERROR_SUCCESS) + { + bool const fIsLocalServer32 = strcmp(pszServerType, "LocalServer32") == 0; + HKEY hkeyServerType; + char szCurClassNameVer[128]; + + /* pszServerType/Default = module. */ + rc = vbpsCreateRegKeyA(pState, hkeyClass, pszServerType, &hkeyServerType, __LINE__); + if (rc == ERROR_SUCCESS) + { + RTUTF16 wszModule[MAX_PATH * 2]; + PRTUTF16 pwszCur = wszModule; + if (fIsLocalServer32) + *pwszCur++ = '"'; + + rc = RTUtf16Copy(pwszCur, MAX_PATH, pwszVBoxDir); AssertRC(rc); + pwszCur += RTUtf16Len(pwszCur); + rc = RTUtf16CopyAscii(pwszCur, MAX_PATH - 3, pszServerSubPath); AssertRC(rc); + pwszCur += RTUtf16Len(pwszCur); + + if (fIsLocalServer32) + *pwszCur++ = '"'; + *pwszCur++ = '\0'; /* included, so ++. */ + + vbpsSetRegValueWW(pState, hkeyServerType, NULL /*pszValueNm*/, wszModule, __LINE__); + + /* pszServerType/ThreadingModel = pszThreading Model. */ + if (pszThreadingModel) + vbpsSetRegValueAA(pState, hkeyServerType, "ThreadingModel", pszThreadingModel, __LINE__); + + vbpsCloseKey(pState, hkeyServerType, __LINE__); + } + + /* ProgId/Default = pszClassName + pszCurClassNameVerSuffix. */ + if (pszClassName) + { + rc = RTStrCopy(szCurClassNameVer, sizeof(szCurClassNameVer), pszClassName); + if (RT_SUCCESS(rc)) + rc = RTStrCat(szCurClassNameVer, sizeof(szCurClassNameVer), pszCurClassNameVerSuffix); + AssertStmt(RT_SUCCESS(rc), pState->rc = rc = ERROR_INVALID_DATA); + if (rc == ERROR_SUCCESS) + vbpsCreateRegKeyWithDefaultValueAA(pState, hkeyClass, "ProgId", szCurClassNameVer, __LINE__); + + /* VersionIndependentProgID/Default = pszClassName. */ + vbpsCreateRegKeyWithDefaultValueAA(pState, hkeyClass, "VersionIndependentProgID", pszClassName, __LINE__); + } + + /* TypeLib/Default = pTypeLibId. */ + if (pTypeLibId) + { + char szTypeLibId[CURLY_UUID_STR_BUF_SIZE]; + vbpsCreateRegKeyWithDefaultValueAA(pState, hkeyClass, "TypeLib", + vbpsFormatUuidInCurly(szTypeLibId, pTypeLibId), __LINE__); + } + + /* AppID = pszAppId */ + if (pszAppId && fIsLocalServer32) + vbpsSetRegValueAA(pState, hkeyClass, "AppID", pszAppId, __LINE__); + + vbpsCloseKey(pState, hkeyClass, __LINE__); + } + } + + return pState->rc; +} + + +/** + * Register modules and classes from the VirtualBox.xidl file. + * + * @returns COM status code. + * @param pState + * @param pwszVBoxDir The VirtualBox application directory. + * @param fIs32On64 Set if this is the 32-bit on 64-bit component. + * + * @todo convert to XSLT. + */ +void RegisterXidlModulesAndClassesGenerated(VBPSREGSTATE *pState, PCRTUTF16 pwszVBoxDir, bool fIs32On64) +{ + const char *pszAppId = "{819B4D85-9CEE-493C-B6FC-64FFE759B3C9}"; + const char *pszInprocDll = !fIs32On64 ? "VBoxC.dll" : "x86\\VBoxClient-x86.dll"; + const char *pszLocalServer = "VBoxSVC.exe"; +#ifdef VBOX_WITH_SDS + const char *pszSdsAppId = "{EC0E78E8-FA43-43E8-AC0A-02C784C4A4FA}"; + const char *pszSdsExe = "VBoxSDS.exe"; + const char *pszSdsServiceName = "VBoxSDS"; +#endif + + /* VBoxSVC */ + VbpsRegisterAppId(pState, pszLocalServer, pszAppId, "VirtualBox Application", NULL); + VbpsRegisterClassName(pState, "VirtualBox.VirtualBox.1", "VirtualBox Class", &CLSID_VirtualBox, NULL); + VbpsRegisterClassName(pState, "VirtualBox.VirtualBox", "VirtualBox Class", &CLSID_VirtualBox, ".1"); + VbpsRegisterClassId(pState, &CLSID_VirtualBox, "VirtualBox Class", pszAppId, "VirtualBox.VirtualBox", ".1", + &LIBID_VirtualBox, "LocalServer32", pwszVBoxDir, pszLocalServer, NULL /*N/A*/); + /* VBoxC */ + VbpsRegisterClassName(pState, "VirtualBox.Session.1", "Session Class", &CLSID_Session, NULL); + VbpsRegisterClassName(pState, "VirtualBox.Session", "Session Class", &CLSID_Session, ".1"); + VbpsRegisterClassId(pState, &CLSID_Session, "Session Class", pszAppId, "VirtualBox.Session", ".1", + &LIBID_VirtualBox, "InprocServer32", pwszVBoxDir, pszInprocDll, "Free"); + + VbpsRegisterClassName(pState, "VirtualBox.VirtualBoxClient.1", "VirtualBoxClient Class", &CLSID_VirtualBoxClient, NULL); + VbpsRegisterClassName(pState, "VirtualBox.VirtualBoxClient", "VirtualBoxClient Class", &CLSID_VirtualBoxClient, ".1"); + VbpsRegisterClassId(pState, &CLSID_VirtualBoxClient, "VirtualBoxClient Class", pszAppId, + "VirtualBox.VirtualBoxClient", ".1", + &LIBID_VirtualBox, "InprocServer32", pwszVBoxDir, pszInprocDll, "Free"); + +#ifdef VBOX_WITH_SDS + /* VBoxSDS */ + VbpsRegisterAppId(pState, pszSdsExe, pszSdsAppId, "VirtualBox System Service", pszSdsServiceName); + VbpsRegisterClassName(pState, "VirtualBox.VirtualBoxSDS.1", "VirtualBoxSDS Class", &CLSID_VirtualBoxSDS, NULL); + VbpsRegisterClassName(pState, "VirtualBox.VirtualBoxSDS", "VirtualBoxSDS Class", &CLSID_VirtualBoxSDS, ".1"); + VbpsRegisterClassId(pState, &CLSID_VirtualBoxSDS, "VirtualBoxSDS Class", pszSdsAppId, "VirtualBox.VirtualBoxSDS", ".1", + &LIBID_VirtualBox, "LocalServer32", pwszVBoxDir, pszSdsExe, NULL /*N/A*/); +#endif +} + + +/** + * Updates the VBox type lib registration. + * + * This is only used when updating COM registrations during com::Initialize. + * For normal registration and unregistrations we use the RegisterTypeLib and + * UnRegisterTypeLib APIs. + * + * @param pState The registry modifier state. + * @param pwszVBoxDir The VirtualBox install directory (unicode), + * trailing slash. + * @param fIs32On64 Set if we're registering the 32-bit proxy stub + * on a 64-bit system. + */ +static void vbpsUpdateTypeLibRegistration(VBPSREGSTATE *pState, PCRTUTF16 pwszVBoxDir, bool fIs32On64) +{ + const char * const pszTypeLibDll = VBPS_PROXY_STUB_FILE(fIs32On64); +#if ARCH_BITS == 32 && !defined(VBOX_IN_32_ON_64_MAIN_API) + const char * const pszWinXx = "win32"; +#else + const char * const pszWinXx = !fIs32On64 ? "win64" : "win32"; +#endif + const char * const pszDescription = "VirtualBox Type Library"; + + char szTypeLibId[CURLY_UUID_STR_BUF_SIZE]; + HKEY hkeyTypeLibs; + HKEY hkeyTypeLibId; + LSTATUS rc; + RT_NOREF(fIs32On64); + + Assert(pState->fUpdate && !pState->fDelete); + + /* + * Type library registration (w/o interfaces). + */ + + /* Open Classes/TypeLib/. */ + rc = vbpsCreateRegKeyA(pState, pState->hkeyClassesRootDst, "TypeLib", &hkeyTypeLibs, __LINE__); + if (rc != ERROR_SUCCESS) + return; + + /* Create TypeLib/{UUID}. */ + rc = vbpsCreateRegKeyA(pState, hkeyTypeLibs, vbpsFormatUuidInCurly(szTypeLibId, &LIBID_VirtualBox), &hkeyTypeLibId, __LINE__); + if (rc == ERROR_SUCCESS) + { + /* {UUID}/Major.Minor/Default = pszDescription. */ + HKEY hkeyMajMin; + char szMajMin[64]; + sprintf(szMajMin, "%u.%u", kTypeLibraryMajorVersion, kTypeLibraryMinorVersion); + rc = vbpsCreateRegKeyWithDefaultValueAAEx(pState, hkeyTypeLibId, szMajMin, pszDescription, &hkeyMajMin, __LINE__); + if (rc == ERROR_SUCCESS) + { + RTUTF16 wszBuf[MAX_PATH * 2]; + + /* {UUID}/Major.Minor/0. */ + HKEY hkey0; + rc = vbpsCreateRegKeyA(pState, hkeyMajMin, "0", &hkey0, __LINE__); + if (rc == ERROR_SUCCESS) + { + /* {UUID}/Major.Minor/0/winXX/Default = VBoxProxyStub. */ + rc = RTUtf16Copy(wszBuf, MAX_PATH, pwszVBoxDir); AssertRC(rc); + rc = RTUtf16CatAscii(wszBuf, MAX_PATH * 2, pszTypeLibDll); AssertRC(rc); + + vbpsCreateRegKeyWithDefaultValueAW(pState, hkey0, pszWinXx, wszBuf, __LINE__); + vbpsCloseKey(pState, hkey0, __LINE__); + } + + /* {UUID}/Major.Minor/FLAGS */ + vbpsCreateRegKeyWithDefaultValueAA(pState, hkeyMajMin, "FLAGS", "0", __LINE__); + + /* {UUID}/Major.Minor/HELPDIR */ + rc = RTUtf16Copy(wszBuf, MAX_PATH, pwszVBoxDir); AssertRC(rc); +#if 0 /* MSI: trailing slash; regsvr32/comregister: strip unnecessary trailing slash. Go with MSI to avoid user issues. */ + { + size_t off = RTUtf16Len(wszBuf); + while (off > 2 && wszBuf[off - 2] != ':' && RTPATH_IS_SLASH(wszBuf[off - 1])) + off--; + wszBuf[off] = '\0'; + } +#endif + vbpsCreateRegKeyWithDefaultValueAW(pState, hkeyMajMin, "HELPDIR", wszBuf, __LINE__); + + vbpsCloseKey(pState, hkeyMajMin, __LINE__); + } + vbpsCloseKey(pState, hkeyTypeLibId, __LINE__); + } + vbpsCloseKey(pState, hkeyTypeLibs, __LINE__); +} + + +/** + * Update the VBox proxy stub registration. + * + * This is only used when updating COM registrations during com::Initialize. + * For normal registration and unregistrations we use the NdrDllRegisterProxy + * and NdrDllUnregisterProxy. + * + * @param pState The registry modifier state. + * @param pwszVBoxDir The VirtualBox install directory (unicode), + * trailing slash. + * @param fIs32On64 Set if we're registering the 32-bit proxy stub + * on a 64-bit system. + */ +static void vbpsUpdateProxyStubRegistration(VBPSREGSTATE *pState, PCRTUTF16 pwszVBoxDir, bool fIs32On64) +{ + /* + * Register the proxy stub factory class ID. + * It's simple compared to the VBox classes, thus all the NULL parameters. + */ + const char *pszPsDll = VBPS_PROXY_STUB_FILE(fIs32On64); + RT_NOREF(fIs32On64); + Assert(pState->fUpdate && !pState->fDelete); + VbpsRegisterClassId(pState, &g_ProxyClsId, "PSFactoryBuffer", NULL /*pszAppId*/, + NULL /*pszClassName*/, NULL /*pszCurClassNameVerSuffix*/, NULL /*pTypeLibId*/, + "InprocServer32", pwszVBoxDir, pszPsDll, "Both"); +} + + +/** + * Updates the VBox interface registrations. + * + * This is only used when updating COM registrations during com::Initialize. + * For normal registration and unregistrations we use the NdrDllRegisterProxy + * and NdrDllUnregisterProxy. + * + * @param pState The registry modifier state. + */ +static void vbpsUpdateInterfaceRegistrations(VBPSREGSTATE *pState) +{ + const ProxyFileInfo **ppProxyFile = &g_apProxyFiles[0]; + const ProxyFileInfo *pProxyFile; + LSTATUS rc; + char szProxyClsId[CURLY_UUID_STR_BUF_SIZE]; + char szTypeLibId[CURLY_UUID_STR_BUF_SIZE]; + char szTypeLibVersion[64]; + + vbpsFormatUuidInCurly(szProxyClsId, &g_ProxyClsId); + vbpsFormatUuidInCurly(szTypeLibId, &LIBID_VirtualBox); + sprintf(szTypeLibVersion, "%u.%u", kTypeLibraryMajorVersion, kTypeLibraryMinorVersion); + + Assert(pState->fUpdate && !pState->fDelete); + rc = vbpsRegOpenInterfaceKeys(pState); + if (rc != ERROR_SUCCESS) + return; + + /* + * We walk the proxy file list (even if we only have one). + */ + while ((pProxyFile = *ppProxyFile++) != NULL) + { + const PCInterfaceStubVtblList * const papStubVtbls = pProxyFile->pStubVtblList; + const char * const *papszNames = pProxyFile->pNamesArray; + unsigned iIf = pProxyFile->TableSize; + AssertStmt(iIf < 1024, iIf = 0); + Assert(pProxyFile->TableVersion == 2); + + /* + * Walk the interfaces in that file, picking data from the various tables. + */ + while (iIf-- > 0) + { + char szIfId[CURLY_UUID_STR_BUF_SIZE]; + const char * const pszIfNm = papszNames[iIf]; + size_t const cchIfNm = RT_VALID_PTR(pszIfNm) ? strlen(pszIfNm) : 0; + char szMethods[32]; + uint32_t const cMethods = papStubVtbls[iIf]->header.DispatchTableCount; + HKEY hkeyIfId; + + AssertReturnVoidStmt(cchIfNm >= 3 && cchIfNm <= 72, pState->rc = ERROR_INVALID_DATA); + + AssertReturnVoidStmt(cMethods >= 3 && cMethods < 1024, pState->rc = ERROR_INVALID_DATA); + sprintf(szMethods, "%u", cMethods); + + rc = vbpsCreateRegKeyWithDefaultValueAAEx(pState, pState->hkeyInterfaceRootDst, + vbpsFormatUuidInCurly(szIfId, papStubVtbls[iIf]->header.piid), + pszIfNm, &hkeyIfId, __LINE__); + if (rc == ERROR_SUCCESS) + { + HKEY hkeyTypeLib; + vbpsCreateRegKeyWithDefaultValueAA(pState, hkeyIfId, "ProxyStubClsid32", szProxyClsId, __LINE__); + vbpsCreateRegKeyWithDefaultValueAA(pState, hkeyIfId, "NumMethods", szMethods, __LINE__); + + /* The MSI seems to still be putting TypeLib keys here. So, let's do that too. */ + rc = vbpsCreateRegKeyWithDefaultValueAAEx(pState, hkeyIfId, "TypeLib", szTypeLibId, &hkeyTypeLib, __LINE__); + if (rc == ERROR_SUCCESS) + { + vbpsSetRegValueAA(pState, hkeyTypeLib, "Version", szTypeLibVersion, __LINE__); + vbpsCloseKey(pState, hkeyTypeLib, __LINE__); + } + + vbpsCloseKey(pState, hkeyIfId, __LINE__); + } + } + } +} + + +static bool vbpsIsUpToDate(VBPSREGSTATE *pState) +{ + /** @todo read some registry key and */ + NOREF(pState); + return false; +} + +static bool vbpsMarkUpToDate(VBPSREGSTATE *pState) +{ + /** @todo write the key vbpsIsUpToDate uses, if pState indicates success. */ + NOREF(pState); + return false; +} + + + +/** + * Strips the stub dll name and any x86 subdir off the full DLL path to get a + * path to the VirtualBox application directory. + * + * @param pwszDllPath The path to strip, returns will end with a slash. + */ +static void vbpsDllPathToVBoxDir(PRTUTF16 pwszDllPath) +{ + RTUTF16 wc; + size_t off = RTUtf16Len(pwszDllPath); + while ( off > 0 + && ( (wc = pwszDllPath[off - 1]) >= 127U + || !RTPATH_IS_SEP((unsigned char)wc))) + off--; + +#ifdef VBOX_IN_32_ON_64_MAIN_API + /* + * The -x86 variant is in a x86 subdirectory, drop it. + */ + while ( off > 0 + && ( (wc = pwszDllPath[off - 1]) < 127U + && RTPATH_IS_SEP((unsigned char)wc))) + off--; + while ( off > 0 + && ( (wc = pwszDllPath[off - 1]) >= 127U + || !RTPATH_IS_SEP((unsigned char)wc))) + off--; +#endif + pwszDllPath[off] = '\0'; +} + + +/** + * Wrapper around RegisterXidlModulesAndClassesGenerated for the convenience of + * the standard registration entry points. + * + * @returns COM status code. + * @param pwszVBoxDir The VirtualBox install directory (unicode), + * trailing slash. + * @param fDelete Whether to delete registration keys and values. + * @param fUpdate Whether to update registration keys and values. + */ +HRESULT RegisterXidlModulesAndClasses(PRTUTF16 pwszVBoxDir, bool fDelete, bool fUpdate) +{ +#ifdef VBOX_IN_32_ON_64_MAIN_API + bool const fIs32On64 = true; +#else + bool const fIs32On64 = false; +#endif + VBPSREGSTATE State; + LSTATUS rc; + + /* + * Do registration for the current execution mode of the DLL. + */ + rc = vbpsRegInit(&State, HKEY_CLASSES_ROOT, NULL /* Alt: HKEY_LOCAL_MACHINE, "Software\\Classes", */, fDelete, fUpdate, 0); + if (rc == ERROR_SUCCESS) + { + if (!fUpdate) + { + /* When only unregistering, really purge everything twice or trice. :-) */ + vbpsRegAddAltDelete(&State, HKEY_LOCAL_MACHINE, "Software\\Classes"); + vbpsRegAddAltDelete(&State, HKEY_CURRENT_USER, "Software\\Classes"); + vbpsRegAddAltDelete(&State, HKEY_CLASSES_ROOT, NULL); + } + + RegisterXidlModulesAndClassesGenerated(&State, pwszVBoxDir, fIs32On64); + rc = State.rc; + } + vbpsRegTerm(&State); + + /* + * Translate error code? Return. + */ + if (rc == ERROR_SUCCESS) + return S_OK; + return E_FAIL; +} + + +/** + * Checks if the string matches any of our type library versions. + * + * @returns true on match, false on mismatch. + * @param pwszTypeLibVersion The type library version string. + */ +DECLINLINE(bool) vbpsIsTypeLibVersionToRemove(PCRTUTF16 pwszTypeLibVersion) +{ + AssertCompile(RT_ELEMENTS(g_apwszTypelibVersions) == 2); + + /* ASSUMES: 1.x version strings and that the input buffer is at least 3 wchars long. */ + if ( g_apwszTypelibVersions[0][3] == pwszTypeLibVersion[3] + && RTUtf16Cmp(g_apwszTypelibVersions[0], pwszTypeLibVersion) == 0) + return true; + if ( g_apwszTypelibVersions[1][3] == pwszTypeLibVersion[3] + && RTUtf16Cmp(g_apwszTypelibVersions[1], pwszTypeLibVersion) == 0) + return true; + + return false; +} + + +/** + * Quick check whether the given string looks like a UUID in braces. + * + * This does not check the whole string, just do a quick sweep. + * + * @returns true if possible UUID, false if definitely not. + * @param pwszUuid Alleged UUID in braces. + */ +DECLINLINE(bool) vbpsIsUuidInBracesQuickW(PCRTUTF16 pwszUuid) +{ + return pwszUuid[ 0] == '{' + && pwszUuid[ 9] == '-' + && pwszUuid[14] == '-' + && pwszUuid[19] == '-' + && pwszUuid[24] == '-' + && pwszUuid[37] == '}' + && pwszUuid[38] == '\0' + && RT_C_IS_XDIGIT(pwszUuid[1]); +} + + +/** + * Compares two UUIDs (in braces). + * + * @returns true on match, false if no match. + * @param pwszUuid1 The first UUID. + * @param pwszUuid2 The second UUID. + */ +static bool vbpsCompareUuidW(PCRTUTF16 pwszUuid1, PCRTUTF16 pwszUuid2) +{ +#define COMPARE_EXACT_RET(a_wch1, a_wch2) \ + if ((a_wch1) == (a_wch2)) { } else return false + +#define COMPARE_XDIGITS_RET(a_wch1, a_wch2) \ + if ((a_wch1) == (a_wch2)) { } \ + else if (RT_C_TO_UPPER(a_wch1) != RT_C_TO_UPPER(a_wch2) || (a_wch1) >= 127U || (a_wch2) >= 127U) \ + return false + COMPARE_EXACT_RET( pwszUuid1[ 0], pwszUuid2[ 0]); /* { */ + COMPARE_XDIGITS_RET(pwszUuid1[ 1], pwszUuid2[ 1]); /* 5 */ + COMPARE_XDIGITS_RET(pwszUuid1[ 2], pwszUuid2[ 2]); /* e */ + COMPARE_XDIGITS_RET(pwszUuid1[ 3], pwszUuid2[ 3]); /* 5 */ + COMPARE_XDIGITS_RET(pwszUuid1[ 4], pwszUuid2[ 4]); /* e */ + COMPARE_XDIGITS_RET(pwszUuid1[ 5], pwszUuid2[ 5]); /* 3 */ + COMPARE_XDIGITS_RET(pwszUuid1[ 6], pwszUuid2[ 6]); /* 6 */ + COMPARE_XDIGITS_RET(pwszUuid1[ 7], pwszUuid2[ 7]); /* 4 */ + COMPARE_XDIGITS_RET(pwszUuid1[ 8], pwszUuid2[ 8]); /* 0 */ + COMPARE_EXACT_RET( pwszUuid1[ 9], pwszUuid2[ 9]); /* - */ + COMPARE_XDIGITS_RET(pwszUuid1[10], pwszUuid2[10]); /* 7 */ + COMPARE_XDIGITS_RET(pwszUuid1[11], pwszUuid2[11]); /* 4 */ + COMPARE_XDIGITS_RET(pwszUuid1[12], pwszUuid2[12]); /* f */ + COMPARE_XDIGITS_RET(pwszUuid1[13], pwszUuid2[13]); /* 3 */ + COMPARE_EXACT_RET( pwszUuid1[14], pwszUuid2[14]); /* - */ + COMPARE_XDIGITS_RET(pwszUuid1[15], pwszUuid2[15]); /* 4 */ + COMPARE_XDIGITS_RET(pwszUuid1[16], pwszUuid2[16]); /* 6 */ + COMPARE_XDIGITS_RET(pwszUuid1[17], pwszUuid2[17]); /* 8 */ + COMPARE_XDIGITS_RET(pwszUuid1[18], pwszUuid2[18]); /* 9 */ + COMPARE_EXACT_RET( pwszUuid1[19], pwszUuid2[19]); /* - */ + COMPARE_XDIGITS_RET(pwszUuid1[20], pwszUuid2[20]); /* 9 */ + COMPARE_XDIGITS_RET(pwszUuid1[21], pwszUuid2[21]); /* 7 */ + COMPARE_XDIGITS_RET(pwszUuid1[22], pwszUuid2[22]); /* 9 */ + COMPARE_XDIGITS_RET(pwszUuid1[23], pwszUuid2[23]); /* f */ + COMPARE_EXACT_RET( pwszUuid1[24], pwszUuid2[24]); /* - */ + COMPARE_XDIGITS_RET(pwszUuid1[25], pwszUuid2[25]); /* 6 */ + COMPARE_XDIGITS_RET(pwszUuid1[26], pwszUuid2[26]); /* b */ + COMPARE_XDIGITS_RET(pwszUuid1[27], pwszUuid2[27]); /* 1 */ + COMPARE_XDIGITS_RET(pwszUuid1[28], pwszUuid2[28]); /* b */ + COMPARE_XDIGITS_RET(pwszUuid1[29], pwszUuid2[29]); /* 8 */ + COMPARE_XDIGITS_RET(pwszUuid1[30], pwszUuid2[30]); /* d */ + COMPARE_XDIGITS_RET(pwszUuid1[31], pwszUuid2[31]); /* 7 */ + COMPARE_XDIGITS_RET(pwszUuid1[32], pwszUuid2[32]); /* 6 */ + COMPARE_XDIGITS_RET(pwszUuid1[33], pwszUuid2[33]); /* 0 */ + COMPARE_XDIGITS_RET(pwszUuid1[34], pwszUuid2[34]); /* 9 */ + COMPARE_XDIGITS_RET(pwszUuid1[35], pwszUuid2[35]); /* a */ + COMPARE_XDIGITS_RET(pwszUuid1[36], pwszUuid2[36]); /* 5 */ + COMPARE_EXACT_RET( pwszUuid1[37], pwszUuid2[37]); /* } */ + COMPARE_EXACT_RET( pwszUuid1[38], pwszUuid2[38]); /* \0 */ +#undef COMPARE_EXACT_RET +#undef COMPARE_XDIGITS_RET + return true; +} + + +/** + * Checks if the type library ID is one of the ones we wish to clean up. + * + * @returns true if it should be cleaned up, false if not. + * @param pwszTypeLibId The type library ID as a bracketed string. + */ +DECLINLINE(bool) vbpsIsTypeLibIdToRemove(PRTUTF16 pwszTypeLibId) +{ + AssertCompile(RT_ELEMENTS(g_apwszTypeLibIds) == 2); +#ifdef VBOX_STRICT + static bool s_fDoneStrict = false; + if (s_fDoneStrict) { } + else + { + Assert(RT_ELEMENTS(g_apwszTypeLibIds) == 2); + Assert(g_apwszTypeLibIds[0][0] == '{'); + Assert(g_apwszTypeLibIds[1][0] == '{'); + Assert(RT_C_IS_XDIGIT(g_apwszTypeLibIds[0][1])); + Assert(RT_C_IS_XDIGIT(g_apwszTypeLibIds[1][1])); + Assert(RT_C_IS_UPPER(g_apwszTypeLibIds[0][1]) || RT_C_IS_DIGIT(g_apwszTypeLibIds[0][1])); + Assert(RT_C_IS_UPPER(g_apwszTypeLibIds[1][1]) || RT_C_IS_DIGIT(g_apwszTypeLibIds[1][1])); + s_fDoneStrict = true; + } +#endif + + /* + * Rolled out matching with inlined check of the opening braces + * and first two digits. + * + * ASSUMES input buffer is at least 3 wchars big and uppercased UUID in + * our matching array. + */ + if (pwszTypeLibId[0] == '{') + { + RTUTF16 const wcFirstDigit = RT_C_TO_UPPER(pwszTypeLibId[1]); + RTUTF16 const wcSecondDigit = RT_C_TO_UPPER(pwszTypeLibId[2]); + PCRTUTF16 pwsz2 = g_apwszTypeLibIds[0]; + if ( wcFirstDigit == pwsz2[1] + && wcSecondDigit == pwsz2[2] + && vbpsCompareUuidW(pwszTypeLibId, pwsz2)) + return true; + pwsz2 = g_apwszTypeLibIds[1]; + if ( wcFirstDigit == pwsz2[1] + && wcSecondDigit == pwsz2[2] + && vbpsCompareUuidW(pwszTypeLibId, pwsz2)) + return true; + } + return false; +} + + +/** + * Checks if the proxy stub class ID is one of the ones we wish to clean up. + * + * @returns true if it should be cleaned up, false if not. + * @param pwszProxyStubId The proxy stub class ID. + */ +DECLINLINE(bool) vbpsIsProxyStubClsIdToRemove(PRTUTF16 pwszProxyStubId) +{ + AssertCompile(RT_ELEMENTS(g_apwszProxyStubClsIds) == 2); +#ifdef VBOX_STRICT + static bool s_fDoneStrict = false; + if (s_fDoneStrict) { } + else + { + Assert(RT_ELEMENTS(g_apwszProxyStubClsIds) == 2); + Assert(g_apwszProxyStubClsIds[0][0] == '{'); + Assert(g_apwszProxyStubClsIds[1][0] == '{'); + Assert(RT_C_IS_XDIGIT(g_apwszProxyStubClsIds[0][1])); + Assert(RT_C_IS_XDIGIT(g_apwszProxyStubClsIds[1][1])); + Assert(RT_C_IS_UPPER(g_apwszProxyStubClsIds[0][1]) || RT_C_IS_DIGIT(g_apwszProxyStubClsIds[0][1])); + Assert(RT_C_IS_UPPER(g_apwszProxyStubClsIds[1][1]) || RT_C_IS_DIGIT(g_apwszProxyStubClsIds[1][1])); + s_fDoneStrict = true; + } +#endif + + /* + * Rolled out matching with inlined check of the opening braces + * and first two digits. + * + * ASSUMES input buffer is at least 3 wchars big and uppercased UUID in + * our matching array. + */ + if (pwszProxyStubId[0] == '{') + { + RTUTF16 const wcFirstDigit = RT_C_TO_UPPER(pwszProxyStubId[1]); + RTUTF16 const wcSecondDigit = RT_C_TO_UPPER(pwszProxyStubId[2]); + PCRTUTF16 pwsz2 = g_apwszProxyStubClsIds[0]; + if ( wcFirstDigit == pwsz2[1] + && wcSecondDigit == pwsz2[2] + && vbpsCompareUuidW(pwszProxyStubId, pwsz2)) + return true; + pwsz2 = g_apwszProxyStubClsIds[1]; + if ( wcFirstDigit == pwsz2[1] + && wcSecondDigit == pwsz2[2] + && vbpsCompareUuidW(pwszProxyStubId, pwsz2)) + return true; + } + return false; +} + + +/** + * Hack to clean out the interfaces belonging to obsolete typelibs on + * development boxes and such likes. + */ +static void vbpsRemoveOldInterfaces(VBPSREGSTATE *pState) +{ + unsigned iAlt = pState->cAltDeletes; + while (iAlt-- > 0) + { + /* + * Open the interface root key. Not using the vbpsRegOpenInterfaceKeys feature + * here in case it messes things up by keeping the special HKEY_CLASSES_ROOT key + * open with possibly pending deletes in parent views or other weird stuff. + */ + HKEY hkeyInterfaces; + LRESULT rc = RegOpenKeyExW(pState->aAltDeletes[iAlt].hkeyClasses, L"Interface", + 0 /*fOptions*/, pState->fSamDelete, &hkeyInterfaces); + if (rc == ERROR_SUCCESS) + { + /* + * This is kind of expensive, but we have to check all registered interfaces. + * Only use wide APIs to avoid wasting time on string conversion. + */ + DWORD idxKey; + for (idxKey = 0;; idxKey++) + { + RTUTF16 wszCurNm[128 + 48]; + DWORD cwcCurNm = 128; + rc = RegEnumKeyExW(hkeyInterfaces, idxKey, wszCurNm, &cwcCurNm, + NULL /*pdwReserved*/, NULL /*pwszClass*/, NULL /*pcwcClass*/, NULL /*pLastWriteTime*/); + if (rc == ERROR_SUCCESS) + { + /* + * We match the interface by type library ID or proxy stub class ID. + * + * We have to check the proxy ID last, as it is almost always there + * and we can safely skip it if there is a mismatching type lib + * associated with the interface. + */ + static RTUTF16 const s_wszTypeLib[] = L"\\TypeLib"; + bool fDeleteMe = false; + HKEY hkeySub; + RTUTF16 wszValue[128]; + DWORD cbValue; + DWORD dwType; + + /* Skip this entry if it doesn't look like a braced UUID. */ + wszCurNm[cwcCurNm] = '\0'; /* paranoia */ + if (vbpsIsUuidInBracesQuickW(wszCurNm)) { } + else continue; + + /* Try the TypeLib sub-key. */ + memcpy(&wszCurNm[cwcCurNm], s_wszTypeLib, sizeof(s_wszTypeLib)); + rc = RegOpenKeyExW(hkeyInterfaces, wszCurNm, 0 /*fOptions*/, KEY_QUERY_VALUE, &hkeySub); + if (rc == ERROR_SUCCESS) + { + cbValue = sizeof(wszValue) - sizeof(RTUTF16); + rc = RegQueryValueExW(hkeySub, NULL /*pszValueNm*/, NULL /*pdwReserved*/, + &dwType, (PBYTE)&wszValue[0], &cbValue); + if (rc != ERROR_SUCCESS || dwType != REG_SZ) + cbValue = 0; + wszValue[cbValue / sizeof(RTUTF16)] = '\0'; + + if ( rc == ERROR_SUCCESS + && vbpsIsTypeLibIdToRemove(wszValue)) + { + /* Check the TypeLib/Version value to make sure. */ + cbValue = sizeof(wszValue) - sizeof(RTUTF16); + rc = RegQueryValueExW(hkeySub, L"Version", 0 /*pdwReserved*/, &dwType, (PBYTE)&wszValue[0], &cbValue); + if (rc != ERROR_SUCCESS) + cbValue = 0; + wszValue[cbValue] = '\0'; + + if ( rc == ERROR_SUCCESS + && vbpsIsTypeLibVersionToRemove(wszValue)) + fDeleteMe = true; + } + vbpsCloseKey(pState, hkeySub, __LINE__); + } + else if (rc == ERROR_FILE_NOT_FOUND) + { + /* No TypeLib, try the ProxyStubClsid32 sub-key next. */ + static RTUTF16 const s_wszProxyStubClsid32[] = L"\\ProxyStubClsid32"; + memcpy(&wszCurNm[cwcCurNm], s_wszProxyStubClsid32, sizeof(s_wszProxyStubClsid32)); + rc = RegOpenKeyExW(hkeyInterfaces, wszCurNm, 0 /*fOptions*/, KEY_QUERY_VALUE, &hkeySub); + if (rc == ERROR_SUCCESS) + { + cbValue = sizeof(wszValue) - sizeof(RTUTF16); + rc = RegQueryValueExW(hkeySub, NULL /*pszValueNm*/, NULL /*pdwReserved*/, + &dwType, (PBYTE)&wszValue[0], &cbValue); + if (rc != ERROR_SUCCESS || dwType != REG_SZ) + cbValue = 0; + wszValue[cbValue / sizeof(RTUTF16)] = '\0'; + + if ( rc == ERROR_SUCCESS + && vbpsIsProxyStubClsIdToRemove(wszValue)) + fDeleteMe = true; + + vbpsCloseKey(pState, hkeySub, __LINE__); + } + } + + if (fDeleteMe) + { + /* + * Ok, it's an orphaned VirtualBox interface. Delete it. + */ + wszCurNm[cwcCurNm] = '\0'; + vbpsDeleteKeyRecursiveW(pState, hkeyInterfaces, wszCurNm, __LINE__); + } + } + else + { + Assert(rc == ERROR_NO_MORE_ITEMS); + break; + } + } + + vbpsCloseKey(pState, hkeyInterfaces, __LINE__); + } + } +} + + +/** + * Hack to clean out the class IDs belonging to obsolete typelibs on development + * boxes and such likes. + */ +static void vbpsRemoveOldClassIDs(VBPSREGSTATE *pState) +{ + unsigned iAlt = pState->cAltDeletes; + while (iAlt-- > 0) + { + /* + * Open the CLSID key if it exists. + * We don't use the hKeyClsid member for the same paranoid reasons as + * already stated in vbpsRemoveOldInterfaces. + */ + HKEY hkeyClsIds; + LRESULT rc; + rc = RegOpenKeyExW(pState->aAltDeletes[iAlt].hkeyClasses, L"CLSID", 0 /*fOptions*/, pState->fSamDelete, &hkeyClsIds); + if (rc == ERROR_SUCCESS) + { + /* + * This is kind of expensive, but we have to check all registered interfaces. + * Only use wide APIs to avoid wasting time on string conversion. + */ + DWORD idxKey; + for (idxKey = 0;; idxKey++) + { + RTUTF16 wszCurNm[128 + 48]; + DWORD cwcCurNm = 128; + rc = RegEnumKeyExW(hkeyClsIds, idxKey, wszCurNm, &cwcCurNm, + NULL /*pdwReserved*/, NULL /*pwszClass*/, NULL /*pcwcClass*/, NULL /*pLastWriteTime*/); + if (rc == ERROR_SUCCESS) + { + /* + * Match both the type library ID and the program ID. + */ + static RTUTF16 const s_wszTypeLib[] = L"\\TypeLib"; + HKEY hkeySub; + RTUTF16 wszValue[128]; + DWORD cbValue; + DWORD dwType; + + + /* Skip this entry if it doesn't look like a braced UUID. (Microsoft + has one two malformed ones plus a hack.) */ + wszCurNm[cwcCurNm] = '\0'; /* paranoia */ + if (vbpsIsUuidInBracesQuickW(wszCurNm)) { } + else continue; + + /* The TypeLib sub-key. */ + memcpy(&wszCurNm[cwcCurNm], s_wszTypeLib, sizeof(s_wszTypeLib)); + rc = RegOpenKeyExW(hkeyClsIds, wszCurNm, 0 /*fOptions*/, KEY_QUERY_VALUE, &hkeySub); + if (rc == ERROR_SUCCESS) + { + bool fDeleteMe = false; + + cbValue = sizeof(wszValue) - sizeof(RTUTF16); + rc = RegQueryValueExW(hkeySub, NULL /*pszValueNm*/, NULL /*pdwReserved*/, + &dwType, (PBYTE)&wszValue[0], &cbValue); + if (rc != ERROR_SUCCESS || dwType != REG_SZ) + cbValue = 0; + wszValue[cbValue / sizeof(RTUTF16)] = '\0'; + + if ( rc == ERROR_SUCCESS + && vbpsIsTypeLibIdToRemove(wszValue)) + fDeleteMe = true; + + vbpsCloseKey(pState, hkeySub, __LINE__); + + if (fDeleteMe) + { + /* The ProgId sub-key. */ + static RTUTF16 const s_wszProgId[] = L"\\ProgId"; + memcpy(&wszCurNm[cwcCurNm], s_wszProgId, sizeof(s_wszProgId)); + rc = RegOpenKeyExW(hkeyClsIds, wszCurNm, 0 /*fOptions*/, KEY_QUERY_VALUE, &hkeySub); + if (rc == ERROR_SUCCESS) + { + static RTUTF16 const s_wszProgIdPrefix[] = L"VirtualBox."; + + cbValue = sizeof(wszValue) - sizeof(RTUTF16); + rc = RegQueryValueExW(hkeySub, NULL /*pszValueNm*/, NULL /*pdwReserved*/, + &dwType, (PBYTE)&wszValue[0], &cbValue); + if (rc != ERROR_SUCCESS || dwType != REG_SZ) + cbValue = 0; + wszValue[cbValue / sizeof(RTUTF16)] = '\0'; + + if ( cbValue < sizeof(s_wszProgIdPrefix) + || memcmp(wszValue, s_wszProgIdPrefix, sizeof(s_wszProgIdPrefix) - sizeof(RTUTF16)) != 0) + fDeleteMe = false; + + vbpsCloseKey(pState, hkeySub, __LINE__); + } + else + AssertStmt(rc == ERROR_FILE_NOT_FOUND, fDeleteMe = false); + + if (fDeleteMe) + { + /* + * Ok, it's an orphaned VirtualBox interface. Delete it. + */ + wszCurNm[cwcCurNm] = '\0'; + vbpsDeleteKeyRecursiveW(pState, hkeyClsIds, wszCurNm, __LINE__); + } + } + } + else + Assert(rc == ERROR_FILE_NOT_FOUND); + } + else + { + Assert(rc == ERROR_NO_MORE_ITEMS); + break; + } + } + + vbpsCloseKey(pState, hkeyClsIds, __LINE__); + } + else + Assert(rc == ERROR_FILE_NOT_FOUND); + } +} + + +/** + * Hack to clean obsolete typelibs on development boxes and such. + */ +static void vbpsRemoveOldTypeLibs(VBPSREGSTATE *pState) +{ + unsigned iAlt = pState->cAltDeletes; + while (iAlt-- > 0) + { + /* + * Open the TypeLib key, if it exists. + */ + HKEY hkeyTypeLibs; + LSTATUS rc; + rc = RegOpenKeyExW(pState->aAltDeletes[iAlt].hkeyClasses, L"TypeLib", 0 /*fOptions*/, pState->fSamDelete, &hkeyTypeLibs); + if (rc == ERROR_SUCCESS) + { + /* + * Look for our type library IDs. + */ + unsigned iTlb = RT_ELEMENTS(g_apwszTypeLibIds); + while (iTlb-- > 0) + { + HKEY hkeyTypeLibId; + rc = RegOpenKeyExW(hkeyTypeLibs, g_apwszTypeLibIds[iTlb], 0 /*fOptions*/, pState->fSamDelete, &hkeyTypeLibId); + if (rc == ERROR_SUCCESS) + { + unsigned iVer = RT_ELEMENTS(g_apwszTypelibVersions); + while (iVer-- > 0) + { + HKEY hkeyVer; + rc = RegOpenKeyExW(hkeyTypeLibId, g_apwszTypelibVersions[iVer], 0, KEY_READ, &hkeyVer); + if (rc == ERROR_SUCCESS) + { + char szValue[128]; + DWORD cbValue = sizeof(szValue) - 1; + rc = RegQueryValueExA(hkeyVer, NULL, NULL, NULL, (PBYTE)&szValue[0], &cbValue); + vbpsCloseKey(pState, hkeyVer, __LINE__); + if (rc == ERROR_SUCCESS) + { + szValue[cbValue] = '\0'; + if (!strcmp(szValue, "VirtualBox Type Library")) + { + /* + * Delete the type library version. + * We do not delete the whole type library ID, just this version of it. + */ + vbpsDeleteKeyRecursiveW(pState, hkeyTypeLibId, g_apwszTypelibVersions[iVer], __LINE__); + } + } + } + } + vbpsCloseKey(pState, hkeyTypeLibId, __LINE__); + + /* + * The type library ID key should be empty now, so we can try remove it (non-recursively). + */ + rc = RegDeleteKeyW(hkeyTypeLibs, g_apwszTypeLibIds[iTlb]); + Assert(rc == ERROR_SUCCESS); + } + } + } + else + Assert(rc == ERROR_FILE_NOT_FOUND); + } +} + + +/** + * Hack to clean out obsolete typelibs on development boxes and such. + */ +static void vbpsRemoveOldMessSub(REGSAM fSamWow) +{ + /* + * Note! The worker procedures does not use the default destination, + * because it's much much simpler to enumerate alternative locations. + */ + VBPSREGSTATE State; + LRESULT rc = vbpsRegInit(&State, HKEY_CLASSES_ROOT, NULL, true /*fDelete*/, false /*fUpdate*/, fSamWow); + if (rc == ERROR_SUCCESS) + { + vbpsRegAddAltDelete(&State, HKEY_CURRENT_USER, "Software\\Classes"); + vbpsRegAddAltDelete(&State, HKEY_LOCAL_MACHINE, "Software\\Classes"); + vbpsRegAddAltDelete(&State, HKEY_CLASSES_ROOT, NULL); + + vbpsRemoveOldInterfaces(&State); + vbpsRemoveOldClassIDs(&State); + vbpsRemoveOldTypeLibs(&State); + } + vbpsRegTerm(&State); +} + + +/** + * Hack to clean out obsolete typelibs on development boxes and such. + */ +static void removeOldMess(void) +{ + vbpsRemoveOldMessSub(0 /*fSamWow*/); +#if ARCH_BITS == 64 || defined(VBOX_IN_32_ON_64_MAIN_API) + vbpsRemoveOldMessSub(KEY_WOW64_32KEY); +#endif +} + + + +/** + * Register the interfaces proxied by this DLL, and to avoid duplication and + * minimize work the VBox type library, classes and servers are also registered. + * + * This is normally only used by developers via comregister.cmd and the heat.exe + * tool during MSI creation. The only situation where users may end up here is + * if they're playing around or we recommend it as a solution to COM problems. + * So, no problem if this approach is less gentle, though we leave the cleaning + * up of orphaned interfaces to DllUnregisterServer. + * + * @returns COM status code. + */ +HRESULT STDAPICALLTYPE DllRegisterServer(void) +{ + HRESULT hrc; + + /* + * Register the type library first. + */ + ITypeLib *pITypeLib; + WCHAR wszDllName[MAX_PATH]; + DWORD cwcRet = GetModuleFileNameW(g_hDllSelf, wszDllName, RT_ELEMENTS(wszDllName)); + AssertReturn(cwcRet > 0 && cwcRet < RT_ELEMENTS(wszDllName), CO_E_PATHTOOLONG); + + hrc = LoadTypeLib(wszDllName, &pITypeLib); + AssertMsgReturn(SUCCEEDED(hrc), ("%Rhrc\n", hrc), hrc); + hrc = RegisterTypeLib(pITypeLib, wszDllName, NULL /*pszHelpDir*/); + pITypeLib->lpVtbl->Release(pITypeLib); + AssertMsgReturn(SUCCEEDED(hrc), ("%Rhrc\n", hrc), hrc); + + /* + * Register proxy stub. + */ + hrc = NdrDllRegisterProxy(g_hDllSelf, &g_apProxyFiles[0], &g_ProxyClsId); /* see DLLREGISTRY_ROUTINES in RpcProxy.h */ + AssertMsgReturn(SUCCEEDED(hrc), ("%Rhrc\n", hrc), hrc); + + /* + * Register the VBox modules and classes. + */ + vbpsDllPathToVBoxDir(wszDllName); + hrc = RegisterXidlModulesAndClasses(wszDllName, true /*fDelete*/, true /*fUpdate*/); + AssertMsgReturn(SUCCEEDED(hrc), ("%Rhrc\n", hrc), hrc); + + return S_OK; +} + + +/** + * Reverse of DllRegisterServer. + * + * This is normally only used by developers via comregister.cmd. Users may be + * asked to perform it in order to fix some COM issue. So, it's OK if we spend + * some extra time and clean up orphaned interfaces, because developer boxes + * will end up with a bunch of those as interface UUIDs changes. + * + * @returns COM status code. + */ +HRESULT STDAPICALLTYPE DllUnregisterServer(void) +{ + HRESULT hrc = S_OK; + HRESULT hrc2; + + /* + * Unregister the type library. + * + * We ignore TYPE_E_REGISTRYACCESS as that is what is returned if the + * type lib hasn't been registered (W10). + */ + hrc2 = UnRegisterTypeLib(&LIBID_VirtualBox, kTypeLibraryMajorVersion, kTypeLibraryMinorVersion, + 0 /*LCid*/, RT_CONCAT(SYS_WIN, ARCH_BITS)); + AssertMsgStmt(SUCCEEDED(hrc2) || hrc2 == TYPE_E_REGISTRYACCESS, ("%Rhrc\n", hrc2), if (SUCCEEDED(hrc)) hrc = hrc2); + + /* + * Unregister the proxy stub. + * + * We ignore ERROR_FILE_NOT_FOUND as that is returned if not registered (W10). + */ + hrc2 = NdrDllUnregisterProxy(g_hDllSelf, &g_apProxyFiles[0], &g_ProxyClsId); /* see DLLREGISTRY_ROUTINES in RpcProxy.h */ + AssertMsgStmt( SUCCEEDED(hrc2) + || hrc2 == MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, ERROR_FILE_NOT_FOUND) + || hrc2 == REGDB_E_INVALIDVALUE, + ("%Rhrc\n", hrc2), if (SUCCEEDED(hrc)) hrc = hrc2); + + /* + * Register the VBox modules and classes. + */ + hrc2 = RegisterXidlModulesAndClasses(NULL, true /*fDelete*/, false /*fUpdate*/); + AssertMsgStmt(SUCCEEDED(hrc2), ("%Rhrc\n", hrc2), if (SUCCEEDED(hrc)) hrc = hrc2); + + /* + * Purge old mess. + */ + removeOldMess(); + + return hrc; +} + + +#ifdef VBOX_WITH_SDS +/** + * Update a SCM service. + * + * @param pState The state. + * @param pwszVBoxDir The VirtualBox install directory (unicode), + * trailing slash. + * @param pwszModule The service module. + * @param pwszServiceName The service name. + * @param pwszDisplayName The service display name. + * @param pwszDescription The service description. + */ +static void vbpsUpdateWindowsService(VBPSREGSTATE *pState, const WCHAR *pwszVBoxDir, const WCHAR *pwszModule, + const WCHAR *pwszServiceName, const WCHAR *pwszDisplayName, const WCHAR *pwszDescription) +{ + SC_HANDLE hSCM; + + /* Configuration options that are currently standard. */ + uint32_t const uServiceType = SERVICE_WIN32_OWN_PROCESS; + uint32_t const uStartType = SERVICE_DEMAND_START; + uint32_t const uErrorControl = SERVICE_ERROR_NORMAL; + WCHAR const * const pwszServiceStartName = L"LocalSystem"; + static WCHAR const wszzDependencies[] = L"RPCSS\0"; + + /* + * Make double quoted executable file path. ASSUMES pwszVBoxDir ends with a slash! + */ + WCHAR wszFilePath[MAX_PATH + 2]; + int rc = RTUtf16CopyAscii(wszFilePath, RT_ELEMENTS(wszFilePath), "\""); + if (RT_SUCCESS(rc)) + rc = RTUtf16Cat(wszFilePath, RT_ELEMENTS(wszFilePath), pwszVBoxDir); + if (RT_SUCCESS(rc)) + rc = RTUtf16Cat(wszFilePath, RT_ELEMENTS(wszFilePath), pwszModule); + if (RT_SUCCESS(rc)) + rc = RTUtf16CatAscii(wszFilePath, RT_ELEMENTS(wszFilePath), "\""); + AssertLogRelRCReturnVoid(rc); + + /* + * Open the service manager for the purpose of checking the configuration. + */ + hSCM = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT); + if (hSCM != NULL) + { + union + { + QUERY_SERVICE_CONFIGW Config; + SERVICE_STATUS Status; + SERVICE_DESCRIPTIONW Desc; + uint8_t abPadding[sizeof(QUERY_SERVICE_CONFIGW) + 5 * _1K]; + } uBuf; + SC_HANDLE hService; + bool fCreateIt = pState->fUpdate; + bool fDeleteIt = true; + + /* + * Step #1: Open the service and validate the configuration. + */ + if (pState->fUpdate) + { + hService = OpenServiceW(hSCM, pwszServiceName, SERVICE_QUERY_CONFIG); + if (hService != NULL) + { + DWORD cbNeeded = 0; + if (QueryServiceConfigW(hService, &uBuf.Config, sizeof(uBuf), &cbNeeded)) + { + if (uBuf.Config.dwErrorControl) + { + uint32_t cErrors = 0; + if (uBuf.Config.dwServiceType != uServiceType) + { + LogRel(("update service '%ls': dwServiceType %u, expected %u\n", + pwszServiceName, uBuf.Config.dwServiceType, uServiceType)); + cErrors++; + } + if (uBuf.Config.dwStartType != uStartType) + { + LogRel(("update service '%ls': dwStartType %u, expected %u\n", + pwszServiceName, uBuf.Config.dwStartType, uStartType)); + cErrors++; + } + if (uBuf.Config.dwErrorControl != uErrorControl) + { + LogRel(("update service '%ls': dwErrorControl %u, expected %u\n", + pwszServiceName, uBuf.Config.dwErrorControl, uErrorControl)); + cErrors++; + } + if (RTUtf16ICmp(uBuf.Config.lpBinaryPathName, wszFilePath) != 0) + { + LogRel(("update service '%ls': lpBinaryPathName '%ls', expected '%ls'\n", + pwszServiceName, uBuf.Config.lpBinaryPathName, wszFilePath)); + cErrors++; + } + if ( uBuf.Config.lpServiceStartName != NULL + && *uBuf.Config.lpServiceStartName != L'\0' + && RTUtf16ICmp(uBuf.Config.lpServiceStartName, pwszServiceStartName) != 0) + { + LogRel(("update service '%ls': lpServiceStartName '%ls', expected '%ls'\n", + pwszServiceName, uBuf.Config.lpBinaryPathName, pwszServiceStartName)); + cErrors++; + } + + fDeleteIt = fCreateIt = cErrors > 0; + } + } + else + AssertLogRelMsgFailed(("QueryServiceConfigW returned %u (cbNeeded=%u vs %zu)\n", + GetLastError(), cbNeeded, sizeof(uBuf))); + } + else + { + DWORD dwErr = GetLastError(); + fDeleteIt = dwErr != ERROR_SERVICE_DOES_NOT_EXIST; + AssertLogRelMsg(dwErr == ERROR_SERVICE_DOES_NOT_EXIST, ("OpenServiceW('%ls') -> %u\n", pwszServiceName, dwErr)); + } + CloseServiceHandle(hService); + } + + /* + * Step #2: Stop and delete the service if needed. + * We can do this without reopening the service manager. + */ + if (fDeleteIt) + { + hService = OpenServiceW(hSCM, pwszServiceName, SERVICE_STOP | DELETE); + if (hService) + { + BOOL fRet; + DWORD dwErr; + RT_ZERO(uBuf.Status); + SetLastError(ERROR_SERVICE_NOT_ACTIVE); + fRet = ControlService(hService, SERVICE_CONTROL_STOP, &uBuf.Status); + dwErr = GetLastError(); + if ( fRet + || dwErr == ERROR_SERVICE_NOT_ACTIVE + || ( dwErr == ERROR_SERVICE_CANNOT_ACCEPT_CTRL + && uBuf.Status.dwCurrentState == SERVICE_STOP_PENDING) ) + { + if (DeleteService(hService)) + LogRel(("update service '%ls': deleted\n", pwszServiceName)); + else + AssertLogRelMsgFailed(("Failed to not delete service %ls: %u\n", pwszServiceName, GetLastError())); + } + else + AssertMsg(dwErr == ERROR_ACCESS_DENIED, + ("Failed to stop service %ls: %u (state=%u)\n", pwszServiceName, dwErr, uBuf.Status.dwCurrentState)); + CloseServiceHandle(hService); + } + else + { + pState->rc = GetLastError(); + LogRel(("Failed to not open service %ls for stop+delete: %u\n", pwszServiceName, pState->rc)); + hService = OpenServiceW(hSCM, pwszServiceName, SERVICE_CHANGE_CONFIG); + } + CloseServiceHandle(hService); + } + + CloseServiceHandle(hSCM); + + /* + * Step #3: Create the service (if requested). + * Need to have the SC_MANAGER_CREATE_SERVICE access right for this. + */ + if (fCreateIt) + { + Assert(pState->fUpdate); + hSCM = OpenSCManagerW(NULL, NULL, SC_MANAGER_CREATE_SERVICE); + if (hSCM) + { + hService = CreateServiceW(hSCM, + pwszServiceName, + pwszDisplayName, + SERVICE_CHANGE_CONFIG /* dwDesiredAccess */, + uServiceType, + uStartType, + uErrorControl, + wszFilePath, + NULL /* pwszLoadOrderGroup */, + NULL /* pdwTagId */, + wszzDependencies, + NULL /* pwszServiceStartName */, + NULL /* pwszPassword */); + if (hService != NULL) + { + uBuf.Desc.lpDescription = (WCHAR *)pwszDescription; + if (ChangeServiceConfig2W(hService, SERVICE_CONFIG_DESCRIPTION, &uBuf.Desc)) + LogRel(("update service '%ls': created\n", pwszServiceName)); + else + AssertMsgFailed(("Failed to set service description for %ls: %u\n", pwszServiceName, GetLastError())); + CloseServiceHandle(hService); + } + else + { + pState->rc = GetLastError(); + AssertMsgFailed(("Failed to create service '%ls': %u\n", pwszServiceName, pState->rc)); + } + CloseServiceHandle(hSCM); + } + else + { + pState->rc = GetLastError(); + LogRel(("Failed to open service manager with create service access: %u\n", pState->rc)); + } + } + } + else + AssertLogRelMsgFailed(("OpenSCManagerW failed: %u\n", GetLastError())); +} +#endif /* VBOX_WITH_SDS */ + + + +/** + * Gently update the COM registrations for VirtualBox. + * + * API that com::Initialize (VBoxCOM/initterm.cpp) calls the first time COM is + * initialized in a process. ASSUMES that the caller has initialized IPRT. + * + * @returns Windows error code. + */ +DECLEXPORT(uint32_t) VbpsUpdateRegistrations(void) +{ + LSTATUS rc; + VBPSREGSTATE State; +#ifdef VBOX_IN_32_ON_64_MAIN_API + bool const fIs32On64 = true; +#else + bool const fIs32On64 = false; +#endif + + /** @todo Should probably skip this when VBoxSVC is already running... Use + * some mutex or something for checking. */ + + /* + * Find the VirtualBox application directory first. + */ + WCHAR wszVBoxDir[MAX_PATH]; + DWORD cwcRet = GetModuleFileNameW(g_hDllSelf, wszVBoxDir, RT_ELEMENTS(wszVBoxDir)); + AssertReturn(cwcRet > 0 && cwcRet < RT_ELEMENTS(wszVBoxDir), ERROR_BUFFER_OVERFLOW); + vbpsDllPathToVBoxDir(wszVBoxDir); + + /* + * Update registry entries for the current CPU bitness. + */ + rc = vbpsRegInit(&State, HKEY_CLASSES_ROOT, NULL, false /*fDelete*/, true /*fUpdate*/, 0); + if (rc == ERROR_SUCCESS && !vbpsIsUpToDate(&State)) + { + +#ifdef VBOX_WITH_SDS + vbpsUpdateWindowsService(&State, wszVBoxDir, L"VBoxSDS.exe", L"VBoxSDS", + L"VirtualBox system service", L"Used as a COM server for VirtualBox API."); +#endif + vbpsUpdateTypeLibRegistration(&State, wszVBoxDir, fIs32On64); + vbpsUpdateProxyStubRegistration(&State, wszVBoxDir, fIs32On64); + vbpsUpdateInterfaceRegistrations(&State); + RegisterXidlModulesAndClassesGenerated(&State, wszVBoxDir, fIs32On64); + vbpsMarkUpToDate(&State); + rc = State.rc; + } + vbpsRegTerm(&State); + + +#if (ARCH_BITS == 64 && defined(VBOX_WITH_32_ON_64_MAIN_API)) /*|| defined(VBOX_IN_32_ON_64_MAIN_API) ??*/ + /* + * Update registry entries for the other CPU bitness. + */ + if (rc == ERROR_SUCCESS) + { + rc = vbpsRegInit(&State, HKEY_CLASSES_ROOT, NULL, false /*fDelete*/, true /*fUpdate*/, + !fIs32On64 ? KEY_WOW64_32KEY : KEY_WOW64_64KEY); + if (rc == ERROR_SUCCESS && !vbpsIsUpToDate(&State)) + { + vbpsUpdateTypeLibRegistration(&State, wszVBoxDir, !fIs32On64); + vbpsUpdateProxyStubRegistration(&State, wszVBoxDir, !fIs32On64); + vbpsUpdateInterfaceRegistrations(&State); + RegisterXidlModulesAndClassesGenerated(&State, wszVBoxDir, !fIs32On64); + vbpsMarkUpToDate(&State); + rc = State.rc; + } + vbpsRegTerm(&State); + } +#endif + + return VINF_SUCCESS; +} diff --git a/src/VBox/Main/src-all/win/VBoxProxyStub.def b/src/VBox/Main/src-all/win/VBoxProxyStub.def new file mode 100644 index 00000000..f21ac77d --- /dev/null +++ b/src/VBox/Main/src-all/win/VBoxProxyStub.def @@ -0,0 +1,36 @@ +; $Id: VBoxProxyStub.def $ +;; @file +; VBoxProxyStub DLL Definition File. +; + +; +; Copyright (C) 2006-2022 Oracle and/or its affiliates. +; +; This file is part of VirtualBox base platform packages, as +; available from https://www.virtualbox.org. +; +; This program is free software; you can redistribute it and/or +; modify it under the terms of the GNU General Public License +; as published by the Free Software Foundation, in version 3 of the +; License. +; +; This program is distributed in the hope that it will be useful, but +; WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +; General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program; if not, see <https://www.gnu.org/licenses>. +; +; SPDX-License-Identifier: GPL-3.0-only +; + +LIBRARY VBoxProxyStub.dll + +EXPORTS + DllGetClassObject PRIVATE + DllCanUnloadNow PRIVATE + DllRegisterServer PRIVATE + DllUnregisterServer PRIVATE + GetProxyDllInfo PRIVATE + diff --git a/src/VBox/Main/src-all/win/VBoxProxyStub.rc b/src/VBox/Main/src-all/win/VBoxProxyStub.rc new file mode 100644 index 00000000..81e0d5da --- /dev/null +++ b/src/VBox/Main/src-all/win/VBoxProxyStub.rc @@ -0,0 +1,34 @@ +/* $Id: VBoxProxyStub.rc $ */ +/** @file + * VBoxProxyStub - Resource file containing version info, icon and typelib. + */ + +/* + * Copyright (C) 2006-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#define IN_FILE_DESCRIPTION "VirtualBox COM Proxy Stub and Typelib" +#define IN_FILE_BASENAME "VBoxProxyStub" + +#include "../../../Artwork/win/TemplateDll.rc" + +1 TYPELIB "VirtualBox.tlb" + diff --git a/src/VBox/Main/src-all/win/VBoxProxyStubLegacy.rc b/src/VBox/Main/src-all/win/VBoxProxyStubLegacy.rc new file mode 100644 index 00000000..34815f80 --- /dev/null +++ b/src/VBox/Main/src-all/win/VBoxProxyStubLegacy.rc @@ -0,0 +1,34 @@ +/* $Id: VBoxProxyStubLegacy.rc $ */ +/** @file + * VBoxProxyStub - Resource file containing version info, icon and typelib. + */ + +/* + * Copyright (C) 2006-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#define IN_FILE_DESCRIPTION "VirtualBox COM Proxy Stub and Type Library (pre Windows 7)" +#define IN_FILE_BASENAME "VBoxProxyStubLegacy" + +#include "../../../Artwork/win/TemplateDll.rc" + +1 TYPELIB "VirtualBox.tlb" + diff --git a/src/VBox/Main/src-all/win/VirtualBox_rgs.xsl b/src/VBox/Main/src-all/win/VirtualBox_rgs.xsl new file mode 100644 index 00000000..96b95f41 --- /dev/null +++ b/src/VBox/Main/src-all/win/VirtualBox_rgs.xsl @@ -0,0 +1,196 @@ +<?xml version="1.0"?> + +<!-- + * A template to generate a RGS resource script that contains + * registry definitions necessary to properly register + * VirtualBox Main API COM components. +--> +<!-- + Copyright (C) 2007-2022 Oracle and/or its affiliates. + + This file is part of VirtualBox base platform packages, as + available from https://www.virtualbox.org. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation, in version 3 of the + License. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see <https://www.gnu.org/licenses>. + + SPDX-License-Identifier: GPL-3.0-only +--> + +<xsl:stylesheet version="1.0" + xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> + +<xsl:output method="text"/> + +<xsl:strip-space elements="*"/> + +<!-- +// parameters +///////////////////////////////////////////////////////////////////////////// +--> + +<!-- Name of the application to generate the RGS script for --> +<xsl:param name="Application"/> +<!-- Name of the module to generate the RGS script for --> +<xsl:param name="Module"/> + + +<!-- +// templates +///////////////////////////////////////////////////////////////////////////// +--> + +<!-- + * header +--> +<xsl:template match="/idl"> + <xsl:apply-templates/> +</xsl:template> + + +<!-- + * libraries +--> +<xsl:template match="library"> + <xsl:apply-templates/> +</xsl:template> + + +<!-- + * applications +--> +<xsl:template match="application"> + <xsl:if test="@name=$Application"> + <xsl:variable name="context" select="//module[@name=$Module]/@context"/> +<xsl:text>HKCR +{ + NoRemove AppID + { + ForceRemove {</xsl:text><xsl:value-of select="@uuid"/>} = s '<xsl:value-of select="@name"/><xsl:text> </xsl:text> + <xsl:choose> + <xsl:when test="$context='LocalService'"> + <xsl:text>Service</xsl:text> + </xsl:when> + <xsl:otherwise> + <xsl:text>Application</xsl:text> + </xsl:otherwise> + </xsl:choose> + <xsl:text>' +</xsl:text> + <xsl:if test="$context='LocalService'"> + <xsl:text> { + val LocalService = s '</xsl:text><xsl:value-of select="$Module"/><xsl:text>' + } +</xsl:text> + </xsl:if> + <xsl:text> '</xsl:text><xsl:value-of select="$Module"/> + <xsl:choose> + <xsl:when test="$context='InprocServer'"> + <xsl:text>.dll</xsl:text> + </xsl:when> + <xsl:otherwise> + <xsl:text>.exe</xsl:text> + </xsl:otherwise> + </xsl:choose> + <xsl:text>' + { + val AppID = s '{</xsl:text><xsl:value-of select="//library/application[@name=$Application]/@uuid"/><xsl:text>}' + } + } + +</xsl:text> + <xsl:apply-templates select="module[@name=$Module]/class"/> +<xsl:text>} +</xsl:text> + </xsl:if> +</xsl:template> + + +<!-- + * classes +--> +<xsl:template match="library//module/class"> + <xsl:variable name="cname" select="concat(//library/application/@name,'.',@name)"/> + <xsl:variable name="desc" select="concat(@name,' Class')"/> + <xsl:text> </xsl:text> + <xsl:value-of select="concat($cname,'.1')"/> = s '<xsl:value-of select="$desc"/>' + { + CLSID = s '{<xsl:value-of select="@uuid"/>}' + } + <xsl:value-of select="$cname"/> = s '<xsl:value-of select="$desc"/>' + { + CLSID = s '{<xsl:value-of select="@uuid"/>}' + CurVer = s '<xsl:value-of select="concat($cname,'.1')"/>' + } + NoRemove CLSID + { + ForceRemove {<xsl:value-of select="@uuid"/>} = s '<xsl:value-of select="$desc"/>' + { + val AppID = s '{<xsl:value-of select="//library/application[@name=$Application]/@uuid"/><xsl:text>}' +</xsl:text> + <xsl:if test="../@context!='LocalService'"> + <xsl:text> ProgID = s '</xsl:text><xsl:value-of select="concat($cname,'.1')"/><xsl:text>' + VersionIndependentProgID = s '</xsl:text><xsl:value-of select="$cname"/><xsl:text>' + </xsl:text> + <xsl:choose> + <xsl:when test="../@context='InprocServer'">InprocServer32</xsl:when> + <xsl:when test="../@context='LocalServer'">LocalServer32</xsl:when> + <xsl:otherwise> + <xsl:message terminate="yes"> + <xsl:value-of select="concat(../../@name,'::',../@name,': ')"/> + <xsl:text>module context </xsl:text> + <xsl:value-of select="concat('"',../@context,'"')"/> + <xsl:text> is invalid!</xsl:text> + </xsl:message> + </xsl:otherwise> + </xsl:choose><xsl:text> = s '%MODULE%' +</xsl:text> + <xsl:if test="../@context='InprocServer'"> + <xsl:variable name="tmodel" select="(./@threadingModel | ../@threadingModel)[last()]"/><xsl:text> { + val ThreadingModel = s '</xsl:text> + <xsl:choose> + <xsl:when test="$tmodel='Apartment'">Apartment</xsl:when> + <xsl:when test="$tmodel='Free'">Free</xsl:when> + <xsl:when test="$tmodel='Both'">Both</xsl:when> + <xsl:when test="$tmodel='Neutral'">Neutral</xsl:when> + <xsl:when test="$tmodel='Single'">Single</xsl:when> + <xsl:when test="$tmodel='Rental'">Rental</xsl:when> + <xsl:otherwise> + <xsl:message terminate="yes"> + <xsl:value-of select="concat(../../@name,'::',@name,': ')"/> + <xsl:text>class (or module) threading model </xsl:text> + <xsl:value-of select="concat('"',$tmodel,'"')"/> + <xsl:text> is invalid!</xsl:text> + </xsl:message> + </xsl:otherwise> + </xsl:choose><xsl:text>' + } +</xsl:text> + </xsl:if> + <xsl:text> TypeLib = s '{</xsl:text><xsl:value-of select="//library/@uuid"/><xsl:text>}' +</xsl:text> + </xsl:if> + <xsl:text> } + } +</xsl:text> +</xsl:template> + + +<!-- + * eat everything else not explicitly matched +--> +<xsl:template match="*"> +</xsl:template> + + +</xsl:stylesheet> diff --git a/src/VBox/Main/src-all/win/comregister.cmd b/src/VBox/Main/src-all/win/comregister.cmd new file mode 100644 index 00000000..17340f77 --- /dev/null +++ b/src/VBox/Main/src-all/win/comregister.cmd @@ -0,0 +1,212 @@ +@echo off
+REM $Id: comregister.cmd $
+REM
+REM Script to register the VirtualBox COM classes
+REM (both inproc and out-of-process)
+REM
+
+REM
+REM Copyright (C) 2006-2022 Oracle and/or its affiliates.
+REM
+REM This file is part of VirtualBox base platform packages, as
+REM available from https://www.virtualbox.org.
+REM
+REM This program is free software; you can redistribute it and/or
+REM modify it under the terms of the GNU General Public License
+REM as published by the Free Software Foundation, in version 3 of the
+REM License.
+REM
+REM This program is distributed in the hope that it will be useful, but
+REM WITHOUT ANY WARRANTY; without even the implied warranty of
+REM MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+REM General Public License for more details.
+REM
+REM You should have received a copy of the GNU General Public License
+REM along with this program; if not, see <https://www.gnu.org/licenses>.
+REM
+REM SPDX-License-Identifier: GPL-3.0-only
+REM
+
+setlocal
+
+REM Check if the current user is an administrator. Otherwise
+REM all the COM registration will fail silently.
+NET FILE 1>NUL 2>NUL & IF ERRORLEVEL 1 (ECHO Must be run as Administrator. Exiting.) & GOTO end
+
+REM
+REM Figure out where the script lives first, so that we can invoke the
+REM correct VBoxSVC and register the right VBoxC.dll.
+REM
+
+REM Determine the current directory.
+set _SCRIPT_CURDIR=%CD%
+for /f "tokens=*" %%d in ('cd') do set _SCRIPT_CURDIR=%%d
+
+REM Determine a correct self - by %0.
+set _SCRIPT_SELF=%0
+if exist "%_SCRIPT_SELF%" goto found_self
+set _SCRIPT_SELF=%_SCRIPT_SELF%.cmd
+if exist "%_SCRIPT_SELF%" goto found_self
+
+REM Determine a correct self - by current working directory.
+set _SCRIPT_SELF=%_SCRIPT_CURDIR%\comregister.cmd
+if exist "%_SCRIPT_SELF%" goto found_self
+
+REM Determine a correct self - by the PATH
+REM This is very verbose because nested for loops didn't work out.
+for /f "tokens=1 delims=;" %%d in ("%PATH%") do set _SCRIPT_SELF=%%d\comregister.cmd
+if exist "%_SCRIPT_SELF%" goto found_self
+for /f "tokens=2 delims=;" %%d in ("%PATH%") do set _SCRIPT_SELF=%%d\comregister.cmd
+if exist "%_SCRIPT_SELF%" goto found_self
+for /f "tokens=3 delims=;" %%d in ("%PATH%") do set _SCRIPT_SELF=%%d\comregister.cmd
+if exist "%_SCRIPT_SELF%" goto found_self
+for /f "tokens=4 delims=;" %%d in ("%PATH%") do set _SCRIPT_SELF=%%d\comregister.cmd
+if exist "%_SCRIPT_SELF%" goto found_self
+for /f "tokens=5 delims=;" %%d in ("%PATH%") do set _SCRIPT_SELF=%%d\comregister.cmd
+if exist "%_SCRIPT_SELF%" goto found_self
+for /f "tokens=6 delims=;" %%d in ("%PATH%") do set _SCRIPT_SELF=%%d\comregister.cmd
+if exist "%_SCRIPT_SELF%" goto found_self
+for /f "tokens=7 delims=;" %%d in ("%PATH%") do set _SCRIPT_SELF=%%d\comregister.cmd
+if exist "%_SCRIPT_SELF%" goto found_self
+for /f "tokens=8 delims=;" %%d in ("%PATH%") do set _SCRIPT_SELF=%%d\comregister.cmd
+if exist "%_SCRIPT_SELF%" goto found_self
+for /f "tokens=9 delims=;" %%d in ("%PATH%") do set _SCRIPT_SELF=%%d\comregister.cmd
+if exist "%_SCRIPT_SELF%" goto found_self
+for /f "tokens=10 delims=;" %%d in ("%PATH%") do set _SCRIPT_SELF=%%d\comregister.cmd
+if exist "%_SCRIPT_SELF%" goto found_self
+for /f "tokens=11 delims=;" %%d in ("%PATH%") do set _SCRIPT_SELF=%%d\comregister.cmd
+if exist "%_SCRIPT_SELF%" goto found_self
+for /f "tokens=12 delims=;" %%d in ("%PATH%") do set _SCRIPT_SELF=%%d\comregister.cmd
+if exist "%_SCRIPT_SELF%" goto found_self
+for /f "tokens=13 delims=;" %%d in ("%PATH%") do set _SCRIPT_SELF=%%d\comregister.cmd
+if exist "%_SCRIPT_SELF%" goto found_self
+for /f "tokens=14 delims=;" %%d in ("%PATH%") do set _SCRIPT_SELF=%%d\comregister.cmd
+if exist "%_SCRIPT_SELF%" goto found_self
+for /f "tokens=15 delims=;" %%d in ("%PATH%") do set _SCRIPT_SELF=%%d\comregister.cmd
+if exist "%_SCRIPT_SELF%" goto found_self
+for /f "tokens=16 delims=;" %%d in ("%PATH%") do set _SCRIPT_SELF=%%d\comregister.cmd
+if exist "%_SCRIPT_SELF%" goto found_self
+for /f "tokens=17 delims=;" %%d in ("%PATH%") do set _SCRIPT_SELF=%%d\comregister.cmd
+if exist "%_SCRIPT_SELF%" goto found_self
+for /f "tokens=18 delims=;" %%d in ("%PATH%") do set _SCRIPT_SELF=%%d\comregister.cmd
+if exist "%_SCRIPT_SELF%" goto found_self
+for /f "tokens=19 delims=;" %%d in ("%PATH%") do set _SCRIPT_SELF=%%d\comregister.cmd
+if exist "%_SCRIPT_SELF%" goto found_self
+for /f "tokens=20 delims=;" %%d in ("%PATH%") do set _SCRIPT_SELF=%%d\comregister.cmd
+if exist "%_SCRIPT_SELF%" goto found_self
+echo Warning: Not able to determin the comregister.cmd location.
+set _VBOX_DIR=
+goto register
+
+:found_self
+set _VBOX_DIR=
+cd "%_SCRIPT_SELF%\.."
+for /f "tokens=*" %%d in ('cd') do set _VBOX_DIR=%%d\
+cd "%_SCRIPT_CURDIR%"
+
+REM
+REM Check for 64-bitness.
+REM
+set fIs64BitWindows=0
+if not "%ProgramW6432%x" == "x" set fIs64BitWindows=1
+if exist "%windir\syswow64\kernel32.dll" set fIs64BitWindows=1
+
+REM
+REM Figure out the Windows version as the proxy stub requires 6.0 or later (at least for 64-bit).
+REM
+set WinVer=Version 4.0.1381
+set WinVerMajor=4
+set WinVerMinor=0
+set WinVerBuild=1381
+for /f "tokens=2 delims=[]" %%a in ('ver') do set WinVer=%%a
+for /f "tokens=2,3,4 delims=. " %%a in ("%WinVer%") do (
+ set WinVerMajor=%%a
+ set WinVerMinor=%%b
+ set WinVerBuild=%%c
+)
+REM echo WinVerMajor=%WinVerMajor% WinVerMinor=%WinVerMinor% WinVerBuild=%WinVerBuild% WinVer=%WinVer%
+
+REM
+REM Parse arguments.
+REM
+set fNoProxy=0
+set fUninstallOnly=0
+
+:arg_loop
+if "%1x" == "x" goto arg_done
+
+if "%1" == "-u" goto arg_uninstall
+if "%1" == "--uninstall" goto arg_uninstall
+if "%1" == "--proxy" goto arg_proxy
+if "%1" == "--no-proxy" goto arg_no_proxy
+echo syntax error: Unknown option %1
+echo usage: comregister.cmd [-u,--uninstall] [--no-proxy] [--proxy]
+goto end
+
+:arg_uninstall
+set fUninstallOnly=1
+goto arg_next
+
+:arg_proxy
+set fNoProxy=0
+goto arg_next
+
+:arg_no_proxy
+set fNoProxy=1
+goto arg_next
+
+:arg_next
+shift
+goto arg_loop
+:arg_done
+
+REM
+REM Do the registrations.
+REM
+@if %fIs64BitWindows% == 1 goto register_amd64
+
+:register_x86
+@echo on
+"%_VBOX_DIR%VBoxSVC.exe" /UnregServer
+regsvr32 /s /u "%_VBOX_DIR%VBoxC.dll"
+%windir%\system32\regsvr32 /s /u "%_VBOX_DIR%VBoxProxyStub.dll"
+@if %fUninstallOnly% == 1 goto end
+"%_VBOX_DIR%VBoxSVC.exe" /RegServer
+"%_VBOX_DIR%VBoxSDS.exe" /RegService
+regsvr32 /s "%_VBOX_DIR%VBoxC.dll"
+@if %fNoProxy% == 1 goto end
+if exist "%_VBOX_DIR%VBoxProxyStub.dll" %windir%\system32\regsvr32 /s "%_VBOX_DIR%VBoxProxyStub.dll"
+@echo off
+goto end
+
+REM Unregister all first, then register them. The order matters here.
+:register_amd64
+if "%WinVerMajor%" == "5" goto register_amd64_legacy
+if not "%WinVerMajor%" == "6" goto register_amd64_not_legacy
+if not "%WinVerMinor%" == "0" goto register_amd64_not_legacy
+:register_amd64_legacy
+set s64BitProxyStub=VBoxProxyStubLegacy.dll
+goto register_amd64_begin
+:register_amd64_not_legacy
+set s64BitProxyStub=VBoxProxyStub.dll
+:register_amd64_begin
+echo s64BitProxyStub=%s64BitProxyStub%
+@echo on
+"%_VBOX_DIR%VBoxSVC.exe" /UnregServer
+"%_VBOX_DIR%VBoxSDS.exe" /UnregService
+%windir%\system32\regsvr32 /s /u "%_VBOX_DIR%VBoxC.dll"
+%windir%\syswow64\regsvr32 /s /u "%_VBOX_DIR%x86\VBoxClient-x86.dll"
+%windir%\system32\regsvr32 /s /u "%_VBOX_DIR%%s64BitProxyStub%"
+%windir%\syswow64\regsvr32 /s /u "%_VBOX_DIR%x86\VBoxProxyStub-x86.dll"
+if %fUninstallOnly% == 1 goto end
+"%_VBOX_DIR%VBoxSVC.exe" /RegServer
+"%_VBOX_DIR%VBoxSDS.exe" /RegService
+%windir%\system32\regsvr32 /s "%_VBOX_DIR%VBoxC.dll"
+%windir%\syswow64\regsvr32 /s "%_VBOX_DIR%x86\VBoxClient-x86.dll"
+if %fNoProxy% == 1 goto end
+if exist "%_VBOX_DIR%%s64BitProxyStub%" %windir%\system32\regsvr32 /s "%_VBOX_DIR%%s64BitProxyStub%"
+if exist "%_VBOX_DIR%x86\VBoxProxyStub-x86.dll" %windir%\syswow64\regsvr32 /s "%_VBOX_DIR%x86\VBoxProxyStub-x86.dll"
+@echo off
+
+:end
+@endlocal
|