VBoxDrvInst.cpp revision 4b012a4981369812b1becbbb47e82add99e47090
/* $Id$ */
/** @file
* VBoxDrvInst - Driver and service installation helper for Windows guests.
*/
/*
* Copyright (C) 2011-2013 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#ifndef UNICODE
# define UNICODE
#endif
#include <Windows.h>
#include <setupapi.h>
#include <stdio.h>
#include <tchar.h>
/*******************************************************************************
* Defines *
*******************************************************************************/
/* Exit codes */
#define EXIT_OK (0)
#define EXIT_REBOOT (1)
#define EXIT_FAIL (2)
#define EXIT_USAGE (3)
/* Prototypes */
typedef struct {
typedef const PINSTALLERINFO PCINSTALLERINFO;
typedef enum {
} DIFXAPI_LOG;
typedef void (WINAPI * DIFXLOGCALLBACK_W)( DIFXAPI_LOG Event, DWORD Error, PCWSTR EventDescription, PVOID CallbackContext);
typedef void ( __cdecl* DIFXAPILOGCALLBACK_W)( DIFXAPI_LOG Event, DWORD Error, PCWSTR EventDescription, PVOID CallbackContext);
typedef DWORD (WINAPI *fnDriverPackageInstall) (PCTSTR DriverPackageInfPath, DWORD Flags, PCINSTALLERINFO pInstallerInfo, BOOL *pNeedReboot);
typedef DWORD (WINAPI *fnDriverPackageUninstall) (PCTSTR DriverPackageInfPath, DWORD Flags, PCINSTALLERINFO pInstallerInfo, BOOL *pNeedReboot);
typedef VOID (WINAPI *fnDIFXAPISetLogCallback) (DIFXAPILOGCALLBACK_W LogCallback, PVOID CallbackContext);
/* Defines */
#define DRIVER_PACKAGE_REPAIR 0x00000001
#define DRIVER_PACKAGE_SILENT 0x00000002
#define DRIVER_PACKAGE_FORCE 0x00000004
#define DRIVER_PACKAGE_ONLY_IF_DEVICE_PRESENT 0x00000008
#define DRIVER_PACKAGE_LEGACY_MODE 0x00000010
#define DRIVER_PACKAGE_DELETE_FILES 0x00000020
/* DIFx error codes */
/** @todo any reason why we're not using difxapi.h instead of these redefinitions? */
#ifndef ERROR_DRIVER_STORE_ADD_FAILED
# define ERROR_DRIVER_STORE_ADD_FAILED \
#endif
#define ERROR_DEPENDENT_APPLICATIONS_EXIST \
#define ERROR_DRIVER_PACKAGE_NOT_IN_STORE \
/* Registry string list flags */
#define VBOX_REG_STRINGLIST_ALLOW_DUPLICATES 0x00000001 /* Allows duplicates in list when adding a value. */
#ifdef DEBUG
# define VBOX_DRVINST_LOGFILE "C:\\Temp\\VBoxDrvInstDIFx.log"
#endif
{
if (::FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwLastError, 0, pszMsg, dwBufSize / sizeof(TCHAR), NULL) == 0)
{
return false;
}
else
{
if (p != NULL)
*p = _T('\0');
}
return true;
}
/**
* Log callback for DIFxAPI calls.
*
* @param Event The event's structure to log.
* @param dwError The event's error level.
* @param pEventDescription The event's text description.
* @param pCallbackContext User-supplied callback context.
*/
void LogCallback(DIFXAPI_LOG Event, DWORD dwError, PCWSTR pEventDescription, PVOID pCallbackContext)
{
if (dwError == 0)
else
if (pCallbackContext)
}
/**
* Loads a system DLL.
*
* @returns Module handle or NULL
* @param pwszName The DLL name.
*/
{
/* Get the process image path. */
return NULL;
/* Drop the image filename. */
for (;;)
{
{
break;
}
if (!off--)
return NULL; /* No path? Shouldn't ever happen! */
}
/* Check if there is room in the buffer to construct the desired name. */
cwcName++;
return NULL;
return LoadLibraryW(wszPath);
}
/**
*
* @return Exit code (EXIT_OK, EXIT_FAIL)
* @param fInstall Flag indicating whether to install (TRUE) or uninstall (FALSE) a driver.
* @param pszDriverPath Pointer to full qualified path to the driver's .INF file (+ driver files).
* @param fSilent Flag indicating a silent installation (TRUE) or not (FALSE).
* @param pszLogFile Pointer to full qualified path to log file to be written during installation.
* Optional.
*/
const _TCHAR *pszLogFile)
{
{
}
else
{
if (fInstall)
{
g_pfnDriverPackageInstall = (fnDriverPackageInstall)GetProcAddress(hDIFxAPI, "DriverPackageInstallW");
if (g_pfnDriverPackageInstall == NULL)
{
}
}
else
{
g_pfnDriverPackageUninstall = (fnDriverPackageUninstall)GetProcAddress(hDIFxAPI, "DriverPackageUninstallW");
if (g_pfnDriverPackageUninstall == NULL)
{
}
}
{
g_pfnDIFXAPISetLogCallback = (fnDIFXAPISetLogCallback)GetProcAddress(hDIFxAPI, "DIFXAPISetLogCallbackW");
if (g_pfnDIFXAPISetLogCallback == NULL)
{
}
}
}
{
if (pszLogFile)
{
if (!phFile)
}
{
TEXT("{7d2c708d-c202-40ab-b3e8-de21da1dc629}"), /* Our GUID for representing this installation tool. */
TEXT("VirtualBox Guest Additions Install Helper"),
TEXT("Oracle Corporation")
};
{
}
else
{
if (fInstall)
else
if (!fInstall)
if ( (GetVersionEx(&osi) != 0)
{
if (fInstall)
{
}
}
if (fSilent)
{
/*
* Don't add DRIVER_PACKAGE_SILENT to dwFlags here, otherwise
* installation will fail because we (still) don't have WHQL certified
* drivers. See CERT_E_WRONG_USAGE on MSDN for more information.
*/
}
if (dwRet != ERROR_SUCCESS)
{
switch (dwRet)
{
case CRYPT_E_FILE_ERROR:
break;
case ERROR_ACCESS_DENIED:
break;
case ERROR_BAD_ENVIRONMENT:
break;
case ERROR_CANT_ACCESS_FILE:
break;
_tprintf(_T("ERROR: DriverPackageUninstall removed an association between the driver package and the specified application but the function did not uninstall the driver package because other applications are associated with the driver package!\n"));
break;
_tprintf(_T("ERROR: There is no INF file in the DIFx driver store that corresponds to the INF file %ws!\n"), szDriverInf);
break;
case ERROR_FILE_NOT_FOUND:
break;
case ERROR_IN_WOW64:
_tprintf(_T("ERROR: The calling application is a 32-bit application attempting to execute in a 64-bit environment, which is not allowed!\n"));
break;
case ERROR_INVALID_FLAGS:
break;
case ERROR_INSTALL_FAILURE:
_tprintf(_T("ERROR: The (un)install operation failed! Consult the Setup API logs for more information.\n"));
break;
case ERROR_NO_MORE_ITEMS:
_T(
"ERROR: The function found a match for the HardwareId value, but the specified driver was not a better match than the current driver and the caller did not specify the INSTALLFLAG_FORCE flag!\n"));
break;
case ERROR_NO_DRIVER_SELECTED:
break;
case ERROR_SECTION_NOT_FOUND:
break;
case ERROR_SHARING_VIOLATION:
_tprintf(_T("ERROR: A component of the driver package in the DIFx driver store is locked by a thread or process\n"));
break;
/*
* ! sig: Verifying file against specific Authenticode(tm) catalog failed! (0x800b0109)
* ! sig: Error 0x800b0109: A certificate chain processed, but terminated in a root certificate which is not trusted by the trust provider.
* !!! sto: No error message will be displayed as client is running in non-interactive mode.
* !!! ndv: Driver package failed signature validation. Error = 0xE0000247
*/
break;
case ERROR_UNSUPPORTED_TYPE:
break;
default:
{
/* Try error lookup with GetErrorMsg(). */
break;
}
}
}
if (phFile)
{
if (fReboot)
}
}
}
}
{
#ifdef DEBUG
#endif
switch (Notification)
{
case SPFILENOTIFY_NEEDMEDIA:
break;
case SPFILENOTIFY_STARTCOPY:
break;
case SPFILENOTIFY_TARGETNEWER:
return TRUE;
}
}
/**
*
* @return Exit code (EXIT_OK, EXIT_FAIL)
* @param pszSection Section to execute; usually it's "DefaultInstall".
* @param iMode Execution mode to use (see MSDN).
* @param pszInf Full qualified path of the .INF file to use.
*/
{
pszInf, pszSection);
UINT uErrorLine = 0;
if (hINF != INVALID_HANDLE_VALUE)
{
hINF,
NULL,
NULL,
);
if (fSuccess)
{
L"DefaultInstall.Services",
0 /* Flags */);
if (fSuccess)
{
}
else
{
switch (dwErr)
{
break;
case ERROR_SECTION_NOT_FOUND:
break;
default:
break;
}
}
}
else
if (pvQueue)
}
else
return EXIT_OK;
}
/**
* Adds a string entry to a MULTI_SZ registry list.
*
* @return Exit code (EXIT_OK, EXIT_FAIL)
* @param pszSubKey Sub key containing the list.
* @param pszKeyValue The actual key name of the list.
* @param pszValueToRemove The value to add to the list.
* @param uiOrder Position (zero-based) of where to add the value to the list.
*/
int RegistryAddStringToMultiSZ(const TCHAR *pszSubKey, const TCHAR *pszKeyValue, const TCHAR *pszValueToAdd, unsigned int uiOrder)
{
#ifdef DEBUG
_tprintf(_T("AddStringToMultiSZ: Adding MULTI_SZ string %ws to %ws\\%ws (Order = %d)\n"), pszValueToAdd, pszSubKey, pszKeyValue, uiOrder);
#endif
LONG lRet = RegCreateKeyEx(HKEY_LOCAL_MACHINE, pszSubKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE, NULL, &hKey, &disp);
if (lRet != ERROR_SUCCESS)
if (lRet == ERROR_SUCCESS)
{
if ( lRet != ERROR_SUCCESS
|| dwType != REG_MULTI_SZ)
{
_tprintf(_T("AddStringToMultiSZ: RegQueryValueEx failed with error %ld, key type = 0x%x!\n"), lRet, dwType);
}
else
{
/* Look if the network provider is already in the list. */
unsigned int iPos = 0;
/* Replace delimiting "\0"'s with "," to make tokenizing work. */
for (unsigned i = 0; i < cbKeyValue / sizeof(TCHAR); i++)
{
/* Append new value (at beginning if iOrder=0). */
{
iPos++;
}
{
iPos++;
}
}
/* Append as last item if needed. */
{
}
if (lRet != ERROR_SUCCESS)
}
#ifdef DEBUG
if (lRet == ERROR_SUCCESS)
#endif
}
}
/**
* Removes a string entry from a MULTI_SZ registry list.
*
* @return Exit code (EXIT_OK, EXIT_FAIL)
* @param pszSubKey Sub key containing the list.
* @param pszKeyValue The actual key name of the list.
* @param pszValueToRemove The value to remove from the list.
*/
int RegistryRemoveStringFromMultiSZ(const TCHAR *pszSubKey, const TCHAR *pszKeyValue, const TCHAR *pszValueToRemove)
{
// @todo Make string sizes dynamically allocated!
#ifdef DEBUG
_tprintf(_T("RemoveStringFromMultiSZ: Removing MULTI_SZ string: %ws from %ws\\%ws ...\n"), pszValueToRemove, pszSubKey, pszKeyValue);
#endif
LONG lRet = RegCreateKeyEx(HKEY_LOCAL_MACHINE, pszKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE, NULL, &hkey, &disp);
if (lRet != ERROR_SUCCESS)
if (lRet == ERROR_SUCCESS)
{
if ( lRet != ERROR_SUCCESS
|| dwType != REG_MULTI_SZ)
{
_tprintf(_T("RemoveStringFromMultiSZ: RegQueryValueEx failed with %d, key type = 0x%x!\n"), lRet, dwType);
}
else
{
#ifdef DEBUG
#endif
int iIndex = 0;
int iNewIndex = 0;
for (unsigned i = 0; i < cbKeyValue / sizeof(TCHAR); i++)
{
{
{
if (iNewIndex == 0)
}
iIndex = 0;
}
}
#ifdef DEBUG
#endif
lRet = RegSetValueExW(hkey, pszKeyValue, 0, REG_MULTI_SZ, (LPBYTE)szFinalString, iNewIndex * sizeof(TCHAR));
if (lRet != ERROR_SUCCESS)
}
#ifdef DEBUG
if (lRet == ERROR_SUCCESS)
#endif
}
}
/**
* Adds a string to a registry string list (STRING_SZ).
* Only operates in HKLM for now, needs to be extended later for
* using other hives. Only processes lists with a "," separator
* at the moment.
*
* @return Exit code (EXIT_OK, EXIT_FAIL)
* @param pszSubKey Sub key containing the list.
* @param pszKeyValue The actual key name of the list.
* @param pszValueToAdd The value to add to the list.
* @param uiOrder Position (zero-based) of where to add the value to the list.
* @param dwFlags Flags.
*/
int RegistryAddStringToList(const TCHAR *pszSubKey, const TCHAR *pszKeyValue, const TCHAR *pszValueToAdd,
{
LONG lRet = RegCreateKeyEx(HKEY_LOCAL_MACHINE, pszSubKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE, NULL, &hKey, &disp);
if (lRet != ERROR_SUCCESS)
_tprintf(_T("RegistryAddStringToList: RegCreateKeyEx %ts failed with error %ld!\n"), pszSubKey, lRet);
if ( lRet != ERROR_SUCCESS
{
_tprintf(_T("RegistryAddStringToList: RegQueryValueEx failed with %d, key type = 0x%x!\n"), lRet, dwType);
}
if (lRet == ERROR_SUCCESS)
{
#ifdef DEBUG
#endif
/* Create entire new list. */
unsigned int iPos = 0;
{
/* Append new provider name (at beginning if iOrder=0). */
{
iPos++;
}
fAddToList = TRUE;
fAddToList = TRUE;
if (fAddToList)
{
iPos++;
}
#ifdef DEBUG
#endif
}
/* Append as last item if needed. */
/* Last char a delimiter? Cut off ... */
#ifdef DEBUG
_tprintf(_T("RegistryAddStringToList: New provider list: %ws (%u bytes)\n"), szNewKeyValue, iNewLen);
#endif
if (lRet != ERROR_SUCCESS)
}
}
/**
* Removes a string from a registry string list (STRING_SZ).
* Only operates in HKLM for now, needs to be extended later for
* using other hives. Only processes lists with a "," separator
* at the moment.
*
* @return Exit code (EXIT_OK, EXIT_FAIL)
* @param pszSubKey Sub key containing the list.
* @param pszKeyValue The actual key name of the list.
* @param pszValueToRemove The value to remove from the list.
*/
int RegistryRemoveStringFromList(const TCHAR *pszSubKey, const TCHAR *pszKeyValue, const TCHAR *pszValueToRemove)
{
LONG lRet = RegCreateKeyEx(HKEY_LOCAL_MACHINE, pszSubKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE, NULL, &hKey, &disp);
if (lRet != ERROR_SUCCESS)
_tprintf(_T("RegistryRemoveStringFromList: RegCreateKeyEx %ts failed with error %ld!\n"), pszSubKey, lRet);
if ( lRet != ERROR_SUCCESS
{
_tprintf(_T("RegistryRemoveStringFromList: RegQueryValueEx failed with %d, key type = 0x%x!\n"), lRet, dwType);
}
if (lRet == ERROR_SUCCESS)
{
#ifdef DEBUG
#endif
/* Create entire new list. */
int iPos = 0;
{
/* Append all list values as long as it's not the
* value we want to remove. */
{
iPos++;
}
#ifdef DEBUG
#endif
}
/* Last char a delimiter? Cut off ... */
#ifdef DEBUG
_tprintf(_T("RegistryRemoveStringFromList: New provider list: %ws (%u bytes)\n"), szNewKeyValue, iNewLen);
#endif
if (lRet != ERROR_SUCCESS)
}
}
/**
* Adds a network provider with a specified order to the system.
*
* @return Exit code (EXIT_OK, EXIT_FAIL)
* @param pszProvider Name of network provider to add.
* @param uiOrder Position in list (zero-based) of where to add.
*/
{
_T("ProviderOrder"),
return rc;
}
/**
* Removes a network provider from the system.
*
* @return Exit code (EXIT_OK, EXIT_FAIL)
* @param pszProvider Name of network provider to remove.
*/
{
int rc = RegistryRemoveStringFromList(_T("System\\CurrentControlSet\\Control\\NetworkProvider\\Order"),
_T("ProviderOrder"),
return rc;
}
const TCHAR *pszDisplayName,
int iServiceType,
int iStartType,
const TCHAR *pszBinPath,
const TCHAR *pszLoadOrderGroup,
const TCHAR *pszDependencies,
const TCHAR *pszLogonUser,
const TCHAR *pszLogonPassword)
{
int rc = ERROR_SUCCESS;
if (hSCManager == NULL)
{
return EXIT_FAIL;
}
/* Fixup end of multistring. */
if (pszDependencies != NULL)
{
/* Replace comma separator on null separator. */
{
if (',' == szDepend [i])
szDepend [i] = 0;
}
}
pszStartStopName, /* Name of service. */
pszDisplayName, /* Name to display. */
SERVICE_ALL_ACCESS, /* Desired access. */
iServiceType, /* Service type. */
iStartType, /* Start type. */
SERVICE_ERROR_NORMAL, /* Error control type. */
pszBinPath, /* Service's binary. */
pszLoadOrderGroup, /* Ordering group. */
{
switch (dwErr)
{
case ERROR_SERVICE_EXISTS:
{
pszStartStopName, /* Name of service. */
SERVICE_ALL_ACCESS); /* Desired access. */
{
dwErr = GetLastError();
}
else
{
iServiceType, /* Service type. */
iStartType, /* Start type. */
SERVICE_ERROR_NORMAL, /* Error control type. */
pszBinPath, /* Service's binary. */
pszLoadOrderGroup, /* Ordering group. */
pszDisplayName); /* Name to display. */
if (fResult)
else
{
dwErr = GetLastError();
}
}
/*
* This entire branch do not return an error to avoid installations failures,
* if updating service parameters. Better to have a running system with old
* parameters and the failure information in the installation log.
*/
break;
}
case ERROR_INVALID_PARAMETER:
break;
default:
break;
}
goto cleanup;
}
else
{
}
if (hSCManager != NULL)
return rc;
}
{
int rc = ERROR_SUCCESS;
if (hSCManager == NULL)
{
}
else
{
{
}
}
{
{
{
switch (dwErr)
{
break;
default:
break;
}
}
else
{
}
}
else
{
}
}
if (hSCManager != NULL)
return rc;
}
const _TCHAR *pszValueName,
{
0, /* Reserved */
NULL, /* lpClass [in, optional] */
0, /* dwOptions [in] */
NULL, /* lpSecurityAttributes [in, optional] */
&hKey,
NULL); /* lpdwDisposition [out, optional] */
if (lRet != ERROR_SUCCESS)
{
}
else
{
if (lRet != ERROR_SUCCESS)
}
return lRet;
}
void PrintHelp(void)
{
_tprintf(_T("Version: %d.%d.%d.%d\n\n"), VBOX_VERSION_MAJOR, VBOX_VERSION_MINOR, VBOX_VERSION_BUILD, VBOX_SVN_REV);
_T("\t <key name> <key type> <value>\n")
_T("\t [type] [size]\n"));
_T("\t <value> [order]\n"));
_T("\t <key name> <value to remove>\n"));
/** @todo Add "service" category! */
}
{
int rc = EXIT_USAGE;
if (argc >= 2)
{
&& argc >= 3)
{
&& argc >= 4)
{
{
}
else
{
if (argc > 4)
}
}
&& argc == 4)
{
}
}
&& argc >= 3)
{
&& argc >= 4)
{
int iOrder = 0;
if (argc > 4)
}
&& argc >= 4)
{
}
}
&& argc >= 3)
{
&& argc >= 8)
{
argv[4],
argv[7],
}
&& argc == 4)
{
}
}
&& argc >= 3)
{
/** @todo add a handleRegistry(argc, argv) method to keep things cleaner */
&& argc == 7)
{
}
&& argc == 6)
{
}
&& argc >= 8)
{
if (argc > 8)
{
{
}
}
{
}
if (argc > 9)
REG_BINARY, /** @todo needs to be expanded (argv[6]) */
pbVal, /* The value itself */
dwValSize); /* Size of the value */
}
#if 0
{
}
{
}
#endif
}
{
_tprintf(_T("%d.%d.%d.%d\n"), VBOX_VERSION_MAJOR, VBOX_VERSION_MINOR, VBOX_VERSION_BUILD, VBOX_SVN_REV);
}
{
PrintHelp();
}
}
if (rc == EXIT_USAGE)
_tprintf(_T("No or wrong parameters given! Please consult the help (\"--help\" or \"/?\") for more information.\n"));
return rc;
}