diff --git a/bin/win/elevator.exe b/bin/win/elevator.exe
new file mode 100644
index 000000000000..0ce89bbdd1dd
Binary files /dev/null and b/bin/win/elevator.exe differ
diff --git a/bin/win/launcher.exe b/bin/win/launcher.exe
new file mode 100644
index 000000000000..bca7846e27a0
Binary files /dev/null and b/bin/win/launcher.exe differ
diff --git a/native/WinElevator/Elevator.sln b/native/WinElevator/Elevator.sln
new file mode 100644
index 000000000000..cc42445ac66e
--- /dev/null
+++ b/native/WinElevator/Elevator.sln
@@ -0,0 +1,53 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 14
+VisualStudioVersion = 14.0.25420.1
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "launcher", "launcher\launcher.vcxproj", "{6631215A-50A4-49BE-A0B5-BDAC4C75B955}"
+ ProjectSection(ProjectDependencies) = postProject
+ {9D71A73C-2570-488A-958B-A126F4EEECB7} = {9D71A73C-2570-488A-958B-A126F4EEECB7}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "elevator", "elevator\elevator.vcxproj", "{9D71A73C-2570-488A-958B-A126F4EEECB7}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "elevShared", "elevShared\elevShared.vcxitems", "{45D41ACC-2C3C-43D2-BC10-02AA73FFC7C7}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{0AFC8DF7-8237-4909-8993-9CF44995AA42}"
+ ProjectSection(SolutionItems) = preProject
+ README.txt = README.txt
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SharedMSBuildProjectFiles) = preSolution
+ elevShared\elevShared.vcxitems*{45d41acc-2c3c-43d2-bc10-02aa73ffc7c7}*SharedItemsImports = 9
+ elevShared\elevShared.vcxitems*{6631215a-50a4-49be-a0b5-bdac4c75b955}*SharedItemsImports = 4
+ elevShared\elevShared.vcxitems*{9d71a73c-2570-488a-958b-a126f4eeecb7}*SharedItemsImports = 4
+ EndGlobalSection
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {6631215A-50A4-49BE-A0B5-BDAC4C75B955}.Debug|x64.ActiveCfg = Debug|x64
+ {6631215A-50A4-49BE-A0B5-BDAC4C75B955}.Debug|x64.Build.0 = Debug|x64
+ {6631215A-50A4-49BE-A0B5-BDAC4C75B955}.Debug|x86.ActiveCfg = Debug|Win32
+ {6631215A-50A4-49BE-A0B5-BDAC4C75B955}.Debug|x86.Build.0 = Debug|Win32
+ {6631215A-50A4-49BE-A0B5-BDAC4C75B955}.Release|x64.ActiveCfg = Release|x64
+ {6631215A-50A4-49BE-A0B5-BDAC4C75B955}.Release|x64.Build.0 = Release|x64
+ {6631215A-50A4-49BE-A0B5-BDAC4C75B955}.Release|x86.ActiveCfg = Release|Win32
+ {6631215A-50A4-49BE-A0B5-BDAC4C75B955}.Release|x86.Build.0 = Release|Win32
+ {9D71A73C-2570-488A-958B-A126F4EEECB7}.Debug|x64.ActiveCfg = Debug|x64
+ {9D71A73C-2570-488A-958B-A126F4EEECB7}.Debug|x64.Build.0 = Debug|x64
+ {9D71A73C-2570-488A-958B-A126F4EEECB7}.Debug|x86.ActiveCfg = Debug|Win32
+ {9D71A73C-2570-488A-958B-A126F4EEECB7}.Debug|x86.Build.0 = Debug|Win32
+ {9D71A73C-2570-488A-958B-A126F4EEECB7}.Release|x64.ActiveCfg = Release|x64
+ {9D71A73C-2570-488A-958B-A126F4EEECB7}.Release|x64.Build.0 = Release|x64
+ {9D71A73C-2570-488A-958B-A126F4EEECB7}.Release|x86.ActiveCfg = Release|Win32
+ {9D71A73C-2570-488A-958B-A126F4EEECB7}.Release|x86.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/native/WinElevator/README.txt b/native/WinElevator/README.txt
new file mode 100644
index 000000000000..926f9cb1c5ae
--- /dev/null
+++ b/native/WinElevator/README.txt
@@ -0,0 +1,37 @@
+With UAC enabled even administrator has limited access token and can't modify some folders like "Program Files".
+The only official way to elevate token for process is to launch app as elevated using shell api so user will have chance to accept it.
+Since it may lead to security risks it is not recomended to run whole app as elevated, but run only certain tools instead.
+
+This app consists of 2 apps:
+* launcher: accepts command line to run as elevated and launches "elevator" using shell api
+* elevator: has "UAC execution level" set "administrator" in its manifest, so UAC is displayed. It then runs provided command line.
+
+Since elevator is launched with elevated priviliges, it has separate console in conhost (technically it is not child of launcher but of AppInfo instead),
+so there is some machinery to connect elevated process to console and its pipes.
+
+Launcher provides its pid to elevator and elevator attaches to its console.
+
+But if std(out|err|in) are redirected to files or pipes, attaching to console is not enough.
+In this case launcher creates named pipes, elevator connects to them and provides their handlers as handlers for newly created process.
+Launcher then creates threads to read/write them to console.
+
+-------
+How to build.
+You may open .sln from Visual Studio or use msbuild from VS command prompt:
+ msbuild Elevator.sln /p:Configuration=release
+
+-------
+How to test.
+From unelevated command.com run commands and click "yes"
+
+Testing console
+> launcher.exe %ComSpec%
+You should be taken to admin console (i.e. has write access to c:\windows\)
+
+Testing output
+> echo spam | launcher.exe python.exe -c "import sys; sys.stderr.write('err'); print(sys.stdin.read()); " > out.txt 2> err.txt
+Check you got "spam" in out.txt and "err" in "err.txt"
+
+Ensure permissions
+> launcher.exe python.exe -c "print(open('c:\\windows\\eggs.txt', 'w'))"
+Check no error
diff --git a/native/WinElevator/elevShared/elevShared.vcxitems b/native/WinElevator/elevShared/elevShared.vcxitems
new file mode 100644
index 000000000000..c5ac1ff70529
--- /dev/null
+++ b/native/WinElevator/elevShared/elevShared.vcxitems
@@ -0,0 +1,19 @@
+
+
+
+ $(MSBuildAllProjects);$(MSBuildThisFileFullPath)
+ true
+ {45d41acc-2c3c-43d2-bc10-02aa73ffc7c7}
+
+
+
+ %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory)
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/native/WinElevator/elevShared/elevShared.vcxitems.filters b/native/WinElevator/elevShared/elevShared.vcxitems.filters
new file mode 100644
index 000000000000..cdd7d46ee8f4
--- /dev/null
+++ b/native/WinElevator/elevShared/elevShared.vcxitems.filters
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/native/WinElevator/elevShared/elevTools.h b/native/WinElevator/elevShared/elevTools.h
new file mode 100644
index 000000000000..2c43ec6dff11
--- /dev/null
+++ b/native/WinElevator/elevShared/elevTools.h
@@ -0,0 +1,42 @@
+#pragma once
+/*
+ * Copyright 2000-2017 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Shared file for elevator and launcher
+
+// Author: Ilya Kazakevich
+
+
+// So called "descriptors". Used as arguments in many macros and can also be used as binary flags
+#define ELEV_DESCR_STDOUT 1
+#define ELEV_DESCR_STDERR 2
+#define ELEV_DESCR_STDIN 4
+
+// Rules to generate pipe name
+#define ELEV_GEN_PIPE_NAME(sDest, nPid, nDescriptor) wsprintf(sDest, L"\\\\.\\pipe\\_jetbrains%ld_%d", nPid, nDescriptor)
+
+#define ELEV_BUF_SIZE 1024 //Buf to read/write between processes
+
+// Convert descriptor to Win32API handler
+#define ELEV_DESCR_GET_HANDLE(nDescriptor) (nDescriptor == ELEV_DESCR_STDOUT ? STD_OUTPUT_HANDLE : \
+ (nDescriptor == ELEV_DESCR_STDERR ? STD_ERROR_HANDLE : STD_INPUT_HANDLE))
+
+// Pipe name
+typedef wchar_t ELEV_PIPE_NAME[32];
+
+// Separates arguments provided to elevator and user command line
+#define ELEV_COMMAND_LINE_SEPARATOR L"--::--"
+
diff --git a/native/WinElevator/elevator/Resource.rc b/native/WinElevator/elevator/Resource.rc
new file mode 100644
index 000000000000..324bb62a5ba2
Binary files /dev/null and b/native/WinElevator/elevator/Resource.rc differ
diff --git a/native/WinElevator/elevator/elevator.c b/native/WinElevator/elevator/elevator.c
new file mode 100644
index 000000000000..bad1ed9b0f2c
--- /dev/null
+++ b/native/WinElevator/elevator/elevator.c
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2000-2017 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include
+#include
+#include
+#include
+#include
+
+
+// UAC-enabled (in manifset) tool to launch ptocesses.
+// Connects to pipes and console, and then launches new process using CreateProcess
+
+// Author: Ilya Kazakevich
+
+
+// Connects and waits for pipe if required by descriptor flags
+// nDescriptor ELEV_DESC_*
+// nDescriptorFlags flags passed from launcher to check if descriptor should be connected
+// Returns 0 if ok, error otherwise. Could be windows error, EBADF or EMFILE for dup2
+static DWORD _ConnectIfNeededPipe(DWORD nParentPid, DWORD nDescriptor, FILE* stream, int nDescriptorFlags, _Out_ PHANDLE pRemoteProcessHandle)
+{
+ if (!(nDescriptorFlags & nDescriptor))
+ {
+ *pRemoteProcessHandle = GetStdHandle(ELEV_DESCR_GET_HANDLE(nDescriptor));
+ return 0; // Not needed to connect pipe, use real descriptor
+ }
+ ELEV_PIPE_NAME sPipeName;
+ ELEV_GEN_PIPE_NAME(sPipeName, nParentPid, nDescriptor);
+ WaitNamedPipe(sPipeName, INFINITE);
+ BOOL bStdIn = nDescriptor == ELEV_DESCR_STDIN;
+ unsigned long access = (bStdIn ? GENERIC_READ : GENERIC_WRITE);
+ HANDLE hPipe = CreateFile(sPipeName, access, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hPipe == INVALID_HANDLE_VALUE || hPipe == NULL)
+ {
+ return GetLastError();
+ }
+
+ // Make inheritable by remote process
+ if (!SetHandleInformation(hPipe, HANDLE_FLAG_INHERIT, TRUE))
+ {
+ return GetLastError();
+ }
+
+ // Fix CRT
+ if (_dup2(_open_osfhandle((intptr_t)hPipe, _O_WTEXT | _O_TEXT), _fileno(stream)) != 0)
+ {
+ return errno;
+ }
+
+ // Fix Win32API
+ DWORD hStdHandleToChange = ELEV_DESCR_GET_HANDLE(nDescriptor);
+ if (!SetStdHandle(hStdHandleToChange, hPipe))
+ {
+ return GetLastError();
+ }
+
+ *pRemoteProcessHandle = hPipe;
+ return 0;
+}
+
+// PID Directory DescriptorFlags ProgramToRun Arguments
+#define _ARG_PID 1
+#define _ARG_DIR 2
+#define _ARG_DESCRIPTORS 3
+
+int wmain(int argc, wchar_t* argv[], wchar_t* envp[])
+{
+ if (argc <= _ARG_DESCRIPTORS)
+ {
+ fwprintf(stderr, L"Bad command line");
+ return -1;
+ }
+ if (!SetCurrentDirectory(argv[_ARG_DIR]))
+ {
+ fwprintf(stderr, L"Failed to set directory to %s : %ld", argv[_ARG_DIR], GetLastError());
+ return -1;
+ }
+ DWORD nParentPid = _wtol(argv[_ARG_PID]);
+ if (!nParentPid)
+ {
+ fwprintf(stderr, L"Failed to get parent pid from %s", argv[_ARG_PID]);
+ return -1;
+ }
+
+ wchar_t* sDescriptorsStr = argv[_ARG_DESCRIPTORS];
+ size_t nDescriptorsLen = wcslen(sDescriptorsStr);
+ if (!nDescriptorsLen)
+ {
+ fwprintf(stderr, L"Failed to get descriptors from %s", sDescriptorsStr);
+ return -1;
+ }
+ for(int i = 0; i < nDescriptorsLen; i++)
+ {
+ if (! iswdigit(sDescriptorsStr[i]))
+ {
+ fwprintf(stderr, L"Bad descriptor %s", sDescriptorsStr);
+ return -1;
+ }
+ }
+ int nDescriptorFlags = _wtoi(sDescriptorsStr);
+
+
+
+
+ wchar_t* sFromSeparator = wcsstr(GetCommandLine(), ELEV_COMMAND_LINE_SEPARATOR);
+ if (! sFromSeparator)
+ {
+ fwprintf(stderr, L"Failed to find %s in %s", ELEV_COMMAND_LINE_SEPARATOR, GetCommandLine());
+ return -1;
+ }
+ // Add rest commandline
+ WCHAR* sCommandLine = sFromSeparator + wcslen(ELEV_COMMAND_LINE_SEPARATOR);
+
+ // Fix console
+ FreeConsole();
+ if (!AttachConsole(nParentPid))
+ {
+ fwprintf(stderr, L"Failed to attach console: %d", GetLastError());
+ return 1;
+ }
+
+ STARTUPINFO startupInfo;
+ ZeroMemory(&startupInfo, sizeof(startupInfo));
+ startupInfo.cb = sizeof(startupInfo);
+ startupInfo.dwFlags = STARTF_USESTDHANDLES; // To pass handles to remote process
+
+ DWORD nError; // No place to output errors yet. Event log is overkill here, so we use exit code.
+
+ nError = _ConnectIfNeededPipe(nParentPid, ELEV_DESCR_STDIN, stdin, nDescriptorFlags, &startupInfo.hStdInput);
+ if (nError != 0)
+ {
+ exit(nError);
+ }
+ nError = _ConnectIfNeededPipe(nParentPid, ELEV_DESCR_STDOUT, stdout, nDescriptorFlags, &startupInfo.hStdOutput);
+ if (nError != 0)
+ {
+ exit(nError);
+ }
+ nError = _ConnectIfNeededPipe(nParentPid, ELEV_DESCR_STDERR, stderr, nDescriptorFlags, &startupInfo.hStdError);
+ if (nError != 0)
+ {
+ exit(nError);
+ }
+
+ HANDLE parentProcess = OpenProcess(SYNCHRONIZE, FALSE, nParentPid);
+ if (!parentProcess)
+ {
+ exit(GetLastError()); // If parent process can't be opened it probably dead
+ }
+
+ PROCESS_INFORMATION processInfo;
+
+ if (!CreateProcess(NULL, sCommandLine, NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS, NULL, NULL, &startupInfo, &processInfo))
+ {
+ fwprintf(stderr, L"Error launching process. Exit code %ld, command was %ls", GetLastError(), sCommandLine);
+ return 1;
+ }
+ HANDLE processesToWait[] = { parentProcess, processInfo.hProcess };
+
+ DWORD nWaitResult = WaitForMultipleObjects(2, processesToWait, FALSE, INFINITE);
+ if (WAIT_FAILED == nWaitResult)
+ {
+ fwprintf(stderr, L"Error waiting processes: %ld", GetLastError());
+ return -1;
+ }
+ if (nWaitResult - WAIT_OBJECT_0 == 0)
+ {
+ fwprintf(stderr, L"Parent process (launcher) died?");
+ TerminateProcess(processInfo.hProcess, -1);
+ return -1;
+ }
+
+
+
+ DWORD nExitCode = 0;
+ GetExitCodeProcess(processInfo.hProcess, &nExitCode);
+
+ return nExitCode;
+}
diff --git a/native/WinElevator/elevator/elevator.vcxproj b/native/WinElevator/elevator/elevator.vcxproj
new file mode 100644
index 000000000000..b862b3ffdfee
--- /dev/null
+++ b/native/WinElevator/elevator/elevator.vcxproj
@@ -0,0 +1,176 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ {9D71A73C-2570-488A-958B-A126F4EEECB7}
+ Win32Proj
+ elevator
+ 8.1
+
+
+
+ Application
+ true
+ v140
+ Unicode
+
+
+ Application
+ false
+ v140
+ true
+ Unicode
+
+
+ Application
+ true
+ v140
+ Unicode
+
+
+ Application
+ false
+ v140
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ $(IncludePath);
+ $(VC_SourcePath);
+
+
+ true
+ $(IncludePath);
+ $(VC_SourcePath);
+
+
+ false
+ $(IncludePath);
+ $(VC_SourcePath);
+
+
+ false
+ $(IncludePath);
+ $(VC_SourcePath);
+
+
+
+ NotUsing
+ Level3
+ Disabled
+ WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)
+ true
+ MultiThreadedDebug
+
+
+ Console
+ true
+ RequireAdministrator
+
+
+
+
+ NotUsing
+ Level3
+ Disabled
+ _DEBUG;_WINDOWS;%(PreprocessorDefinitions)
+ true
+ MultiThreadedDebug
+
+
+ Console
+ true
+ RequireAdministrator
+
+
+
+
+ Level3
+ NotUsing
+ MaxSpeed
+ true
+ true
+ WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)
+ true
+ MultiThreaded
+
+
+ Console
+ true
+ true
+ true
+ RequireAdministrator
+
+
+
+
+ Level3
+ NotUsing
+ MaxSpeed
+ true
+ true
+ NDEBUG;_WINDOWS;%(PreprocessorDefinitions)
+ true
+ MultiThreaded
+
+
+ Console
+ true
+ true
+ true
+ RequireAdministrator
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/native/WinElevator/elevator/elevator.vcxproj.filters b/native/WinElevator/elevator/elevator.vcxproj.filters
new file mode 100644
index 000000000000..38ea8fb79f5f
--- /dev/null
+++ b/native/WinElevator/elevator/elevator.vcxproj.filters
@@ -0,0 +1,23 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
+
+
+
+
+ Source Files
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/native/WinElevator/elevator/elevator.vcxproj.user b/native/WinElevator/elevator/elevator.vcxproj.user
new file mode 100644
index 000000000000..abe8dd8961e3
--- /dev/null
+++ b/native/WinElevator/elevator/elevator.vcxproj.user
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/native/WinElevator/elevator/jb.ico b/native/WinElevator/elevator/jb.ico
new file mode 100644
index 000000000000..325ab6b7de42
Binary files /dev/null and b/native/WinElevator/elevator/jb.ico differ
diff --git a/native/WinElevator/elevator/resource.h b/native/WinElevator/elevator/resource.h
new file mode 100644
index 000000000000..661a5bafe934
Binary files /dev/null and b/native/WinElevator/elevator/resource.h differ
diff --git a/native/WinElevator/launcher/Resource.rc b/native/WinElevator/launcher/Resource.rc
new file mode 100644
index 000000000000..d4c6613e7a62
Binary files /dev/null and b/native/WinElevator/launcher/Resource.rc differ
diff --git a/native/WinElevator/launcher/launcher.c b/native/WinElevator/launcher/launcher.c
new file mode 100644
index 000000000000..54247c0a794b
--- /dev/null
+++ b/native/WinElevator/launcher/launcher.c
@@ -0,0 +1,275 @@
+/*
+ * Copyright 2000-2017 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include
+#include
+#include
+
+// Elevation "frontend". Launched by user it starts elevator, connects to it and reads data from it
+
+// Author: Ilya Kazakevich
+
+// Pipe that should be connected to remote process
+typedef struct
+{
+ DWORD nRemoteProcessPid;
+ DWORD nDescriptor; //One of ELEV_* descriptors
+ BOOL bFromExternalProcess; // True if pipe is for READING from EXTERNAL process. Otherwise to write to it
+} _PIPE_CONNECTION_INFO;
+
+
+// Pipes to remote process. Accessed from another threads so they are global
+static _PIPE_CONNECTION_INFO g_stdOutPipe, g_stdErrPipe, g_stdInPipe;
+
+
+#define _CONFIGURE_PIPE_INFO(pipeInfo, nDescriptorToSet, nPid, bFromExternalProcessDirection) { \
+ pipeInfo.nDescriptor = nDescriptorToSet; \
+ pipeInfo.nRemoteProcessPid = nPid; \
+ pipeInfo.bFromExternalProcess = bFromExternalProcessDirection; \
+}
+
+// Returns full command line excluding program itself
+static WCHAR* _GetCommandLineNoProgram()
+{
+ WCHAR* sCommandLine = GetCommandLine();
+ int nNumberOfArgs;
+ WCHAR** args = CommandLineToArgvW(sCommandLine, &nNumberOfArgs);
+ WCHAR* sProgram = args[0];
+ size_t nProgramLengthChars = wcslen(sProgram);
+ LocalFree(args);
+ if (sCommandLine[0] == L'"')
+ {
+ nProgramLengthChars += 2; //Program name is in quotes
+ }
+ WCHAR * sCommandLineAfterProgram = sCommandLine + nProgramLengthChars;
+ for (; sCommandLineAfterProgram[0] == L' '; sCommandLineAfterProgram++) {} // Remove spaces after program
+
+ return sCommandLineAfterProgram;
+}
+
+// Adds argument to command line.
+// pchCurrentBufferSize should be *psCommandLine size. Incremeted automatically.
+// psCommandLine to append to
+// sStringToAdd arugment to add
+// Escaping is not supported, so string can't have quotes
+static void _AddStringToCommandLine(_Inout_ size_t* pchCurrentBufferChars, _Inout_ WCHAR** psCommandLine, _In_ WCHAR* sStringToAdd, _In_ BOOL bAddQuotes)
+{
+ // TODO: Doc suboptimal. Use line length instead of wcslen(*psCommandLine) ("shlemiel the painter algorithm")
+
+ size_t nCurrentLineLengthChars = wcslen(*psCommandLine);
+ size_t nStringToAddLengthChars = wcslen(sStringToAdd);
+ size_t nSpaceLeftInBufferChars = (*pchCurrentBufferChars) - nCurrentLineLengthChars;
+ // "\"new_string_goes_here\" "
+ size_t nRequiredSizeChars = (*pchCurrentBufferChars) + nStringToAddLengthChars;
+ if (bAddQuotes)
+ {
+ nRequiredSizeChars += wcslen(L"\"\" ");
+ }
+ if (nSpaceLeftInBufferChars < nRequiredSizeChars)
+ {
+ // Not enough space, add more space
+ (*pchCurrentBufferChars) = nRequiredSizeChars;
+ *psCommandLine = realloc(*psCommandLine, sizeof(WCHAR) * (*pchCurrentBufferChars));
+ }
+ WCHAR* endOfCommandLine = (*psCommandLine) + nCurrentLineLengthChars;
+ if (bAddQuotes) {
+ wsprintf(endOfCommandLine, L"\"%ls\" ", sStringToAdd);
+ }
+ else
+ {
+ wcscat_s((*psCommandLine), (*pchCurrentBufferChars), sStringToAdd);
+ }
+}
+
+
+// ThreadProc to connect pipe to remote process
+static DWORD _CreateConnectPipe(_PIPE_CONNECTION_INFO* pPipeInfo)
+{
+ ELEV_PIPE_NAME sPipeName;
+ ELEV_GEN_PIPE_NAME(sPipeName, pPipeInfo->nRemoteProcessPid, pPipeInfo->nDescriptor);
+ int access = (pPipeInfo->bFromExternalProcess ? PIPE_ACCESS_INBOUND : PIPE_ACCESS_OUTBOUND);
+ HANDLE hExternalPipe = CreateNamedPipe(
+ sPipeName,
+ access,
+ PIPE_READMODE_BYTE | PIPE_WAIT | PIPE_REJECT_REMOTE_CLIENTS | PIPE_TYPE_BYTE,
+ 1,
+ ELEV_BUF_SIZE,
+ ELEV_BUF_SIZE,
+ 0,
+ NULL);
+ if (hExternalPipe == NULL || hExternalPipe == INVALID_HANDLE_VALUE)
+ {
+ fwprintf(stderr, L"Failed to create in pipe: %ld", GetLastError());
+ exit(-1);
+ }
+
+ if (!ConnectNamedPipe(hExternalPipe, NULL))
+ {
+ fwprintf(stderr, L"Failed to wait for in pipe: %ld", GetLastError());
+ exit(-1);
+ }
+
+
+ char buffer[ELEV_BUF_SIZE];
+ DWORD nBytesRead;
+ DWORD nBytesWritten;
+ HANDLE hInternal = GetStdHandle(ELEV_DESCR_GET_HANDLE(pPipeInfo->nDescriptor));
+ HANDLE hToRead = (pPipeInfo->bFromExternalProcess ? hExternalPipe : hInternal);
+ HANDLE hToWrite = ((! pPipeInfo->bFromExternalProcess) ? hExternalPipe : hInternal);
+
+ while (1)
+ {
+ if (!ReadFile(hToRead, buffer, ELEV_BUF_SIZE, &nBytesRead, NULL))
+ {
+ DWORD nError = GetLastError();
+
+ if (nError == ERROR_BROKEN_PIPE)
+ {
+ break;
+ }
+ fwprintf(stderr, L"Failed to read from %ld: %ld", pPipeInfo->nDescriptor, GetLastError());
+ exit(-1);
+ }
+ if (!WriteFile(hToWrite, buffer, nBytesRead, &nBytesWritten, NULL))
+ {
+ DWORD nError = GetLastError();
+
+ if ((!pPipeInfo->bFromExternalProcess) && nError == ERROR_BROKEN_PIPE)
+ {
+ break;
+ }
+ fwprintf(stderr, L"Failed to write: %ld", GetLastError());
+ exit(-1);
+ }
+ FlushFileBuffers(hToWrite);
+ }
+ CloseHandle(hToWrite);
+ CloseHandle(hToRead);
+ return 0;
+}
+
+// If pipe is not console this function connects it, sets thread handler and configures descriptor flags to mark this descriptor is connected
+static void _LaunchPipeThread(_PIPE_CONNECTION_INFO* pPipeInfo, _Out_opt_ PHANDLE pThreadHandle, _Inout_ int* pDescriptorFlags)
+{
+ HANDLE hHandle = GetStdHandle(ELEV_DESCR_GET_HANDLE(pPipeInfo->nDescriptor));
+
+ // Console apps may act differently if its stream is not connected to console, so we only connect when file or pipe is used
+ if (GetFileType(hHandle) == FILE_TYPE_CHAR ) // Console (same as *nix istty()), do not touch
+ {
+ if (pThreadHandle) {
+ *pThreadHandle = NULL;
+ }
+ return;
+ }
+ HANDLE hThread = CreateThread(NULL, 0, _CreateConnectPipe, pPipeInfo, 0, NULL);
+ if (pThreadHandle)
+ {
+ *pThreadHandle = hThread;
+ }
+ *pDescriptorFlags |= pPipeInfo->nDescriptor;
+}
+
+int wmain(int argc, wchar_t* argv[], wchar_t* envp[])
+{
+
+
+ DWORD nExitCode = 0;
+
+ // Get pids
+ DWORD nPid = GetCurrentProcessId();
+ WCHAR sPid[20];
+ _ltow_s(nPid, sPid, 20, 10);
+
+ _CONFIGURE_PIPE_INFO(g_stdOutPipe, ELEV_DESCR_STDOUT, nPid, TRUE);
+ _CONFIGURE_PIPE_INFO(g_stdErrPipe, ELEV_DESCR_STDERR, nPid, TRUE);
+ _CONFIGURE_PIPE_INFO(g_stdInPipe, ELEV_DESCR_STDIN, nPid, FALSE);
+
+
+ _mm_mfence(); // To make sure threads has access to g_
+
+ HANDLE arHandlesToWait[] = {NULL, NULL};
+ int nDescriptorFlags = 0;
+ _LaunchPipeThread(&g_stdInPipe, NULL, &nDescriptorFlags); // No need to wait stdin thread so we do not need its handle
+ _LaunchPipeThread(&g_stdOutPipe, &arHandlesToWait[0], &nDescriptorFlags);
+ _LaunchPipeThread(&g_stdErrPipe, &arHandlesToWait[1], &nDescriptorFlags);
+
+
+ //Get current dir
+ WCHAR sCurrentDirectory[MAX_PATH + 1];
+ GetCurrentDirectory(MAX_PATH, sCurrentDirectory);
+
+ //Build commandline
+ size_t chCurrentSize = 1;
+ WCHAR* sNewCommandLine = calloc(1, sizeof(WCHAR));
+
+ _AddStringToCommandLine(&chCurrentSize, &sNewCommandLine, sPid, TRUE);
+ _AddStringToCommandLine(&chCurrentSize, &sNewCommandLine, sCurrentDirectory, TRUE);
+ WCHAR sDescriptorFlags[3];
+ _itow_s(nDescriptorFlags, sDescriptorFlags, 2, 10);
+ _AddStringToCommandLine(&chCurrentSize, &sNewCommandLine, sDescriptorFlags, TRUE);
+
+
+ _AddStringToCommandLine(&chCurrentSize, &sNewCommandLine, ELEV_COMMAND_LINE_SEPARATOR, FALSE);
+
+
+ // Add arguments provided by user to the tail of command line
+ // https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/
+ WCHAR * sOriginalCommandLineNoProgram = _GetCommandLineNoProgram();
+ size_t nNewBufferSizeChars = wcslen(sOriginalCommandLineNoProgram) + wcslen(sNewCommandLine) + 1;
+ sNewCommandLine = realloc(sNewCommandLine, nNewBufferSizeChars * sizeof(WCHAR));
+ wcscat_s(sNewCommandLine, nNewBufferSizeChars, sOriginalCommandLineNoProgram);
+
+ // Get full path to elevator
+ WCHAR sPath[MAX_PATH + 1];
+ if(!GetModuleFileName(NULL, sPath, MAX_PATH))
+ {
+ fprintf(stderr, "Failed to get directory: %ld", GetLastError());
+ return 1;
+ }
+
+ WCHAR sDrive[_MAX_DRIVE], sDir[_MAX_DIR], sFile[_MAX_FNAME], sExt[_MAX_EXT];
+ _wsplitpath_s(sPath, sDrive, _MAX_DRIVE, sDir, _MAX_DIR, sFile, _MAX_FNAME, sExt, _MAX_EXT);
+
+ swprintf_s(sPath, MAX_PATH, L"%ls%lselevator.exe", sDrive, sDir);
+
+ //Execute elevator
+ SHELLEXECUTEINFO execInfo;
+ ZeroMemory(&execInfo, sizeof(SHELLEXECUTEINFO));
+ execInfo.cbSize = sizeof(SHELLEXECUTEINFO);
+ execInfo.lpParameters = sNewCommandLine;
+ execInfo.lpFile = sPath;
+ execInfo.lpDirectory = sCurrentDirectory;
+ execInfo.fMask = SEE_MASK_NOASYNC | SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE;
+
+ if (!ShellExecuteEx(&execInfo))
+ {
+ fprintf(stderr, "Failed to launch process: %ld", GetLastError());
+ return 1;
+ }
+
+ // Wait for all threads
+ for(int i = 0; i < 2; i++)
+ {
+ if (arHandlesToWait[i])
+ {
+ WaitForSingleObject(arHandlesToWait[i], INFINITE);
+ }
+ }
+
+ // Process should be ended here, lets wait
+ WaitForSingleObject(execInfo.hProcess, INFINITE);
+ GetExitCodeProcess(execInfo.hProcess, &nExitCode);
+ return nExitCode;
+}
diff --git a/native/WinElevator/launcher/launcher.vcxproj b/native/WinElevator/launcher/launcher.vcxproj
new file mode 100644
index 000000000000..f100fc0ca5f3
--- /dev/null
+++ b/native/WinElevator/launcher/launcher.vcxproj
@@ -0,0 +1,173 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ {6631215A-50A4-49BE-A0B5-BDAC4C75B955}
+ Win32Proj
+ launcher
+ 8.1
+
+
+
+ Application
+ true
+ v140
+ Unicode
+
+
+ Application
+ false
+ v140
+ true
+ Unicode
+
+
+ Application
+ true
+ v140
+ Unicode
+
+
+ Application
+ false
+ v140
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ $(IncludePath);
+ $(VC_SourcePath);
+
+
+ true
+ $(IncludePath);
+ $(VC_SourcePath);
+
+
+ false
+ $(IncludePath);
+ $(VC_SourcePath);
+
+
+ false
+ $(IncludePath);
+ $(VC_SourcePath);
+
+
+
+ NotUsing
+ Level3
+ Disabled
+ WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)
+ true
+ MultiThreadedDebug
+
+
+ Console
+ true
+ AsInvoker
+
+
+
+
+ NotUsing
+ Level3
+ Disabled
+ _DEBUG;_WINDOWS;%(PreprocessorDefinitions)
+ true
+ MultiThreadedDebug
+
+
+ Console
+ true
+ AsInvoker
+
+
+
+
+ Level3
+ NotUsing
+ MaxSpeed
+ true
+ true
+ WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)
+ true
+ MultiThreaded
+
+
+ Console
+ true
+ true
+ true
+ AsInvoker
+
+
+
+
+ Level3
+ NotUsing
+ MaxSpeed
+ true
+ true
+ NDEBUG;_WINDOWS;%(PreprocessorDefinitions)
+ true
+ MultiThreaded
+
+
+ Console
+ true
+ true
+ true
+ AsInvoker
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/native/WinElevator/launcher/launcher.vcxproj.filters b/native/WinElevator/launcher/launcher.vcxproj.filters
new file mode 100644
index 000000000000..dc9b18d9ad11
--- /dev/null
+++ b/native/WinElevator/launcher/launcher.vcxproj.filters
@@ -0,0 +1,20 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
+
+
+
+
+ Source Files
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/native/WinElevator/launcher/launcher.vcxproj.user b/native/WinElevator/launcher/launcher.vcxproj.user
new file mode 100644
index 000000000000..abe8dd8961e3
--- /dev/null
+++ b/native/WinElevator/launcher/launcher.vcxproj.user
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/native/WinElevator/launcher/resource.h b/native/WinElevator/launcher/resource.h
new file mode 100644
index 000000000000..647c9bd9480d
Binary files /dev/null and b/native/WinElevator/launcher/resource.h differ
diff --git a/platform/platform-api/src/com/intellij/execution/util/ExecUtil.java b/platform/platform-api/src/com/intellij/execution/util/ExecUtil.java
index b78203d1ef69..430da9276eab 100644
--- a/platform/platform-api/src/com/intellij/execution/util/ExecUtil.java
+++ b/platform/platform-api/src/com/intellij/execution/util/ExecUtil.java
@@ -144,8 +144,17 @@ public class ExecUtil {
command.add(commandLine.getExePath());
command.addAll(commandLine.getParametersList().getList());
- GeneralCommandLine sudoCommandLine;
- if (SystemInfo.isMac) {
+ final GeneralCommandLine sudoCommandLine;
+ if (SystemInfo.isWinVistaOrNewer) {
+ // launcher.exe process with elevated permissions on UAC.
+ final File launcherExe = PathManager.findBinFileWithException("launcher.exe");
+ sudoCommandLine = new GeneralCommandLine(launcherExe.getPath());
+ sudoCommandLine.setWorkDirectory(commandLine.getWorkDirectory());
+ sudoCommandLine.addParameter(commandLine.getExePath());
+ sudoCommandLine.addParameters(commandLine.getParametersList().getParameters());
+ sudoCommandLine.getEnvironment().putAll(commandLine.getEffectiveEnvironment());
+ }
+ else if (SystemInfo.isMac) {
String escapedCommandLine = StringUtil.join(command, ExecUtil::escapeAppleScriptArgument, " & \" \" & ");
String escapedScript = "tell current application\n" +
" activate\n" +
diff --git a/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java b/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java
index cf768491038f..d2c1c45fab48 100644
--- a/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java
+++ b/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java
@@ -118,7 +118,7 @@ public class PyPackageManagerImpl extends PyPackageManager {
private boolean refreshAndCheckForSetuptools() throws ExecutionException {
try {
final List packages = refreshAndGetPackages(false);
- return PyPackageUtil.findPackage(packages, PyPackageUtil.SETUPTOOLS) != null ||
+ return PyPackageUtil.findPackage(packages, PyPackageUtil.SETUPTOOLS) != null ||
PyPackageUtil.findPackage(packages, PyPackageUtil.DISTRIBUTE) != null;
}
catch (PyExecutionException e) {
@@ -233,7 +233,7 @@ public class PyPackageManagerImpl extends PyPackageManager {
if (canModify) {
final String location = pkg.getLocation();
if (location != null) {
- canModify = FileUtil.ensureCanCreateFile(new File(location));
+ canModify = ensureCanCreateFile(new File(location));
}
}
args.add(pkg.getName());
@@ -249,6 +249,29 @@ public class PyPackageManagerImpl extends PyPackageManager {
}
}
+ // TODO: Move to FileUtil.ensureCanCreateFile ?
+
+ /**
+ * When file it protected with UAC on Windows, you can't relay on {@link File#canWrite()}.
+ *
+ * @param file file to check if writable (works in UAC too)
+ */
+ private static boolean ensureCanCreateFile(@NotNull final File file) {
+ if (SystemInfo.isWinVistaOrNewer) {
+ try {
+ final File folder = (file.isFile() ? file.getParentFile() : file);
+ final File tmpFile = File.createTempFile("pycharm", null, folder);
+ tmpFile.deleteOnExit();
+ tmpFile.delete();
+ }
+ catch (final IOException ignored) {
+ return false;
+ }
+ return true;
+ }
+ return FileUtil.ensureCanCreateFile(file);
+ }
+
@Nullable
@Override
@@ -439,8 +462,8 @@ public class PyPackageManagerImpl extends PyPackageManager {
cmdline.addAll(args);
LOG.info("Running packaging tool: " + StringUtil.join(cmdline, " "));
- final boolean canCreate = FileUtil.ensureCanCreateFile(new File(homePath));
- final boolean useSudo = !canCreate && !SystemInfo.isWindows && askForSudo;
+ final boolean canCreate = ensureCanCreateFile(new File(homePath));
+ final boolean useSudo = !canCreate && askForSudo;
try {
final GeneralCommandLine commandLine = new GeneralCommandLine(cmdline).withWorkDirectory(workingDir);