Pārlūkot izejas kodu

Tweaking info text (+11 squashed commits)
Squashed commits:
[5d5705b] Add additional guard for reg key value (was throwing)
[c999aeb] Guard against non-existant regkey
[39bbf63] Helps to use the right operator
[ed13668] Put -toolbootstrap at end of net compile command
[6d8f715] Support error message if VS2017 registry entry doesn't exist
[f13173d] Support Visual Studio Mac
[1d39c47] Working on VS2015/VS2017 support
[069d705] Adding ide support VS2017
[9ae22cb] Removing some workarounds in root CMake
[b166a16] Cleanups for VS2015/VS2017 build
[769e74e] Visual Studio 2017 support (WIP)

Josh Engebretson 9 gadi atpakaļ
vecāks
revīzija
a28af1821c

+ 7 - 4
Build/Scripts/Bootstrap.js

@@ -27,10 +27,6 @@ function printHelp() {
     console.log("--with-android  : Build with Android platform support");
     console.log("--with-ios      : Build with iOS platform support");
     console.log("--with-web      : Build with Web platform support");
-if (os.platform() == "win32") {
-    console.log("--opengl        : Enable OpenGL renderer");
-    console.log("--d3d11         : Enable DirectX 11 renderer");
-}
     console.log("--debug         : Build debug version of the editor and associated platform runtimes");
     console.log("--noclean       : Do not clean before building, useful during development");
     console.log("--nonet         : Build without AtomicNET C# scripting support");
@@ -38,6 +34,13 @@ if (os.platform() == "win32") {
     console.log("--noexamples    : Don't include examples with editor");
     console.log("--task=name     : Build the specified task (for development)");
     console.log("--package       : packages the editor to Artifacts/Dist");
+    if (os.platform() == "win32") {
+      console.log("--vs2015        : Build with VS2015");
+      console.log("--vs2017        : Build with VS2017");
+      console.log("--opengl        : Enable OpenGL renderer");
+      console.log("--d3d11         : Enable DirectX 11 renderer");
+    }
+
     console.log("--------------------------")
 
     process.exit(0);

+ 10 - 1
Build/Scripts/BuildAtomicNET.js

@@ -64,7 +64,16 @@ namespace('build', function() {
 
         var cmds = [];
 
-        var netCmd = host.atomicTool + " net compile " + atomicRoot + "Script/AtomicNET/AtomicNETProject.json " + platforms + " -config " + config["config"] + " -toolbootstrap";
+        var netCmd = host.atomicTool + " net compile " + atomicRoot + "Script/AtomicNET/AtomicNETProject.json " + platforms + " -config " + config["config"];
+
+        if (os.platform() == 'win32') {
+
+          var vsver = (config["vs2017"] ? "VS2017" : "VS2015");
+          netCmd += " -toolversion " + vsver;
+
+        }
+
+        netCmd += " -toolbootstrap"
 
         console.log(netCmd);
 

+ 26 - 36
Build/Scripts/BuildWindows.js

@@ -30,7 +30,7 @@ function copyAtomicEditor() {
     // copy AtomicTool
     fs.copySync(buildDir +  "Source/AtomicTool/" + config["config"] +"/AtomicTool.exe",
     editorAppFolder + "AtomicTool.exe");
-    
+
     // We need some resources to run
     fs.copySync(atomicRoot + "Resources/CoreData",
     editorAppFolder + "Resources/CoreData");
@@ -59,14 +59,34 @@ function copyAtomicEditor() {
 
 namespace('build', function() {
 
+    // get CMake flags for generator, vsver parameter can be VS2017/VS2015, etc
+    function getCMakeFlags(vsver) {
+
+      var flags = "\"";
+
+      // Redistributable editor build
+      flags += "-DATOMIC_DEV_BUILD=0";
+
+      // graphics backend (overrides default DX9)
+      flags += " -DATOMIC_OPENGL=" + (config["opengl"] ? "ON" : "OFF");
+      flags += " -DATOMIC_D3D11=" + (config["d3d11"] ? "ON" : "OFF");
+
+      flags += "\"";
+
+      return flags;
+
+    }
+
     task('atomiceditor_phase2', {
         async: true
     }, function() {
 
         process.chdir(buildDir);
 
+        var vsver = (config["vs2017"] ? "VS2017" : "VS2015");
+
         var cmds = [];
-        cmds.push(atomicRoot + "Build/Scripts/Windows/CompileAtomicEditorPhase2.bat " + config["config"]);
+        cmds.push(atomicRoot + "Build/Scripts/Windows/CompileAtomicEditorPhase2.bat " + config["config"] + " " + vsver);
 
         jake.exec(cmds, function() {
 
@@ -102,10 +122,13 @@ namespace('build', function() {
 
         process.chdir(buildDir);
 
+        var vsver = (config["vs2017"] ? "VS2017" : "VS2015");
+
         var cmds = [];
 
         // Generate Atomic solution, AtomicTool binary, and script bindings
-        cmds.push(atomicRoot + "Build/Scripts/Windows/CompileAtomicEditorPhase1.bat " + config["config"]);
+        cmds.push(atomicRoot + "Build/Scripts/Windows/CompileAtomicEditorPhase1.bat " + config["config"] + " " +
+                  vsver + " " + getCMakeFlags(vsver));
 
         jake.exec(cmds, function() {
 
@@ -129,37 +152,4 @@ namespace('build', function() {
 
     });
 
-    // Generate a Visual Studio 2015 solution
-    task('genvs2015', {
-        async: true
-    }, function(devBuild) {
-        if (devBuild === undefined)
-        devBuild = 1;
-
-        var opengl = config["opengl"] ? "ON" : "OFF"; 
-        var d3d11 = config["d3d11"] ? "ON" : "OFF"; 
-
-        var slnRoot = path.resolve(atomicRoot, "") + "-VS2015\\";
-
-        if (!fs.existsSync(slnRoot)) {
-            jake.mkdirP(slnRoot);
-        }
-
-        process.chdir(slnRoot);
-
-        var cmds = [];
-
-        cmds.push(atomicRoot + "Build/Scripts/Windows/GenerateVS2015.bat " + atomicRoot + " " + devBuild + " " + opengl + " " + d3d11);
-
-        jake.exec(cmds, function() {
-
-            complete();
-
-        }, {
-            printStdout: true
-        });
-
-    });
-
-
 });// end of build namespace

+ 104 - 0
Build/Scripts/CMakeWindows.js

@@ -0,0 +1,104 @@
+var fs = require('fs-extra');
+var path = require("path");
+var host = require("./Host");
+var buildTasks = require("./BuildTasks");
+var config = require("./BuildConfig");
+
+const nodeSpawn = require('child_process').spawn;
+
+var atomicRoot = config.atomicRoot;
+var buildDir = config.artifactsRoot + "Build/Windows/";
+var editorAppFolder = config.editorAppFolder
+
+namespace('build', function() {
+
+  // converts / to \ and removes trailing slash
+  function fixpath(path) {
+    return path.replace(/\//g, "\\").replace(/\\$/, "");
+  }
+
+  // get CMake flags for generator, vsver parameter can be VS2017/VS2015, etc
+  function getCMakeFlags(vsver) {
+
+    // local cmake builds are always dev builds
+    var flags = "-DATOMIC_DEV_BUILD=1";
+
+    // graphics backend (overrides default DX9)
+    flags += " -DATOMIC_OPENGL=" + (config["opengl"] ? "ON" : "OFF");
+    flags += " -DATOMIC_D3D11=" + (config["d3d11"] ? "ON" : "OFF");
+
+    return flags;
+
+  }
+
+  // spawn cmake process
+  function spawnCMake(vsver) {
+
+    host.cleanCreateDir(atomicRoot + "/Artifacts/Build/Source/Generated");
+
+    var slnRoot = fixpath(path.resolve(atomicRoot, "") + "-" + vsver);
+
+    // we're running cmd.exe, this exits the shell when the command have finished
+    var args = ["/C"];
+
+    // Windows batch file which runs cmake
+    args.push(fixpath(atomicRoot + "\\Build\\Scripts\\Windows\\GenerateVSSolution.bat"));
+
+    // vsver VS2015/VS2017
+    args.push(vsver);
+
+    // Atomic root source dir
+    args.push(fixpath(atomicRoot));
+
+    // Folder to put generated solution in
+    args.push(fixpath(slnRoot));
+
+    // CMake flags
+    args.push(getCMakeFlags(vsver));
+
+    // we're using nodeSpawn here instead of jake.exec as the later was having much trouble with quotes
+    var cmakeProcess = nodeSpawn("cmd.exe", args);
+
+    cmakeProcess.stdout.on('data', (data) => {
+      process.stdout.write(data.toString());
+    });
+
+    cmakeProcess.stderr.on('data', (data) => {
+      process.stdout.write(data.toString());
+    });
+
+    cmakeProcess.on('exit', (code) => {
+
+      if (code != 0) {
+        fail(`CMake process exited with code ${code}`);
+      }
+
+      console.log("\n\n" + vsver + " solution created in " + fixpath(slnRoot) + "\n\n");
+
+      complete();
+
+    });
+
+  }
+
+  task('genvs2017', {
+    async: true
+  }, function() {
+
+    spawnCMake("VS2017");
+
+  }, {
+    printStdout: true,
+    printStderr: true
+  });
+
+  // Generate a Visual Studio 2015 solution
+  task('genvs2015', {
+    async: true
+  }, function() {
+
+    spawnCMake("VS2015");
+
+  });
+
+});// end of build namespace

+ 1 - 0
Build/Scripts/Host.js

@@ -3,6 +3,7 @@ var os = require('os');
 if (os.platform() == "win32") {
   module.exports = require("./HostWindows");
   require("./BuildWindows");
+  require("./CMakeWindows");
 } else if (os.platform() == "darwin") {
   module.exports = require("./HostMac");
   require("./BuildMac");

+ 0 - 3
Build/Scripts/Windows/CompileAtomicEditor.bat

@@ -1,3 +0,0 @@
-call "%VS140COMNTOOLS%..\..\VC\bin\amd64\vcvars64.bat"
-cmake ..\\..\\..\\ -DATOMIC_DEV_BUILD=0 -G "Visual Studio 14 2015 Win64"
-msbuild /m Atomic.sln /t:AtomicEditor /t:AtomicPlayer /t:AtomicNETNative /p:Configuration=Release /p:Platform=x64

+ 15 - 2
Build/Scripts/Windows/CompileAtomicEditorPhase1.bat

@@ -1,4 +1,17 @@
-call "%VS140COMNTOOLS%..\..\VC\bin\amd64\vcvars64.bat"
-cmake ..\\..\\..\\ -DATOMIC_DEV_BUILD=0 -G "Visual Studio 14 2015 Win64"
+@echo OFF
+
+call %0\..\SetupVSEnvironment.bat %2
+
+if not defined ATOMIC_CMAKE_GENERATOR (
+  @echo Problem setting up %2 compilation environment
+  exit /b 1
+)
+
+set ATOMIC_CMAKE_FLAGS=%3
+set ATOMIC_CMAKE_FLAGS=%ATOMIC_CMAKE_FLAGS:\=%
+set ATOMIC_CMAKE_FLAGS=%ATOMIC_CMAKE_FLAGS:"=%
+
+%ATOMIC_CMAKE% %0\..\..\..\..\ %ATOMIC_CMAKE_FLAGS% -G %ATOMIC_CMAKE_GENERATOR%
+
 :: Note, we're building LibCpuId as it uses masm as getting XamlFactory load errors if delayed
 msbuild /m Atomic.sln /t:LibCpuId /t:AtomicNETNative /p:Configuration=%1 /p:Platform=x64

+ 9 - 1
Build/Scripts/Windows/CompileAtomicEditorPhase2.bat

@@ -1,2 +1,10 @@
-call "%VS140COMNTOOLS%..\..\VC\bin\amd64\vcvars64.bat"
+@echo OFF
+
+call %0\..\SetupVSEnvironment.bat %2
+
+if not defined ATOMIC_CMAKE_GENERATOR (
+  @echo Problem setting up %2 compilation environment
+  exit /b 1
+)
+
 msbuild /m Atomic.sln /t:AtomicEditor /t:AtomicPlayer /p:Configuration=%1 /p:Platform=x64

+ 0 - 3
Build/Scripts/Windows/GenerateVS2015.bat

@@ -1,3 +0,0 @@
-call "%VS140COMNTOOLS%..\..\VC\bin\amd64\vcvars64.bat"
-cmake %1 -DATOMIC_DEV_BUILD=%2 -DATOMIC_OPENGL=%3 -DATOMIC_D3D11=%4 -G "Visual Studio 14 2015 Win64"
-

+ 17 - 0
Build/Scripts/Windows/GenerateVSSolution.bat

@@ -0,0 +1,17 @@
+@echo OFF
+
+call %0\..\SetupVSEnvironment.bat %1
+
+if not defined ATOMIC_CMAKE_GENERATOR (
+  @echo Problem setting up %1 compilation environment
+  exit
+)
+
+@echo OFF
+
+set ATOMIC_ROOT=%~2
+set ATOMIC_SOLUTION_PATH=%~3
+set ATOMIC_CMAKE_FLAGS="%4"
+
+%ATOMIC_CMAKE% -E make_directory "%ATOMIC_SOLUTION_PATH%"
+%ATOMIC_CMAKE% -E chdir "%ATOMIC_SOLUTION_PATH%" %ATOMIC_CMAKE% "%ATOMIC_ROOT%" %ATOMIC_CMAKE_FLAGS% -G %ATOMIC_CMAKE_GENERATOR%

+ 30 - 0
Build/Scripts/Windows/SetupVSEnvironment.bat

@@ -0,0 +1,30 @@
+@echo OFF
+
+:: locate VS2017, this is done first as inside the conditional was having problems
+FOR /F "usebackq tokens=2,* skip=2" %%L IN (
+    `reg query ""HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\VisualStudio\SxS\VS7"" /v 15.0 2^> nul`
+) DO (SET ATOMIC_VS2017PATH=%%M)
+
+if "%1"=="VS2017" (
+
+  if defined ATOMIC_VS2017PATH (
+
+    set ATOMIC_CMAKE="%ATOMIC_VS2017PATH%\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin\cmake.exe"
+    set ATOMIC_CMAKE_GENERATOR="Visual Studio 15 2017 Win64"
+
+    call "%ATOMIC_VS2017PATH%\Common7\Tools\VsDevCmd.bat"
+
+  )
+
+) else (
+
+  if defined VS140COMNTOOLS (
+
+    set ATOMIC_CMAKE=cmake
+    set ATOMIC_CMAKE_GENERATOR="Visual Studio 14 2015 Win64"
+
+    call "%VS140COMNTOOLS%..\..\VC\bin\amd64\vcvars64.bat"
+
+  )
+
+)

+ 0 - 6
CMakeLists.txt

@@ -22,12 +22,6 @@ if (ATOMIC_DEV_BUILD)
     add_definitions("-DATOMIC_DEV_BUILD=1")
 endif()
 
-# Disable until https://github.com/AtomicGameEngine/AtomicGameEngine/issues/554 is addressed
-# add_definitions("-DATOMIC_SOURCE_BUILD=1")
-
-# this is here as QtCreator is having trouble picking up #include <Atomic/*> without it
-include_directories(${ATOMIC_SOURCE_DIR}/Source ${ATOMIC_SOURCE_DIR}/Source/AtomicEditor/Source)
-
 add_link_libraries_exported (Atomic Box2D Duktape TurboBadger FreeType JO LZ4 PugiXml STB SDL)
 
 if (NOT ATOMIC_BUILD_2D)

+ 1 - 52
CMake_VS2015.bat

@@ -1,53 +1,2 @@
 @ECHO OFF
-@echo:
-@echo:
-SET ARG=%1
-@echo:
-@echo:
-ECHO Generating Visual Studio Solution (64 bit)
-@echo:
-SET BUILDMODE="d3d9"
-IF DEFINED ARG (
-    IF "%ARG%"=="--opengl" (
-    	SET BUILDMODE="opengl"
-		ECHO OpenGL configuration selected.
-	)
-	IF "%ARG%"=="/opengl" (
-    	SET BUILDMODE="opengl"
-		ECHO OpenGL configuration selected.
-	)
-    IF "%ARG%"=="--d3d11" (
-    	SET BUILDMODE="d3d11"
-		ECHO Direct3D 11 configuration selected.
-	)
-    IF "%ARG%"=="/d3d11" (
-    	SET BUILDMODE="d3d11"
-		ECHO Direct3D 11 configuration selected.
-	)
-)
-IF %BUILDMODE%=="d3d9" (
-  ECHO Direct3D 9 configuration selected by default (Use /opengl or /d3d11 switches to specify OpenGL or DirectX 11)
-)
-@echo: 
-call "%VS140COMNTOOLS%..\..\VC\bin\amd64\vcvars64.bat"
-:: remove any generated sources
-if exist "%~p0Artifacts\Build\Source\Generated\" rd /q /s "%~p0Artifacts\Build\Source\Generated\"
-:: get current foldername
-set "cdir=%~dp0"
-:: for loop requires removing trailing backslash from %~dp0 output
-set "cdir=%cdir:~0,-1%"
-for %%i IN ("%cdir%") do set "foldername=%%~nxi"
-SET "DIRECTORY=..\%foldername%-VS2015" 
-IF %BUILDMODE%=="d3d11" ( 
-   SET "DIRECTORY=%DIRECTORY%_D3D11"
-   SET "RENDERERFLAG=-DATOMIC_D3D11=ON"
-) 
-IF %BUILDMODE%=="opengl" (
-	SET "DIRECTORY=%DIRECTORY%_OPENGL"
-	SET "RENDERERFLAG=-DATOMIC_OPENGL=ON"
-)
-:: run cmake
-cmake -E make_directory %DIRECTORY% && cmake -E chdir %DIRECTORY% cmake %~dp0 -G "Visual Studio 14 2015 Win64" %RENDERERFLAG%
-@echo:
-ECHO Solution created in %DIRECTORY%
-@echo:
+Build\Windows\node\node.exe ./Build/Scripts/Bootstrap.js --task=build:genvs2015 %*

+ 2 - 0
CMake_VS2017.bat

@@ -0,0 +1,2 @@
+@ECHO OFF
+Build\Windows\node\node.exe ./Build/Scripts/Bootstrap.js --task=build:genvs2017 %*

+ 6 - 8
Script/AtomicEditor/ui/modal/info/AtomicNETWindow.ts

@@ -64,13 +64,13 @@ class AtomicNETWindow extends ModalWindow {
 
                 this.hide();
 
-                if ( Atomic.platform == "Windows") 
+                if ( Atomic.platform == "Windows")
                     Atomic.fileSystem.systemOpen( "https://www.visualstudio.com/vs/community/" );
 
-                if ( Atomic.platform == "MacOSX") 
+                if ( Atomic.platform == "MacOSX")
                     Atomic.fileSystem.systemOpen( "https://www.xamarin.com/download/");
 
-                if ( Atomic.platform == "Linux") 
+                if ( Atomic.platform == "Linux")
                     Atomic.fileSystem.systemOpen( "https://github.com/AtomicGameEngine/AtomicGameEngine/wiki/Detailed-instructions-for-building-on-Linux");
 
             }
@@ -81,8 +81,8 @@ class AtomicNETWindow extends ModalWindow {
     generateAtomicNETText(): string {
         // start at Atomic.platform == "Windows"
         let ideText:string = "Visual Studio";
-        if ( Atomic.platform == "MacOSX") ideText = "Xamarin Studio";
-        if ( Atomic.platform == "Linux") ideText = "monodevelop";
+        if ( Atomic.platform == "MacOSX") ideText = "Visual Studio for Mac";
+        if ( Atomic.platform == "Linux") ideText = "MonoDevelop";
 
 
         let installText = `Please install ${ideText} with <color #D4FB79>Xamarin.Android</color> and <color #D4FB79>Xamarin.iOS</color>`;
@@ -95,12 +95,10 @@ class AtomicNETWindow extends ModalWindow {
         let text = "";
 
         text += `
-Atomic C# is integrated with <color #D4FB79>Visual Studio</color> and <color #D4FB79>Xamarin Studio</color> and <color #D4FB79>monodevelop</color> to provide a first class editing, debugging, and deployment experience.
+Atomic C# is integrated with <color #D4FB79>Visual Studio</color>, <color #D4FB79>Visual Studio for Mac</color>, and <color #D4FB79>MonoDevelop</color> to provide a first class editing, debugging, and deployment experience.
 
 ${installText}
 
-<color #76D6FF>Visual Studio Code support is also coming soon!</color>
-
 `;
 
         return text;

+ 5 - 0
Source/ThirdParty/Poco/CMakeLists.txt

@@ -178,4 +178,9 @@ set (SOURCE_FILES
     Foundation/src/trees.c
     Foundation/src/zutil.c )
 
+if (MSVC)
+    # Add windows registry key from Utils
+    set (SOURCE_FILES ${SOURCE_FILES} Foundation/src/WinRegistryKey.cpp )
+endif()
+
 add_library(Poco ${SOURCE_FILES})

+ 196 - 0
Source/ThirdParty/Poco/Foundation/include/Poco/WinRegistryKey.h

@@ -0,0 +1,196 @@
+//
+// WinRegistryKey.h
+//
+// $Id: //poco/1.4/Util/include/Poco/Util/WinRegistryKey.h#2 $
+//
+// Library: Util
+// Package: Windows
+// Module:  WinRegistryKey
+//
+// Definition of the WinRegistryKey class.
+//
+// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH.
+// and Contributors.
+//
+// SPDX-License-Identifier:	BSL-1.0
+//
+
+
+#ifndef Util_WinRegistryKey_INCLUDED
+#define Util_WinRegistryKey_INCLUDED
+
+#include "Poco/Foundation.h"
+#include "Poco/UnWindows.h"
+#include <vector>
+
+namespace Poco {
+
+class WinRegistryKey
+	/// This class implements a convenient interface to the
+	/// Windows Registry.
+	///
+	/// This class is only available on Windows platforms.
+{
+public:
+	typedef std::vector<std::string> Keys;
+	typedef std::vector<std::string> Values;
+
+	enum Type
+	{
+		REGT_NONE = 0,
+		REGT_STRING = 1,
+		REGT_STRING_EXPAND = 2,
+		REGT_BINARY = 3,
+		REGT_DWORD = 4,
+		REGT_DWORD_BIG_ENDIAN = 5,
+		REGT_LINK = 6,
+		REGT_MULTI_STRING = 7,
+		REGT_RESOURCE_LIST = 8,
+		REGT_FULL_RESOURCE_DESCRIPTOR = 9,
+		REGT_RESOURCE_REQUIREMENTS_LIST = 10,
+		REGT_QWORD = 11
+	};
+
+	WinRegistryKey(const std::string& key, bool readOnly = false, REGSAM extraSam = 0);
+		/// Creates the WinRegistryKey.
+		///
+		/// The key must start with one of the root key names
+		/// like HKEY_CLASSES_ROOT, e.g. HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services.
+		///
+		/// If readOnly is true, then only read access to the registry
+		/// is available and any attempt to write to the registry will
+		/// result in an exception.
+		///
+		/// extraSam is used to pass extra flags (in addition to KEY_READ and KEY_WRITE)
+		/// to the samDesired argument of RegOpenKeyEx() or RegCreateKeyEx().
+
+	WinRegistryKey(HKEY hRootKey, const std::string& subKey, bool readOnly = false, REGSAM extraSam = 0);
+		/// Creates the WinRegistryKey.
+		///
+		/// If readOnly is true, then only read access to the registry
+		/// is available and any attempt to write to the registry will
+		/// result in an exception.
+		///
+		/// extraSam is used to pass extra flags (in addition to KEY_READ and KEY_WRITE)
+		/// to the samDesired argument of RegOpenKeyEx() or RegCreateKeyEx().
+
+	~WinRegistryKey();
+		/// Destroys the WinRegistryKey.
+
+	void setString(const std::string& name, const std::string& value);
+		/// Sets the string value (REG_SZ) with the given name.
+		/// An empty name denotes the default value.
+
+	std::string getString(const std::string& name);
+		/// Returns the string value (REG_SZ) with the given name.
+		/// An empty name denotes the default value.
+		///
+		/// Throws a NotFoundException if the value does not exist.
+
+	void setStringExpand(const std::string& name, const std::string& value);
+		/// Sets the expandable string value (REG_EXPAND_SZ) with the given name.
+		/// An empty name denotes the default value.
+
+	std::string getStringExpand(const std::string& name);
+		/// Returns the string value (REG_EXPAND_SZ) with the given name.
+		/// An empty name denotes the default value.
+		/// All references to environment variables (%VAR%) in the string
+		/// are expanded.
+		///
+		/// Throws a NotFoundException if the value does not exist.
+
+	void setBinary(const std::string& name, const std::vector<char>& value);
+		/// Sets the string value (REG_BINARY) with the given name.
+		/// An empty name denotes the default value.
+
+	std::vector<char> getBinary(const std::string& name);
+		/// Returns the string value (REG_BINARY) with the given name.
+		/// An empty name denotes the default value.
+		///
+		/// Throws a NotFoundException if the value does not exist.
+
+	void setInt(const std::string& name, int value);
+		/// Sets the numeric (REG_DWORD) value with the given name.
+		/// An empty name denotes the default value.
+
+	int getInt(const std::string& name);
+		/// Returns the numeric value (REG_DWORD) with the given name.
+		/// An empty name denotes the default value.
+		///
+		/// Throws a NotFoundException if the value does not exist.
+
+#if defined(POCO_HAVE_INT64)
+
+	void setInt64(const std::string& name, Poco::Int64 value);
+		/// Sets the numeric (REG_QWORD) value with the given name.
+		/// An empty name denotes the default value.
+
+	Poco::Int64 getInt64(const std::string& name);
+		/// Returns the numeric value (REG_QWORD) with the given name.
+		/// An empty name denotes the default value.
+		///
+		/// Throws a NotFoundException if the value does not exist.
+
+#endif // POCO_HAVE_INT64
+
+	void deleteValue(const std::string& name);
+		/// Deletes the value with the given name.
+		///
+		/// Throws a NotFoundException if the value does not exist.
+
+	void deleteKey();
+		/// Recursively deletes the key and all subkeys.
+
+	bool exists();
+		/// Returns true iff the key exists.
+
+	Type type(const std::string& name);
+		/// Returns the type of the key value.
+
+	bool exists(const std::string& name);
+		/// Returns true iff the given value exists under that key.
+
+	void subKeys(Keys& keys);
+		/// Appends all subKey names to keys.
+
+	void values(Values& vals);
+		/// Appends all value names to vals;
+
+	bool isReadOnly() const;
+		/// Returns true iff the key has been opened for read-only access only.
+
+protected:
+	void open();
+	void close();
+	std::string key() const;
+	std::string key(const std::string& valueName) const;
+	HKEY handle();
+	void handleSetError(const std::string& name);
+	static HKEY handleFor(const std::string& rootKey);
+
+private:
+	WinRegistryKey();
+	WinRegistryKey(const WinRegistryKey&);
+	WinRegistryKey& operator = (const WinRegistryKey&);
+
+	HKEY        _hRootKey;
+	std::string _subKey;
+	HKEY        _hKey;
+	bool        _readOnly;
+	REGSAM      _extraSam;
+};
+
+
+//
+// inlines
+//
+inline bool WinRegistryKey::isReadOnly() const
+{
+	return _readOnly;
+}
+
+
+} // namespace Poco
+
+
+#endif // Util_WinRegistryKey_INCLUDED

+ 673 - 0
Source/ThirdParty/Poco/Foundation/src/WinRegistryKey.cpp

@@ -0,0 +1,673 @@
+//
+// WinRegistryKey.cpp
+//
+// $Id: //poco/1.4/Util/src/WinRegistryKey.cpp#6 $
+//
+// Library: Util
+// Package: Windows
+// Module:  WinRegistryKey
+//
+// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH.
+// and Contributors.
+//
+// SPDX-License-Identifier:	BSL-1.0
+//
+
+
+#include "Poco/WinRegistryKey.h"
+#include "Poco/Exception.h"
+#include "Poco/Buffer.h"
+#if defined(POCO_WIN32_UTF8)
+#include "Poco/UnicodeConverter.h"
+#endif
+
+
+using Poco::SystemException;
+using Poco::NotFoundException;
+using Poco::InvalidArgumentException;
+
+
+namespace Poco {
+
+namespace
+{
+	class AutoHandle
+	{
+	public:
+		AutoHandle(HMODULE h):
+			_h(h)
+		{
+		}
+
+		~AutoHandle()
+		{
+			FreeLibrary(_h);
+		}
+
+		HMODULE handle()
+		{
+			return _h;
+		}
+
+	private:
+		HMODULE _h;
+	};
+}
+
+
+WinRegistryKey::WinRegistryKey(const std::string& key, bool readOnly, REGSAM extraSam):
+	_hKey(0),
+	_readOnly(readOnly),
+	_extraSam(extraSam)
+{
+	std::string::size_type pos = key.find('\\');
+	if (pos != std::string::npos)
+	{
+		std::string rootKey = key.substr(0, pos);
+		_hRootKey = handleFor(rootKey);
+		_subKey   = key.substr(pos + 1);
+	}
+	else throw InvalidArgumentException("Not a valid registry key", key);
+}
+
+
+WinRegistryKey::WinRegistryKey(HKEY hRootKey, const std::string& subKey, bool readOnly, REGSAM extraSam):
+	_hRootKey(hRootKey),
+	_subKey(subKey),
+	_hKey(0),
+	_readOnly(readOnly),
+	_extraSam(extraSam)
+{
+}
+
+
+WinRegistryKey::~WinRegistryKey()
+{
+	close();
+}
+
+
+void WinRegistryKey::setString(const std::string& name, const std::string& value)
+{
+	open();
+#if defined(POCO_WIN32_UTF8)
+	std::wstring uname;
+	Poco::UnicodeConverter::toUTF16(name, uname);
+	std::wstring uvalue;
+	Poco::UnicodeConverter::toUTF16(value, uvalue);
+	if (RegSetValueExW(_hKey, uname.c_str(), 0, REG_SZ, (CONST BYTE*) uvalue.c_str(), (DWORD) (uvalue.size() + 1)*sizeof(wchar_t)) != ERROR_SUCCESS)
+		handleSetError(name);
+#else
+	if (RegSetValueExA(_hKey, name.c_str(), 0, REG_SZ, (CONST BYTE*) value.c_str(), (DWORD) value.size() + 1) != ERROR_SUCCESS)
+		handleSetError(name);
+#endif
+}
+
+
+std::string WinRegistryKey::getString(const std::string& name)
+{
+	open();
+	DWORD type;
+	DWORD size;
+#if defined(POCO_WIN32_UTF8)
+	std::wstring uname;
+	Poco::UnicodeConverter::toUTF16(name, uname);
+	if (RegQueryValueExW(_hKey, uname.c_str(), NULL, &type, NULL, &size) != ERROR_SUCCESS || (type != REG_SZ && type != REG_EXPAND_SZ && type != REG_LINK))
+		throw NotFoundException(key(name));
+	if (size > 0)
+	{
+		DWORD len = size/2;
+		Poco::Buffer<wchar_t> buffer(len + 1);
+		RegQueryValueExW(_hKey, uname.c_str(), NULL, NULL, (BYTE*) buffer.begin(), &size);
+		buffer[len] = 0;
+		std::wstring uresult(buffer.begin());
+		std::string result;
+		Poco::UnicodeConverter::toUTF8(uresult, result);
+		return result;
+	}
+#else
+	if (RegQueryValueExA(_hKey, name.c_str(), NULL, &type, NULL, &size) != ERROR_SUCCESS || (type != REG_SZ && type != REG_EXPAND_SZ && type != REG_LINK))
+		throw NotFoundException(key(name));
+	if (size > 0)
+	{
+		Poco::Buffer<char> buffer(size + 1);
+		RegQueryValueExA(_hKey, name.c_str(), NULL, NULL, (BYTE*) buffer.begin(), &size);
+		buffer[size] = 0;
+		std::string result(buffer.begin());
+		return result;
+	}
+#endif
+	return std::string();
+}
+
+
+void WinRegistryKey::setStringExpand(const std::string& name, const std::string& value)
+{
+	open();
+#if defined(POCO_WIN32_UTF8)
+	std::wstring uname;
+	Poco::UnicodeConverter::toUTF16(name, uname);
+	std::wstring uvalue;
+	Poco::UnicodeConverter::toUTF16(value, uvalue);
+	if (RegSetValueExW(_hKey, uname.c_str(), 0, REG_EXPAND_SZ, (CONST BYTE*) uvalue.c_str(), (DWORD) (uvalue.size() + 1)*sizeof(wchar_t)) != ERROR_SUCCESS)
+		handleSetError(name);
+#else
+	if (RegSetValueExA(_hKey, name.c_str(), 0, REG_EXPAND_SZ, (CONST BYTE*) value.c_str(), (DWORD) value.size() + 1) != ERROR_SUCCESS)
+		handleSetError(name);
+#endif
+}
+
+
+std::string WinRegistryKey::getStringExpand(const std::string& name)
+{
+	open();
+	DWORD type;
+	DWORD size;
+#if defined(POCO_WIN32_UTF8)
+	std::wstring uname;
+	Poco::UnicodeConverter::toUTF16(name, uname);
+	if (RegQueryValueExW(_hKey, uname.c_str(), NULL, &type, NULL, &size) != ERROR_SUCCESS || (type != REG_SZ && type != REG_EXPAND_SZ && type != REG_LINK))
+		throw NotFoundException(key(name));
+	if (size > 0)
+	{
+		DWORD len = size/2;
+		Poco::Buffer<wchar_t> buffer(len + 1);
+		RegQueryValueExW(_hKey, uname.c_str(), NULL, NULL, (BYTE*) buffer.begin(), &size);
+		buffer[len] = 0;
+#if !defined(_WIN32_WCE)
+		wchar_t temp;
+		DWORD expSize = ExpandEnvironmentStringsW(buffer.begin(), &temp, 1);
+		Poco::Buffer<wchar_t> expBuffer(expSize);
+		ExpandEnvironmentStringsW(buffer.begin(), expBuffer.begin(), expSize);
+		std::string result;
+		UnicodeConverter::toUTF8(expBuffer.begin(), result);
+#else
+		std::string result;
+		UnicodeConverter::toUTF8(buffer.begin(), result);
+#endif
+		return result;
+	}
+#else
+	if (RegQueryValueExA(_hKey, name.c_str(), NULL, &type, NULL, &size) != ERROR_SUCCESS || (type != REG_SZ && type != REG_EXPAND_SZ && type != REG_LINK))
+		throw NotFoundException(key(name));
+	if (size > 0)
+	{
+		Poco::Buffer<char> buffer(size + 1);
+		RegQueryValueExA(_hKey, name.c_str(), NULL, NULL, (BYTE*) buffer.begin(), &size);
+		buffer[size] = 0;
+		char temp;
+		DWORD expSize = ExpandEnvironmentStringsA(buffer, &temp, 1);
+		Poco::Buffer<char> expBuffer(expSize);
+		ExpandEnvironmentStringsA(buffer.begin(), expBuffer.begin(), expSize);
+		std::string result(expBuffer.begin());
+		return result;
+	}
+#endif
+	return std::string();
+}
+
+
+
+void WinRegistryKey::setBinary( const std::string& name, const std::vector<char>& value )
+{
+	open();
+#if defined(POCO_WIN32_UTF8)
+	std::wstring uname;
+	Poco::UnicodeConverter::toUTF16(name, uname);
+	if (RegSetValueExW(_hKey, uname.c_str(), 0, REG_BINARY, (CONST BYTE*) &value[0], (DWORD) value.size()) != ERROR_SUCCESS)
+		handleSetError(name);
+#else
+	if (RegSetValueExA(_hKey,  name.c_str(), 0, REG_BINARY, (CONST BYTE*) &value[0], (DWORD) value.size()) != ERROR_SUCCESS)
+		handleSetError(name);
+#endif
+}
+
+
+std::vector<char> WinRegistryKey::getBinary( const std::string& name )
+{
+	open();
+	DWORD type;
+	DWORD size;
+	std::vector<char> result;
+
+#if defined(POCO_WIN32_UTF8)
+	std::wstring uname;
+	Poco::UnicodeConverter::toUTF16(name, uname);
+	if (RegQueryValueExW(_hKey, uname.c_str(), NULL, &type, NULL, &size) != ERROR_SUCCESS || type != REG_BINARY)
+		throw NotFoundException(key(name));
+	if (size > 0)
+	{
+		result.resize(size);
+		RegQueryValueExW(_hKey, uname.c_str(), NULL, NULL, (BYTE*) &result[0], &size);
+	}
+#else
+	if (RegQueryValueExA(_hKey, name.c_str(), NULL, &type, NULL, &size) != ERROR_SUCCESS || type != REG_BINARY)
+		throw NotFoundException(key(name));
+	if (size > 0)
+	{
+		result.resize(size);
+		RegQueryValueExA(_hKey, name.c_str(), NULL, NULL, (BYTE*) &result[0], &size);
+	}
+#endif
+	return result;
+}
+
+
+void WinRegistryKey::setInt(const std::string& name, int value)
+{
+	open();
+	DWORD data = value;
+#if defined(POCO_WIN32_UTF8)
+	std::wstring uname;
+	Poco::UnicodeConverter::toUTF16(name, uname);
+	if (RegSetValueExW(_hKey, uname.c_str(), 0, REG_DWORD, (CONST BYTE*) &data, sizeof(data)) != ERROR_SUCCESS)
+		handleSetError(name);
+#else
+	if (RegSetValueExA(_hKey, name.c_str(), 0, REG_DWORD, (CONST BYTE*) &data, sizeof(data)) != ERROR_SUCCESS)
+		handleSetError(name);
+#endif
+}
+
+
+int WinRegistryKey::getInt(const std::string& name)
+{
+	open();
+	DWORD type;
+	DWORD data;
+	DWORD size = sizeof(data);
+#if defined(POCO_WIN32_UTF8)
+	std::wstring uname;
+	Poco::UnicodeConverter::toUTF16(name, uname);
+	if (RegQueryValueExW(_hKey, uname.c_str(), NULL, &type, (BYTE*) &data, &size) != ERROR_SUCCESS || (type != REG_DWORD && type != REG_DWORD_BIG_ENDIAN))
+		throw NotFoundException(key(name));
+#else
+	if (RegQueryValueExA(_hKey, name.c_str(), NULL, &type, (BYTE*) &data, &size) != ERROR_SUCCESS || (type != REG_DWORD && type != REG_DWORD_BIG_ENDIAN))
+		throw NotFoundException(key(name));
+#endif
+	return data;
+}
+
+
+#if defined(POCO_HAVE_INT64)
+
+
+void WinRegistryKey::setInt64(const std::string& name, Poco::Int64 value)
+{
+	open();
+#if defined(POCO_WIN32_UTF8)
+	std::wstring uname;
+	Poco::UnicodeConverter::toUTF16(name, uname);
+	if (RegSetValueExW(_hKey, uname.c_str(), 0, REG_QWORD, (CONST BYTE*) &value, sizeof(value)) != ERROR_SUCCESS)
+		handleSetError(name);
+#else
+	if (RegSetValueExA(_hKey, name.c_str(), 0, REG_QWORD, (CONST BYTE*) &value, sizeof(value)) != ERROR_SUCCESS)
+		handleSetError(name);
+#endif
+}
+
+Poco::Int64 WinRegistryKey::getInt64(const std::string& name)
+{
+	open();
+	DWORD type;
+	Poco::Int64 data;
+	DWORD size = sizeof(data);
+#if defined(POCO_WIN32_UTF8)
+	std::wstring uname;
+	Poco::UnicodeConverter::toUTF16(name, uname);
+	if (RegQueryValueExW(_hKey, uname.c_str(), NULL, &type, (BYTE*) &data, &size) != ERROR_SUCCESS || type != REG_QWORD)
+		throw NotFoundException(key(name));
+#else
+	if (RegQueryValueExA(_hKey, name.c_str(), NULL, &type, (BYTE*) &data, &size) != ERROR_SUCCESS || type != REG_QWORD)
+		throw NotFoundException(key(name));
+#endif
+	return data;
+}
+
+
+#endif // POCO_HAVE_INT64
+
+
+void WinRegistryKey::deleteValue(const std::string& name)
+{
+	open();
+#if defined(POCO_WIN32_UTF8)
+	std::wstring uname;
+	Poco::UnicodeConverter::toUTF16(name, uname);
+	if (RegDeleteValueW(_hKey, uname.c_str()) != ERROR_SUCCESS)
+		throw NotFoundException(key(name));
+#else
+	if (RegDeleteValueA(_hKey, name.c_str()) != ERROR_SUCCESS)
+		throw NotFoundException(key(name));
+#endif
+}
+
+
+void WinRegistryKey::deleteKey()
+{
+	Keys keys;
+	subKeys(keys);
+	close();
+	for (Keys::iterator it = keys.begin(); it != keys.end(); ++it)
+	{
+		std::string subKey(_subKey);
+		subKey += "\\";
+		subKey += *it;
+		WinRegistryKey subRegKey(_hRootKey, subKey);
+		subRegKey.deleteKey();
+	}
+
+	// NOTE: RegDeleteKeyEx is only available on Windows XP 64-bit SP3, Windows Vista or later.
+	// We cannot call it directly as this would prevent the code running on Windows XP 32-bit.
+	// Therefore, if we need to call RegDeleteKeyEx (_extraSam != 0) we need to check for
+	// its existence in ADVAPI32.DLL and call it indirectly.
+#if defined(POCO_WIN32_UTF8)
+	std::wstring usubKey;
+	Poco::UnicodeConverter::toUTF16(_subKey, usubKey);
+
+#if !defined(_WIN32_WCE)
+	typedef LONG (WINAPI *RegDeleteKeyExWFunc)(HKEY hKey, const wchar_t* lpSubKey, REGSAM samDesired, DWORD Reserved);
+	if (_extraSam != 0)
+	{
+		AutoHandle advAPI32(LoadLibraryW(L"ADVAPI32.DLL"));
+		if (advAPI32.handle())
+		{
+			RegDeleteKeyExWFunc pRegDeleteKeyExW = reinterpret_cast<RegDeleteKeyExWFunc>(GetProcAddress(advAPI32.handle() , "RegDeleteKeyExW"));
+			if (pRegDeleteKeyExW)
+			{
+				if ((*pRegDeleteKeyExW)(_hRootKey, usubKey.c_str(), _extraSam, 0) != ERROR_SUCCESS)
+					throw NotFoundException(key());
+				return;
+			}
+		}
+	}
+#endif
+	if (RegDeleteKeyW(_hRootKey, usubKey.c_str()) != ERROR_SUCCESS)
+		throw NotFoundException(key());
+#else
+	typedef LONG (WINAPI *RegDeleteKeyExAFunc)(HKEY hKey, const char* lpSubKey, REGSAM samDesired, DWORD Reserved);
+	if (_extraSam != 0)
+	{
+		AutoHandle advAPI32(LoadLibraryA("ADVAPI32.DLL"));
+		if (advAPI32.handle())
+		{
+			RegDeleteKeyExAFunc pRegDeleteKeyExA = reinterpret_cast<RegDeleteKeyExAFunc>(GetProcAddress(advAPI32.handle() , "RegDeleteKeyExA"));
+			if (pRegDeleteKeyExA)
+			{
+				if ((*pRegDeleteKeyExA)(_hRootKey, _subKey.c_str(), _extraSam, 0) != ERROR_SUCCESS)
+					throw NotFoundException(key());
+				return;
+			}
+		}
+	}
+	if (RegDeleteKey(_hRootKey, _subKey.c_str()) != ERROR_SUCCESS)
+		throw NotFoundException(key());
+#endif
+}
+
+
+bool WinRegistryKey::exists()
+{
+	HKEY hKey;
+#if defined(POCO_WIN32_UTF8)
+	std::wstring usubKey;
+	Poco::UnicodeConverter::toUTF16(_subKey, usubKey);
+	if (RegOpenKeyExW(_hRootKey, usubKey.c_str(), 0, KEY_READ | _extraSam, &hKey) == ERROR_SUCCESS)
+	{
+		RegCloseKey(hKey);
+		return true;
+	}
+#else
+	if (RegOpenKeyExA(_hRootKey, _subKey.c_str(), 0, KEY_READ | _extraSam, &hKey) == ERROR_SUCCESS)
+	{
+		RegCloseKey(hKey);
+		return true;
+	}
+#endif
+	return false;
+}
+
+
+WinRegistryKey::Type WinRegistryKey::type(const std::string& name)
+{
+	open();
+	DWORD type = REG_NONE;
+	DWORD size;
+#if defined(POCO_WIN32_UTF8)
+	std::wstring uname;
+	Poco::UnicodeConverter::toUTF16(name, uname);
+	if (RegQueryValueExW(_hKey, uname.c_str(), NULL, &type, NULL, &size) != ERROR_SUCCESS)
+		throw NotFoundException(key(name));
+#else
+	if (RegQueryValueExA(_hKey, name.c_str(), NULL, &type, NULL, &size) != ERROR_SUCCESS)
+		throw NotFoundException(key(name));
+#endif
+
+	Type aType = (Type)type;
+	return aType;
+}
+
+
+bool WinRegistryKey::exists(const std::string& name)
+{
+	bool exists = false;
+	HKEY hKey;
+#if defined(POCO_WIN32_UTF8)
+	std::wstring usubKey;
+	Poco::UnicodeConverter::toUTF16(_subKey, usubKey);
+	if (RegOpenKeyExW(_hRootKey, usubKey.c_str(), 0, KEY_READ | _extraSam, &hKey) == ERROR_SUCCESS)
+	{
+		std::wstring uname;
+		Poco::UnicodeConverter::toUTF16(name, uname);
+		exists = RegQueryValueExW(hKey, uname.c_str(), NULL, NULL, NULL, NULL) == ERROR_SUCCESS;
+		RegCloseKey(hKey);
+	}
+#else
+	if (RegOpenKeyExA(_hRootKey, _subKey.c_str(), 0, KEY_READ | _extraSam, &hKey) == ERROR_SUCCESS)
+	{
+		exists = RegQueryValueExA(hKey, name.c_str(), NULL, NULL, NULL, NULL) == ERROR_SUCCESS;
+		RegCloseKey(hKey);
+	}
+#endif
+	return exists;
+}
+
+
+void WinRegistryKey::open()
+{
+	if (!_hKey)
+	{
+#if defined(POCO_WIN32_UTF8)
+		std::wstring usubKey;
+		Poco::UnicodeConverter::toUTF16(_subKey, usubKey);
+		if (_readOnly)
+		{
+			if (RegOpenKeyExW(_hRootKey, usubKey.c_str(), 0, KEY_READ | _extraSam, &_hKey) != ERROR_SUCCESS)
+				throw NotFoundException("Cannot open registry key: ", key());
+		}
+		else
+		{
+			if (RegCreateKeyExW(_hRootKey, usubKey.c_str(), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE | _extraSam, NULL, &_hKey, NULL) != ERROR_SUCCESS)
+				throw SystemException("Cannot open registry key: ", key());
+		}
+#else
+		if (_readOnly)
+		{
+			if (RegOpenKeyExA(_hRootKey, _subKey.c_str(), 0, KEY_READ | _extraSam, &_hKey) != ERROR_SUCCESS)
+				throw NotFoundException("Cannot open registry key: ", key());
+		}
+		else
+		{
+			if (RegCreateKeyExA(_hRootKey, _subKey.c_str(), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE | _extraSam, NULL, &_hKey, NULL) != ERROR_SUCCESS)
+				throw SystemException("Cannot open registry key: ", key());
+		}
+#endif
+	}
+}
+
+
+void WinRegistryKey::close()
+{
+	if (_hKey)
+	{
+		RegCloseKey(_hKey);
+		_hKey = 0;
+	}
+}
+
+
+std::string WinRegistryKey::key() const
+{
+	std::string result;
+	if (_hRootKey == HKEY_CLASSES_ROOT)
+		result = "HKEY_CLASSES_ROOT";
+#if defined(HKEY_CURRENT_CONFIG)
+	else if (_hRootKey == HKEY_CURRENT_CONFIG)
+		result = "HKEY_CURRENT_CONFIG";
+#endif
+	else if (_hRootKey == HKEY_CURRENT_USER)
+		result = "HKEY_CURRENT_USER";
+	else if (_hRootKey == HKEY_LOCAL_MACHINE)
+		result = "HKEY_LOCAL_MACHINE";
+	else if (_hRootKey == HKEY_USERS)
+		result = "HKEY_USERS";
+#if defined(HKEY_PERFORMANCE_DATA)
+	else if (_hRootKey == HKEY_PERFORMANCE_DATA)
+		result = "HKEY_PERFORMANCE_DATA";
+#endif
+	else
+		result = "(UNKNOWN)";
+	result += '\\';
+	result += _subKey;
+	return result;
+}
+
+
+std::string WinRegistryKey::key(const std::string& valueName) const
+{
+	std::string result = key();
+	if (!valueName.empty())
+	{
+		result += '\\';
+		result += valueName;
+	}
+	return result;
+}
+
+
+HKEY WinRegistryKey::handle()
+{
+	open();
+	return _hKey;
+}
+
+
+HKEY WinRegistryKey::handleFor(const std::string& rootKey)
+{
+	if (rootKey == "HKEY_CLASSES_ROOT")
+		return HKEY_CLASSES_ROOT;
+#if defined(HKEY_CURRENT_CONFIG)
+	else if (rootKey == "HKEY_CURRENT_CONFIG")
+		return HKEY_CURRENT_CONFIG;
+#endif
+	else if (rootKey == "HKEY_CURRENT_USER")
+		return HKEY_CURRENT_USER;
+	else if (rootKey == "HKEY_LOCAL_MACHINE")
+		return HKEY_LOCAL_MACHINE;
+	else if (rootKey == "HKEY_USERS")
+		return HKEY_USERS;
+#if defined(HKEY_PERFORMANCE_DATA)
+	else if (rootKey == "HKEY_PERFORMANCE_DATA")
+		return HKEY_PERFORMANCE_DATA;
+#endif
+	else
+		throw InvalidArgumentException("Not a valid root key", rootKey);
+}
+
+
+void WinRegistryKey::handleSetError(const std::string& name)
+{
+	std::string msg = "Failed to set registry value";
+	throw SystemException(msg, key(name));
+}
+
+
+void WinRegistryKey::subKeys(WinRegistryKey::Keys& keys)
+{
+	open();
+
+	DWORD subKeyCount = 0;
+	DWORD valueCount = 0;
+
+	if (RegQueryInfoKey(_hKey, NULL, NULL, NULL, &subKeyCount, NULL, NULL, &valueCount, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
+		return;
+
+#if defined(POCO_WIN32_UTF8)
+	wchar_t buf[256];
+	DWORD bufSize = sizeof(buf)/sizeof(wchar_t);
+	for (DWORD i = 0; i< subKeyCount; ++i)
+	{
+		if (RegEnumKeyExW(_hKey, i, buf, &bufSize, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
+		{
+			std::wstring uname(buf);
+			std::string name;
+			Poco::UnicodeConverter::toUTF8(uname, name);
+			keys.push_back(name);
+		}
+		bufSize = sizeof(buf)/sizeof(wchar_t);
+	}
+#else
+	char buf[256];
+	DWORD bufSize = sizeof(buf);
+	for (DWORD i = 0; i< subKeyCount; ++i)
+	{
+		if (RegEnumKeyExA(_hKey, i, buf, &bufSize, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
+		{
+			std::string name(buf);
+			keys.push_back(name);
+		}
+		bufSize = sizeof(buf);
+	}
+#endif
+}
+
+
+void WinRegistryKey::values(WinRegistryKey::Values& vals)
+{
+	open();
+
+	DWORD valueCount = 0;
+
+	if (RegQueryInfoKey(_hKey, NULL, NULL, NULL, NULL, NULL, NULL, &valueCount, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
+		return ;
+
+#if defined(POCO_WIN32_UTF8)
+	wchar_t buf[256];
+	DWORD bufSize = sizeof(buf)/sizeof(wchar_t);
+	for (DWORD i = 0; i< valueCount; ++i)
+	{
+		if (RegEnumValueW(_hKey, i, buf, &bufSize, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
+		{
+			std::wstring uname(buf);
+			std::string name;
+			Poco::UnicodeConverter::toUTF8(uname, name);
+			vals.push_back(name);
+		}
+		bufSize = sizeof(buf)/sizeof(wchar_t);
+	}
+#else
+	char buf[256];
+	DWORD bufSize = sizeof(buf);
+	for (DWORD i = 0; i< valueCount; ++i)
+	{
+		if (RegEnumValueA(_hKey, i, buf, &bufSize, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
+		{
+			std::string name(buf);
+			vals.push_back(name);
+		}
+		bufSize = sizeof(buf);
+	}
+#endif
+}
+
+
+} // namespace Poco

+ 8 - 0
Source/ToolCore/Command/NETCmd.cpp

@@ -86,6 +86,11 @@ bool NETCmd::Parse(const Vector<String>& arguments, unsigned startIndex, String&
                     configurations_.Push(value);
                     i++;
                 }
+                else if (argument == "toolversion" && !value.Empty())
+                {
+                    toolVersion_ = value;
+                }
+
             }
         }
     }
@@ -175,6 +180,9 @@ void NETCmd::Run()
         NETBuildSystem* buildSystem = new NETBuildSystem(context_);
         context_->RegisterSubsystem(buildSystem);
 
+        if (toolVersion_.Length())
+            buildSystem->SetToolVersion(toolVersion_);
+
         NETBuild* build = 0;
 
         String solutionPath;

+ 2 - 1
Source/ToolCore/Command/NETCmd.h

@@ -63,7 +63,8 @@ private:
     String assemblyPath_;
 
     // compile
-    String solutionPath_;
+    String       solutionPath_;
+    String       toolVersion_;
     StringVector platforms_;
     StringVector configurations_;
 

+ 29 - 2
Source/ToolCore/NETTools/NETBuildSystem.cpp

@@ -38,6 +38,10 @@
 #include "NETProjectGen.h"
 #include "NETBuildSystem.h"
 
+#ifdef ATOMIC_PLATFORM_WINDOWS
+#include <Poco/WinRegistryKey.h>
+#endif
+
 namespace ToolCore
 {
 
@@ -305,11 +309,34 @@ namespace ToolCore
 
 #ifdef ATOMIC_PLATFORM_WINDOWS
 
-            String cmdToolsPath = Poco::Environment::get("VS140COMNTOOLS", "").c_str();
+            // VS2015
+            String vs2015ToolsPath = Poco::Environment::get("VS140COMNTOOLS", "").c_str();
+
+            // VS2017
+            String vs2017ToolsPath;
+            Poco::WinRegistryKey regKey("HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\Microsoft\\VisualStudio\\SxS\\VS7", true);
+            if (regKey.exists() && regKey.exists("15.0"))
+                vs2017ToolsPath = regKey.getString("15.0").c_str();
+
+            if (vs2017ToolsPath.Length())
+            {
+                vs2017ToolsPath += "Common7\\Tools\\";
+            }
+
+            String cmdToolsPath;
+
+            if (toolVersion_ == "VS2017" && vs2017ToolsPath.Length())
+            {
+                cmdToolsPath = vs2017ToolsPath;
+            }
+            else
+            {
+                cmdToolsPath = vs2015ToolsPath;
+            }
 
             if (!cmdToolsPath.Length())
             {
-                CurrentBuildError("VS140COMNTOOLS environment variable not found, cannot proceed");
+                CurrentBuildError("VS140COMNTOOLS environment variable and VS2017 registry key not found, cannot proceed");
                 return;
             }
 

+ 10 - 1
Source/ToolCore/NETTools/NETBuildSystem.h

@@ -66,8 +66,9 @@ namespace ToolCore
         virtual ~NETBuild() {}
 
     private:
+
         /// .sln or .json configuration file
-        String solutionPath_;
+        String       solutionPath_;
         StringVector configurations_;
         StringVector platforms_;
         StringVector targets_;
@@ -97,6 +98,12 @@ namespace ToolCore
 
         void SetVerbose(bool verbose) { verbose_ = verbose; }
 
+        /// Set compiler tooling version, for example "VS2015", "VS2017", used default for host if undefined
+        void SetToolVersion(const String& toolVersion) { toolVersion_ = toolVersion.ToUpper();  }
+
+        /// Get compiler tooling version
+        const String& GetToolVersion() const { return toolVersion_; }
+
     private:
 
         void CurrentBuildError(String errorText);
@@ -112,6 +119,8 @@ namespace ToolCore
         List<SharedPtr<NETBuild>> builds_;
 
         bool verbose_;
+        // compiler tooling to use, "VS2015/VS2017", default if undefined
+        String toolVersion_;
 
     };
 

+ 32 - 1
Source/ToolCore/NETTools/NETProjectSystem.cpp

@@ -45,6 +45,10 @@
 #include "NETBuildSystem.h"
 #include "NETProjectSystem.h"
 
+#ifdef ATOMIC_PLATFORM_WINDOWS
+#include <Poco/WinRegistryKey.h>
+#endif
+
 namespace ToolCore
 {
 
@@ -441,6 +445,10 @@ namespace ToolCore
 
 #ifdef ATOMIC_PLATFORM_WINDOWS
 
+        // On Windows, we first check for VS2015, then VS2017 which 
+        // at the time of this comment is in RC, refactor once 
+        // in general release
+
         FileSystem* fileSystem = GetSubsystem<FileSystem>();
 
         // Query for Visual Studio 2015 path
@@ -453,14 +461,37 @@ namespace ToolCore
             if (!fileSystem->FileExists(idePath_))
                 idePath_.Clear();
         }
+        else
+        {
+            // check for VS2017
+            Poco::WinRegistryKey regKey("HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\Microsoft\\VisualStudio\\SxS\\VS7", true);
+            if (regKey.exists() && regKey.exists("15.0"))
+                idePath_ = regKey.getString("15.0").c_str();
+
+            if (idePath_.Length())
+            {
+                // We have VS2017
+                idePath_ += "Common7\\IDE\\devenv.exe";
+            }
+        }
 
 #elif defined ATOMIC_PLATFORM_OSX
 
         FileSystem* fileSystem = GetSubsystem<FileSystem>();
 
-        if (fileSystem->DirExists("/Applications/Xamarin Studio.app"))
+        // first look for Visual Studio Mac
+        idePath_ = "/Applications/Visual Studio.app/Contents/MacOS/VisualStudio";
+
+        if (!fileSystem->FileExists(idePath_))
         {
+            // not found, look for Xamarin Studio
             idePath_ = "/Applications/Xamarin Studio.app/Contents/MacOS/XamarinStudio";
+
+            if (!fileSystem->FileExists(idePath_))
+            {
+                idePath_.Clear();
+            }
+
         }
 
 #elif defined ATOMIC_PLATFORM_LINUX