diff --git a/jlib/base/config.h b/jlib/base/config.h
index 98f4398c9913b0a36c22577350b32b69c0568aa5..341e10fe3514f1153f57277567130e1a25445c9a 100644
--- a/jlib/base/config.h
+++ b/jlib/base/config.h
@@ -7,3 +7,7 @@
 #else
 #error "jlib only support linux and windows"
 #endif
+
+#ifdef JLIB_WINDOWS
+#define _CRT_SECURE_NO_WARNINGS 
+#endif
diff --git a/jlib/base/logging.h b/jlib/base/logging.h
index 9af1146fe2b37202af21b91035d159eed5d623e3..d3d6d5b3d5b354ea6df583ecf4eb76b803a112ab 100644
--- a/jlib/base/logging.h
+++ b/jlib/base/logging.h
@@ -1,7 +1,10 @@
 #pragma once
 
+#include "config.h"
 #include "logstream.h"
 #include "timestamp.h"
+#include "currentthread.h"
+#include <stdlib.h> // getenv
 
 namespace jlib
 {
@@ -11,59 +14,169 @@ class TimeZone;
 class Logger
 {
 public:
-    enum class LogLevel {
-        LOGLEVEL_TRACE,
-        LOGLEVEL_DEBUG,
-        LOGLEVEL_INFO,
-        LOGLEVEL_WARN,
-        LOGLEVEL_ERROR,
-        LOGLEVEL_FATAL,
-        LOGLEVEL_COUNT,
-    };
-
-    // compile time calculation of basename of source file
-    struct SourceFile
-    {
-        template <int N>
-        SourceFile(const char (&arr)[N]) : data_(arr) , size_(N - 1) {
-            const char* slash = strrchr(data_, '/');
-            if (slash) { data_ = slash + 1; }
-            size_ -= static_cast<int>(data_ - arr);
-        }
-
-        explicit SourceFile(const char* filename) : data_(filename) {
-            const char* slash = strrchr(filename, '/');
-            if (slash) { data_ = slash + 1; }
-            size_ = static_cast<int>(strlen(data_));
-        }
-
-        const char* data_;
-        int size_;
-    };
-
-    Logger(SourceFile file, int line);
+	enum LogLevel {
+		LOGLEVEL_TRACE,
+		LOGLEVEL_DEBUG,
+		LOGLEVEL_INFO,
+		LOGLEVEL_WARN,
+		LOGLEVEL_ERROR,
+		LOGLEVEL_FATAL,
+		LOGLEVEL_COUNT,
+	};
+
+	// compile time calculation of basename of source file
+	struct SourceFile
+	{
+		template <int N>
+		SourceFile(const char(&arr)[N]) : data_(arr), size_(N - 1) {
+			const char* slash = strrchr(data_, '/');
+			if (slash) { data_ = slash + 1; }
+			size_ -= static_cast<int>(data_ - arr);
+		}
+
+		explicit SourceFile(const char* filename) : data_(filename) {
+			const char* slash = strrchr(filename, '/');
+			if (slash) { data_ = slash + 1; }
+			size_ = static_cast<int>(strlen(data_));
+		}
+
+		const char* data_;
+		int size_;
+	};
+
+	Logger(SourceFile file, int line);
 	Logger(SourceFile file, int line, LogLevel level);
-	Logger(SourceFile file, int line, LogLevel level, const char *func);
+	Logger(SourceFile file, int line, LogLevel level, const char* func);
 	Logger(SourceFile file, int line, bool toAbort);
 	~Logger();
 
-	LogStream &stream() { return impl_.stream_; }
+	LogStream& stream() { return impl_.stream_; }
 
-	static LogLevel logLevel();
+	static LogLevel logLevel() { return logLevel_; }
 	static void setLogLevel(LogLevel level);
 
-	typedef void (*OutputFunc)(const char *msg, int len);
+	typedef void (*OutputFunc)(const char* msg, int len);
 	typedef void (*FlushFunc)();
 	static void setOutput(OutputFunc);
 	static void setFlush(FlushFunc);
-	static void setTimeZone(const TimeZone &tz);
+	static void setTimeZone(const TimeZone& tz);
 
 private:
-    class Impl
-    {
-    public:
 
-    };
+	class Impl
+	{
+	public:
+		typedef Logger::LogLevel LogLevel;
+
+		Impl(LogLevel level, int old_errno, const SourceFile& file, int line);
+		void formatTime();
+		void finish();
+
+		Timestamp time_;
+		LogStream stream_;
+		LogLevel level_;
+		int line_;
+		SourceFile basename_;
+	};
+
+	static LogLevel logLevel_;
+
+	Impl impl_;
+};
+
+
+static void defaultOutput(const char* msg, int len) { fwrite(msg, 1, len, stdout); }
+static void defaultFlush() { fflush(stdout); }
+
+static Logger::OutputFunc g_output = defaultOutput;
+static Logger::FlushFunc g_flush = defaultFlush;
+
+
+namespace detail
+{
+
+static const char* LogLevelName[Logger::LogLevel::LOGLEVEL_COUNT] =
+{
+	"TRACE ",
+	"DEBUG ",
+	"INFO  ",
+	"WARN  ",
+	"ERROR ",
+	"FATAL ",
+};
+
+static Logger::LogLevel initLogLevel() {
+	if (::getenv("JLIB_LOGLEVEL_TRACE")) {
+		return Logger::LogLevel::LOGLEVEL_TRACE;
+	} else if (::getenv("JLIB_LOGLEVEL_DEBUG")) {
+		return Logger::LogLevel::LOGLEVEL_DEBUG;
+	} else {
+		return Logger::LogLevel::LOGLEVEL_INFO;
+	}
 }
 
+struct T
+{
+	explicit T(const char* str, unsigned int len)
+		: str_(str)
+		, len_(len)
+	{
+		assert(strlen(str) == len);
+	}
+
+	const char* str_;
+	const unsigned int len_;
+};
+
+thread_local char t_errnobuf[512] = { 0 };
+thread_local char t_time[64] = { 0 };
+thread_local time_t t_lastSecond = 0;
+
+} // detail
+
+
+Logger::LogLevel Logger::logLevel_ = detail::initLogLevel();
+
+inline LogStream& operator<<(LogStream& s, const Logger::SourceFile& f) {
+	s.append(f.data_, f.size_); return s;
+}
+
+inline LogStream& operator<<(LogStream& s, detail::T t) {
+	s.append(t.str_, t.len_); return s;
+}
+
+static const char* strerror_t(int savedErrno) {
+	strerror_s(detail::t_errnobuf, savedErrno);
+	return detail::t_errnobuf;
+}
+
+
+Logger::Impl::Impl(LogLevel level, int old_errno, const SourceFile& file, int line)
+	: time_(Timestamp::now())
+	, stream_()
+	, level_(level)
+	, line_(line)
+	, basename_(file)
+{
+	formatTime();
+	CurrentThread::tid();
+	stream_ << detail::T(CurrentThread::tidString(), CurrentThread::tidStringLength());
+	stream_ << detail::T(detail::LogLevelName[level], 6);
+	if (old_errno != 0) {
+		stream_ << strerror_t(old_errno) << " (errorno=" << old_errno << ") ";
+	}
+}
+
+void Logger::Impl::formatTime()
+{
+	int64_t microSecsSinceEpoch = time_.microSecondsSinceEpoch();
+	time_t seconds = static_cast<time_t>(microSecsSinceEpoch / Timestamp::MICRO_SECONDS_PER_SECOND);
+	int microsecs = static_cast<int>(microSecsSinceEpoch % Timestamp::MICRO_SECONDS_PER_SECOND);
+	if (seconds != detail::t_lastSecond) {
+		detail::t_lastSecond = seconds;
+
+	}
+}
+
+
 } // namespace jlib
diff --git a/jlib/base/timezone.h b/jlib/base/timezone.h
new file mode 100644
index 0000000000000000000000000000000000000000..610d659266b0bcf1b60d81455a1aa39e023a929b
--- /dev/null
+++ b/jlib/base/timezone.h
@@ -0,0 +1,120 @@
+#pragma once
+
+#include "config.h"
+#include "copyable.h"
+#include <time.h>
+#include <memory>
+#include <vector>
+#include <string>
+
+
+namespace jlib
+{
+
+namespace detail
+{
+
+struct Transition
+{
+	time_t gmtime_;
+	time_t localtime_;
+	int localTimeIdx_;
+
+	Transition(time_t gmt, time_t localt, int localIdx)
+		: gmtime_(gmt)
+		, localtime_(localt)
+		, localTimeIdx_(localIdx)
+	{}
+};
+
+struct Compare
+{
+	bool compareGmt_ = true;
+
+	Compare(bool gmt)
+		: compareGmt_(gmt)
+	{}
+
+	bool operator()(const Transition& lhs, const Transition& rhs) const {
+		if (compareGmt_) {
+			return lhs.gmtime_ < rhs.gmtime_;
+		} else {
+			return lhs.localtime_ < rhs.localtime_;
+		}
+	}
+
+	bool equal(const Transition& lhs, const Transition& rhs) const {
+		if (compareGmt_) {
+			return lhs.gmtime_ == rhs.gmtime_;
+		} else {
+			return lhs.localtime_ == rhs.localtime_;
+		}
+	}
+};
+
+struct LocalTime
+{
+	time_t gmtOffset;
+	bool isDst;
+	int abbrIdx;
+
+	LocalTime(time_t offset, bool dst, int abbr)
+		: gmtOffset(offset)
+		, isDst(dst)
+		, abbrIdx(abbr)
+	{}
+};
+
+} // namespace detail
+
+
+static constexpr int SECONDS_PER_DAY = 24 * 60 * 60;
+    
+
+class Timezone : public copyable
+{
+public:
+    explicit Timezone(const char* zonename);
+    Timezone(int bias, const char* tzname);
+    Timezone() = default; // invalid timezone
+
+    bool valid() const{
+        return static_cast<bool>(data_);
+    }
+
+	struct tm toLocalTime(time_t secondsSinceEpoch) const {
+
+	}
+
+	time_t fromLocalTime(const struct tm& tm_time) const {
+
+	}
+
+	static struct tm toUtcTime(time_t secondsSinceEpoch, bool yday = false);
+	static time_t fromUtcTime(const struct tm& tm_time);
+	
+	/**
+	* @param year [1900, 2500]
+	* @param month [1, 12]
+	* @param day [1, 31]
+	* @param hour [0, 23]
+	* @param minute [0, 59]
+	* @param seconds [0, 59]
+	*/
+	static time_t fromUtcTime(int year, int month, int day,
+							  int hour, int minute, int seconds) {
+
+	}
+
+	struct Data {
+		std::vector<detail::Transition> transitions;
+		std::vector<detail::LocalTime> localtimes;
+		std::vector<std::string> names;
+		std::string abbreviation;
+	};
+
+private:
+	std::shared_ptr<Data> data_ = {};
+};
+
+} // namespace jlib
diff --git a/jlib/win32/registry.h b/jlib/win32/registry.h
new file mode 100644
index 0000000000000000000000000000000000000000..6f16a69373eaaff9e51f41adbdd8220bd87594f3
--- /dev/null
+++ b/jlib/win32/registry.h
@@ -0,0 +1,933 @@
+#pragma once
+
+
+// mainly inspired by Giovanni Dicanio
+// with some simplifications and changes
+// see his original license below:
+
+
+////////////////////////////////////////////////////////////////////////////////
+//
+//      *** Modern C++ Wrappers Around Windows Registry C API ***
+// 
+//               Copyright (C) by Giovanni Dicanio 
+//  
+// First version: 2017, January 22nd
+// Last update: 2019, March 26th
+// 
+// E-mail: <giovanni.dicanio AT gmail.com>
+// 
+// Registry key handles are safely and conveniently wrapped 
+// in the RegKey resource manager C++ class.
+// 
+// Errors are signaled throwing exceptions of class RegException.
+// 
+// Unicode UTF-16 strings are represented using the std::wstring class; 
+// ATL's CString is not used, to avoid dependencies from ATL or MFC.
+// 
+// This is a header-only self-contained reusable module.
+//
+// Compiler: Visual Studio 2015
+// Code compiles clean at /W4 on both 32-bit and 64-bit builds.
+// 
+// ===========================================================================
+//
+// The MIT License(MIT)
+//
+// Copyright(c) 2017 Giovanni Dicanio
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files(the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions :
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+
+
+#include <Windows.h>
+#include <strsafe.h>
+#include <string>
+#include <memory>
+#include <vector>
+#include <stdexcept>
+#include <utility>
+#include <assert.h>
+#include "../base/noncopyable.h"
+
+
+namespace jlib
+{
+
+namespace win32
+{
+
+namespace reg
+{
+
+class RegException : public std::runtime_error
+{
+public:
+	explicit RegException(const std::string& msg, DWORD errorCode, const std::string& desc)
+		: std::runtime_error(msg)
+		, lastError_(errorCode)
+		, description_(desc)
+	{}
+
+	DWORD errorCode() const { return lastError_; }
+	std::string description() const { return description_; }
+
+private:
+	DWORD lastError_ = ERROR_SUCCESS;
+	std::string description_ = {};
+};
+
+
+inline void throwRegException(const std::string& msg, DWORD errorCode)
+{
+	LPVOID lpMsgBuf;
+
+	FormatMessageA(
+		FORMAT_MESSAGE_ALLOCATE_BUFFER |
+		FORMAT_MESSAGE_FROM_SYSTEM |
+		FORMAT_MESSAGE_IGNORE_INSERTS,
+		NULL,
+		errorCode,
+		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+		(LPTSTR)& lpMsgBuf,
+		0, NULL);
+
+	std::string desc(reinterpret_cast<const char*>(lpMsgBuf));
+	LocalFree(lpMsgBuf);
+
+	throw RegException(msg, errorCode, desc);
+}
+
+
+#define THROW_REG_EXCEPTION(msg, err)											\
+do {																			\
+	std::string errMsg = msg; errMsg += " SourceFile: "; errMsg += __FILE__;	\
+	errMsg += " Line: "; errMsg += std::to_string(__LINE__);					\
+	throwRegException(errMsg, err);												\
+} while(0);
+
+
+class RegKey : public noncopyable
+{
+public:
+	RegKey() = default;
+	explicit RegKey(HKEY hKey) noexcept : hKey_(hKey) {}
+	~RegKey() noexcept { close(); }
+
+	RegKey& operator=(RegKey&& rhs) noexcept {
+		// prevent self-move-assign
+		if ((this != &rhs) && (hKey_ != rhs.hKey_)) {
+			close(); hKey_ = rhs.hKey_; rhs.hKey_ = nullptr;
+		}
+		return *this;
+	}
+
+	// properties
+
+	HKEY hKey() const noexcept { return hKey_; }
+	bool valid() const noexcept { return hKey_ != nullptr; }
+	explicit operator bool() const noexcept { return valid(); }
+	bool isPredefined() const noexcept;
+
+
+	// operations
+
+	void close() noexcept;
+	void open(HKEY hKeyParent, const std::wstring& subKey, REGSAM desiredAccess = KEY_READ | KEY_WRITE);
+	void create(HKEY hKeyParent, const std::wstring& subKey, REGSAM desiredAccess = KEY_READ | KEY_WRITE);
+	void create(HKEY hKeyParent, const std::wstring& subKey, REGSAM desiredAccess, DWORD options, SECURITY_ATTRIBUTES* sa, DWORD* disposition);
+
+
+	// value getters
+
+	DWORD getDwordValue(const std::wstring& valueName);
+	ULONGLONG getQwordValue(const std::wstring& valueName);
+	std::wstring getStringValue(const std::wstring& valueName);
+
+	enum class ExpandStringOption {
+		DonotExpand,
+		Expand,
+	};
+
+	std::wstring getExpandStringValue(const std::wstring& valueName, ExpandStringOption option = ExpandStringOption::DonotExpand);
+	std::vector<std::wstring> getMultiStringValue(const std::wstring& valueName);
+	std::vector<BYTE> getBinaryValue(const std::wstring& valueName);
+
+
+	// value setters
+
+	void setDwordValue(const std::wstring& valueName, DWORD data);
+	void setQwordValue(const std::wstring& valueName, const ULONGLONG& data);
+	void setStringValue(const std::wstring& valueName, const std::wstring& data);
+	void setExpandStringValue(const std::wstring& valueName, const std::wstring& data);
+	void setMultiStringValue(const std::wstring& valueName, const std::vector<std::wstring>& data);
+	void setBinaryValue(const std::wstring& valueName, const std::vector<BYTE>& data);
+	void setBinaryValue(const std::wstring& valueName, const void* data, DWORD dataSize);
+
+
+	// query
+
+	DWORD queryValueType(const std::wstring& valueName);
+	std::vector<std::wstring> querySubKeys();
+	//                    value-name    value-type
+	std::vector<std::pair<std::wstring, DWORD>> queryValues();
+
+
+	// delete
+
+	void deleteValue(const std::wstring& valueName);
+	void deleteKey(const std::wstring& subKey, REGSAM desiredAccess);
+	void deleteTree(const std::wstring& subKey);
+
+
+	static std::wstring regTypeToString(DWORD regType);
+
+private:
+	HKEY hKey_ = nullptr;
+};
+
+
+// implementation
+
+inline bool RegKey::isPredefined() const noexcept
+{
+	// Predefined keys
+	// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724836(v=vs.85).aspx
+
+	return ((hKey_ == HKEY_CURRENT_USER)
+			|| (hKey_ == HKEY_LOCAL_MACHINE)
+			|| (hKey_ == HKEY_CLASSES_ROOT)
+			|| (hKey_ == HKEY_CURRENT_CONFIG)
+			|| (hKey_ == HKEY_CURRENT_USER_LOCAL_SETTINGS)
+			|| (hKey_ == HKEY_PERFORMANCE_DATA)
+			|| (hKey_ == HKEY_PERFORMANCE_NLSTEXT)
+			|| (hKey_ == HKEY_PERFORMANCE_TEXT)
+			|| (hKey_ == HKEY_USERS));
+}
+
+inline void RegKey::close() noexcept
+{
+	if (valid()) {
+		if (!isPredefined()) {
+			::RegCloseKey(hKey_);
+		}
+
+		hKey_ = nullptr;
+	}
+}
+
+inline void RegKey::open(HKEY hKeyParent, const std::wstring& subKey, REGSAM desiredAccess)
+{
+	HKEY hKey{ nullptr };
+	DWORD retCode = ::RegOpenKeyExW(
+		hKeyParent,
+		subKey.c_str(),
+		REG_NONE,           // default options
+		desiredAccess,
+		&hKey
+	);
+
+	if (retCode != ERROR_SUCCESS) {
+		THROW_REG_EXCEPTION("RegOpenKeyEx failed.", retCode);
+	}
+
+	close();
+	hKey_ = hKey;
+}
+
+inline void RegKey::create(const HKEY hKeyParent, const std::wstring& subKey, const REGSAM desiredAccess)
+{
+	constexpr DWORD kDefaultOptions = REG_OPTION_NON_VOLATILE;
+
+	create(hKeyParent, subKey, desiredAccess, kDefaultOptions,
+		   nullptr, // no security attributes,
+		   nullptr  // no disposition 
+	);
+}
+
+inline void RegKey::create(HKEY hKeyParent, const std::wstring& subKey, REGSAM desiredAccess, DWORD options,
+							 SECURITY_ATTRIBUTES* securityAttributes, DWORD* disposition)
+{
+	HKEY hKey{ nullptr };
+	DWORD retCode = ::RegCreateKeyExW(
+		hKeyParent,
+		subKey.c_str(),
+		0,          // reserved
+		REG_NONE,   // user-defined class type parameter not supported
+		options,
+		desiredAccess,
+		securityAttributes,
+		&hKey,
+		disposition
+	);
+
+	if (retCode != ERROR_SUCCESS) {
+		THROW_REG_EXCEPTION("RegCreateKeyEx failed.", retCode);
+	}
+
+	close();
+	hKey_ = hKey;
+}
+
+inline DWORD RegKey::getDwordValue(const std::wstring& valueName)
+{
+	assert(valid());
+
+	DWORD data{};                   // to be read from the registry
+	DWORD dataSize = sizeof(data);  // size of data, in bytes
+
+	const DWORD flags = RRF_RT_REG_DWORD;
+	DWORD retCode = ::RegGetValueW(
+		hKey_,
+		nullptr, // no subkey
+		valueName.c_str(),
+		flags,
+		nullptr, // type not required
+		&data,
+		&dataSize
+	);
+
+	if (retCode != ERROR_SUCCESS) {
+		THROW_REG_EXCEPTION("Cannot get DWORD value: RegGetValue failed.", retCode);
+	}
+
+	return data;
+}
+
+inline ULONGLONG RegKey::getQwordValue(const std::wstring& valueName)
+{
+	assert(valid());
+
+	ULONGLONG data{};               // to be read from the registry
+	DWORD dataSize = sizeof(data);  // size of data, in bytes
+
+	const DWORD flags = RRF_RT_REG_QWORD;
+	DWORD retCode = ::RegGetValueW(
+		hKey_,
+		nullptr, // no subkey
+		valueName.c_str(),
+		flags,
+		nullptr, // type not required
+		&data,
+		&dataSize
+	); 
+	
+	if (retCode != ERROR_SUCCESS) {
+		THROW_REG_EXCEPTION("Cannot get QWORD value: RegGetValue failed.", retCode);
+	}
+
+	return data;
+}
+
+inline std::wstring RegKey::getStringValue(const std::wstring& valueName)
+{
+	assert(valid());
+
+	// Get the size of the result string
+	DWORD dataSize = 0; // size of data, in bytes
+	const DWORD flags = RRF_RT_REG_SZ;
+	DWORD retCode = ::RegGetValueW(
+		hKey_,
+		nullptr, // no subkey
+		valueName.c_str(),
+		flags,
+		nullptr, // type not required
+		nullptr, // output buffer not needed now
+		&dataSize
+	);
+
+	if (retCode != ERROR_SUCCESS) {
+		THROW_REG_EXCEPTION("Cannot get size of string value: RegGetValue failed.", retCode);
+	}
+
+	// Allocate a string of proper size.
+	// Note that dataSize is in bytes and includes the terminating NUL;
+	// we have to convert the size from bytes to wchar_ts for wstring::resize.
+	std::wstring result;
+	result.resize(dataSize / sizeof(wchar_t));
+
+	// Call RegGetValue for the second time to read the string's content
+	retCode = ::RegGetValueW(
+		hKey_,
+		nullptr,    // no subkey
+		valueName.c_str(),
+		flags,
+		nullptr,    // type not required
+		&result[0], // output buffer
+		&dataSize
+	);
+
+	if (retCode != ERROR_SUCCESS) {
+		THROW_REG_EXCEPTION("Cannot get string value: RegGetValue failed.", retCode);
+	}
+
+	// Remove the NUL terminator scribbled by RegGetValue from the wstring
+	result.resize((dataSize / sizeof(wchar_t)) - 1);
+
+	return result;
+}
+
+inline std::wstring RegKey::getExpandStringValue(const std::wstring& valueName, ExpandStringOption expandOption)
+{
+	assert(valid());
+
+	DWORD flags = RRF_RT_REG_EXPAND_SZ;
+
+	// Adjust the flag for RegGetValue considering the expand string option specified by the caller   
+	if (expandOption == ExpandStringOption::DonotExpand) {
+		flags |= RRF_NOEXPAND;
+	}
+
+	// Get the size of the result string
+	DWORD dataSize = 0; // size of data, in bytes
+	DWORD retCode = ::RegGetValueW(
+		hKey_,
+		nullptr,    // no subkey
+		valueName.c_str(),
+		flags,
+		nullptr,    // type not required
+		nullptr,    // output buffer not needed now
+		&dataSize
+	);
+
+	if (retCode != ERROR_SUCCESS) {
+		THROW_REG_EXCEPTION("Cannot get size of expand string value: RegGetValue failed.", retCode);
+	}
+
+	// Allocate a string of proper size.
+	// Note that dataSize is in bytes and includes the terminating NUL.
+	// We must convert from bytes to wchar_ts for wstring::resize.
+	std::wstring result;
+	result.resize(dataSize / sizeof(wchar_t));
+
+	// Call RegGetValue for the second time to read the string's content
+	retCode = ::RegGetValueW(
+		hKey_,
+		nullptr,    // no subkey
+		valueName.c_str(),
+		flags,
+		nullptr,    // type not required
+		&result[0], // output buffer
+		&dataSize
+	);
+
+	if (retCode != ERROR_SUCCESS) {
+		THROW_REG_EXCEPTION("Cannot get expand string value: RegGetValue failed.", retCode);
+	}
+
+	// Remove the NUL terminator scribbled by RegGetValue from the wstring
+	result.resize((dataSize / sizeof(wchar_t)) - 1);
+
+	return result;
+}
+
+inline std::vector<std::wstring> RegKey::getMultiStringValue(const std::wstring& valueName)
+{
+	assert(valid());
+
+	// Request the size of the multi-string, in bytes
+	DWORD dataSize = 0;
+	const DWORD flags = RRF_RT_REG_MULTI_SZ;
+	DWORD retCode = ::RegGetValueW(
+		hKey_,
+		nullptr,    // no subkey
+		valueName.c_str(),
+		flags,
+		nullptr,    // type not required
+		nullptr,    // output buffer not needed now
+		&dataSize
+	);
+
+	if (retCode != ERROR_SUCCESS) {
+		THROW_REG_EXCEPTION("Cannot get size of multi-string value: RegGetValue failed.", retCode);
+	}
+
+	// Allocate room for the result multi-string.
+	// Note that dataSize is in bytes, but our vector<wchar_t>::resize method requires size 
+	// to be expressed in wchar_ts.
+	std::vector<wchar_t> data;
+	data.resize(dataSize / sizeof(wchar_t));
+
+	// Read the multi-string from the registry into the vector object
+	retCode = ::RegGetValueW(
+		hKey_,
+		nullptr,    // no subkey
+		valueName.c_str(),
+		flags,
+		nullptr,    // no type required
+		&data[0],   // output buffer
+		&dataSize
+	);
+
+	if (retCode != ERROR_SUCCESS) {
+		THROW_REG_EXCEPTION("Cannot get multi-string value: RegGetValue failed.", retCode);
+	}
+
+	// Resize vector to the actual size returned by GetRegValue.
+	// Note that the vector is a vector of wchar_ts, instead the size returned by GetRegValue
+	// is in bytes, so we have to scale from bytes to wchar_t count.
+	data.resize(dataSize / sizeof(wchar_t));
+
+	// Parse the double-NUL-terminated string into a vector<wstring>, 
+	// which will be returned to the caller
+	std::vector<std::wstring> result;
+	const wchar_t* currStringPtr = &data[0];
+	while (*currStringPtr != L'\0') {
+		// Current string is NUL-terminated, so get its length calling wcslen
+		const size_t currStringLength = wcslen(currStringPtr);
+
+		// Add current string to the result vector
+		result.push_back(std::wstring{ currStringPtr, currStringLength });
+
+		// Move to the next string
+		currStringPtr += currStringLength + 1;
+	}
+
+	return result;
+}
+
+inline std::vector<BYTE> RegKey::getBinaryValue(const std::wstring& valueName)
+{
+	assert(valid());
+
+	// Get the size of the binary data
+	DWORD dataSize = 0; // size of data, in bytes
+	const DWORD flags = RRF_RT_REG_BINARY;
+	DWORD retCode = ::RegGetValueW(
+		hKey_,
+		nullptr,    // no subkey
+		valueName.c_str(),
+		flags,
+		nullptr,    // type not required
+		nullptr,    // output buffer not needed now
+		&dataSize
+	);
+
+	if (retCode != ERROR_SUCCESS) {
+		THROW_REG_EXCEPTION("Cannot get size of binary data: RegGetValue failed.", retCode);
+	}
+
+	// Allocate a buffer of proper size to store the binary data
+	std::vector<BYTE> data(dataSize);
+
+	// Call RegGetValue for the second time to read the data content
+	retCode = ::RegGetValueW(
+		hKey_,
+		nullptr,    // no subkey
+		valueName.c_str(),
+		flags,
+		nullptr,    // type not required
+		&data[0],   // output buffer
+		&dataSize
+	);
+
+	if (retCode != ERROR_SUCCESS) {
+		THROW_REG_EXCEPTION("Cannot get binary data: RegGetValue failed.", retCode);
+	}
+
+	return data;
+}
+
+inline void RegKey::setDwordValue(const std::wstring& valueName, DWORD data)
+{
+	assert(valid());
+
+	DWORD retCode = ::RegSetValueExW(
+		hKey_,
+		valueName.c_str(),
+		0, // reserved
+		REG_DWORD,
+		reinterpret_cast<const BYTE*>(&data),
+		sizeof(data)
+	);
+
+	if (retCode != ERROR_SUCCESS) {
+		THROW_REG_EXCEPTION("Cannot write DWORD value: RegSetValueEx failed.", retCode);
+	}
+}
+
+inline void RegKey::setQwordValue(const std::wstring& valueName, const ULONGLONG& data)
+{
+	assert(valid());
+
+	DWORD retCode = ::RegSetValueExW(
+		hKey_,
+		valueName.c_str(),
+		0, // reserved
+		REG_QWORD,
+		reinterpret_cast<const BYTE*>(&data),
+		sizeof(data)
+	);
+
+	if (retCode != ERROR_SUCCESS) {
+		THROW_REG_EXCEPTION("Cannot write QWORD value: RegSetValueEx failed.", retCode);
+	}
+}
+
+inline void RegKey::setStringValue(const std::wstring& valueName, const std::wstring& data)
+{
+	assert(valid());
+
+	// String size including the terminating NUL, in bytes
+	const DWORD dataSize = static_cast<DWORD>((data.length() + 1) * sizeof(wchar_t));
+
+	DWORD retCode = ::RegSetValueExW(
+		hKey_,
+		valueName.c_str(),
+		0, // reserved
+		REG_SZ,
+		reinterpret_cast<const BYTE*>(data.c_str()),
+		dataSize
+	);
+
+	if (retCode != ERROR_SUCCESS) {
+		THROW_REG_EXCEPTION("Cannot write string value: RegSetValueEx failed.", retCode);
+	}
+}
+
+inline void RegKey::setExpandStringValue(const std::wstring& valueName, const std::wstring& data)
+{
+	assert(valid());
+
+	// String size including the terminating NUL, in bytes
+	const DWORD dataSize = static_cast<DWORD>((data.length() + 1) * sizeof(wchar_t));
+
+	DWORD retCode = ::RegSetValueExW(
+		hKey_,
+		valueName.c_str(),
+		0, // reserved
+		REG_EXPAND_SZ,
+		reinterpret_cast<const BYTE*>(data.c_str()),
+		dataSize
+	);
+
+	if (retCode != ERROR_SUCCESS) {
+		THROW_REG_EXCEPTION("Cannot write expand string value: RegSetValueEx failed.", retCode);
+	}
+}
+
+inline void RegKey::setMultiStringValue(const std::wstring& valueName, const std::vector<std::wstring>& data)
+{
+	assert(valid());
+
+	static auto buildMultiString = [](const std::vector<std::wstring>& data) -> std::vector<wchar_t> {
+		// Special case of the empty multi-string
+		if (data.empty()) {
+			// Build a vector containing just two NULs
+			return std::vector<wchar_t>(2, L'\0');
+		}
+
+		// Get the total length in wchar_ts of the multi-string
+		size_t totalLen = 0;
+		for (const auto& s : data) {
+			// Add one to current string's length for the terminating NUL
+			totalLen += (s.length() + 1);
+		}
+
+		// Add one for the last NUL terminator (making the whole structure double-NUL terminated)
+		totalLen++;
+
+		// Allocate a buffer to store the multi-string
+		std::vector<wchar_t> multiString;
+		multiString.reserve(totalLen);
+
+		// Copy the single strings into the multi-string
+		for (const auto& s : data) {
+			multiString.insert(multiString.end(), s.begin(), s.end());
+
+			// Don't forget to NUL-terminate the current string
+			multiString.push_back(L'\0');
+		}
+
+		// Add the last NUL-terminator
+		multiString.push_back(L'\0');
+
+		return multiString;
+	};
+
+	// First, we have to build a double-NUL-terminated multi-string from the input data
+	const std::vector<wchar_t> multiString = buildMultiString(data);
+
+	// Total size, in bytes, of the whole multi-string structure
+	const DWORD dataSize = static_cast<DWORD>(multiString.size() * sizeof(wchar_t));
+
+	DWORD retCode = ::RegSetValueExW(
+		hKey_,
+		valueName.c_str(),
+		0, // reserved
+		REG_MULTI_SZ,
+		reinterpret_cast<const BYTE*>(&multiString[0]),
+		dataSize
+	);
+
+	if (retCode != ERROR_SUCCESS) {
+		THROW_REG_EXCEPTION("Cannot write multi-string value: RegSetValueEx failed.", retCode);
+	}
+}
+
+inline void RegKey::setBinaryValue(const std::wstring& valueName, const std::vector<BYTE>& data)
+{
+	assert(valid());
+
+	// Total data size, in bytes
+	const DWORD dataSize = static_cast<DWORD>(data.size());
+
+	DWORD retCode = ::RegSetValueExW(
+		hKey_,
+		valueName.c_str(),
+		0, // reserved
+		REG_BINARY,
+		&data[0],
+		dataSize
+	);
+
+	if (retCode != ERROR_SUCCESS) {
+		THROW_REG_EXCEPTION("Cannot write binary data value: RegSetValueEx failed.", retCode);
+	}
+}
+
+inline void RegKey::setBinaryValue(const std::wstring& valueName, const void* data, DWORD dataSize)
+{
+	assert(valid());
+
+	DWORD retCode = ::RegSetValueExW(
+		hKey_,
+		valueName.c_str(),
+		0, // reserved
+		REG_BINARY,
+		static_cast<const BYTE*>(data),
+		dataSize
+	);
+
+	if (retCode != ERROR_SUCCESS) {
+		THROW_REG_EXCEPTION("Cannot write binary data value: RegSetValueEx failed.", retCode);
+	}
+}
+
+inline DWORD RegKey::queryValueType(const std::wstring& valueName)
+{
+	assert(valid());
+
+	DWORD typeId{};     // will be returned by RegQueryValueEx
+
+	DWORD retCode = ::RegQueryValueExW(
+		hKey_,
+		valueName.c_str(),
+		nullptr,    // reserved
+		&typeId,
+		nullptr,    // not interested
+		nullptr     // not interested
+	);
+
+	if (retCode != ERROR_SUCCESS) {
+		THROW_REG_EXCEPTION("Cannot get the value type: RegQueryValueEx failed.", retCode);
+	}
+
+	return typeId;
+}
+
+inline std::vector<std::wstring> RegKey::querySubKeys()
+{
+	assert(valid());
+
+	// Get some useful enumeration info, like the total number of subkeys
+	// and the maximum length of the subkey names
+	DWORD subKeyCount{};
+	DWORD maxSubKeyNameLen{};
+	DWORD retCode = ::RegQueryInfoKeyW(
+		hKey_,
+		nullptr,    // no user-defined class
+		nullptr,    // no user-defined class size
+		nullptr,    // reserved
+		&subKeyCount,
+		&maxSubKeyNameLen,
+		nullptr,    // no subkey class length
+		nullptr,    // no value count
+		nullptr,    // no value name max length
+		nullptr,    // no max value length
+		nullptr,    // no security descriptor
+		nullptr     // no last write time
+	);
+
+	if (retCode != ERROR_SUCCESS) {
+		THROW_REG_EXCEPTION("RegQueryInfoKey failed while preparing for subkey enumeration.", retCode);
+	}
+
+	// NOTE: According to the MSDN documentation, the size returned for subkey name max length
+	// does *not* include the terminating NUL, so let's add +1 to take it into account
+	// when I allocate the buffer for reading subkey names.
+	maxSubKeyNameLen++;
+
+	// Preallocate a buffer for the subkey names
+	auto nameBuffer = std::make_unique<wchar_t[]>(maxSubKeyNameLen);
+
+	// The result subkey names will be stored here
+	std::vector<std::wstring> subkeyNames;
+
+	// Enumerate all the subkeys
+	for (DWORD index = 0; index < subKeyCount; index++) {
+		// Get the name of the current subkey
+		DWORD subKeyNameLen = maxSubKeyNameLen;
+		retCode = ::RegEnumKeyExW(
+			hKey_,
+			index,
+			nameBuffer.get(),
+			&subKeyNameLen,
+			nullptr, // reserved
+			nullptr, // no class
+			nullptr, // no class
+			nullptr  // no last write time
+		);
+
+		if (retCode != ERROR_SUCCESS) {
+			THROW_REG_EXCEPTION("Cannot enumerate subkeys: RegEnumKeyEx failed.", retCode);
+		}
+
+		// On success, the ::RegEnumKeyEx API writes the length of the
+		// subkey name in the subKeyNameLen output parameter 
+		// (not including the terminating NUL).
+		// So I can build a wstring based on that length.
+		subkeyNames.push_back(std::wstring{ nameBuffer.get(), subKeyNameLen });
+	}
+
+	return subkeyNames;
+}
+
+std::vector<std::pair<std::wstring, DWORD>> RegKey::queryValues()
+{
+	assert(valid());
+
+	// Get useful enumeration info, like the total number of values
+	// and the maximum length of the value names
+	DWORD valueCount{};
+	DWORD maxValueNameLen{};
+	DWORD retCode = ::RegQueryInfoKeyW(
+		hKey_,
+		nullptr,    // no user-defined class
+		nullptr,    // no user-defined class size
+		nullptr,    // reserved
+		nullptr,    // no subkey count
+		nullptr,    // no subkey max length
+		nullptr,    // no subkey class length
+		&valueCount,
+		&maxValueNameLen,
+		nullptr,    // no max value length
+		nullptr,    // no security descriptor
+		nullptr     // no last write time
+	);
+
+	if (retCode != ERROR_SUCCESS) {
+		THROW_REG_EXCEPTION("RegQueryInfoKey failed while preparing for value enumeration.", retCode);
+	}
+
+	// NOTE: According to the MSDN documentation, the size returned for value name max length
+	// does *not* include the terminating NUL, so let's add +1 to take it into account
+	// when I allocate the buffer for reading value names.
+	maxValueNameLen++;
+
+	// Preallocate a buffer for the value names
+	auto nameBuffer = std::make_unique<wchar_t[]>(maxValueNameLen);
+
+	// The value names and types will be stored here
+	std::vector<std::pair<std::wstring, DWORD>> valueInfo;
+
+	// Enumerate all the values
+	for (DWORD index = 0; index < valueCount; index++) {
+		// Get the name and the type of the current value
+		DWORD valueNameLen = maxValueNameLen;
+		DWORD valueType{};
+		retCode = ::RegEnumValueW(
+			hKey_,
+			index,
+			nameBuffer.get(),
+			&valueNameLen,
+			nullptr,    // reserved
+			&valueType,
+			nullptr,    // no data
+			nullptr     // no data size
+		);
+
+		if (retCode != ERROR_SUCCESS) {
+			THROW_REG_EXCEPTION("Cannot enumerate values: RegEnumValue failed.", retCode);
+		}
+
+		// On success, the RegEnumValue API writes the length of the
+		// value name in the valueNameLen output parameter 
+		// (not including the terminating NUL).
+		// So we can build a wstring based on that.
+		valueInfo.push_back(
+			std::make_pair(std::wstring{ nameBuffer.get(), valueNameLen }, valueType)
+		);
+	}
+
+	return valueInfo;
+}
+
+inline void RegKey::deleteValue(const std::wstring& valueName)
+{
+	assert(valid());
+
+	DWORD retCode = ::RegDeleteValueW(hKey_, valueName.c_str());
+	if (retCode != ERROR_SUCCESS) {
+		THROW_REG_EXCEPTION("RegDeleteValue failed.", retCode);
+	}
+}
+
+inline void RegKey::deleteKey(const std::wstring& subKey, const REGSAM desiredAccess)
+{
+	assert(valid());
+
+	DWORD retCode = ::RegDeleteKeyExW(hKey_, subKey.c_str(), desiredAccess, 0);
+	if (retCode != ERROR_SUCCESS) {
+		THROW_REG_EXCEPTION("RegDeleteKeyEx failed.", retCode);
+	}
+}
+
+inline void RegKey::deleteTree(const std::wstring& subKey)
+{
+	assert(valid());
+
+	DWORD retCode = ::RegDeleteTreeW(hKey_, subKey.c_str());
+	if (retCode != ERROR_SUCCESS) {
+		THROW_REG_EXCEPTION("RegDeleteTree failed.", retCode);
+	}
+}
+
+inline std::wstring RegKey::regTypeToString(const DWORD regType)
+{
+	switch (regType)
+	{
+	case REG_SZ:        return L"REG_SZ";
+	case REG_EXPAND_SZ: return L"REG_EXPAND_SZ";
+	case REG_MULTI_SZ:  return L"REG_MULTI_SZ";
+	case REG_DWORD:     return L"REG_DWORD";
+	case REG_QWORD:     return L"REG_QWORD";
+	case REG_BINARY:    return L"REG_BINARY";
+
+	default:            return L"Unknown/unsupported registry type";
+	}
+}
+
+} // namespace reg
+
+} // namespace win32
+
+} // namespace jlib
diff --git a/test/test.sln b/test/test.sln
index 190d8c6891c1eaacec659f6c1f897e0ec3398695..f98224eaf590ed2ded82ac2c1a2172841b3ff4b7 100644
--- a/test/test.sln
+++ b/test/test.sln
@@ -48,10 +48,44 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "base", "base", "{608A105E-4
 		..\jlib\base\singleton.h = ..\jlib\base\singleton.h
 		..\jlib\base\stringpiece.h = ..\jlib\base\stringpiece.h
 		..\jlib\base\timestamp.h = ..\jlib\base\timestamp.h
+		..\jlib\base\timezone.h = ..\jlib\base\timezone.h
 	EndProjectSection
 EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_singleton", "test_singleton\test_singleton.vcxproj", "{B4B8F20B-1B3E-42CD-8B37-A734EF3CA279}"
 EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_logging", "test_logging\test_logging.vcxproj", "{3E322E9F-E560-4300-AFF8-1DCE44E7DDA2}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_timezone", "test_timezone\test_timezone.vcxproj", "{8A9333D5-2981-4E92-87BF-AFFC5F99B1A4}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "win32", "win32", "{B5D9E71E-2BFE-4D04-A66A-4CB0A103CD77}"
+	ProjectSection(SolutionItems) = preProject
+		..\jlib\win32\clipboard.h = ..\jlib\win32\clipboard.h
+		..\jlib\win32\DeviceUniqueIdentifier.cpp = ..\jlib\win32\DeviceUniqueIdentifier.cpp
+		..\jlib\win32\DeviceUniqueIdentifier.h = ..\jlib\win32\DeviceUniqueIdentifier.h
+		..\jlib\win32\deviceuniqueidentifierheaderonly.h = ..\jlib\win32\deviceuniqueidentifierheaderonly.h
+		..\jlib\win32\file_op = ..\jlib\win32\file_op
+		..\jlib\win32\file_op.h = ..\jlib\win32\file_op.h
+		..\jlib\win32\memory_micros.h = ..\jlib\win32\memory_micros.h
+		..\jlib\win32\MtVerify.h = ..\jlib\win32\MtVerify.h
+		..\jlib\win32\MtVerify.h.bak = ..\jlib\win32\MtVerify.h.bak
+		..\jlib\win32\MyWSAError.h = ..\jlib\win32\MyWSAError.h
+		..\jlib\win32\odbccp32.lib = ..\jlib\win32\odbccp32.lib
+		..\jlib\win32\path_op.h = ..\jlib\win32\path_op.h
+		..\jlib\win32\registry.h = ..\jlib\win32\registry.h
+		..\jlib\win32\UnicodeTool.h = ..\jlib\win32\UnicodeTool.h
+	EndProjectSection
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "mfc", "mfc", "{9A3F45EB-C57C-4789-8177-0DEFCC5AFD0E}"
+	ProjectSection(SolutionItems) = preProject
+		..\jlib\win32\mfc\FileOper.h = ..\jlib\win32\mfc\FileOper.h
+	EndProjectSection
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{21DC893D-AB0B-48E1-9E23-069A025218D9}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "win32_tests", "win32_tests", "{0E6598D3-602D-4552-97F7-DC5AB458D553}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_registry", "test_registry\test_registry.vcxproj", "{519E5179-6E95-44EF-A63B-65C9C9C47A3B}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|x64 = Debug|x64
@@ -122,13 +156,51 @@ Global
 		{B4B8F20B-1B3E-42CD-8B37-A734EF3CA279}.Release|x64.Build.0 = Release|x64
 		{B4B8F20B-1B3E-42CD-8B37-A734EF3CA279}.Release|x86.ActiveCfg = Release|Win32
 		{B4B8F20B-1B3E-42CD-8B37-A734EF3CA279}.Release|x86.Build.0 = Release|Win32
+		{3E322E9F-E560-4300-AFF8-1DCE44E7DDA2}.Debug|x64.ActiveCfg = Debug|x64
+		{3E322E9F-E560-4300-AFF8-1DCE44E7DDA2}.Debug|x64.Build.0 = Debug|x64
+		{3E322E9F-E560-4300-AFF8-1DCE44E7DDA2}.Debug|x86.ActiveCfg = Debug|Win32
+		{3E322E9F-E560-4300-AFF8-1DCE44E7DDA2}.Debug|x86.Build.0 = Debug|Win32
+		{3E322E9F-E560-4300-AFF8-1DCE44E7DDA2}.Release|x64.ActiveCfg = Release|x64
+		{3E322E9F-E560-4300-AFF8-1DCE44E7DDA2}.Release|x64.Build.0 = Release|x64
+		{3E322E9F-E560-4300-AFF8-1DCE44E7DDA2}.Release|x86.ActiveCfg = Release|Win32
+		{3E322E9F-E560-4300-AFF8-1DCE44E7DDA2}.Release|x86.Build.0 = Release|Win32
+		{8A9333D5-2981-4E92-87BF-AFFC5F99B1A4}.Debug|x64.ActiveCfg = Debug|x64
+		{8A9333D5-2981-4E92-87BF-AFFC5F99B1A4}.Debug|x64.Build.0 = Debug|x64
+		{8A9333D5-2981-4E92-87BF-AFFC5F99B1A4}.Debug|x86.ActiveCfg = Debug|Win32
+		{8A9333D5-2981-4E92-87BF-AFFC5F99B1A4}.Debug|x86.Build.0 = Debug|Win32
+		{8A9333D5-2981-4E92-87BF-AFFC5F99B1A4}.Release|x64.ActiveCfg = Release|x64
+		{8A9333D5-2981-4E92-87BF-AFFC5F99B1A4}.Release|x64.Build.0 = Release|x64
+		{8A9333D5-2981-4E92-87BF-AFFC5F99B1A4}.Release|x86.ActiveCfg = Release|Win32
+		{8A9333D5-2981-4E92-87BF-AFFC5F99B1A4}.Release|x86.Build.0 = Release|Win32
+		{519E5179-6E95-44EF-A63B-65C9C9C47A3B}.Debug|x64.ActiveCfg = Debug|x64
+		{519E5179-6E95-44EF-A63B-65C9C9C47A3B}.Debug|x64.Build.0 = Debug|x64
+		{519E5179-6E95-44EF-A63B-65C9C9C47A3B}.Debug|x86.ActiveCfg = Debug|Win32
+		{519E5179-6E95-44EF-A63B-65C9C9C47A3B}.Debug|x86.Build.0 = Debug|Win32
+		{519E5179-6E95-44EF-A63B-65C9C9C47A3B}.Release|x64.ActiveCfg = Release|x64
+		{519E5179-6E95-44EF-A63B-65C9C9C47A3B}.Release|x64.Build.0 = Release|x64
+		{519E5179-6E95-44EF-A63B-65C9C9C47A3B}.Release|x86.ActiveCfg = Release|Win32
+		{519E5179-6E95-44EF-A63B-65C9C9C47A3B}.Release|x86.Build.0 = Release|Win32
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
 	EndGlobalSection
 	GlobalSection(NestedProjects) = preSolution
+		{155F525A-FA2F-471F-A2DF-9B77E7CCCFA5} = {21DC893D-AB0B-48E1-9E23-069A025218D9}
+		{1E6D1113-2EDE-4E7E-BF17-A3663101C3AE} = {0E6598D3-602D-4552-97F7-DC5AB458D553}
+		{CA7812A3-9E48-4A94-B39A-32EED587E38A} = {0E6598D3-602D-4552-97F7-DC5AB458D553}
+		{A4B1CDB2-7325-4E22-AD0A-1D2907924CDB} = {0E6598D3-602D-4552-97F7-DC5AB458D553}
+		{B12702AD-ABFB-343A-A199-8E24837244A3} = {21DC893D-AB0B-48E1-9E23-069A025218D9}
+		{8A39D7AF-50AF-43BD-8CC6-DA20B6349F03} = {21DC893D-AB0B-48E1-9E23-069A025218D9}
+		{372670F2-83A5-4058-B93C-BB0DCC1521ED} = {21DC893D-AB0B-48E1-9E23-069A025218D9}
 		{5A2CA1BE-5A4B-41B0-B74A-F86AB433F4A5} = {42703978-A988-403D-9723-E35527FA8A07}
 		{608A105E-40DB-44FD-8FC2-A66AB921688D} = {42703978-A988-403D-9723-E35527FA8A07}
+		{B4B8F20B-1B3E-42CD-8B37-A734EF3CA279} = {21DC893D-AB0B-48E1-9E23-069A025218D9}
+		{3E322E9F-E560-4300-AFF8-1DCE44E7DDA2} = {21DC893D-AB0B-48E1-9E23-069A025218D9}
+		{8A9333D5-2981-4E92-87BF-AFFC5F99B1A4} = {21DC893D-AB0B-48E1-9E23-069A025218D9}
+		{B5D9E71E-2BFE-4D04-A66A-4CB0A103CD77} = {42703978-A988-403D-9723-E35527FA8A07}
+		{9A3F45EB-C57C-4789-8177-0DEFCC5AFD0E} = {B5D9E71E-2BFE-4D04-A66A-4CB0A103CD77}
+		{0E6598D3-602D-4552-97F7-DC5AB458D553} = {21DC893D-AB0B-48E1-9E23-069A025218D9}
+		{519E5179-6E95-44EF-A63B-65C9C9C47A3B} = {0E6598D3-602D-4552-97F7-DC5AB458D553}
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {A8EBEA58-739C-4DED-99C0-239779F57D5D}
diff --git a/test/test_logging/test_logging.cpp b/test/test_logging/test_logging.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..78a3a91ca7b6cc0fe0479c5a7526953ff14fbfeb
--- /dev/null
+++ b/test/test_logging/test_logging.cpp
@@ -0,0 +1,6 @@
+#include "../../jlib/base/logging.h"
+
+int main()
+{
+
+}
\ No newline at end of file
diff --git a/test/test_logging/test_logging.vcxproj b/test/test_logging/test_logging.vcxproj
new file mode 100644
index 0000000000000000000000000000000000000000..512b765541ea6cae7e1f315e5d77441133a933c3
--- /dev/null
+++ b/test/test_logging/test_logging.vcxproj
@@ -0,0 +1,132 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <VCProjectVersion>16.0</VCProjectVersion>
+    <ProjectGuid>{3E322E9F-E560-4300-AFF8-1DCE44E7DDA2}</ProjectGuid>
+    <RootNamespace>testlogging</RootNamespace>
+    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v142</PlatformToolset>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v142</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v142</PlatformToolset>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v142</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="Shared">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup />
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <SDLCheck>true</SDLCheck>
+      <ConformanceMode>true</ConformanceMode>
+      <AdditionalIncludeDirectories>$(BOOST);$(DEVLIBS)\jlib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <SDLCheck>true</SDLCheck>
+      <ConformanceMode>true</ConformanceMode>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <ConformanceMode>true</ConformanceMode>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <ConformanceMode>true</ConformanceMode>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="test_logging.cpp" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/test/test_logging/test_logging.vcxproj.filters b/test/test_logging/test_logging.vcxproj.filters
new file mode 100644
index 0000000000000000000000000000000000000000..12ec501e1240a1c60889768d87ebc50bd5be3039
--- /dev/null
+++ b/test/test_logging/test_logging.vcxproj.filters
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+      <Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
+    </Filter>
+    <Filter Include="Resource Files">
+      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="test_logging.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/test/test_logging/test_logging.vcxproj.user b/test/test_logging/test_logging.vcxproj.user
new file mode 100644
index 0000000000000000000000000000000000000000..88a550947edbc3c5003a41726f0749201fdb6822
--- /dev/null
+++ b/test/test_logging/test_logging.vcxproj.user
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup />
+</Project>
\ No newline at end of file
diff --git a/test/test_registry/test_registry.cpp b/test/test_registry/test_registry.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d7f251c263c5702bc964074fe5a4359486c41ea6
--- /dev/null
+++ b/test/test_registry/test_registry.cpp
@@ -0,0 +1,162 @@
+#include "../../jlib/win32/registry.h"
+#include <exception>
+#include <iostream>
+
+using namespace std;
+using namespace jlib::win32::reg;
+
+int main()
+{
+	constexpr int kExitError = 1;
+
+	try
+	{
+		wcout << L"=========================================\n";
+		wcout << L"*** Testing Giovanni Dicanio's WinReg ***\n";
+		wcout << L"=========================================\n\n";
+
+		//
+		// Test subkey and value enumeration
+		// 
+
+		const wstring testSubKey = L"SOFTWARE\\GioTest";
+		RegKey key;
+
+		key.open(HKEY_CURRENT_USER, testSubKey);
+
+		vector<wstring> subKeyNames = key.querySubKeys();
+		wcout << L"Subkeys:\n";
+		for (const auto& s : subKeyNames)
+		{
+			wcout << L"  [" << s << L"]\n";
+		}
+		wcout << L'\n';
+
+		vector<pair<wstring, DWORD>> values = key.queryValues();
+		wcout << L"Values:\n";
+		for (const auto& v : values)
+		{
+			wcout << L"  [" << v.first << L"](" << RegKey::regTypeToString(v.second) << L")\n";
+		}
+		wcout << L'\n';
+
+		key.close();
+
+
+		//
+		// Test SetXxxValue and GetXxxValue methods
+		// 
+
+		key.open(HKEY_CURRENT_USER, testSubKey);
+
+		const DWORD testDw = 0x1234ABCD;
+		const ULONGLONG testQw = 0xAABBCCDD11223344;
+		const wstring testSz = L"CiaoTestSz";
+		const wstring testExpandSz = L"%PATH%";
+		const vector<BYTE> testBinary{ 0xAA, 0xBB, 0xCC, 0x11, 0x22, 0x33 };
+		const vector<wstring> testMultiSz{ L"Hi", L"Hello", L"Ciao" };
+
+		key.setDwordValue(L"TestValueDword", testDw);
+		key.setQwordValue(L"TestValueQword", testQw);
+		key.setStringValue(L"TestValueString", testSz);
+		key.setExpandStringValue(L"TestValueExpandString", testExpandSz);
+		key.setMultiStringValue(L"TestValueMultiString", testMultiSz);
+		key.setBinaryValue(L"TestValueBinary", testBinary);
+
+		DWORD testDw1 = key.getDwordValue(L"TestValueDword");
+		if (testDw1 != testDw)
+		{
+			wcout << L"RegKey::GetDwordValue failed.\n";
+		}
+
+		DWORD typeId = key.queryValueType(L"TestValueDword");
+		if (typeId != REG_DWORD)
+		{
+			wcout << L"RegKey::QueryValueType failed for REG_DWORD.\n";
+		}
+
+		ULONGLONG testQw1 = key.getQwordValue(L"TestValueQword");
+		if (testQw1 != testQw)
+		{
+			wcout << L"RegKey::GetQwordValue failed.\n";
+		}
+
+		typeId = key.queryValueType(L"TestValueQword");
+		if (typeId != REG_QWORD)
+		{
+			wcout << L"RegKey::QueryValueType failed for REG_QWORD.\n";
+		}
+
+		wstring testSz1 = key.getStringValue(L"TestValueString");
+		if (testSz1 != testSz)
+		{
+			wcout << L"RegKey::GetStringValue failed.\n";
+		}
+
+		typeId = key.queryValueType(L"TestValueString");
+		if (typeId != REG_SZ)
+		{
+			wcout << L"RegKey::QueryValueType failed for REG_SZ.\n";
+		}
+
+		wstring testExpandSz1 = key.getExpandStringValue(L"TestValueExpandString");
+		if (testExpandSz1 != testExpandSz)
+		{
+			wcout << L"RegKey::GetExpandStringValue failed.\n";
+		}
+
+		typeId = key.queryValueType(L"TestValueExpandString");
+		if (typeId != REG_EXPAND_SZ)
+		{
+			wcout << L"RegKey::QueryValueType failed for REG_EXPAND_SZ.\n";
+		}
+
+		vector<wstring> testMultiSz1 = key.getMultiStringValue(L"TestValueMultiString");
+		if (testMultiSz1 != testMultiSz)
+		{
+			wcout << L"RegKey::GetMultiStringValue failed.\n";
+		}
+
+		typeId = key.queryValueType(L"TestValueMultiString");
+		if (typeId != REG_MULTI_SZ)
+		{
+			wcout << L"RegKey::QueryValueType failed for REG_MULTI_SZ.\n";
+		}
+
+		vector<BYTE> testBinary1 = key.getBinaryValue(L"TestValueBinary");
+		if (testBinary1 != testBinary)
+		{
+			wcout << L"RegKey::GetBinaryValue failed.\n";
+		}
+
+		typeId = key.queryValueType(L"TestValueBinary");
+		if (typeId != REG_BINARY)
+		{
+			wcout << L"RegKey::QueryValueType failed for REG_BINARY.\n";
+		}
+
+
+		//
+		// Remove some test values
+		//
+
+		key.deleteValue(L"TestValueDword");
+		key.deleteValue(L"TestValueQword");
+		key.deleteValue(L"TestValueString");
+		key.deleteValue(L"TestValueExpandString");
+		key.deleteValue(L"TestValueMultiString");
+		key.deleteValue(L"TestValueBinary");
+
+
+
+		wcout << L"All right!! :)\n\n";
+	} catch (const RegException& e) {
+		cout << "\n*** Registry Exception: " << e.what();
+		cout << "\n*** [Windows API error code = " << e.errorCode();
+		cout << "\n*** Description: " << e.description() << "\n\n";
+		return kExitError;
+	} catch (const exception& e) {
+		cout << "\n*** ERROR: " << e.what() << '\n';
+		return kExitError;
+	}
+}
diff --git a/test/test_registry/test_registry.vcxproj b/test/test_registry/test_registry.vcxproj
new file mode 100644
index 0000000000000000000000000000000000000000..522d5d934495e4f0a8cc090274a5d8f207c972fa
--- /dev/null
+++ b/test/test_registry/test_registry.vcxproj
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <VCProjectVersion>16.0</VCProjectVersion>
+    <ProjectGuid>{519E5179-6E95-44EF-A63B-65C9C9C47A3B}</ProjectGuid>
+    <RootNamespace>testregistry</RootNamespace>
+    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v142</PlatformToolset>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v142</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v142</PlatformToolset>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v142</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="Shared">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup />
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <SDLCheck>true</SDLCheck>
+      <ConformanceMode>true</ConformanceMode>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <SDLCheck>true</SDLCheck>
+      <ConformanceMode>true</ConformanceMode>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <ConformanceMode>true</ConformanceMode>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <ConformanceMode>true</ConformanceMode>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="test_registry.cpp" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/test/test_registry/test_registry.vcxproj.filters b/test/test_registry/test_registry.vcxproj.filters
new file mode 100644
index 0000000000000000000000000000000000000000..fae7ca8e03e57b31c829ee8cab6dd835bf256570
--- /dev/null
+++ b/test/test_registry/test_registry.vcxproj.filters
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+      <Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
+    </Filter>
+    <Filter Include="Resource Files">
+      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="test_registry.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/test/test_registry/test_registry.vcxproj.user b/test/test_registry/test_registry.vcxproj.user
new file mode 100644
index 0000000000000000000000000000000000000000..88a550947edbc3c5003a41726f0749201fdb6822
--- /dev/null
+++ b/test/test_registry/test_registry.vcxproj.user
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup />
+</Project>
\ No newline at end of file
diff --git a/test/test_timezone/test_timezone.cpp b/test/test_timezone/test_timezone.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..dfce3e7adb25582f3f3681ce748e0c97ebab7d2b
--- /dev/null
+++ b/test/test_timezone/test_timezone.cpp
@@ -0,0 +1,11 @@
+#include <Windows.h>
+#include <stdio.h>
+#include "../../jlib/base/timezone.h"
+
+int main()
+{
+	TIME_ZONE_INFORMATION info = { 0 };
+	auto ret = GetTimeZoneInformation(&info);
+
+	printf("info.bias=%ld\n", info.Bias);
+}
\ No newline at end of file
diff --git a/test/test_timezone/test_timezone.vcxproj b/test/test_timezone/test_timezone.vcxproj
new file mode 100644
index 0000000000000000000000000000000000000000..30634b3b706972b66877bb3a8356c134e2efc442
--- /dev/null
+++ b/test/test_timezone/test_timezone.vcxproj
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <VCProjectVersion>16.0</VCProjectVersion>
+    <ProjectGuid>{8A9333D5-2981-4E92-87BF-AFFC5F99B1A4}</ProjectGuid>
+    <RootNamespace>testtimezone</RootNamespace>
+    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v142</PlatformToolset>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v142</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v142</PlatformToolset>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v142</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="Shared">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup />
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <SDLCheck>true</SDLCheck>
+      <ConformanceMode>true</ConformanceMode>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <SDLCheck>true</SDLCheck>
+      <ConformanceMode>true</ConformanceMode>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <ConformanceMode>true</ConformanceMode>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <ConformanceMode>true</ConformanceMode>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="test_timezone.cpp" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/test/test_timezone/test_timezone.vcxproj.filters b/test/test_timezone/test_timezone.vcxproj.filters
new file mode 100644
index 0000000000000000000000000000000000000000..6d84a357a1b47eaf32fef3ea1a980f121a3a38a1
--- /dev/null
+++ b/test/test_timezone/test_timezone.vcxproj.filters
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+      <Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
+    </Filter>
+    <Filter Include="Resource Files">
+      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="test_timezone.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/test/test_timezone/test_timezone.vcxproj.user b/test/test_timezone/test_timezone.vcxproj.user
new file mode 100644
index 0000000000000000000000000000000000000000..88a550947edbc3c5003a41726f0749201fdb6822
--- /dev/null
+++ b/test/test_timezone/test_timezone.vcxproj.user
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup />
+</Project>
\ No newline at end of file