WDS Deployment Options

Applies To: Windows Server 2008

IT administrators have several options for deploying Windows Desktop Search: using attended mode, unattended mode and Group Policy. This section describes each of these options.

  • Deployment Modes

  • Deploying WDS with Group Policy

  • Wrapping WDS in an MSI Wrapper

Deployment Modes

WDS can be deployed in attended mode or unattended mode, depending on the level of interaction you want your users to have with the computer during the installation. Installations for both modes can be performed through a combination of command-line options. For a full list of supported command line options, visit the Package Installer Command Line Options page on MSDN (https://msdn2.microsoft.com/en-us/library/aa367988.aspx ).

To test command-line options and the behavior of the installation package:

  1. Click Start.

  2. Click Run.

  3. Type CMD in the Open box and click OK.

  4. Locate the installer package, and then test the installer package with various options.

Note

We recommend that you uninstall WDS before you install again with different command-line options.

Attended Mode

Attended mode is the typical installation method for an individually managed environment that requires end-user interaction. If you plan to make WDS available on-demand from an internal IT software download site, you can deploy WDS in attended mode.

In this mode, the Software Update Installation Wizard is started. Your end users must accept the end-user license agreement (EULA) and exit open applications when they are prompted to do so. Your end users must restart their computers, as necessary, at the end of the installation process.

By default, WDS installs in attended mode when no command-line options are specified.

Unattended Mode

Unattended mode enables the automated installation of software updates and doesn't require end-user interaction. There are several ways to accomplish unattended installation. You can develop custom batch installations using the command-line options described below, or you can use automation software, such as SMS or Windows Update Services, to install software updates on all the computers in a network.

If you manually install a software update, the installation runs in the user context. You should be an administrator who has the user permissions that are specified in the “Required User Right” section of the package installer documentation. If a software update is deployed using SMS or Windows Update Services, the package installer runs in the System context because the parent process runs as a service.

Note

If Outlook is running during unattended WDS installation, Outlook remains and the indexing of e-mail does not start until users exit and then restart Outlook.

Installer command-Line Options

Command-line Options Description

/quiet or /q

Provides no status dialog box during the extraction. Unless used together with /extract or /extract:path, this option directs the installation to run completely silently. If a restart is required, the restart occurs automatically. The WDS Deskbar appears automatically after the unattended installation is complete.

/passive or /U

Provides users with a progress bar during the extraction, but does not prompt users for the destination folder name. Displays any errors that occur during installation. Unless used together with /extract or /extract:path, this option directs the installation to run in passive mode.

Note
This option requires user interaction if a restart is required.

/extract or /X

Extracts package files without starting the installation. Prompts you for the path of the destination folder for extraction. When used together with the /q or /U switch, this option extracts the package file to a randomly named folder on the root folder.

/extract:path_name or /X:path_name

Extracts software update package files to the specified folder without starting the package installer or prompting you for a destination folder. When used together with the /q or /U switch, this option extracts the package file to the specified folder.

Deploying WDS with Group Policy

You cannot directly deploy package installer-based or update.exe-based Windows components using Group Policy. Instead, you must write a deployment wrapper, such as an MSI package, that contains the WDS executable file. The package installer and the Windows Installer are not interchangeable. Packages that are built with one installer technology have been tested and optimized to work only with that technology.

Note

Modification of the software package may result in its failure to operate properly. Microsoft does not support customers’ repackaging software updates with a different installer. This would include modifying the current update.exe package headers, files, or installation information to conform to another deployment method. Simply placing the package in a “wrapper,” such as an MSI wrapper described next, is acceptable.

Wrapping WDS in an MSI Wrapper

Microsoft does not guarantee that WDS will install correctly or function correctly when you install WDS from an MSI wrapper that was not written by Microsoft. However, we provide the following steps so that you can create an MSI wrapper for the WDS Setup program.

To follow these steps, you must know how to prepare a general MSI package. The sample code is provided as-is, without any warranties. Error-checking in the code has been mostly omitted.

  1. Author WDSSetup.exe into the Binary table of your MSI package. If you are editing the .wxs files directly, add a line that resembles the following:

    <Binary Id= “WindowsDesktopSearch.exe” src= “path to WDSSetup.exe on the build machine”/>

  2. Author the command-line properties for how WDSSetup.exe will run. For example, to run the WDSSetup.exe file in a quiet mode and to suppress the potential post-setup restart, add the following line to the .wxs file:

    <Property Id=”WDSCOMMANDPARAM” Value=” "/quiet /norestart" />

  3. In your custom action DLL, add actions that stream the WDS installer from the MSI package and that start the MSI package. The following code is sample C++ code that you can use to do this:

    #include <windows.h>
    #include <Msiquery.h>
    #include <TCHAR.h>
    
    // A helper method to retrieve the necessary entries 
    // from the Property table of the MSI.
    LPTSTR GetProperty(MSIHANDLE hInstall, LPCTSTR lpProperty, PDWORD pdwSize)
    {
        LPTSTR lpValue = NULL;
        bool bRet = true;
        DWORD dwSize=0;
        UINT uResult = MsiGetProperty(hInstall, lpProperty, TEXT(""), &dwSize);
        if (ERROR_MORE_DATA == uResult) 
        {
            *pdwSize = ++dwSize;
            lpValue = (LPTSTR) HeapAlloc(GetProcessHeap(), 
                HEAP_ZERO_MEMORY, dwSize*sizeof(TCHAR));
            if (NULL != lpValue)
            {
                uResult = MsiGetProperty(hInstall, lpProperty, lpValue, &dwSize);
                if (ERROR_SUCCESS != uResult) 
                {
                    HeapFree(GetProcessHeap(), 0, lpValue);                
                    lpValue = NULL;
                }
            }
        } 
        return lpValue;
    }
    
    // This routine streams the WDSSetup.exe file from the MSI package.
    UINT __stdcall StreamOutWDSPackage(MSIHANDLE hInstall)
    {
        UINT uRes = 0;
        TCHAR szTempPath[MAX_PATH];
        TCHAR szTempFile[MAX_PATH];
        TCHAR szCommandLine[MAX_PATH];
        HRESULT hr;
    
        ZeroMemory(szTempPath, sizeof(szTempPath));
    
        // Obtain path of the temporary directory.
        DWORD dwRes = GetTempPath(MAX_PATH, szTempPath);
        ZeroMemory(szTempFile, sizeof(szTempFile));
    
        // Generate a fully qualified temporary file name that 
        // has the prefix “WDS.”
        if (dwRes) 
            uRes = GetTempFileName(szTempPath, TEXT("wds"), 0, szTempFile);
    
        if (!dwRes || !uRes)
        {
            // Add code for logging the failure if needed.
            return ERROR_INSTALL_FAILURE;
        }
    
        //Add .exe extension to the name of the temporary file.
        hr = StringCchPrintf(szTempFile, MAX_PATH, TEXT("%s.exe"), szTempFile);
    
        if (FAILED(hr))
        {
            // Add code for logging the failure if needed
            return ERROR_INSTALL_FAILURE;
        }
    
        // Obtain the Windows Desktop Search setup command-line
        // parameters property.
        DWORD dwSize;
        LPTSTR lpCommandParam = GetProperty(hInstall, TEXT("WDSCOMMANDPARAM"), &dwSize);
    
        if (NULL == lpCommandParam)
            return ERROR_INSTALL_FAILURE;
    
        // Create the command-line command that includes the directory
        // path, the command name, and the parameters.
        ZeroMemory(szCommandLine, sizeof(szCommandLine));
        hr = StringCchPrintf(szCommandLine, MAX_PATH, TEXT("\"%s\" %s"), szTempFile, lpCommandParam);
    
        // Free lpCommandParam because we don't need it anymore.
        if (lpCommandParam)
            HeapFree(GetProcessHeap(), 0, lpCommandParam);
        if (FAILED(hr))
            return ERROR_INSTALL_FAILURE;
    
        // Create the actual temporary file and then obtain its handle. 
        // The code uses the handle to write data to this file.
        HANDLE hFile = 0;       
        hFile = CreateFile(szTempFile, GENERIC_WRITE, 0, NULL, 
            CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    
        if (INVALID_HANDLE_VALUE == hFile)
        {
            // Add code for logging the failure if needed.
            return ERROR_INSTALL_FAILURE;
        }
    
        // Send the handle to the MSI database.
        PMSIHANDLE hDB;
        hDB  = MsiGetActiveDatabase(hInstall);
        if (NULL == hDB)
        {
            // Add code for logging the failure if needed
            return ERROR_INSTALL_FAILURE;
        }
    
        // A query to retrieve the WDS Setup file from the MSI package.
        PMSIHANDLE hView;
        if (ERROR_SUCCESS != MsiDatabaseOpenView(hDB, 
            TEXT("SELECT `Data` FROM `Binary` WHERE `Name` = 'WindowsDesktopSearch.exe'"), 
            &hView)
            ||
            ERROR_SUCCESS != MsiViewExecute(hView, 0))
        {
            // Add code for logging the failure if needed.
            return ERROR_INSTALL_FAILURE;
        }
    
        // Fetch data from query result.
        PMSIHANDLE hRec;
        uRes = MsiViewFetch(hView, &hRec);
        if (uRes != ERROR_SUCCESS)
        {
            // Add code for logging the failure if needed.
            return ERROR_INSTALL_FAILURE;
        }
    
        // Extract binary data from Windows Desktop Search setup record.
        char szBuff[MAX_BUFFER*2];
        DWORD dwBuffSize;
        DWORD dwBytesWritten;
    
        do {
            // Extract binary data to buffer.
            dwBuffSize = MAX_BUFFER*2;
            uRes = MsiRecordReadStream(hRec, 1, szBuff, &dwBuffSize);
            if (ERROR_SUCCESS != uRes)
            {
                // Add code for logging the failure if needed.
                break;
            }
    
            if(dwBuffSize)
            {
                // Write the chunk of the binary data to disk.
                if ( !WriteFile(hFile, szBuff, dwBuffSize, &dwBytesWritten, NULL) )
                {
                    // Add code for logging the failure if needed.
                    break;
                }
            }
        } 
        while (dwBuffSize > 0);
        CloseHandle(hFile);
    
        //  This will store the command line into the CustomActionData,
        // to be later retrieved by the InstallWindowsDesktopSearch action. 
        uRes = MsiSetProperty(hInstall, TEXT("InstallWindowsDesktopSearch"), szCommandLine);
        if (ERROR_SUCCESS != uRes)
        {
           // Add code for logging the failure if needed.
        }    
    
        return uRes;
    }
    
    // This routine retrieves the command line that will launch the 
    // WDS Setup file and starts an install helper.
    UINT __stdcall InstallWindowsDesktopSearch(MSIHANDLE hInstall)
    {
        UINT uRes = ERROR_SUCCESS;
        DWORD dwSize;
        LPTSTR lpCommand = GetProperty(hInstall, TEXT("CustomActionData"), &dwSize);
        if ( lpCommand && dwSize > 0)
        {
            uRes = LaunchWDSSetup(hInstall, lpCommand);
        }
        else
            uRes = ERROR_INSTALL_FAILURE;
    
        HeapFree(GetProcessHeap(), 0, lpCommand);                
    
        return uRes;
    }
    
    // A helper that launches the WDS Setup file.
    UINT LaunchWDSSetup(MSIHANDLE hInstall, LPTSTR lpCommandLine)
    {
        STARTUPINFO startInfo;
        ZeroMemory(&startInfo, sizeof(STARTUPINFO));
        startInfo.cb = sizeof(STARTUPINFO);
        startInfo.dwFlags |= STARTF_USESHOWWINDOW;
        startInfo.wShowWindow = SW_SHOW;  // or SW_HIDE, if you want a silent install
    
        PROCESS_INFORMATION procInfo;
        ZeroMemory(&procInfo, sizeof(PROCESS_INFORMATION));
        if(CreateProcess(NULL, 
            lpCommandLine, 
            NULL, 
            NULL,
            TRUE, //inherit handles
            CREATE_NO_WINDOW, // no window
            NULL, //inherit environment
            NULL, //inherit current dir
            &startInfo, 
            &procInfo))
    {
            HANDLE hProcess = procInfo.hProcess;
            WaitForSingleObject(hProcess, INFINITE);
            CloseHandle(procInfo.hThread);
            CloseHandle(procInfo.hProcess);
        }
        else
        {
            // Report error
        }
    
        return ERROR_SUCCESS;
    }
    
  4. Author the actions that you added to the custom action DLL into the Custom Action table. Then, author the custom action DLL into the Binary table if it is not there yet. The action would resemble the following in a scenario in which the custom action DLL is Ca.dll:

    <Binary Id="CA.dll" src=”path to ca.dll” />

    <CustomAction Id="StreamOutWDSPackage" BinaryKey="CA.dll" DllEntry="StreamOutWDSPackage" Return="ignore"/>

    <CustomAction Id="InstallWindowsDesktopSearch" BinaryKey="CA.dll" DllEntry="InstallWindowsDesktopSearch" Execute="deferred"/>

Note

The execution of the InstallWindowsDesktopSearch [function] is deferred because it modifies the target system, so the function runs at the end of the sequence. A benefit of delaying when the function runs is that the InstallWindowsDesktopSearch [function] has to obtain the command line for starting the WDS Setup program through the CustomActionData private property (see the custom action code above).

  1. Finally, appropriately schedule your custom actions into the Install Execute sequence. The exact sequencing depends on the other tasks you want the MSI package to do. Here is an example:

    <InstallExecuteSequence>

    <LaunchConditions/>

    <InstallInitialize/>

    <Custom Action="StreamOutWDSPackage" After="InstallInitialize” />

    <Custom Action="InstallWindowsDesktopSearch" After="StreamOutWDSPackage”/>

    </InstallExecuteSequence>

See Also

Concepts

WDS 3.01 Troubleshooting Guide

Other Resources

Package Installer Command Line Options page on MSDN