summaryrefslogtreecommitdiff
path: root/general/installwdf/Install.cpp
diff options
context:
space:
mode:
authorDave Wilson <[email protected]>2015-03-17 19:50:07 -0700
committerDave Wilson <[email protected]>2015-03-17 19:50:07 -0700
commit97cf5197cf5b882b2c689d8dc2b555f2edf8f418 (patch)
tree46f3701832d70b420eb0fc0eb93261f9da45db3f /general/installwdf/Install.cpp
parentef1905bf1e8825bb31120dfb27e0daf3154d859a (diff)
Initial publish
Diffstat (limited to 'general/installwdf/Install.cpp')
-rw-r--r--general/installwdf/Install.cpp509
1 files changed, 509 insertions, 0 deletions
diff --git a/general/installwdf/Install.cpp b/general/installwdf/Install.cpp
new file mode 100644
index 00000000..cd89e284
--- /dev/null
+++ b/general/installwdf/Install.cpp
@@ -0,0 +1,509 @@
+#include <Windows.h>
+#include <stdio.h>
+#include <strsafe.h>
+#include <wuerror.h>
+
+//
+// These defines control which packages need to be updated. In order to
+// to customize this for your drivers needs, edit needed packages to 1 and
+// unneeded packages to 0.
+//
+// If your UMDF driver uses USB functionality, you also need KMDF and WinUSB
+//
+#define INSTALL_KMDF (TRUE)
+#define INSTALL_WINUSB (TRUE)
+#define INSTALL_UMDF (TRUE)
+
+//
+// Reference strings that are used to build our MSU package name. These will
+// change between releases of the framework and will need to be updated
+//
+#define WINUSB_UPDATE_NAME L"WinUSB_1.9.msu"
+
+//
+// MSU names are of the format:
+// <kmdf|umdf>-<wdf major version>.<wdf minor version>-
+// Win-<windows major version>.<windows minor version>.msu
+//
+#define MSU_FORMAT_STRING L"%s-%d.%d-Win-%d.%d.msu"
+
+#define WUSA_EXE L"%windir%\\system32\\wusa.exe"
+
+#define WUSA_EXE_ARGUMENTS L"/quiet /norestart"
+
+#define WDF_MAJOR_VERSION 1
+#define WDF_MINOR_VERSION 11
+
+DWORD
+ApplyUpdate(
+ PCWSTR MSUName
+)
+{
+ DWORD error = ERROR_SUCCESS;
+ size_t cmdLengthBytes;
+ size_t applicationLengthBytes;
+ BOOL ok;
+ PROCESS_INFORMATION pInfo;
+ STARTUPINFOW startInfo;
+ PWCHAR applicationName = NULL;
+ PWCHAR commandLine = NULL;
+ HRESULT hr;
+
+ ZeroMemory(&startInfo,sizeof(startInfo)) ;
+ startInfo.cb = sizeof(STARTUPINFO) ;
+
+ ZeroMemory(&pInfo,sizeof(pInfo));
+ //
+ // Check that the update package exists
+ //
+ error = GetFileAttributes(MSUName);
+
+ if (error == INVALID_FILE_ATTRIBUTES) {
+ error = GetLastError();
+ wprintf(L"Error: Could not find update file %s; error %x\n", MSUName, error);
+ goto exit;
+ }
+
+ //
+ // Invoke wusa
+ //
+ applicationName = (PWCHAR) LocalAlloc(LPTR, (MAX_PATH + 1)*sizeof(WCHAR));
+ if (applicationName == NULL) {
+ error = ERROR_INSTALL_FAILURE;
+ wprintf(L"Failed to allocate applicationName buffer\n");
+ goto exit;
+ }
+
+ applicationName[0] = L'\0';
+
+ applicationLengthBytes = ExpandEnvironmentStrings(WUSA_EXE,
+ applicationName,
+ MAX_PATH+1);
+
+ if ((applicationLengthBytes == 0) ||
+ (applicationLengthBytes > MAX_PATH+1)) {
+ wprintf(L"Could not expland %s\n", WUSA_EXE);
+ error = ERROR_INSTALL_FAILURE;
+ goto exit;
+ }
+
+ applicationLengthBytes = sizeof(WCHAR) * applicationLengthBytes;
+
+ hr = StringCbLength(MSUName,
+ MAX_PATH * sizeof(WCHAR),
+ &cmdLengthBytes);
+
+ if (hr != S_OK) {
+
+ error = ERROR_INSTALL_FAILURE;
+ wprintf(L"StringCbLength failed MSUName, %x\n",
+ hr);
+ goto exit;
+ }
+ //
+ // Add enough padding for 2 \". The size returned by sizeof() includes
+ // the terminating L'\0'
+ //
+ cmdLengthBytes = applicationLengthBytes + cmdLengthBytes + sizeof(WUSA_EXE_ARGUMENTS) + 3*sizeof(WCHAR);
+
+ commandLine = (PWCHAR) LocalAlloc(LPTR, cmdLengthBytes );
+
+ if (commandLine == NULL) {
+ wprintf(L"Failed to allocate applicationName buffer\n");
+ error = ERROR_INSTALL_FAILURE;
+ goto exit;
+ }
+
+ hr = StringCbPrintf(commandLine,
+ cmdLengthBytes,
+ L"%s \"%s\" %s",
+ applicationName,
+ MSUName,
+ WUSA_EXE_ARGUMENTS);
+ if (hr != S_OK) {
+
+ error = ERROR_INSTALL_FAILURE;
+ wprintf(L"StringCbPrintf failed for applicationParameters, %x\n",
+ hr);
+ goto exit;
+ }
+
+ wprintf(L"Invoking: %s\n", commandLine);
+
+ ok = CreateProcess(applicationName, // name of executable module
+ commandLine, // command line string
+ NULL, // SD
+ NULL, // SD
+ TRUE, // handle inheritance option
+ 0, // creation flags CREATE_NO_WINDOW
+ NULL, // new environment block
+ NULL, // current directory name
+ &startInfo, // startup information
+ &pInfo // process information
+ );
+
+ if (ok == FALSE) {
+
+ error = GetLastError();
+ wprintf(L"Create process failed : %x\n",
+ error);
+ goto exit;
+
+ } else {
+
+ //
+ // Wait until child process exits.
+ //
+
+ error = WaitForSingleObject( pInfo.hProcess, INFINITE );
+
+ if ( error != WAIT_OBJECT_0 ) {
+ //
+ // It can't hurt to add this
+ //
+ TerminateProcess(pInfo.hProcess, (UINT)-1);
+ }
+
+ GetExitCodeProcess(pInfo.hProcess, &error);
+
+ //
+ // The possible return values for wusa.exe are:
+ // 1)ERROR_SUCCESS (0) : installation was successfull
+ // 2)ERROR_SUCCESS_REBOOT_REQUIRED (3010) : installation was successful,
+ // however a reboot is required, so that the binaries will be
+ // loaded to memory
+ // 3)S_FALSE (1) (Vista) OR WU_S_ALREADY_INSTALLED (240006) (Win7):
+ // No action was taken (i.e. files were already installed)
+ // 4)Everything else (e.g. ERROR_INSTALL_FAILURE) is an error
+ //
+
+ switch (error) {
+ case S_FALSE:
+ case WU_S_ALREADY_INSTALLED:
+ wprintf(L"The package was already installed in the system\n");
+ error = ERROR_SUCCESS;
+ break;
+ case ERROR_SUCCESS:
+ wprintf(L"The package was installed successfully\n");
+ break;
+ case ERROR_SUCCESS_REBOOT_REQUIRED:
+ wprintf(L"The package was installed successfully but requires a reboot\n");
+ break;
+ case ERROR_SERVICE_DISABLED:
+ case WU_E_WU_DISABLED:
+
+ //
+ // If the "Windows Update" service is disabled, then wusa
+ // returns ERROR_SERVICE_DISABLED
+ //
+
+ wprintf(L"The \"Windows Update\" service is disabled. It "
+ L"has to be enabled for the installation to succeed."
+ L"\n");
+ break;
+ default:
+ wprintf(L"The update process returned error code :%x. ",
+ error);
+ wprintf(L"For additional information please look at the log "
+ L"files %%windir%%\\windowsupdate.log and "
+ L"%%windir%%\\Logs\\CBS\\CBS.log\n");
+ break;
+ }
+
+ CloseHandle(pInfo.hProcess);
+ CloseHandle(pInfo.hThread);
+ }
+
+exit:
+ if (commandLine != NULL) {
+ LocalFree(commandLine);
+ commandLine = NULL;
+ }
+
+ if (applicationName != NULL) {
+ LocalFree(applicationName);
+ applicationName = NULL;
+ }
+
+ return error;
+}
+
+BOOL
+PromptRestart()
+{
+ HANDLE hToken; // handle to process token
+ TOKEN_PRIVILEGES tkp; // pointer to token structure
+ BOOL fResult; // system shutdown flag
+
+ // Get the current process token handle so we can get shutdown
+ // privilege.
+
+ if (!OpenProcessToken(GetCurrentProcess(),
+ TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
+ return FALSE;
+
+ // Get the LUID for shutdown privilege.
+
+ LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME,
+ &tkp.Privileges[0].Luid);
+
+ tkp.PrivilegeCount = 1; // one privilege to set
+ tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+
+ // Get shutdown privilege for this process.
+
+ AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,
+ (PTOKEN_PRIVILEGES) NULL, 0);
+
+ // Cannot test the return value of AdjustTokenPrivileges.
+
+ if (GetLastError() != ERROR_SUCCESS) {
+ return FALSE;
+ }
+
+ // Display the shutdown dialog box and start the countdown.
+
+ #pragma prefast(suppress:28159, "Ignore the suggestion against system shutdown.")
+ fResult = InitiateSystemShutdownEx(
+ NULL, // shut down local computer
+ NULL, // message for user
+ 0, // time-out period, in seconds
+ FALSE, // ask user to close apps
+ TRUE, // reboot after shutdown
+ SHTDN_REASON_FLAG_PLANNED // shutdown reason
+ | SHTDN_REASON_MAJOR_SOFTWARE
+ | SHTDN_REASON_MINOR_UPGRADE);
+
+ // Disable shutdown privilege.
+
+ tkp.Privileges[0].Attributes = 0;
+ AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,
+ (PTOKEN_PRIVILEGES) NULL, 0);
+
+ return fResult;
+
+}
+
+DWORD
+UpdateWdf(
+ VOID
+ )
+{
+ BOOL ok;
+ OSVERSIONINFO curOsvi;
+ OSVERSIONINFOEX targetOsvi;
+ DWORDLONG dwlConditionMask = 0;
+ WCHAR MSUName[MAX_PATH];
+ BOOL rebootNeeded = FALSE;
+ DWORD error = ERROR_SUCCESS;
+ HRESULT hr;
+
+ //
+ // Make sure updates are valid for this operating system.
+ // Vista SP1/SP2; Win7 RTM
+ // TODO: what happens on Vista RTM/SP3+/Win7 SP1+
+ //
+
+ ZeroMemory(&targetOsvi, sizeof(OSVERSIONINFOEX));
+ dwlConditionMask = 0;
+
+ targetOsvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+ targetOsvi.dwMajorVersion = 6;
+ targetOsvi.dwMinorVersion = 0;
+ targetOsvi.wServicePackMajor = 0;
+
+ VER_SET_CONDITION( dwlConditionMask, VER_MAJORVERSION, VER_LESS_EQUAL );
+ VER_SET_CONDITION( dwlConditionMask, VER_MINORVERSION, VER_LESS_EQUAL );
+ VER_SET_CONDITION( dwlConditionMask, VER_SERVICEPACKMAJOR, VER_LESS_EQUAL );
+
+ ok = VerifyVersionInfo( &targetOsvi,
+ VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR,
+ dwlConditionMask);
+
+ if (ok) {
+ wprintf(L"Error: Updates are not supported on OS before Vista or Vista RTM\n");
+ error = ERROR_OLD_WIN_VERSION;
+ goto exit;
+ }
+
+ //
+ // No need to update on Win8+
+ //
+ ZeroMemory(&targetOsvi, sizeof(OSVERSIONINFOEX));
+ dwlConditionMask = 0;
+
+ targetOsvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+ targetOsvi.dwMajorVersion = 6;
+ targetOsvi.dwMinorVersion = 2;
+
+ VER_SET_CONDITION( dwlConditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL );
+ VER_SET_CONDITION( dwlConditionMask, VER_MINORVERSION, VER_GREATER_EQUAL );
+
+ ok = VerifyVersionInfo( &targetOsvi,
+ VER_MAJORVERSION | VER_MINORVERSION,
+ dwlConditionMask);
+
+ if (ok) {
+ wprintf(L"Updates are not needed to Windows 8, they are already inbox\n");
+ error = ERROR_SUCCESS;
+ goto exit;
+ }
+
+ //
+ // Create MSU name
+ //
+
+ ZeroMemory(&curOsvi, sizeof(OSVERSIONINFO));
+ curOsvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+
+#pragma warning( push )
+#pragma warning( disable : 4996 ) // 'GetVersionEx': was declared deprecated
+ ok = GetVersionEx(&curOsvi);
+
+ if (ok == FALSE) {
+
+ error = GetLastError();
+ wprintf(L"GetVersionEx failed: %x\n", error);
+ goto exit;
+ }
+
+ //
+ // We want to apply the updates in a specific order. This is because UMDF
+ // is potentially dependent on WinUSB. WinUSB is dependent on KMDF. If we
+ // fail to apply an update, we want to make sure the machine is in a good
+ // state. This we apply required framework updates first.
+ // KMDF > WinUSB > UMDF
+ //
+
+#if INSTALL_KMDF
+ hr = StringCchPrintf(MSUName,
+ MAX_PATH,
+ MSU_FORMAT_STRING,
+ L"kmdf",
+ WDF_MAJOR_VERSION,
+ WDF_MINOR_VERSION,
+ curOsvi.dwMajorVersion,
+ curOsvi.dwMinorVersion);
+
+ if (hr != S_OK) {
+ wprintf(L"StringCchPrintf for KMDF MSU failed: %x\n", hr);
+ error = ERROR_INSTALL_FAILURE;
+ goto exit;
+ }
+
+ error = ApplyUpdate(MSUName);
+
+ if (error == ERROR_SUCCESS_REBOOT_REQUIRED) {
+ rebootNeeded = TRUE;
+ } else if (error != ERROR_SUCCESS) {
+ goto exit;
+ }
+#endif // INSTALL_KMDF
+#pragma warning( pop ) // 'GetVersionEx': was declared deprecated
+
+#if (INSTALL_WINUSB)
+ //
+ // WinUSB update only applies to Vista
+ //
+
+ ZeroMemory(&targetOsvi, sizeof(OSVERSIONINFOEX));
+ dwlConditionMask = 0;
+
+ targetOsvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+ targetOsvi.dwMajorVersion = 6;
+ targetOsvi.dwMinorVersion = 0;
+
+ VER_SET_CONDITION( dwlConditionMask, VER_MAJORVERSION, VER_EQUAL );
+ VER_SET_CONDITION( dwlConditionMask, VER_MINORVERSION, VER_EQUAL );
+
+ ok = VerifyVersionInfo( &targetOsvi,
+ VER_MAJORVERSION | VER_MINORVERSION,
+ dwlConditionMask);
+
+ if (ok) {
+ error = ApplyUpdate(WINUSB_UPDATE_NAME);
+
+ if (error == ERROR_SUCCESS_REBOOT_REQUIRED) {
+ rebootNeeded = TRUE;
+ } else if (error != ERROR_SUCCESS) {
+ goto exit;
+ }
+ }
+#endif // INSTALL_WINUSB
+
+#if INSTALL_UMDF
+ hr = StringCchPrintf(MSUName,
+ MAX_PATH,
+ MSU_FORMAT_STRING,
+ L"umdf",
+ WDF_MAJOR_VERSION,
+ WDF_MINOR_VERSION,
+ curOsvi.dwMajorVersion,
+ curOsvi.dwMinorVersion);
+
+ if (hr != S_OK) {
+ wprintf(L"StringCchPrintf for UMDF MSU failed: %x\n", hr);
+ error = ERROR_INSTALL_FAILURE;
+ goto exit;
+ }
+
+ error = ApplyUpdate(MSUName);
+
+ if (error == ERROR_SUCCESS_REBOOT_REQUIRED) {
+ rebootNeeded = TRUE;
+ } else if (error != ERROR_SUCCESS) {
+ goto exit;
+ }
+#endif // INSTALL_UMDF
+
+ //
+ // If we have made it to this point there have been no fatal errors. If
+ // there were fatal errors, these should be caught and we would've jumped
+ // to exit.
+ //
+ // We must account for the fact the latest update applied did not require
+ // a reboot but earlier updates did.
+ //
+ if (rebootNeeded == TRUE) {
+
+ error = ERROR_SUCCESS_REBOOT_REQUIRED;
+ goto exit;
+ }
+
+ error = ERROR_SUCCESS;
+
+exit:
+
+ return error;
+}
+
+int __cdecl
+wmain(
+ _In_ int argc,
+ _In_reads_(argc) char* argv[]
+ )
+{
+ DWORD updateStatus;
+
+ UNREFERENCED_PARAMETER(argc);
+ UNREFERENCED_PARAMETER(argv);
+
+ updateStatus = UpdateWdf();
+
+ if (updateStatus == ERROR_SUCCESS_REBOOT_REQUIRED) {
+
+ int msgboxID = MessageBox(
+ NULL,
+ L"A restart is needed for these changes to take effect\nRestart now?",
+ L"Restart Required",
+ MB_ICONEXCLAMATION | MB_YESNO
+ );
+
+ if (msgboxID == IDYES)
+ {
+ PromptRestart();
+ }
+ }
+
+ return updateStatus;
+}