diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index c1fdabd12..b50ca7b22 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -322,6 +322,8 @@ windows:
- curl -sL --url "https://github.com/ethcore/win-build/raw/master/SimpleFC.dll" -o nsis\SimpleFC.dll
- curl -sL --url "https://github.com/ethcore/win-build/raw/master/vc_redist.x64.exe" -o nsis\vc_redist.x64.exe
- signtool sign /f %keyfile% /p %certpass% target\release\parity.exe
+ - msbuild windows\ptray\ptray.vcxproj /p:Platform=x86 /p:Configuration=Release
+ - signtool sign /f %keyfile% /p %certpass% windows\ptray\release\ptray.exe
- cd nsis
- makensis.exe installer.nsi
- copy installer.exe InstallParity.exe
diff --git a/appveyor.yml b/appveyor.yml
index 75a2da7cb..e04caf233 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -38,6 +38,8 @@ after_test:
- cargo build --verbose --release
- ps: if($env:cert) { Start-FileDownload $env:cert -FileName $env:keyfile }
- ps: if($env:cert) { signtool sign /f $env:keyfile /p $env:certpass target\release\parity.exe }
+ - msbuild windows\ptray\ptray.vcxproj /p:Platform=x86 /p:Configuration=Release
+ - ps: if($env:cert) { signtool sign /f $env:keyfile /p $env:certpass windows\ptray\release\ptray.exe }
- makensis.exe nsis\installer.nsi
- ps: if($env:cert) { signtool sign /f $env:keyfile /p $env:certpass nsis\installer.exe }
diff --git a/nsis/installer.nsi b/nsis/installer.nsi
index 2c7c37428..448618bbf 100644
--- a/nsis/installer.nsi
+++ b/nsis/installer.nsi
@@ -1,10 +1,17 @@
+!include WinMessages.nsh
+!define WND_CLASS "Parity"
+!define WND_TITLE "Parity"
+!define WAIT_MS 5000
+!define SYNC_TERM 0x00100001
+
!define APPNAME "Parity"
!define COMPANYNAME "Ethcore"
!define DESCRIPTION "Fast, light, robust Ethereum implementation"
!define VERSIONMAJOR 1
!define VERSIONMINOR 4
!define VERSIONBUILD 0
+!define ARGS "--warp --mode=passive"
!addplugindir .\
@@ -13,6 +20,10 @@
!define ABOUTURL "https://github.com/ethcore/parity" # "Publisher" link
!define INSTALLSIZE 26120
+!define termMsg "Installer cannot stop running ${WND_TITLE}.$\nDo you want to terminate process?"
+!define stopMsg "Stopping ${WND_TITLE} Application"
+
+
RequestExecutionLevel admin ;Require admin rights on NT6+ (When UAC is turned on)
InstallDir "$PROGRAMFILES64\${COMPANYNAME}\${APPNAME}"
@@ -26,7 +37,7 @@ outFile "installer.exe"
page license
page directory
-Page instfiles
+page instfiles
!macro VerifyUserIsAdmin
UserInfo::GetAccountType
@@ -38,6 +49,31 @@ ${If} $0 != "admin" ;Require admin rights on NT4+
${EndIf}
!macroend
+!macro TerminateApp
+ Push $0 ; window handle
+ Push $1
+ Push $2 ; process handle
+ DetailPrint "$(stopMsg)"
+ FindWindow $0 '${WND_CLASS}' ''
+ IntCmp $0 0 done
+ System::Call 'user32.dll::GetWindowThreadProcessId(i r0, *i .r1) i .r2'
+ System::Call 'kernel32.dll::OpenProcess(i ${SYNC_TERM}, i 0, i r1) i .r2'
+ SendMessage $0 ${WM_CLOSE} 0 0 /TIMEOUT=${TO_MS}
+ System::Call 'kernel32.dll::WaitForSingleObject(i r2, i ${WAIT_MS}) i .r1'
+ IntCmp $1 0 close
+ MessageBox MB_YESNOCANCEL|MB_ICONEXCLAMATION "$(termMsg)" /SD IDYES IDYES terminate IDNO close
+ System::Call 'kernel32.dll::CloseHandle(i r2) i .r1'
+ Quit
+ terminate:
+ System::Call 'kernel32.dll::TerminateProcess(i r2, i 0) i .r1'
+ close:
+ System::Call 'kernel32.dll::CloseHandle(i r2) i .r1'
+ done:
+ Pop $2
+ Pop $1
+ Pop $0
+!macroend
+
function .onInit
setShellVarContext all
!insertmacro VerifyUserIsAdmin
@@ -48,10 +84,13 @@ section "install"
setOutPath $INSTDIR
# Files added here should be removed by the uninstaller (see section "uninstall")
file /oname=parity.exe ..\target\release\parity.exe
+ file /oname=ptray.exe ..\windows\ptray\Release\ptray.exe
+
file "logo.ico"
file vc_redist.x64.exe
ExecWait '"$INSTDIR\vc_redist.x64.exe" /passive /norestart'
+ delete $INSTDIR\vc_redist.x64.exe
# Add any other files for the install directory (license files, app data, etc) here
# Uninstaller - See function un.onInit and section "uninstall" for configuration
@@ -79,11 +118,11 @@ section "install"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "QuietUninstallString" "$\"$INSTDIR\uninstall.exe$\" /S"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "InstallLocation" "$\"$INSTDIR$\""
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "DisplayIcon" "$\"$INSTDIR\logo.ico$\""
- WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "Publisher" "$\"${COMPANYNAME}$\""
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "Publisher" "${COMPANYNAME}"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "HelpLink" "$\"${HELPURL}$\""
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "URLUpdateInfo" "$\"${UPDATEURL}$\""
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "URLInfoAbout" "$\"${ABOUTURL}$\""
- WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "DisplayVersion" "$\"${VERSIONMAJOR}.${VERSIONMINOR}.${VERSIONBUILD}$\""
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "DisplayVersion" "${VERSIONMAJOR}.${VERSIONMINOR}.${VERSIONBUILD}"
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "VersionMajor" ${VERSIONMAJOR}
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "VersionMinor" ${VERSIONMINOR}
# There is no option for modifying or repairing the install
@@ -91,6 +130,9 @@ section "install"
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "NoRepair" 1
# Set the INSTALLSIZE constant (!defined at the top of this script) so Add/Remove Programs can accurately report the size
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "EstimatedSize" ${INSTALLSIZE}
+
+ WriteRegStr HKEY_LOCAL_MACHINE "Software\Microsoft\Windows\CurrentVersion\Run" ${APPNAME} "$INSTDIR\ptray.exe ${ARGS}"
+ ExecShell "" "$INSTDIR\ptray.exe" "${ARGS}"
sectionEnd
# Uninstaller
@@ -107,7 +149,7 @@ function un.onInit
functionEnd
section "uninstall"
-
+ !insertmacro TerminateApp
# Remove Start Menu launcher
delete "$SMPROGRAMS\${COMPANYNAME}\${APPNAME}.lnk"
# Try to remove the Start Menu folder - this will only happen if it is empty
@@ -115,6 +157,7 @@ section "uninstall"
# Remove files
delete $INSTDIR\parity.exe
+ delete $INSTDIR\ptray.exe
delete $INSTDIR\logo.ico
# Always delete uninstaller as the last action
@@ -131,5 +174,6 @@ section "uninstall"
# Remove uninstaller information from the registry
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}"
-
+ DeleteRegValue HKLM "Software\Microsoft\Windows\CurrentVersion\Run" "${APPNAME}"
sectionEnd
+
diff --git a/nsis/logo.ico b/nsis/logo.ico
index 4fbaa4d39..61e68b90b 100644
Binary files a/nsis/logo.ico and b/nsis/logo.ico differ
diff --git a/parity/run.rs b/parity/run.rs
index b0f8dce68..de73ecb45 100644
--- a/parity/run.rs
+++ b/parity/run.rs
@@ -15,6 +15,7 @@
// along with Parity. If not, see .
use std::sync::{Arc, Mutex, Condvar};
+use std::net::{TcpListener};
use ctrlc::CtrlC;
use fdlimit::raise_fd_limit;
use ethcore_rpc::{NetworkSettings, is_major_importing};
@@ -94,6 +95,15 @@ pub struct RunCmd {
}
pub fn execute(cmd: RunCmd, logger: Arc) -> Result<(), String> {
+ if cmd.ui && cmd.dapps_conf.enabled {
+ // Check if Parity is already running
+ let addr = format!("{}:{}", cmd.dapps_conf.interface, cmd.dapps_conf.port);
+ if !TcpListener::bind(&addr as &str).is_ok() {
+ url::open(&format!("http://{}:{}/", cmd.dapps_conf.interface, cmd.dapps_conf.port));
+ return Ok(());
+ }
+ }
+
// set up panic handler
let panic_handler = PanicHandler::new_in_arc();
diff --git a/windows/ptray/ptray.cpp b/windows/ptray/ptray.cpp
new file mode 100644
index 000000000..ac2f197a7
--- /dev/null
+++ b/windows/ptray/ptray.cpp
@@ -0,0 +1,274 @@
+// Copyright 2015, 2016 Ethcore (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+#define WIN32_LEAN_AND_MEAN
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include "resource.h"
+
+#pragma comment(lib, "shlwapi.lib")
+
+#define MAX_LOADSTRING 100
+#define IDM_EXIT 100
+#define IDM_OPEN 101
+#define WM_USER_SHELLICON WM_USER + 1
+
+HANDLE parityHandle = INVALID_HANDLE_VALUE;
+DWORD parityProcId = 0;
+NOTIFYICONDATA nidApp;
+WCHAR szTitle[MAX_LOADSTRING];
+WCHAR szWindowClass[MAX_LOADSTRING];
+
+LPCWSTR cParityExe = _T("parity.exe");
+
+ATOM MyRegisterClass(HINSTANCE hInstance);
+bool InitInstance(HINSTANCE, int, LPWSTR cmdLine);
+LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
+void KillParity();
+void OpenUI();
+bool ParityIsRunning();
+
+bool GetParityExePath(TCHAR* dest, size_t destSize)
+{
+ if (!dest || MAX_PATH > destSize)
+ return false;
+ GetModuleFileName(NULL, dest, destSize);
+ if (!PathRemoveFileSpec(dest))
+ return false;
+ return PathAppend(dest, _T("parity.exe")) == TRUE;
+}
+
+int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
+ _In_opt_ HINSTANCE hPrevInstance,
+ _In_ LPWSTR lpCmdLine,
+ _In_ int nCmdShow)
+{
+ UNREFERENCED_PARAMETER(hPrevInstance);
+ UNREFERENCED_PARAMETER(lpCmdLine);
+
+ CreateMutex(0, FALSE, _T("Local\\ParityTray"));
+ if (GetLastError() == ERROR_ALREADY_EXISTS)
+ return -1;
+
+ LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
+ LoadStringW(hInstance, IDC_PTRAY, szWindowClass, MAX_LOADSTRING);
+ MyRegisterClass(hInstance);
+
+ if (!InitInstance(hInstance, nCmdShow, lpCmdLine))
+ return FALSE;
+
+ HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_PTRAY));
+ MSG msg;
+ // Main message loop:
+ while (GetMessage(&msg, nullptr, 0, 0))
+ {
+ if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ }
+
+ return (int)msg.wParam;
+}
+
+ATOM MyRegisterClass(HINSTANCE hInstance)
+{
+ WNDCLASSEXW wcex;
+
+ wcex.cbSize = sizeof(WNDCLASSEX);
+
+ wcex.style = CS_HREDRAW | CS_VREDRAW;
+ wcex.lpfnWndProc = WndProc;
+ wcex.cbClsExtra = 0;
+ wcex.cbWndExtra = 0;
+ wcex.hInstance = hInstance;
+ wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_PTRAY));
+ wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
+ wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
+ wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_PTRAY);
+ wcex.lpszClassName = szWindowClass;
+ wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
+
+ return RegisterClassExW(&wcex);
+}
+
+
+bool InitInstance(HINSTANCE hInstance, int nCmdShow, LPWSTR cmdLine)
+{
+ // Check if already running
+ PROCESSENTRY32 entry;
+ entry.dwSize = sizeof(PROCESSENTRY32);
+
+ HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
+ if (Process32First(snapshot, &entry) == TRUE)
+ {
+ while (Process32Next(snapshot, &entry) == TRUE)
+ {
+ if (lstrcmp(entry.szExeFile, cParityExe) == 0)
+ {
+ parityHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, entry.th32ProcessID);
+ parityProcId = entry.th32ProcessID;
+ break;
+ }
+ }
+ }
+
+ CloseHandle(snapshot);
+
+ if (parityHandle == INVALID_HANDLE_VALUE)
+ {
+ // Launch parity
+ TCHAR path[MAX_PATH] = { 0 };
+ if (!GetParityExePath(path, MAX_PATH))
+ return false;
+
+ PROCESS_INFORMATION procInfo = { 0 };
+ STARTUPINFO startupInfo = { sizeof(STARTUPINFO) };
+
+ LPWSTR cmd = new WCHAR[lstrlen(cmdLine) + lstrlen(path) + 2];
+ lstrcpy(cmd, path);
+ lstrcat(cmd, _T(" "));
+ lstrcat(cmd, cmdLine);
+ if (!CreateProcess(nullptr, cmd, nullptr, nullptr, false, CREATE_NO_WINDOW, nullptr, nullptr, &startupInfo, &procInfo))
+ return false;
+ delete[] cmd;
+ parityHandle = procInfo.hProcess;
+ parityProcId = procInfo.dwProcessId;
+ }
+
+ HWND hWnd = CreateWindowW(szWindowClass, szTitle, 0,
+ CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
+
+ if (!hWnd)
+ return false;
+
+ HICON hMainIcon = LoadIcon(hInstance, (LPCTSTR)MAKEINTRESOURCE(IDI_PTRAY));
+
+ nidApp.cbSize = sizeof(NOTIFYICONDATA); // sizeof the struct in bytes
+ nidApp.hWnd = (HWND)hWnd; //handle of the window which will process this app. messages
+ nidApp.uID = IDI_PTRAY; //ID of the icon that willl appear in the system tray
+ nidApp.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; //ORing of all the flags
+ nidApp.hIcon = hMainIcon; // handle of the Icon to be displayed, obtained from LoadIcon
+ nidApp.uCallbackMessage = WM_USER_SHELLICON;
+ LoadString(hInstance, IDS_CONTROL_PARITY, nidApp.szTip, MAX_LOADSTRING);
+ Shell_NotifyIcon(NIM_ADD, &nidApp);
+ return TRUE;
+
+}
+
+LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ switch (message)
+ {
+ case WM_USER_SHELLICON:
+ // systray msg callback
+ POINT lpClickPoint;
+ switch (LOWORD(lParam))
+ {
+ case WM_LBUTTONDOWN:
+ OpenUI();
+ break;
+ case WM_RBUTTONDOWN:
+ UINT uFlag = MF_BYPOSITION | MF_STRING;
+ GetCursorPos(&lpClickPoint);
+ HMENU hPopMenu = CreatePopupMenu();
+ InsertMenu(hPopMenu, 0xFFFFFFFF, MF_BYPOSITION | MF_STRING, IDM_EXIT, _T("Open"));
+ InsertMenu(hPopMenu, 0xFFFFFFFF, MF_SEPARATOR | MF_BYPOSITION, 0, nullptr);
+ InsertMenu(hPopMenu, 0xFFFFFFFF, MF_BYPOSITION | MF_STRING, IDM_EXIT, _T("Exit"));
+
+ SetForegroundWindow(hWnd);
+ TrackPopupMenu(hPopMenu, TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_BOTTOMALIGN, lpClickPoint.x, lpClickPoint.y, 0, hWnd, NULL);
+ return TRUE;
+
+ }
+ break;
+ case WM_COMMAND:
+ {
+ int wmId = LOWORD(wParam);
+ // Parse the menu selections:
+ switch (wmId)
+ {
+ case IDM_EXIT:
+ DestroyWindow(hWnd);
+ break;
+ case IDM_OPEN:
+ OpenUI();
+ break;
+ default:
+ return DefWindowProc(hWnd, message, wParam, lParam);
+ }
+ }
+ break;
+ case WM_DESTROY:
+ Shell_NotifyIcon(NIM_DELETE, &nidApp);
+ KillParity();
+ PostQuitMessage(0);
+ break;
+ case WM_TIMER:
+ if (!ParityIsRunning())
+ DestroyWindow(hWnd);
+ default:
+ return DefWindowProc(hWnd, message, wParam, lParam);
+ }
+ return 0;
+}
+
+void KillParity()
+{
+ DWORD procId = parityProcId;
+ //This does not require the console window to be visible.
+ if (AttachConsole(procId))
+ {
+ // Disable Ctrl-C handling for our program
+ SetConsoleCtrlHandler(nullptr, true);
+ GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0);
+ FreeConsole();
+
+ //Re-enable Ctrl-C handling or any subsequently started
+ //programs will inherit the disabled state.
+ SetConsoleCtrlHandler(nullptr, false);
+ }
+ WaitForSingleObject(parityHandle, INFINITE);
+}
+
+bool ParityIsRunning()
+{
+ return WaitForSingleObject(parityHandle, 0) == WAIT_TIMEOUT;
+}
+
+void OpenUI()
+{
+ // Launch parity
+ TCHAR path[MAX_PATH] = { 0 };
+ if (!GetParityExePath(path, MAX_PATH))
+ return;
+
+ PROCESS_INFORMATION procInfo = { 0 };
+ STARTUPINFO startupInfo = { sizeof(STARTUPINFO) };
+
+ LPWSTR cmd = _T("parity.exe ui");
+ CreateProcess(path, cmd, nullptr, nullptr, false, CREATE_NO_WINDOW, nullptr, nullptr, &startupInfo, &procInfo);
+}
\ No newline at end of file
diff --git a/windows/ptray/ptray.ico b/windows/ptray/ptray.ico
new file mode 100644
index 000000000..61e68b90b
Binary files /dev/null and b/windows/ptray/ptray.ico differ
diff --git a/windows/ptray/ptray.rc b/windows/ptray/ptray.rc
new file mode 100644
index 000000000..9f10e0aa8
Binary files /dev/null and b/windows/ptray/ptray.rc differ
diff --git a/windows/ptray/ptray.vcxproj b/windows/ptray/ptray.vcxproj
new file mode 100644
index 000000000..efe1d2e48
--- /dev/null
+++ b/windows/ptray/ptray.vcxproj
@@ -0,0 +1,155 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ {37C89E90-8C9E-4FFC-AAE7-B3695D5EB6F4}
+ Win32Proj
+ ptray
+ 8.1
+
+
+
+ Application
+ true
+ v140
+ Unicode
+
+
+ Application
+ false
+ v140
+ true
+ Unicode
+
+
+ Application
+ true
+ v140
+ Unicode
+
+
+ Application
+ false
+ v140
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+ true
+
+
+ false
+
+
+ false
+
+
+
+ NotUsing
+ Level3
+ Disabled
+ WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)
+
+
+ Windows
+ true
+
+
+
+
+ Use
+ Level3
+ Disabled
+ _DEBUG;_WINDOWS;%(PreprocessorDefinitions)
+
+
+ Windows
+ true
+
+
+
+
+ Level3
+ NotUsing
+ MaxSpeed
+ true
+ true
+ WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)
+
+
+ Windows
+ true
+ true
+ true
+
+
+
+
+ Level3
+ Use
+ MaxSpeed
+ true
+ true
+ NDEBUG;_WINDOWS;%(PreprocessorDefinitions)
+
+
+ Windows
+ true
+ true
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/windows/ptray/resource.h b/windows/ptray/resource.h
new file mode 100644
index 000000000..1f4b02343
Binary files /dev/null and b/windows/ptray/resource.h differ
diff --git a/windows/ptray/targetver.h b/windows/ptray/targetver.h
new file mode 100644
index 000000000..87c0086de
--- /dev/null
+++ b/windows/ptray/targetver.h
@@ -0,0 +1,8 @@
+#pragma once
+
+// Including SDKDDKVer.h defines the highest available Windows platform.
+
+// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and
+// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.
+
+#include