Home | History | Annotate | Download | only in install
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include <windows.h>
      6 #include <setupapi.h>  // Must be included after windows.h
      7 #include <winspool.h>
      8 #include <iomanip>
      9 
     10 #include "base/at_exit.h"
     11 #include "base/command_line.h"
     12 #include "base/file_version_info_win.h"
     13 #include "base/files/file_util.h"
     14 #include "base/files/scoped_temp_dir.h"
     15 #include "base/logging.h"
     16 #include "base/path_service.h"
     17 #include "base/process/launch.h"
     18 #include "base/process/process.h"
     19 #include "base/strings/string16.h"
     20 #include "base/strings/string_util.h"
     21 #include "base/win/registry.h"
     22 #include "base/win/scoped_handle.h"
     23 #include "base/win/windows_version.h"
     24 #include "cloud_print/common/win/cloud_print_utils.h"
     25 #include "cloud_print/common/win/install_utils.h"
     26 #include "cloud_print/virtual_driver/win/virtual_driver_consts.h"
     27 #include "cloud_print/virtual_driver/win/virtual_driver_helpers.h"
     28 #include "grit/virtual_driver_setup_resources.h"
     29 
     30 #include <strsafe.h>  // Must be after base headers to avoid deprecation
     31                       // warnings.
     32 
     33 namespace cloud_print {
     34 
     35 namespace {
     36 
     37 const wchar_t kNameValue[] = L"GCP Virtual Driver";
     38 const wchar_t kUninstallId[] = L"{74AA24E0-AC50-4B28-BA46-9CF05467C9B7}";
     39 const wchar_t kInstallerName[] = L"virtual_driver_setup.exe";
     40 const wchar_t kGcpUrl[] = L"http://www.google.com/cloudprint";
     41 
     42 const wchar_t kDataFileName[] = L"gcp_driver.gpd";
     43 const wchar_t kDriverName[] = L"MXDWDRV.DLL";
     44 const wchar_t kUiDriverName[] = L"UNIDRVUI.DLL";
     45 const wchar_t kHelpName[] = L"UNIDRV.HLP";
     46 const wchar_t* kDependencyList[] = {
     47   kDriverName,
     48   kHelpName,
     49   kUiDriverName,
     50   L"STDDTYPE.GDL",
     51   L"STDNAMES.GPD",
     52   L"STDSCHEM.GDL",
     53   L"STDSCHMX.GDL",
     54   L"UNIDRV.DLL",
     55   L"UNIRES.DLL",
     56   L"XPSSVCS.DLL",
     57 };
     58 
     59 const char kDelete[] = "delete";
     60 const char kInstallSwitch[] = "install";
     61 const char kRegisterSwitch[] = "register";
     62 const char kUninstallSwitch[] = "uninstall";
     63 const char kUnregisterSwitch[] = "unregister";
     64 
     65 base::FilePath GetSystemPath(const base::string16& binary) {
     66   base::FilePath path;
     67   if (!PathService::Get(base::DIR_SYSTEM, &path)) {
     68     LOG(ERROR) << "Unable to get system path.";
     69     return path;
     70   }
     71   return path.Append(binary);
     72 }
     73 
     74 base::FilePath GetNativeSystemPath(const base::string16& binary) {
     75   if (!IsSystem64Bit())
     76     return GetSystemPath(binary);
     77   base::FilePath path;
     78   // Sysnative will bypass filesystem redirection and give us
     79   // the location of the 64bit system32 from a 32 bit process.
     80   if (!PathService::Get(base::DIR_WINDOWS, &path)) {
     81     LOG(ERROR) << "Unable to get windows path.";
     82     return path;
     83   }
     84   return path.Append(L"sysnative").Append(binary);
     85 }
     86 
     87 void SpoolerServiceCommand(const char* command) {
     88   base::FilePath net_path = GetNativeSystemPath(L"net");
     89   if (net_path.empty())
     90     return;
     91   CommandLine command_line(net_path);
     92   command_line.AppendArg(command);
     93   command_line.AppendArg("spooler");
     94   command_line.AppendArg("/y");
     95 
     96   base::LaunchOptions options;
     97   options.wait = true;
     98   options.start_hidden = true;
     99   VLOG(0) << command_line.GetCommandLineString();
    100   base::LaunchProcess(command_line, options, NULL);
    101 }
    102 
    103 HRESULT RegisterPortMonitor(bool install, const base::FilePath& install_path) {
    104   DCHECK(install || install_path.empty());
    105   base::FilePath target_path = GetNativeSystemPath(GetPortMonitorDllName());
    106   if (target_path.empty()) {
    107     LOG(ERROR) << "Unable to get port monitor target path.";
    108     return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
    109   }
    110   if (install) {
    111     base::FilePath source_path =
    112         install_path.Append(GetPortMonitorDllName());
    113     if (!base::CopyFile(source_path, target_path)) {
    114       LOG(ERROR) << "Unable copy port monitor dll from " <<
    115           source_path.value() << " to " << target_path.value();
    116       return GetLastHResult();
    117     }
    118   } else if (!base::PathExists(target_path)) {
    119     // Already removed.  Just "succeed" silently.
    120     return S_OK;
    121   }
    122 
    123   base::FilePath regsvr32_path = GetNativeSystemPath(L"regsvr32.exe");
    124   if (regsvr32_path.empty()) {
    125     LOG(ERROR) << "Can't find regsvr32.exe.";
    126     return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
    127   }
    128 
    129   CommandLine command_line(regsvr32_path);
    130   command_line.AppendArg("/s");
    131   if (!install) {
    132     command_line.AppendArg("/u");
    133   }
    134 
    135   // Use system32 path here because otherwise ::AddMonitor would fail.
    136   command_line.AppendArgPath(GetSystemPath(GetPortMonitorDllName()));
    137 
    138   base::LaunchOptions options;
    139   options.wait = true;
    140 
    141   base::win::ScopedHandle regsvr32_handle;
    142   if (!base::LaunchProcess(command_line.GetCommandLineString(), options,
    143                            &regsvr32_handle)) {
    144     LOG(ERROR) << "Unable to launch regsvr32.exe.";
    145     return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
    146   }
    147 
    148   DWORD exit_code = S_OK;
    149   if (install) {
    150     if (!GetExitCodeProcess(regsvr32_handle.Get(), &exit_code)) {
    151       LOG(ERROR) << "Unable to get regsvr32.exe exit code.";
    152       return GetLastHResult();
    153     }
    154     if (exit_code != 0) {
    155       LOG(ERROR) << "Regsvr32.exe failed with " << exit_code;
    156       return HRESULT_FROM_WIN32(exit_code);
    157     }
    158   } else {
    159     if (!base::DeleteFile(target_path, false)) {
    160       SpoolerServiceCommand("stop");
    161       bool deleted = base::DeleteFile(target_path, false);
    162       SpoolerServiceCommand("start");
    163 
    164       if(!deleted) {
    165         LOG(ERROR) << "Unable to delete " << target_path.value();
    166         return HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
    167       }
    168     }
    169   }
    170   return S_OK;
    171 }
    172 
    173 DWORDLONG GetVersionNumber() {
    174   DWORDLONG retval = 0;
    175   scoped_ptr<FileVersionInfo> version_info(
    176       FileVersionInfo::CreateFileVersionInfoForCurrentModule());
    177   if (version_info.get()) {
    178     FileVersionInfoWin* version_info_win =
    179         static_cast<FileVersionInfoWin*>(version_info.get());
    180     VS_FIXEDFILEINFO* fixed_file_info = version_info_win->fixed_file_info();
    181     retval = fixed_file_info->dwFileVersionMS;
    182     retval <<= 32;
    183     retval |= fixed_file_info->dwFileVersionLS;
    184   }
    185   return retval;
    186 }
    187 
    188 UINT CALLBACK CabinetCallback(PVOID data,
    189                               UINT notification,
    190                               UINT_PTR param1,
    191                               UINT_PTR param2) {
    192   const base::FilePath* temp_path(
    193       reinterpret_cast<const base::FilePath*>(data));
    194   if (notification == SPFILENOTIFY_FILEINCABINET) {
    195     FILE_IN_CABINET_INFO* info =
    196         reinterpret_cast<FILE_IN_CABINET_INFO*>(param1);
    197     for (int i = 0; i < arraysize(kDependencyList); i++) {
    198       base::FilePath base_name(info->NameInCabinet);
    199       base_name = base_name.BaseName();
    200       if (base::FilePath::CompareEqualIgnoreCase(base_name.value().c_str(),
    201                                                  kDependencyList[i])) {
    202         StringCchCopy(info->FullTargetName, MAX_PATH,
    203                       temp_path->Append(kDependencyList[i]).value().c_str());
    204         return FILEOP_DOIT;
    205       }
    206     }
    207     return FILEOP_SKIP;
    208   }
    209   return NO_ERROR;
    210 }
    211 
    212 void ReadyDriverDependencies(const base::FilePath& destination) {
    213   base::FilePath destination_copy(destination);
    214   if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
    215     // GetCorePrinterDrivers and GetPrinterDriverPackagePath only exist on
    216     // Vista and later. Winspool.drv must be delayloaded so these calls don't
    217     // create problems on XP.
    218     DWORD size = MAX_PATH;
    219     wchar_t package_path[MAX_PATH] = {0};
    220     CORE_PRINTER_DRIVER driver;
    221     GetCorePrinterDrivers(NULL, NULL, L"{D20EA372-DD35-4950-9ED8-A6335AFE79F5}",
    222                           1, &driver);
    223     GetPrinterDriverPackagePath(NULL, NULL, NULL, driver.szPackageID,
    224                                 package_path, MAX_PATH, &size);
    225     SetupIterateCabinet(package_path, 0, &CabinetCallback, &destination_copy);
    226   } else {
    227     // Driver files are in the sp3 cab.
    228     base::FilePath package_path;
    229     PathService::Get(base::DIR_WINDOWS, &package_path);
    230     package_path = package_path.Append(L"Driver Cache\\i386\\sp3.cab");
    231     SetupIterateCabinet(package_path.value().c_str(), 0, &CabinetCallback,
    232                         &destination_copy);
    233 
    234     // Copy the rest from the driver cache or system dir.
    235     base::FilePath driver_cache_path;
    236     PathService::Get(base::DIR_WINDOWS, &driver_cache_path);
    237     driver_cache_path = driver_cache_path.Append(L"Driver Cache\\i386");
    238     for (size_t i = 0; i < arraysize(kDependencyList); ++i) {
    239       base::FilePath dst_path = destination.Append(kDependencyList[i]);
    240       if (!base::PathExists(dst_path)) {
    241         base::FilePath src_path = driver_cache_path.Append(kDependencyList[i]);
    242         if (!base::PathExists(src_path))
    243           src_path = GetSystemPath(kDependencyList[i]);
    244         base::CopyFile(src_path, dst_path);
    245       }
    246     }
    247   }
    248 }
    249 
    250 HRESULT InstallDriver(const base::FilePath& install_path) {
    251   base::ScopedTempDir temp_path;
    252   if (!temp_path.CreateUniqueTempDir())
    253     return HRESULT_FROM_WIN32(ERROR_CANNOT_MAKE);
    254   ReadyDriverDependencies(temp_path.path());
    255 
    256   std::vector<base::string16> dependent_array;
    257   // Add all files. AddPrinterDriverEx will removes unnecessary.
    258   for (size_t i = 0; i < arraysize(kDependencyList); ++i) {
    259     base::FilePath file_path = temp_path.path().Append(kDependencyList[i]);
    260     if (base::PathExists(file_path))
    261       dependent_array.push_back(file_path.value());
    262     else
    263       LOG(WARNING) << "File is missing: " << file_path.BaseName().value();
    264   }
    265 
    266   // Set up paths for the files we depend on.
    267   base::FilePath data_file = install_path.Append(kDataFileName);
    268   base::FilePath xps_path = temp_path.path().Append(kDriverName);
    269   base::FilePath ui_path = temp_path.path().Append(kUiDriverName);
    270   base::FilePath ui_help_path = temp_path.path().Append(kHelpName);
    271 
    272   if (!base::PathExists(xps_path)) {
    273     return HRESULT_FROM_WIN32(ERROR_BAD_DRIVER);
    274   }
    275 
    276   DRIVER_INFO_6 driver_info = {0};
    277   // Set up supported print system version.  Must be 3.
    278   driver_info.cVersion = 3;
    279 
    280   // None of the print API structures likes constant strings even though they
    281   // don't modify the string.  const_casting is the cleanest option.
    282   driver_info.pDataFile = const_cast<LPWSTR>(data_file.value().c_str());
    283   driver_info.pHelpFile = const_cast<LPWSTR>(ui_help_path.value().c_str());
    284   driver_info.pDriverPath = const_cast<LPWSTR>(xps_path.value().c_str());
    285   driver_info.pConfigFile = const_cast<LPWSTR>(ui_path.value().c_str());
    286 
    287   base::string16 dependent_files(JoinString(dependent_array, L'\n'));
    288   dependent_files.push_back(L'\n');
    289   std::replace(dependent_files.begin(), dependent_files.end(), L'\n', L'\0');
    290   driver_info.pDependentFiles = &dependent_files[0];
    291 
    292   // Set up user visible strings.
    293   base::string16 manufacturer = LoadLocalString(IDS_GOOGLE);
    294   driver_info.pszMfgName = const_cast<LPWSTR>(manufacturer.c_str());
    295   driver_info.pszProvider = const_cast<LPWSTR>(manufacturer.c_str());
    296   driver_info.pszOEMUrl = const_cast<LPWSTR>(kGcpUrl);
    297   driver_info.dwlDriverVersion = GetVersionNumber();
    298   base::string16 driver_name = LoadLocalString(IDS_DRIVER_NAME);
    299   driver_info.pName = const_cast<LPWSTR>(driver_name.c_str());
    300 
    301   if (!::AddPrinterDriverEx(NULL, 6, reinterpret_cast<BYTE*>(&driver_info),
    302                             APD_COPY_NEW_FILES | APD_COPY_FROM_DIRECTORY)) {
    303     LOG(ERROR) << "Unable to add printer driver";
    304     return GetLastHResult();
    305   }
    306   return S_OK;
    307 }
    308 
    309 HRESULT UninstallDriver() {
    310   int tries = 3;
    311   base::string16 driver_name = LoadLocalString(IDS_DRIVER_NAME);
    312   while (!DeletePrinterDriverEx(NULL,
    313                                 NULL,
    314                                 const_cast<LPWSTR>(driver_name.c_str()),
    315                                 DPD_DELETE_UNUSED_FILES,
    316                                 0) && tries > 0) {
    317     if (GetLastError() == ERROR_UNKNOWN_PRINTER_DRIVER) {
    318       LOG(WARNING) << "Print driver is already uninstalled.";
    319       return S_OK;
    320     }
    321     // After deleting the printer it can take a few seconds before
    322     // the driver is free for deletion.  Retry a few times before giving up.
    323     LOG(WARNING) << "Attempt to delete printer driver failed.  Retrying.";
    324     tries--;
    325     Sleep(2000);
    326   }
    327   if (tries <= 0) {
    328     HRESULT result = GetLastHResult();
    329     LOG(ERROR) << "Unable to delete printer driver.";
    330     return result;
    331   }
    332   return S_OK;
    333 }
    334 
    335 HRESULT InstallPrinter(void) {
    336   PRINTER_INFO_2 printer_info = {0};
    337 
    338   // None of the print API structures likes constant strings even though they
    339   // don't modify the string.  const_casting is the cleanest option.
    340   base::string16 driver_name = LoadLocalString(IDS_DRIVER_NAME);
    341   printer_info.pDriverName = const_cast<LPWSTR>(driver_name.c_str());
    342   printer_info.pPrinterName = const_cast<LPWSTR>(driver_name.c_str());
    343   printer_info.pComment =  const_cast<LPWSTR>(driver_name.c_str());
    344   printer_info.pLocation = const_cast<LPWSTR>(kGcpUrl);
    345   base::string16 port_name;
    346   printer_info.pPortName = const_cast<LPWSTR>(kPortName);
    347   printer_info.Attributes = PRINTER_ATTRIBUTE_DIRECT|PRINTER_ATTRIBUTE_LOCAL;
    348   printer_info.pPrintProcessor = const_cast<LPWSTR>(L"winprint");
    349   HANDLE handle = AddPrinter(NULL, 2, reinterpret_cast<BYTE*>(&printer_info));
    350   if (handle == NULL) {
    351     HRESULT result = GetLastHResult();
    352     LOG(ERROR) << "Unable to add printer";
    353     return result;
    354   }
    355   ClosePrinter(handle);
    356   return S_OK;
    357 }
    358 
    359 HRESULT UninstallPrinter(void) {
    360   HANDLE handle = NULL;
    361   PRINTER_DEFAULTS printer_defaults = {0};
    362   printer_defaults.DesiredAccess = PRINTER_ALL_ACCESS;
    363   base::string16 driver_name = LoadLocalString(IDS_DRIVER_NAME);
    364   if (!OpenPrinter(const_cast<LPWSTR>(driver_name.c_str()),
    365                    &handle,
    366                    &printer_defaults)) {
    367     // If we can't open the printer, it was probably already removed.
    368     LOG(WARNING) << "Unable to open printer";
    369     return S_OK;
    370   }
    371   if (!DeletePrinter(handle)) {
    372     HRESULT result = GetLastHResult();
    373     LOG(ERROR) << "Unable to delete printer";
    374     ClosePrinter(handle);
    375     return result;
    376   }
    377   ClosePrinter(handle);
    378   return S_OK;
    379 }
    380 
    381 bool IsOSSupported() {
    382   // We don't support XP service pack 2 or older.
    383   base::win::Version version = base::win::GetVersion();
    384   return (version > base::win::VERSION_XP) ||
    385       ((version == base::win::VERSION_XP) &&
    386        (base::win::OSInfo::GetInstance()->service_pack().major >= 3));
    387 }
    388 
    389 HRESULT RegisterVirtualDriver(const base::FilePath& install_path) {
    390   HRESULT result = S_OK;
    391 
    392   DCHECK(base::DirectoryExists(install_path));
    393   if (!IsOSSupported()) {
    394     LOG(ERROR) << "Requires XP SP3 or later.";
    395     return HRESULT_FROM_WIN32(ERROR_OLD_WIN_VERSION);
    396   }
    397 
    398   result = InstallDriver(install_path);
    399   if (FAILED(result)) {
    400     LOG(ERROR) << "Unable to install driver.";
    401     return result;
    402   }
    403 
    404   result = RegisterPortMonitor(true, install_path);
    405   if (FAILED(result)) {
    406     LOG(ERROR) << "Unable to register port monitor.";
    407     return result;
    408   }
    409 
    410   result = InstallPrinter();
    411   if (FAILED(result) &&
    412       result != HRESULT_FROM_WIN32(ERROR_PRINTER_ALREADY_EXISTS)) {
    413     LOG(ERROR) << "Unable to install printer.";
    414     return result;
    415   }
    416   return S_OK;
    417 }
    418 
    419 HRESULT TryUnregisterVirtualDriver() {
    420   HRESULT result = S_OK;
    421   result = UninstallPrinter();
    422   if (FAILED(result)) {
    423     LOG(ERROR) << "Unable to delete printer.";
    424     return result;
    425   }
    426   result = UninstallDriver();
    427   if (FAILED(result)) {
    428     LOG(ERROR) << "Unable to remove driver.";
    429     return result;
    430   }
    431   // The second argument is ignored if the first is false.
    432   result = RegisterPortMonitor(false, base::FilePath());
    433   if (FAILED(result)) {
    434     LOG(ERROR) << "Unable to remove port monitor.";
    435     return result;
    436   }
    437   return S_OK;
    438 }
    439 
    440 HRESULT UnregisterVirtualDriver() {
    441   HRESULT hr = S_FALSE;
    442   for (int i = 0; i < 2; ++i) {
    443     hr = TryUnregisterVirtualDriver();
    444     if (SUCCEEDED(hr)) {
    445       break;
    446     }
    447     // Restart spooler and try again.
    448     SpoolerServiceCommand("stop");
    449     SpoolerServiceCommand("start");
    450   }
    451   return hr;
    452 }
    453 
    454 HRESULT DoUninstall() {
    455   DeleteGoogleUpdateKeys(kGoogleUpdateProductId);
    456   HRESULT result = UnregisterVirtualDriver();
    457   if (FAILED(result))
    458     return result;
    459   DeleteUninstallKey(kUninstallId);
    460   DeleteProgramDir(kDelete);
    461   return S_OK;
    462 }
    463 
    464 HRESULT DoUnregister() {
    465   return UnregisterVirtualDriver();
    466 }
    467 
    468 HRESULT DoRegister(const base::FilePath& install_path) {
    469   HRESULT result = UnregisterVirtualDriver();
    470   if (FAILED(result))
    471     return result;
    472   return RegisterVirtualDriver(install_path);
    473 }
    474 
    475 HRESULT DoDelete(const base::FilePath& install_path) {
    476   if (install_path.value().empty())
    477     return E_INVALIDARG;
    478   if (!base::DirectoryExists(install_path))
    479     return S_FALSE;
    480   Sleep(5000);  // Give parent some time to exit.
    481   return base::DeleteFile(install_path, true) ? S_OK : E_FAIL;
    482 }
    483 
    484 HRESULT DoInstall(const base::FilePath& install_path) {
    485   HRESULT result = UnregisterVirtualDriver();
    486   if (FAILED(result)) {
    487     LOG(ERROR) << "Unable to unregister.";
    488     return result;
    489   }
    490   base::FilePath old_install_path = GetInstallLocation(kUninstallId);
    491   if (!old_install_path.value().empty() &&
    492       install_path != old_install_path) {
    493     if (base::DirectoryExists(old_install_path))
    494       base::DeleteFile(old_install_path, true);
    495   }
    496   CreateUninstallKey(kUninstallId, LoadLocalString(IDS_DRIVER_NAME),
    497                      kUninstallSwitch);
    498   result = RegisterVirtualDriver(install_path);
    499   if (FAILED(result))
    500     return result;
    501   SetGoogleUpdateKeys(kGoogleUpdateProductId, kNameValue);
    502   return result;
    503 }
    504 
    505 HRESULT ExecuteCommands() {
    506   const CommandLine& command_line = *CommandLine::ForCurrentProcess();
    507 
    508   base::FilePath exe_path;
    509   if (FAILED(PathService::Get(base::DIR_EXE, &exe_path)) ||
    510       !base::DirectoryExists(exe_path)) {
    511     return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
    512   }
    513 
    514   if (command_line.HasSwitch(kDelete)) {
    515     return DoDelete(command_line.GetSwitchValuePath(kDelete));
    516   } else if (command_line.HasSwitch(kUninstallSwitch)) {
    517     return DoUninstall();
    518   } else if (command_line.HasSwitch(kInstallSwitch)) {
    519     return DoInstall(exe_path);
    520   } else if (command_line.HasSwitch(kUnregisterSwitch)) {
    521     return DoUnregister();
    522   } else if (command_line.HasSwitch(kRegisterSwitch)) {
    523     return DoRegister(exe_path);
    524   }
    525 
    526   return E_INVALIDARG;
    527 }
    528 
    529 }  // namespace
    530 
    531 }  // namespace cloud_print
    532 
    533 int WINAPI WinMain(__in  HINSTANCE hInstance,
    534                    __in  HINSTANCE hPrevInstance,
    535                    __in  LPSTR lpCmdLine,
    536                    __in  int nCmdShow) {
    537   using namespace cloud_print;
    538 
    539   base::AtExitManager at_exit_manager;
    540   CommandLine::Init(0, NULL);
    541 
    542   HRESULT retval = ExecuteCommands();
    543 
    544   if (retval == HRESULT_FROM_WIN32(ERROR_BAD_DRIVER)) {
    545     SetGoogleUpdateError(kGoogleUpdateProductId,
    546                          LoadLocalString(IDS_ERROR_NO_XPS));
    547   } else if (FAILED(retval)) {
    548     SetGoogleUpdateError(kGoogleUpdateProductId, retval);
    549   }
    550 
    551   VLOG(0) << GetErrorMessage(retval)
    552           << " HRESULT=0x" << std::setbase(16) << retval;
    553 
    554   // Installer is silent by default as required by Google Update.
    555   if (CommandLine::ForCurrentProcess()->HasSwitch("verbose")) {
    556     DisplayWindowsMessage(NULL, retval, LoadLocalString(IDS_DRIVER_NAME));
    557   }
    558   return retval;
    559 }
    560