aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/utils
diff options
context:
space:
mode:
Diffstat (limited to 'utils')
-rw-r--r--utils/CMakeLists.txt14
-rw-r--r--utils/install_service.cpp31
-rw-r--r--utils/libservice/CMakeLists.txt8
-rw-r--r--utils/libservice/include/libservice/common.hpp19
-rw-r--r--utils/libservice/include/libservice/interface.hpp15
-rw-r--r--utils/libservice/include/libservice/service.hpp69
-rw-r--r--utils/libservice/include/libservice/service_handle.hpp80
-rw-r--r--utils/libservice/include/libservice/service_manager.hpp65
-rw-r--r--utils/libservice/include/libservice/singleton.hpp45
-rw-r--r--utils/libservice/include/libservice/windows_error.hpp36
-rw-r--r--utils/libservice/src/service.cpp235
-rw-r--r--utils/libservice/src/service_handle.cpp30
-rw-r--r--utils/libservice/src/service_manager.cpp48
-rw-r--r--utils/libservice/src/windows_error.cpp41
-rw-r--r--utils/libservice/test/CMakeLists.txt5
-rw-r--r--utils/libservice/test/windows_error.cpp27
-rw-r--r--utils/start_service.cpp31
-rw-r--r--utils/stop_service.cpp31
-rw-r--r--utils/uninstall_service.cpp31
19 files changed, 861 insertions, 0 deletions
diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt
new file mode 100644
index 0000000..5f0fcb5
--- /dev/null
+++ b/utils/CMakeLists.txt
@@ -0,0 +1,14 @@
+project(windows_drivers_utils)
+add_subdirectory(libservice)
+
+add_executable(install_service install_service.cpp)
+target_link_libraries(install_service libservice)
+
+add_executable(start_service start_service.cpp)
+target_link_libraries(start_service libservice)
+
+add_executable(stop_service stop_service.cpp)
+target_link_libraries(stop_service libservice)
+
+add_executable(uninstall_service uninstall_service.cpp)
+target_link_libraries(uninstall_service libservice)
diff --git a/utils/install_service.cpp b/utils/install_service.cpp
new file mode 100644
index 0000000..f22311b
--- /dev/null
+++ b/utils/install_service.cpp
@@ -0,0 +1,31 @@
+/**
+ * \author Egor Tensin <Egor.Tensin@gmail.com>
+ * \date 2015
+ * \copyright This file is licensed under the terms of the MIT License.
+ * See LICENSE.txt for details.
+ */
+
+#include "libservice/interface.hpp"
+
+#include <iostream>
+#include <system_error>
+
+int main(int argc, char* argv[])
+{
+ if (argc != 3)
+ {
+ std::cout << "Usage: " << argv[0] << " NAME SYS_PATH\n";
+ return 1;
+ }
+
+ try
+ {
+ libservice::Service::install(libservice::ServiceManager::open(), argv[1], argv[2]);
+ }
+ catch (const std::system_error& e)
+ {
+ std::cerr << e.what() << "\n";
+ return 1;
+ }
+ return 0;
+}
diff --git a/utils/libservice/CMakeLists.txt b/utils/libservice/CMakeLists.txt
new file mode 100644
index 0000000..169a641
--- /dev/null
+++ b/utils/libservice/CMakeLists.txt
@@ -0,0 +1,8 @@
+project(libservice)
+file(GLOB ${PROJECT_NAME}_sources "src/*.cpp")
+file(GLOB_RECURSE ${PROJECT_NAME}_headers "include/*.hpp")
+add_library(${PROJECT_NAME} ${${PROJECT_NAME}_sources}
+ ${${PROJECT_NAME}_headers})
+target_include_directories(${PROJECT_NAME} PUBLIC include)
+
+add_subdirectory(test)
diff --git a/utils/libservice/include/libservice/common.hpp b/utils/libservice/include/libservice/common.hpp
new file mode 100644
index 0000000..32e2a50
--- /dev/null
+++ b/utils/libservice/include/libservice/common.hpp
@@ -0,0 +1,19 @@
+/**
+ * \author Egor Tensin <Egor.Tensin@gmail.com>
+ * \date 2015
+ * \copyright This file is licensed under the terms of the MIT License.
+ * See LICENSE.txt for details.
+ */
+
+#pragma once
+
+#define LIBSERVICE_FILE_PATH __FILE__
+#define LIBSERVICE_LINE_NUMBER __LINE__
+#define LIBSERVICE_FUNCTION_NAME __FUNCTION__
+
+#define LIBSERVICE_TO_STRING(s) LIBSERVICE_TO_STRING_(s)
+#define LIBSERVICE_TO_STRING_(s) #s
+
+#define LIBSERVICE_LINE_NUMBER_STRING LIBSERVICE_TO_STRING(LIBSERVICE_LINE_NUMBER)
+
+#define LIBSERVICE_NOEXCEPT throw()
diff --git a/utils/libservice/include/libservice/interface.hpp b/utils/libservice/include/libservice/interface.hpp
new file mode 100644
index 0000000..058470c
--- /dev/null
+++ b/utils/libservice/include/libservice/interface.hpp
@@ -0,0 +1,15 @@
+/**
+ * \author Egor Tensin <Egor.Tensin@gmail.com>
+ * \date 2015
+ * \copyright This file is licensed under the terms of the MIT License.
+ * See LICENSE.txt for details.
+ */
+
+#pragma once
+
+#include "common.hpp"
+#include "service.hpp"
+#include "service_handle.hpp"
+#include "service_manager.hpp"
+#include "singleton.hpp"
+#include "windows_error.hpp"
diff --git a/utils/libservice/include/libservice/service.hpp b/utils/libservice/include/libservice/service.hpp
new file mode 100644
index 0000000..a14f0e2
--- /dev/null
+++ b/utils/libservice/include/libservice/service.hpp
@@ -0,0 +1,69 @@
+/**
+ * \file
+ * \author Egor Tensin <Egor.Tensin@gmail.com>
+ * \date 2015
+ * \copyright This file is licensed under the terms of the MIT License.
+ * See LICENSE.txt for details.
+ */
+
+#pragma once
+
+#include "common.hpp"
+#include "service_handle.hpp"
+#include "service_manager.hpp"
+
+#include <string>
+#include <utility>
+
+namespace libservice
+{
+ class Service
+ {
+ public:
+ static Service open(const ServiceManager&,
+ const std::string& name);
+ static Service install(const ServiceManager&,
+ const std::string& name,
+ const std::string& bin_path);
+
+ void start() const;
+ void stop() const;
+ void uninstall() const;
+
+ Service(Service&& other) LIBSERVICE_NOEXCEPT
+ {
+ swap(other);
+ }
+
+ Service& operator=(Service other) LIBSERVICE_NOEXCEPT
+ {
+ swap(other);
+ return *this;
+ }
+
+ void swap(Service& other) LIBSERVICE_NOEXCEPT
+ {
+ using std::swap;
+ swap(m_handle, other.m_handle);
+ }
+
+ private:
+ explicit Service(ServiceHandle h)
+ : m_handle(std::move(h))
+ { }
+
+ ServiceHandle m_handle;
+
+ Service(const Service&) = delete;
+ };
+
+ void swap(Service&, Service&) LIBSERVICE_NOEXCEPT;
+}
+
+namespace std
+{
+ template <>
+ void swap<libservice::Service>(
+ libservice::Service&,
+ libservice::Service&) LIBSERVICE_NOEXCEPT;
+}
diff --git a/utils/libservice/include/libservice/service_handle.hpp b/utils/libservice/include/libservice/service_handle.hpp
new file mode 100644
index 0000000..2068102
--- /dev/null
+++ b/utils/libservice/include/libservice/service_handle.hpp
@@ -0,0 +1,80 @@
+/**
+ * \file
+ * \author Egor Tensin <Egor.Tensin@gmail.com>
+ * \date 2015
+ * \copyright This file is licensed under the terms of the MIT License.
+ * See LICENSE.txt for details.
+ */
+
+#pragma once
+
+#include "common.hpp"
+
+#include <Windows.h>
+
+#include <memory>
+#include <type_traits>
+#include <utility>
+
+namespace libservice
+{
+ class ServiceHandle
+ {
+ public:
+ ServiceHandle() = default;
+
+ explicit ServiceHandle(SC_HANDLE raw)
+ : m_impl(raw)
+ { }
+
+ ServiceHandle(ServiceHandle&& other) LIBSERVICE_NOEXCEPT
+ {
+ swap(other);
+ }
+
+ ServiceHandle& operator=(ServiceHandle other) LIBSERVICE_NOEXCEPT
+ {
+ swap(other);
+ return *this;
+ }
+
+ explicit operator bool() const
+ {
+ return static_cast<bool>(m_impl);
+ }
+
+ explicit operator SC_HANDLE() const
+ {
+ return m_impl.get();
+ }
+
+ void swap(ServiceHandle& other) LIBSERVICE_NOEXCEPT
+ {
+ using std::swap;
+ swap(m_impl, other.m_impl);
+ }
+
+ private:
+ struct Deleter
+ {
+ void operator()(SC_HANDLE raw)
+ {
+ ::CloseServiceHandle(raw);
+ }
+ };
+
+ std::unique_ptr<SC_HANDLE__, Deleter> m_impl;
+
+ ServiceHandle(const ServiceHandle&) = delete;
+ };
+
+ void swap(ServiceHandle&, ServiceHandle&) LIBSERVICE_NOEXCEPT;
+}
+
+namespace std
+{
+ template <>
+ void swap<libservice::ServiceHandle>(
+ libservice::ServiceHandle&,
+ libservice::ServiceHandle&) LIBSERVICE_NOEXCEPT;
+}
diff --git a/utils/libservice/include/libservice/service_manager.hpp b/utils/libservice/include/libservice/service_manager.hpp
new file mode 100644
index 0000000..a2bb268
--- /dev/null
+++ b/utils/libservice/include/libservice/service_manager.hpp
@@ -0,0 +1,65 @@
+/**
+ * \author Egor Tensin <Egor.Tensin@gmail.com>
+ * \date 2015
+ * \copyright This file is licensed under the terms of the MIT License.
+ * See LICENSE.txt for details.
+ */
+
+#pragma once
+
+#include "common.hpp"
+#include "service_handle.hpp"
+
+#include <Windows.h>
+
+#include <utility>
+
+namespace libservice
+{
+ class ServiceManager
+ {
+ public:
+ static ServiceManager open();
+
+ ServiceManager(ServiceManager&& other) LIBSERVICE_NOEXCEPT
+ {
+ swap(other);
+ }
+
+ ServiceManager& operator=(ServiceManager other) LIBSERVICE_NOEXCEPT
+ {
+ swap(other);
+ return *this;
+ }
+
+ void swap(ServiceManager& other) LIBSERVICE_NOEXCEPT
+ {
+ using std::swap;
+ swap(m_handle, other.m_handle);
+ }
+
+ explicit operator SC_HANDLE() const
+ {
+ return static_cast<SC_HANDLE>(m_handle);
+ }
+
+ private:
+ explicit ServiceManager(ServiceHandle h)
+ : m_handle(std::move(h))
+ { }
+
+ ServiceHandle m_handle;
+
+ ServiceManager(const ServiceManager&) = delete;
+ };
+
+ void swap(ServiceManager& a, ServiceManager& b) LIBSERVICE_NOEXCEPT;
+}
+
+namespace std
+{
+ template <>
+ void swap<libservice::ServiceManager>(
+ libservice::ServiceManager&,
+ libservice::ServiceManager&) LIBSERVICE_NOEXCEPT;
+}
diff --git a/utils/libservice/include/libservice/singleton.hpp b/utils/libservice/include/libservice/singleton.hpp
new file mode 100644
index 0000000..bd137e7
--- /dev/null
+++ b/utils/libservice/include/libservice/singleton.hpp
@@ -0,0 +1,45 @@
+/**
+ * \author Egor Tensin <Egor.Tensin@gmail.com>
+ * \date 2015
+ * \copyright This file is licensed under the terms of the MIT License.
+ * See LICENSE.txt for details.
+ */
+
+#pragma once
+
+#include <mutex>
+
+namespace libservice
+{
+ template <typename DerivedType>
+ class Singleton
+ {
+ public:
+ static DerivedType& get()
+ {
+ std::call_once(is_initialized, initialize);
+ return get_unsafe();
+ }
+
+ protected:
+ Singleton() = default;
+ virtual ~Singleton() = default;
+
+ private:
+ static void initialize()
+ {
+ get_unsafe();
+ }
+
+ static DerivedType& get_unsafe()
+ {
+ static DerivedType instance;
+ return instance;
+ }
+
+ static std::once_flag is_initialized;
+ };
+
+ template <typename DerivedType>
+ std::once_flag Singleton<DerivedType>::is_initialized;
+}
diff --git a/utils/libservice/include/libservice/windows_error.hpp b/utils/libservice/include/libservice/windows_error.hpp
new file mode 100644
index 0000000..57730d5
--- /dev/null
+++ b/utils/libservice/include/libservice/windows_error.hpp
@@ -0,0 +1,36 @@
+/**
+ * \author Egor Tensin <Egor.Tensin@gmail.com>
+ * \date 2015
+ * \copyright This file is licensed under the terms of the MIT License.
+ * See LICENSE.txt for details.
+ */
+
+#pragma once
+
+#include "common.hpp"
+#include "singleton.hpp"
+
+#include <string>
+#include <system_error>
+
+namespace libservice
+{
+ class WinErrorCategory : public std::error_category
+ , public Singleton<WinErrorCategory>
+ {
+ public:
+ const char* name() const LIBSERVICE_NOEXCEPT { return "windows"; }
+
+ std::string message(int) const;
+
+ private:
+ friend class Singleton<WinErrorCategory>;
+ };
+}
+
+#define LIBSERVICE_ERROR_PREFIX "Error in function '" \
+ LIBSERVICE_FUNCTION_NAME \
+ "' at file '" \
+ LIBSERVICE_FILE_PATH \
+ "', line " \
+ LIBSERVICE_LINE_NUMBER_STRING
diff --git a/utils/libservice/src/service.cpp b/utils/libservice/src/service.cpp
new file mode 100644
index 0000000..1f7cbef
--- /dev/null
+++ b/utils/libservice/src/service.cpp
@@ -0,0 +1,235 @@
+/**
+ * \file
+ * \author Egor Tensin <Egor.Tensin@gmail.com>
+ * \date 2015
+ * \copyright This file is licensed under the terms of the MIT License.
+ * See LICENSE.txt for details.
+ */
+
+#include "libservice/common.hpp"
+#include "libservice/service.hpp"
+#include "libservice/service_handle.hpp"
+#include "libservice/service_manager.hpp"
+#include "libservice/windows_error.hpp"
+
+#include <Windows.h>
+
+#include <string>
+#include <utility>
+#include <vector>
+
+namespace libservice
+{
+ namespace
+ {
+ ServiceHandle open_service(const ServiceManager& mgr, const std::string& name)
+ {
+ const auto raw = OpenServiceA(
+ static_cast<SC_HANDLE>(mgr),
+ name.c_str(),
+ SERVICE_ALL_ACCESS);
+
+ if (NULL == raw)
+ {
+ const auto ec = GetLastError();
+ throw std::system_error(ec, WinErrorCategory::get(), LIBSERVICE_ERROR_PREFIX);
+ }
+
+ return ServiceHandle(raw);
+ }
+
+ ServiceHandle install_service(
+ const ServiceManager& mgr,
+ const std::string& name,
+ const std::string& bin_path)
+ {
+ const auto raw = CreateServiceA(
+ static_cast<SC_HANDLE>(mgr),
+ name.c_str(),
+ name.c_str(),
+ SERVICE_ALL_ACCESS,
+ SERVICE_KERNEL_DRIVER,
+ SERVICE_DEMAND_START,
+ SERVICE_ERROR_NORMAL,
+ bin_path.c_str(),
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+
+ if (NULL == raw)
+ {
+ const auto ec = GetLastError();
+ throw std::system_error(ec, WinErrorCategory::get(), LIBSERVICE_ERROR_PREFIX);
+ }
+
+ return ServiceHandle(raw);
+ }
+
+ void start_service(const ServiceHandle& handle)
+ {
+ if (!StartService(static_cast<SC_HANDLE>(handle), 0, NULL))
+ {
+ const auto ec = GetLastError();
+ throw std::system_error(ec, WinErrorCategory::get(), LIBSERVICE_ERROR_PREFIX);
+ }
+ }
+
+ void stop_service(const ServiceHandle& handle)
+ {
+ SERVICE_STATUS service_status;
+
+ if (!ControlService(static_cast<SC_HANDLE>(handle), SERVICE_CONTROL_STOP, &service_status))
+ {
+ const auto ec = GetLastError();
+ throw std::system_error(ec, WinErrorCategory::get(), LIBSERVICE_ERROR_PREFIX);
+ }
+ }
+
+ void uninstall_service(const ServiceHandle& handle)
+ {
+ if (!DeleteService(static_cast<SC_HANDLE>(handle)))
+ {
+ const auto ec = GetLastError();
+ throw std::system_error(ec, WinErrorCategory::get(), LIBSERVICE_ERROR_PREFIX);
+ }
+ }
+
+ SERVICE_STATUS_PROCESS query_service_status(const ServiceHandle& handle)
+ {
+ SERVICE_STATUS_PROCESS status;
+ DWORD nbreq;
+
+ if (!QueryServiceStatusEx(static_cast<SC_HANDLE>(handle),
+ SC_STATUS_PROCESS_INFO,
+ reinterpret_cast<BYTE*>(&status),
+ sizeof(status),
+ &nbreq))
+ {
+ const auto ec = GetLastError();
+ throw std::system_error(ec, WinErrorCategory::get(), LIBSERVICE_ERROR_PREFIX);
+ }
+
+ return status;
+ }
+
+ DWORD query_service_state(const ServiceHandle& handle)
+ {
+ return query_service_status(handle).dwCurrentState;
+ }
+
+ SERVICE_STATUS_PROCESS wait_for_service_state(
+ const ServiceHandle& handle,
+ const DWORD desired_state)
+ {
+ auto status = query_service_status(handle);
+
+ DWORD old_timestamp = GetTickCount();
+
+ DWORD old_check_point = status.dwCheckPoint;
+ DWORD old_wait_hint = status.dwWaitHint;
+
+ while (desired_state != status.dwCurrentState)
+ {
+ DWORD wait_time = old_wait_hint / 10;
+
+ if (wait_time < 1000)
+ wait_time = 1000;
+ else if (wait_time > 10000)
+ wait_time = 10000;
+
+ Sleep(wait_time);
+
+ status = query_service_status(handle);
+
+ if (desired_state == status.dwCurrentState)
+ break;
+
+ if (status.dwCheckPoint > old_check_point)
+ {
+ old_timestamp = GetTickCount();
+
+ old_check_point = status.dwCheckPoint;
+ old_wait_hint = status.dwWaitHint;
+ }
+ else if (GetTickCount() - old_timestamp > old_wait_hint)
+ {
+ return status;
+ }
+ }
+ return status;
+ }
+ }
+
+ Service Service::open(const ServiceManager& mgr, const std::string& name)
+ {
+ return Service(open_service(mgr, name));
+ }
+
+ Service Service::install(const ServiceManager& mgr,
+ const std::string& name,
+ const std::string& bin_path)
+ {
+ return Service(install_service(mgr, name, bin_path));
+ }
+
+ void Service::start() const
+ {
+ const auto state = query_service_state(m_handle);
+
+ switch (state)
+ {
+ case SERVICE_STOPPED:
+ break;
+
+ case SERVICE_STOP_PENDING:
+ wait_for_service_state(m_handle, SERVICE_STOPPED);
+ break;
+
+ default:
+ return;
+ }
+
+ start_service(m_handle);
+ wait_for_service_state(m_handle, SERVICE_RUNNING);
+ }
+
+ void Service::stop() const
+ {
+ switch (query_service_state(m_handle))
+ {
+ case SERVICE_STOPPED:
+ return;
+
+ case SERVICE_STOP_PENDING:
+ wait_for_service_state(m_handle, SERVICE_STOPPED);
+ return;
+ }
+
+ stop_service(m_handle);
+ wait_for_service_state(m_handle, SERVICE_STOPPED);
+ }
+
+ void Service::uninstall() const
+ {
+ stop();
+ uninstall_service(m_handle);
+ }
+
+ void swap(Service& a, Service& b) LIBSERVICE_NOEXCEPT
+ {
+ a.swap(b);
+ }
+}
+
+namespace std
+{
+ template <>
+ void swap<libservice::Service>(
+ libservice::Service& a,
+ libservice::Service& b) LIBSERVICE_NOEXCEPT
+ {
+ a.swap(b);
+ }
+}
diff --git a/utils/libservice/src/service_handle.cpp b/utils/libservice/src/service_handle.cpp
new file mode 100644
index 0000000..5356b50
--- /dev/null
+++ b/utils/libservice/src/service_handle.cpp
@@ -0,0 +1,30 @@
+/**
+ * \author Egor Tensin <Egor.Tensin@gmail.com>
+ * \date 2015
+ * \copyright This file is licensed under the terms of the MIT License.
+ * See LICENSE.txt for details.
+ */
+
+#include "libservice/common.hpp"
+#include "libservice/service_handle.hpp"
+
+#include <utility>
+
+namespace libservice
+{
+ void swap(ServiceHandle& a, ServiceHandle& b) LIBSERVICE_NOEXCEPT
+ {
+ a.swap(b);
+ }
+}
+
+namespace std
+{
+ template <>
+ void swap<libservice::ServiceHandle>(
+ libservice::ServiceHandle& a,
+ libservice::ServiceHandle& b) LIBSERVICE_NOEXCEPT
+ {
+ a.swap(b);
+ }
+}
diff --git a/utils/libservice/src/service_manager.cpp b/utils/libservice/src/service_manager.cpp
new file mode 100644
index 0000000..4888406
--- /dev/null
+++ b/utils/libservice/src/service_manager.cpp
@@ -0,0 +1,48 @@
+/**
+ * \author Egor Tensin <Egor.Tensin@gmail.com>
+ * \date 2015
+ * \copyright This file is licensed under the terms of the MIT License.
+ * See LICENSE.txt for details.
+ */
+
+#include "libservice/common.hpp"
+#include "libservice/service_handle.hpp"
+#include "libservice/service_manager.hpp"
+#include "libservice/windows_error.hpp"
+
+#include <Windows.h>
+
+#include <system_error>
+#include <utility>
+
+namespace libservice
+{
+ ServiceManager ServiceManager::open()
+ {
+ SC_HANDLE raw = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
+
+ if (NULL == raw)
+ {
+ const auto ec = GetLastError();
+ throw std::system_error(ec, WinErrorCategory::get());
+ }
+
+ return ServiceManager(ServiceHandle(raw));
+ }
+
+ void swap(ServiceManager& a, ServiceManager& b) LIBSERVICE_NOEXCEPT
+ {
+ a.swap(b);
+ }
+}
+
+namespace std
+{
+ template <>
+ void swap<libservice::ServiceManager>(
+ libservice::ServiceManager& a,
+ libservice::ServiceManager& b) LIBSERVICE_NOEXCEPT
+ {
+ a.swap(b);
+ }
+}
diff --git a/utils/libservice/src/windows_error.cpp b/utils/libservice/src/windows_error.cpp
new file mode 100644
index 0000000..6cc7302
--- /dev/null
+++ b/utils/libservice/src/windows_error.cpp
@@ -0,0 +1,41 @@
+/**
+ * \author Egor Tensin <Egor.Tensin@gmail.com>
+ * \date 2015
+ * \copyright This file is licensed under the terms of the MIT License.
+ * See LICENSE.txt for details.
+ */
+
+#include "libservice/windows_error.hpp"
+
+#include <Windows.h>
+
+#include <string>
+
+namespace libservice
+{
+ std::string WinErrorCategory::message(int code) const
+ {
+ char* buf_ptr;
+
+ DWORD written = FormatMessageA(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER
+ | FORMAT_MESSAGE_FROM_SYSTEM
+ | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ code,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ reinterpret_cast<char*>(&buf_ptr),
+ 0,
+ NULL);
+
+ if (0 == written)
+ {
+ LocalFree(buf_ptr);
+ return "Couldn't format error message";
+ }
+
+ std::string str(buf_ptr, written - 2);
+ LocalFree(buf_ptr);
+ return str;
+ }
+}
diff --git a/utils/libservice/test/CMakeLists.txt b/utils/libservice/test/CMakeLists.txt
new file mode 100644
index 0000000..2cd656a
--- /dev/null
+++ b/utils/libservice/test/CMakeLists.txt
@@ -0,0 +1,5 @@
+project(libservice_test)
+
+project(libservice_test_windows_error)
+add_executable(${PROJECT_NAME} windows_error.cpp)
+target_link_libraries(${PROJECT_NAME} libservice)
diff --git a/utils/libservice/test/windows_error.cpp b/utils/libservice/test/windows_error.cpp
new file mode 100644
index 0000000..f165681
--- /dev/null
+++ b/utils/libservice/test/windows_error.cpp
@@ -0,0 +1,27 @@
+/**
+ * \author Egor Tensin <Egor.Tensin@gmail.com>
+ * \date 2015
+ * \copyright This file is licensed under the terms of the MIT License.
+ * See LICENSE.txt for details.
+ */
+
+#include "libservice/interface.hpp"
+
+#include <Windows.h>
+
+#include <iostream>
+#include <system_error>
+
+int main()
+{
+ try
+ {
+ throw std::system_error(ERROR_FILE_NOT_FOUND, libservice::WinErrorCategory::get(), LIBSERVICE_ERROR_PREFIX);
+ }
+ catch (const std::system_error& e)
+ {
+ std::cerr << e.what() << "\n";
+ return -1;
+ }
+ return 0;
+}
diff --git a/utils/start_service.cpp b/utils/start_service.cpp
new file mode 100644
index 0000000..15aaaed
--- /dev/null
+++ b/utils/start_service.cpp
@@ -0,0 +1,31 @@
+/**
+ * \author Egor Tensin <Egor.Tensin@gmail.com>
+ * \date 2015
+ * \copyright This file is licensed under the terms of the MIT License.
+ * See LICENSE.txt for details.
+ */
+
+#include "libservice/interface.hpp"
+
+#include <iostream>
+#include <system_error>
+
+int main(int argc, char* argv[])
+{
+ if (argc != 2)
+ {
+ std::cout << "Usage: " << argv[0] << " NAME\n";
+ return 1;
+ }
+
+ try
+ {
+ libservice::Service::open(libservice::ServiceManager::open(), argv[1]).start();
+ }
+ catch (const std::system_error& e)
+ {
+ std::cerr << e.what() << "\n";
+ return 1;
+ }
+ return 0;
+}
diff --git a/utils/stop_service.cpp b/utils/stop_service.cpp
new file mode 100644
index 0000000..928c12b
--- /dev/null
+++ b/utils/stop_service.cpp
@@ -0,0 +1,31 @@
+/**
+ * \author Egor Tensin <Egor.Tensin@gmail.com>
+ * \date 2015
+ * \copyright This file is licensed under the terms of the MIT License.
+ * See LICENSE.txt for details.
+ */
+
+#include "libservice/interface.hpp"
+
+#include <iostream>
+#include <system_error>
+
+int main(int argc, char* argv[])
+{
+ if (argc != 2)
+ {
+ std::cout << "Usage: " << argv[0] << " NAME\n";
+ return 1;
+ }
+
+ try
+ {
+ libservice::Service::open(libservice::ServiceManager::open(), argv[1]).stop();
+ }
+ catch (const std::system_error& e)
+ {
+ std::cerr << e.what() << "\n";
+ return 1;
+ }
+ return 0;
+}
diff --git a/utils/uninstall_service.cpp b/utils/uninstall_service.cpp
new file mode 100644
index 0000000..1a6c44d
--- /dev/null
+++ b/utils/uninstall_service.cpp
@@ -0,0 +1,31 @@
+/**
+ * \author Egor Tensin <Egor.Tensin@gmail.com>
+ * \date 2015
+ * \copyright This file is licensed under the terms of the MIT License.
+ * See LICENSE.txt for details.
+ */
+
+#include "libservice/interface.hpp"
+
+#include <iostream>
+#include <system_error>
+
+int main(int argc, char* argv[])
+{
+ if (argc != 2)
+ {
+ std::cout << "Usage: " << argv[0] << " NAME\n";
+ return 1;
+ }
+
+ try
+ {
+ libservice::Service::open(libservice::ServiceManager::open(), argv[1]).uninstall();
+ }
+ catch (const std::system_error& e)
+ {
+ std::cerr << e.what() << "\n";
+ return 1;
+ }
+ return 0;
+}