Example #1
0
  void Service::install()
  {
    // Open service manager
    SC_HANDLE pSCM = OpenSCManagerW( nullptr, nullptr,
      SC_MANAGER_CREATE_SERVICE );
    if ( !pSCM )
      EXCEPT_WINAPI( L"Couldn't open service manager" );

    // Fetch & escape path, add argument
    WCHAR wszPath[MAX_PATH];
    if ( !GetModuleFileNameW( nullptr, wszPath, MAX_PATH ) )
      EXCEPT_WINAPI( L"Couldn't get module file name" );
    PathQuoteSpacesW( wszPath );
    wcscat_s( wszPath, MAX_PATH, L" -s" );

    // Create service
    SC_HANDLE hService = CreateServiceW( pSCM, mName.c_str(),
      mDisplayName.c_str(), SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
      SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, wszPath, nullptr,
      nullptr, nullptr, L"NT AUTHORITY\\NetworkService", nullptr );
    if ( !hService )
      EXCEPT_WINAPI( L"Couldn't create service" );

    // Set description
    SERVICE_DESCRIPTIONW descriptor;
    descriptor.lpDescription = new WCHAR[mDescription.length()];
    wcscpy_s( descriptor.lpDescription, mDescription.length(), mDescription.c_str() );
    ChangeServiceConfig2W( hService, SERVICE_CONFIG_DESCRIPTION, &descriptor );
    delete[] descriptor.lpDescription;

    // Done
    CloseServiceHandle( hService );
    CloseServiceHandle( pSCM );
  }
Example #2
0
/*************************************************************************
 * PathQuoteSpaces [SHELL32.55]
 */
VOID WINAPI PathQuoteSpacesAW (LPVOID lpszPath)
{
	if(SHELL_OsIsUnicode())
            PathQuoteSpacesW(lpszPath);
        else
            PathQuoteSpacesA(lpszPath);
}
Example #3
0
/**
 * Determines if the MozillaMaintenance service path needs to be updated
 * and fixes it if it is wrong.
 *
 * @param service             A handle to the service to fix.
 * @param currentServicePath  The current (possibly wrong) path that is used.
 * @param servicePathWasWrong Out parameter set to TRUE if a fix was needed.
 * @return TRUE if the service path is now correct.
*/
BOOL
FixServicePath(SC_HANDLE service,
               LPCWSTR currentServicePath,
               BOOL &servicePathWasWrong)
{
  // When we originally upgraded the MozillaMaintenance service we
  // would uninstall the service on each upgrade.  This had an
  // intermittent error which could cause the service to use the file
  // maintenanceservice_tmp.exe as the install path.  Only a small number
  // of Nightly users would be affected by this, but we check for this
  // state here and fix the user if they are affected.
  //
  // We also fix the path in the case of the path not being quoted.
  size_t currentServicePathLen = wcslen(currentServicePath);
  bool doesServiceHaveCorrectPath =
    currentServicePathLen > 2 &&
    !wcsstr(currentServicePath, L"maintenanceservice_tmp.exe") &&
    currentServicePath[0] == L'\"' &&
    currentServicePath[currentServicePathLen - 1] == L'\"';

  if (doesServiceHaveCorrectPath) {
    LOG(("The MozillaMaintenance service path is correct."));
    servicePathWasWrong = FALSE;
    return TRUE;
  }
  // This is a recoverable situation so not logging as a warning
  LOG(("The MozillaMaintenance path is NOT correct. It was: %ls",
       currentServicePath));

  servicePathWasWrong = TRUE;
  WCHAR fixedPath[MAX_PATH + 1] = { L'\0' };
  wcsncpy(fixedPath, currentServicePath, MAX_PATH);
  PathUnquoteSpacesW(fixedPath);
  if (!PathRemoveFileSpecW(fixedPath)) {
    LOG_WARN(("Couldn't remove file spec.  (%d)", GetLastError()));
    return FALSE;
  }
  if (!PathAppendSafe(fixedPath, L"maintenanceservice.exe")) {
    LOG_WARN(("Couldn't append file spec.  (%d)", GetLastError()));
    return FALSE;
  }
  PathQuoteSpacesW(fixedPath);


  if (!ChangeServiceConfigW(service, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE,
                            SERVICE_NO_CHANGE, fixedPath, nullptr, nullptr,
                            nullptr, nullptr, nullptr, nullptr)) {
    LOG_WARN(("Could not fix service path.  (%d)", GetLastError()));
    return FALSE;
  }

  LOG(("Fixed service path to: %ls.", fixedPath));
  return TRUE;
}
Example #4
0
bool LSAPIInit::setShellFolderVariable(LPCWSTR pwzVariable, int nFolder)
{
    bool bSuccess = false;
    wchar_t wzPath[MAX_PATH] = { 0 };

    if (GetShellFolderPath(nFolder, wzPath, MAX_PATH))
    {
        PathAddBackslashEx(wzPath, MAX_PATH);
        PathQuoteSpacesW(wzPath);

        m_smSettingsManager->SetVariable(pwzVariable, wzPath);
        bSuccess = true;
    }

    return bSuccess;
}
Example #5
0
/**
 * Determines if the MozillaMaintenance service path needs to be updated
 * and fixes it if it is wrong.
 *
 * @param service             A handle to the service to fix.
 * @param currentServicePath  The current (possibly wrong) path that is used.
 * @param servicePathWasWrong Out parameter set to TRUE if a fix was needed.
 * @return TRUE if the service path is now correct.
*/
BOOL
FixServicePath(SC_HANDLE service,
               LPCWSTR currentServicePath,
               BOOL &servicePathWasWrong)
{
  // When we originally upgraded the MozillaMaintenance service we
  // would uninstall the service on each upgrade.  This had an
  // intermittent error which could cause the service to use the file
  // maintenanceservice_tmp.exe as the install path.  Only a small number
  // of Nightly users would be affected by this, but we check for this
  // state here and fix the user if they are affected.
  bool doesServiceHaveCorrectPath =
    !wcsstr(currentServicePath, L"maintenanceservice_tmp.exe");
  if (doesServiceHaveCorrectPath) {
    LOG(("The MozillaMaintenance service path is correct.\n"));
    servicePathWasWrong = FALSE;
    return TRUE;
  }
  LOG(("The MozillaMaintenance path is NOT correct.\n"));
  servicePathWasWrong = TRUE;

  WCHAR fixedPath[MAX_PATH + 1] = { L'\0' };
  wcsncpy(fixedPath, currentServicePath, MAX_PATH);
  PathUnquoteSpacesW(fixedPath);
  if (!PathRemoveFileSpecW(fixedPath)) {
    LOG(("Couldn't remove file spec. (%d)\n", GetLastError()));
    return FALSE;
  }
  if (!PathAppendSafe(fixedPath, L"maintenanceservice.exe")) {
    LOG(("Couldn't append file spec. (%d)\n", GetLastError()));
    return FALSE;
  }
  PathQuoteSpacesW(fixedPath);


  if (!ChangeServiceConfigW(service, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE,
                            SERVICE_NO_CHANGE, fixedPath, NULL, NULL, NULL,
                            NULL, NULL, NULL)) {
    LOG(("Could not fix service path. (%d)\n", GetLastError()));
    return FALSE;
  }

  LOG(("Fixed service path to: %ls.\n", fixedPath));
  return TRUE;
}
Example #6
0
/**
 * Installs or upgrades the SVC_NAME service.
 * If an existing service is already installed, we replace it with the
 * currently running process.
 *
 * @param  action The action to perform.
 * @return TRUE if the service was installed/upgraded
 */
BOOL
SvcInstall(SvcInstallAction action)
{
  // Get a handle to the local computer SCM database with full access rights.
  nsAutoServiceHandle schSCManager(OpenSCManager(NULL, NULL, 
                                                 SC_MANAGER_ALL_ACCESS));
  if (!schSCManager) {
    LOG(("Could not open service manager.  (%d)\n", GetLastError()));
    return FALSE;
  }

  WCHAR newServiceBinaryPath[MAX_PATH + 1];
  if (!GetModuleFileNameW(NULL, newServiceBinaryPath, 
                          sizeof(newServiceBinaryPath) / 
                          sizeof(newServiceBinaryPath[0]))) {
    LOG(("Could not obtain module filename when attempting to "
         "install service. (%d)\n",
         GetLastError()));
    return FALSE;
  }

  // Check if we already have the service installed.
  nsAutoServiceHandle schService(OpenServiceW(schSCManager, 
                                              SVC_NAME, 
                                              SERVICE_ALL_ACCESS));
  DWORD lastError = GetLastError();
  if (!schService && ERROR_SERVICE_DOES_NOT_EXIST != lastError) {
    // The service exists but we couldn't open it
    LOG(("Could not open service.  (%d)\n", GetLastError()));
    return FALSE;
  }
  
  if (schService) {
    // The service exists but it may not have the correct permissions.
    // This could happen if the permissions were not set correctly originally
    // or have been changed after the installation.  This will reset the 
    // permissions back to allow limited user accounts.
    if (!SetUserAccessServiceDACL(schService)) {
      LOG(("Could not reset security ACE on service handle. It might not be "
           "possible to start the service. This error should never "
           "happen.  (%d)\n", GetLastError()));
    }

    // The service exists and we opened it
    DWORD bytesNeeded;
    if (!QueryServiceConfigW(schService, NULL, 0, &bytesNeeded) && 
        GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
      LOG(("Could not determine buffer size for query service config.  (%d)\n", 
           GetLastError()));
      return FALSE;
    }

    // Get the service config information, in particular we want the binary 
    // path of the service.
    nsAutoArrayPtr<char> serviceConfigBuffer = new char[bytesNeeded];
    if (!QueryServiceConfigW(schService, 
        reinterpret_cast<QUERY_SERVICE_CONFIGW*>(serviceConfigBuffer.get()), 
        bytesNeeded, &bytesNeeded)) {
      LOG(("Could open service but could not query service config.  (%d)\n", 
           GetLastError()));
      return FALSE;
    }
    QUERY_SERVICE_CONFIGW &serviceConfig = 
      *reinterpret_cast<QUERY_SERVICE_CONFIGW*>(serviceConfigBuffer.get());

    // Check if we need to fix the service path
    BOOL servicePathWasWrong;
    static BOOL alreadyCheckedFixServicePath = FALSE;
    if (!alreadyCheckedFixServicePath) {
      if (!FixServicePath(schService, serviceConfig.lpBinaryPathName,
                          servicePathWasWrong)) {
        LOG(("Could not fix service path. This should never happen. (%d)\n",
              GetLastError()));
        // True is returned because the service is pointing to
        // maintenanceservice_tmp.exe so it actually was upgraded to the
        // newest installed service.
        return TRUE;
      } else if (servicePathWasWrong) {
        // Now that the path is fixed we should re-attempt the install.
        // This current process' image path is maintenanceservice_tmp.exe.
        // The service used to point to maintenanceservice_tmp.exe.
        // The service was just fixed to point to maintenanceservice.exe.
        // Re-attempting an install from scratch will work as normal.
        alreadyCheckedFixServicePath = TRUE;
        LOG(("Restarting install action: %d\n", action));
        return SvcInstall(action);
      }
    }

    // Ensure the service path is not quoted. We own this memory and know it to
    // be large enough for the quoted path, so it is large enough for the
    // unquoted path.  This function cannot fail.
    PathUnquoteSpacesW(serviceConfig.lpBinaryPathName);

    // Obtain the existing maintenanceservice file's version number and
    // the new file's version number.  Versions are in the format of
    // A.B.C.D.
    DWORD existingA, existingB, existingC, existingD;
    DWORD newA, newB, newC, newD; 
    BOOL obtainedExistingVersionInfo = 
      GetVersionNumberFromPath(serviceConfig.lpBinaryPathName, 
                               existingA, existingB, 
                               existingC, existingD);
    if (!GetVersionNumberFromPath(newServiceBinaryPath, newA, 
                                 newB, newC, newD)) {
      LOG(("Could not obtain version number from new path\n"));
      return FALSE;
    }

    // Check if we need to replace the old binary with the new one
    // If we couldn't get the old version info then we assume we should 
    // replace it.
    if (ForceInstallSvc == action ||
        !obtainedExistingVersionInfo || 
        (existingA < newA) ||
        (existingA == newA && existingB < newB) ||
        (existingA == newA && existingB == newB && 
         existingC < newC) ||
        (existingA == newA && existingB == newB && 
         existingC == newC && existingD < newD)) {

      // We have a newer updater, so update the description from the INI file.
      UpdateServiceDescription(schService);

      schService.reset();
      if (!StopService()) {
        return FALSE;
      }

      if (!wcscmp(newServiceBinaryPath, serviceConfig.lpBinaryPathName)) {
        LOG(("File is already in the correct location, no action needed for "
             "upgrade.  The path is: \"%ls\"\n", newServiceBinaryPath));
        return TRUE;
      }

      BOOL result = TRUE;

      // Attempt to copy the new binary over top the existing binary.
      // If there is an error we try to move it out of the way and then
      // copy it in.  First try the safest / easiest way to overwrite the file.
      if (!CopyFileW(newServiceBinaryPath, 
                     serviceConfig.lpBinaryPathName, FALSE)) {
        LOG(("Could not overwrite old service binary file. "
             "This should never happen, but if it does the next upgrade will "
             "fix it, the service is not a critical component that needs to be "
             "installed for upgrades to work. (%d)\n", GetLastError()));

        // We rename the last 3 filename chars in an unsafe way.  Manually
        // verify there are more than 3 chars for safe failure in MoveFileExW.
        const size_t len = wcslen(serviceConfig.lpBinaryPathName);
        if (len > 3) {
          // Calculate the temp file path that we're moving the file to. This 
          // is the same as the proper service path but with a .old extension.
          LPWSTR oldServiceBinaryTempPath = 
            new WCHAR[len + 1];
          memset(oldServiceBinaryTempPath, 0, (len + 1) * sizeof (WCHAR));
          wcsncpy(oldServiceBinaryTempPath, serviceConfig.lpBinaryPathName, len);
          // Rename the last 3 chars to 'old'
          wcsncpy(oldServiceBinaryTempPath + len - 3, L"old", 3);

          // Move the current (old) service file to the temp path.
          if (MoveFileExW(serviceConfig.lpBinaryPathName, 
                          oldServiceBinaryTempPath, 
                          MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH)) {
            // The old binary is moved out of the way, copy in the new one.
            if (!CopyFileW(newServiceBinaryPath, 
                           serviceConfig.lpBinaryPathName, FALSE)) {
              // It is best to leave the old service binary in this condition.
              LOG(("ERROR: The new service binary could not be copied in."
                   " The service will not be upgraded.\n"));
              result = FALSE;
            } else {
              LOG(("The new service binary was copied in by first moving the"
                   " old one out of the way.\n"));
            }

            // Attempt to get rid of the old service temp path.
            if (DeleteFileW(oldServiceBinaryTempPath)) {
              LOG(("The old temp service path was deleted: %ls.\n", 
                   oldServiceBinaryTempPath));
            } else {
              // The old temp path could not be removed.  It will be removed
              // the next time the user can't copy the binary in or on uninstall.
              LOG(("WARNING: The old temp service path was not deleted.\n"));
            }
          } else {
            // It is best to leave the old service binary in this condition.
            LOG(("ERROR: Could not move old service file out of the way from:"
                 " \"%ls\" to \"%ls\". Service will not be upgraded. (%d)\n", 
                 serviceConfig.lpBinaryPathName, 
                 oldServiceBinaryTempPath, GetLastError()));
            result = FALSE;
          }
          delete[] oldServiceBinaryTempPath;
        } else {
            // It is best to leave the old service binary in this condition.
            LOG(("ERROR: Service binary path was less than 3, service will"
                 " not be updated.  This should never happen.\n"));
            result = FALSE;
        }
      } else {
        LOG(("The new service binary was copied in.\n"));
      }

      // We made a copy of ourselves to the existing location.
      // The tmp file (the process of which we are executing right now) will be
      // left over.  Attempt to delete the file on the next reboot.
      if (MoveFileExW(newServiceBinaryPath, NULL, MOVEFILE_DELAY_UNTIL_REBOOT)) {
        LOG(("Deleting the old file path on the next reboot: %ls.\n", 
             newServiceBinaryPath));
      } else {
        LOG(("Call to delete the old file path failed: %ls.\n", 
             newServiceBinaryPath));
      }
      
      return result;
    }

    // We don't need to copy ourselves to the existing location.
    // The tmp file (the process of which we are executing right now) will be
    // left over.  Attempt to delete the file on the next reboot.
    MoveFileExW(newServiceBinaryPath, NULL, MOVEFILE_DELAY_UNTIL_REBOOT);
    
    // nothing to do, we already have a newer service installed
    return TRUE; 
  }
  
  // If the service does not exist and we are upgrading, don't install it.
  if (UpgradeSvc == action) {
    // The service does not exist and we are upgrading, so don't install it
    return TRUE;
  }

  // Quote the path only if it contains spaces.
  PathQuoteSpacesW(newServiceBinaryPath);
  // The service does not already exist so create the service as on demand
  schService.own(CreateServiceW(schSCManager, SVC_NAME, SVC_DISPLAY_NAME,
                                SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
                                SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,
                                newServiceBinaryPath, NULL, NULL, NULL, 
                                NULL, NULL));
  if (!schService) {
    LOG(("Could not create Windows service. "
         "This error should never happen since a service install "
         "should only be called when elevated. (%d)\n", GetLastError()));
    return FALSE;
  } 

  if (!SetUserAccessServiceDACL(schService)) {
    LOG(("Could not set security ACE on service handle, the service will not "
         "be able to be started from unelevated processes. "
         "This error should never happen.  (%d)\n", 
         GetLastError()));
  }

  UpdateServiceDescription(schService);

  return TRUE;
}
Example #7
0
void LSAPIInit::setLitestepVars()
{
    wchar_t wzTemp[MAX_PATH];
    DWORD dwLength = MAX_PATH;

    // just using a shorter name, no real reason to re-assign.
    SettingsManager *pSM = m_smSettingsManager;

    // Set the variable "litestepdir" since it was never set
    if (SUCCEEDED(StringCchCopyW(wzTemp, MAX_PATH, m_wzLitestepPath)))
    {
        PathAddBackslashEx(wzTemp, MAX_PATH);
        PathQuoteSpacesW(wzTemp);
        pSM->SetVariable(L"litestepdir", wzTemp);
    }

    if (GetWindowsDirectoryW(wzTemp, MAX_PATH))
    {
        PathAddBackslashEx(wzTemp, MAX_PATH);
        pSM->SetVariable(L"windir", wzTemp);
    }

    if (GetUserNameW(wzTemp, &dwLength))
    {
        PathQuoteSpacesW(wzTemp);
        pSM->SetVariable(L"username", wzTemp);
    }

    pSM->SetVariable(L"nl", L"\n", true);
    pSM->SetVariable(L"cr", L"\r", true);
    pSM->SetVariable(L"dollar", L"$", true);
    pSM->SetVariable(L"at", L"@", true);

    pSM->SetVariable(L"bitbucket", L"::{645FF040-5081-101B-9F08-00AA002F954E}");
    pSM->SetVariable(L"documents", L"::{450D8FBA-AD25-11D0-98A8-0800361B1103}");
    pSM->SetVariable(L"drives", L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}");
    pSM->SetVariable(L"network", L"::{208D2C60-3AEA-1069-A2D7-08002B30309D}");
    pSM->SetVariable(L"controls", L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}");
    pSM->SetVariable(L"dialup", L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{992CFFA0-F557-101A-88EC-00DD010CCC48}");
    pSM->SetVariable(L"networkanddialup", L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{7007ACC7-3202-11D1-AAD2-00805FC1270E}");
    pSM->SetVariable(L"printers", L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{2227A280-3AEA-1069-A2DE-08002B30309D}");
    pSM->SetVariable(L"scheduled", L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{D6277990-4C6A-11CF-8D87-00AA0060F5BF}");
    pSM->SetVariable(L"admintools", L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{D20EA4E1-3957-11d2-A40B-0C5020524153}");

    setShellFolderVariable(L"quicklaunch", LS_CSIDL_QUICKLAUNCH);
    setShellFolderVariable(L"commondesktopdir", CSIDL_COMMON_DESKTOPDIRECTORY);
    setShellFolderVariable(L"commonfavorites", CSIDL_COMMON_FAVORITES);
    setShellFolderVariable(L"commonprograms", CSIDL_COMMON_PROGRAMS);
    setShellFolderVariable(L"commonstartmenu", CSIDL_COMMON_STARTMENU);
    setShellFolderVariable(L"commonstartup", CSIDL_COMMON_STARTUP);
    setShellFolderVariable(L"cookies", CSIDL_COOKIES);
    setShellFolderVariable(L"desktop", CSIDL_DESKTOP);
    setShellFolderVariable(L"desktopdir", CSIDL_DESKTOPDIRECTORY);
    setShellFolderVariable(L"favorites", CSIDL_FAVORITES);
    setShellFolderVariable(L"fonts", CSIDL_FONTS);
    setShellFolderVariable(L"history", CSIDL_HISTORY);
    setShellFolderVariable(L"internet", CSIDL_INTERNET);
    setShellFolderVariable(L"internetcache", CSIDL_INTERNET_CACHE);
    setShellFolderVariable(L"nethood", CSIDL_NETHOOD);
    setShellFolderVariable(L"documentsdir", CSIDL_PERSONAL);
    setShellFolderVariable(L"printhood", CSIDL_PRINTHOOD);
    setShellFolderVariable(L"programs", CSIDL_PROGRAMS);
    setShellFolderVariable(L"recent", CSIDL_RECENT);
    setShellFolderVariable(L"sendto", CSIDL_SENDTO);
    setShellFolderVariable(L"startmenu", CSIDL_STARTMENU);
    setShellFolderVariable(L"startup", CSIDL_STARTUP);
    setShellFolderVariable(L"templates", CSIDL_TEMPLATES);
    setShellFolderVariable(L"commonadmintoolsdir", CSIDL_COMMON_ADMINTOOLS);
    setShellFolderVariable(L"admintoolsdir", CSIDL_ADMINTOOLS);

    //
    // Set version identification variables
    //
    struct VersionToVariable
    {
        UINT uVersion;
        LPCWSTR pszVariable;
    }
    versions[] = \
    {
        { WINVER_WIN95,     L"Win95"      },
        { WINVER_WIN98,     L"Win98"      },
        { WINVER_WINME,     L"WinME"      },

        { WINVER_WINNT4,    L"WinNT4"     },
        { WINVER_WIN2000,   L"Win2000"    },
        { WINVER_WINXP,     L"WinXP"      },
        { WINVER_VISTA,     L"WinVista"   },
        { WINVER_WIN7,      L"Win7"       },
        { WINVER_WIN8,      L"Win8"       },
        { WINVER_WIN81,     L"Win81"      },

        { WINVER_WIN2003,   L"Win2003"    },
        { WINVER_WHS,       L"Win2003"    },  // WHS is Win2003 in disguise
        { WINVER_WIN2008,   L"Win2008"    },
        { WINVER_WIN2008R2, L"Win2008R2"  },
        { WINVER_WIN2012,   L"Win2012"    },
        { WINVER_WIN2012R2, L"Win2012R2"  }
    };

    UINT uVersion = GetWindowsVersion();

    for (size_t idx = 0; idx < COUNTOF(versions); ++idx)
    {
        if (versions[idx].uVersion == uVersion)
        {
            pSM->SetVariable(versions[idx].pszVariable, L"true");
        }
        else
        {
            pSM->SetVariable(versions[idx].pszVariable, L"false");
        }
    }

    if (IsOS(OS_NT))
    {
        pSM->SetVariable(L"Win9x", L"false");
        pSM->SetVariable(L"WinNT", L"true");
    }
    else
    {
        pSM->SetVariable(L"Win9x", L"true");
        pSM->SetVariable(L"WinNT", L"false");
    }

#if defined(_WIN64)
    pSM->SetVariable(L"Win64", L"true");
#else
    if (IsOS(OS_WOW6432))
    {
        pSM->SetVariable(L"Win64", L"true");
    }
    else
    {
        pSM->SetVariable(L"Win64", L"false");
    }
#endif

    // screen resolution
    StringCchPrintfW(wzTemp, MAX_PATH, L"%d", GetSystemMetrics(SM_CXSCREEN));
    pSM->SetVariable(L"ResolutionX", wzTemp);

    StringCchPrintfW(wzTemp, MAX_PATH, L"%d", GetSystemMetrics(SM_CYSCREEN));
    pSM->SetVariable(L"ResolutionY", wzTemp);

    // build date/time from PE headers
    getCompileTime(wzTemp, MAX_PATH);
    pSM->SetVariable(L"CompileDate", wzTemp);

#if defined(LS_CUSTOM_INCLUDEFOLDER)
    pSM->SetVariable(L"IncludeFolder", L"1");
#endif // LS_CUSTOM_INCLUDEFOLDER
}