JetBrains products updater: invoke UAC elevation if needed (IDEA-52571)
authorKirill.Safonov <kirill.safonov@jetbrains.com>
Thu, 1 Sep 2011 07:39:42 +0000 (11:39 +0400)
committerKirill.Safonov <kirill.safonov@jetbrains.com>
Thu, 1 Sep 2011 07:41:25 +0000 (11:41 +0400)
bin/win/vistalauncher.exe
native/VistaUpdaterLauncher/VistaUpdaterLauncher/VistaUpdaterLauncher.cpp
native/VistaUpdaterLauncher/VistaUpdaterLauncher/VistaUpdaterLauncher.rc

index 31594768d66463659a1e914d80728447a6267761..7a24123f268cd958fc915aa489a62c8421c01ceb 100644 (file)
Binary files a/bin/win/vistalauncher.exe and b/bin/win/vistalauncher.exe differ
index dd2e41980873351a2f9cb7a7c465ecc4630ce675..b030fe95fa93f941ad54f2969b06b3e829b4a002 100644 (file)
@@ -1,18 +1,18 @@
 /*
- * Copyright 2000-2009 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.
- */
+* Copyright 2000-2009 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 "stdafx.h"
 #include <windows.h>
 #include <string.h>
 #include <stdlib.h>
 
+static const wchar_t* INSTALL_PARAM = L"install";
+static const wchar_t* SKIP_ELEVATION_PARAM = L"--skip-uac-elevation--";
 
-int _tmain(int argc, _TCHAR* argv[])
+
+bool isWritable(wchar_t* path)
 {
-       STARTUPINFO startupInfo = {0};
-       startupInfo.cb = sizeof(startupInfo);
-       PROCESS_INFORMATION processInformation = {0};
+       wprintf(L"Trying to create temporary file in\"%s\"\n", path);
+       wchar_t fileName[32768] = L"";
+       wcscat_s(fileName, path);
+       wcscat_s(fileName, L"\\.jetbrains-uac-check");
+       HANDLE file = CreateFile(fileName, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL);
+       if (file == INVALID_HANDLE_VALUE)
+       {
+               DWORD error = GetLastError();
+               if (error == ERROR_ACCESS_DENIED)
+               {
+                       // looks like we need to request elevation
+                       return false;
+               }
+               else
+               {
+                       // there's no need to request elevaion since patcher will most likely fail anyway
+                       wprintf(L"Unexpected error when creating temp file: %d\n", error);
+                       fflush(stdout);
+                       return true;
+               }
+       }
 
-       startupInfo.hStdInput  = GetStdHandle(STD_INPUT_HANDLE);
-       startupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
-       startupInfo.hStdError  = GetStdHandle(STD_ERROR_HANDLE);
+       CloseHandle(file);
+       DeleteFile(fileName);
+       return true;
+}
 
-       wchar_t commandLine[32768] = L"";
+void appendArgument(wchar_t result[], wchar_t argument[])
+{
+       bool needsQuoting = wcschr(argument, L' ') != NULL;
+       if (needsQuoting)
+       {
+               wcscat_s(result, 32768, L"\"");
+       }
+       wcscat_s(result, 32768, argument);
+       if (needsQuoting)
+       {
+               wcscat_s(result, 32768, L"\"");
+       }
+       wcscat_s(result, 32768, L" ");
+}
 
+bool getElevationPath(int argc, _TCHAR* argv[], wchar_t result[])
+{
+       int start = -1;
        for (int i = 1; i < argc; i++) {
-        wcscat_s(commandLine, L"\"");
-        wcscat_s(commandLine, argv[i]);
-        wcscat_s(commandLine, L"\" ");
+               if (wcscmp(argv[i], SKIP_ELEVATION_PARAM) == 0)
+               {
+                       wprintf(L"Elevation suppressed\n");
+                       fflush(stdout);
+                       return false;
+               }
+
+               if (wcscmp(argv[i], INSTALL_PARAM) == 0)
+               {
+                       start = i;
+               }
+               else if (start >= 0)
+               {
+                       if (i > start + 1)
+                       {
+                               wcscat_s(result, 32768, L" ");
+                       }
+                       wcscat_s(result, 32768, argv[i]);
+               }
        }
+       return start >= 0;
+}
 
-       wprintf(L"Creating new process: %s\n", commandLine);
-       fflush(stdout);
-
-       if (!CreateProcess(
-               NULL, /*LPCTSTR lpApplicationName*/
-               commandLine,/* LPTSTR lpCommandLine*/
-               NULL, /*LPSECURITY_ATTRIBUTES lpProcessAttributes*/
-               NULL, /*LPSECURITY_ATTRIBUTES lpThreadAttributes*/
-               TRUE, /*BOOL bInheritHandles,*/
-               0,    /*DWORD dwCreationFlags*/
-               NULL, /*LPVOID lpEnvironment*/
-               NULL, /*LPCTSTR lpCurrentDirectory*/
-               &startupInfo, /*LPSTARTUPINFO lpStartupInfo*/
-               &processInformation /*LPPROCESS_INFORMATION lpProcessInformation*/))
+int _tmain(int argc, _TCHAR* argv[])
+{
+       wchar_t elevationPath[32768] = L"";
+       HANDLE processHandle = NULL;
+       if (getElevationPath(argc, argv, elevationPath) && !isWritable(elevationPath))
+       {
+               wchar_t paramsLine[32768] = L"";
+               wcscat_s(paramsLine, SKIP_ELEVATION_PARAM);
+
+               for (int i = 1; i < argc; i++) 
+               {
+                       wcscat_s(paramsLine, L" ");
+                       appendArgument(paramsLine, argv[i]);
+               }
+
+               wprintf(L"Creating elevated process: %s %s\n", argv[0], paramsLine);
+               fflush(stdout);
+
+               SHELLEXECUTEINFO shExecInfo;
+               shExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
+               shExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
+               shExecInfo.hwnd = NULL;
+               shExecInfo.lpVerb = L"runas";
+               shExecInfo.lpFile = argv[0];
+               shExecInfo.lpParameters = paramsLine;
+               shExecInfo.lpDirectory = NULL;
+               shExecInfo.nShow = SW_HIDE;
+               shExecInfo.hInstApp = NULL;
+
+               if (ShellExecuteEx(&shExecInfo) == FALSE)
+               {
+                       wprintf(L"ShellExecuteEx() failed with error code %d\n", GetLastError());
+                       fflush(stdout);
+                       return -1;
+               }
+               processHandle = shExecInfo.hProcess;
+       }
+       else 
        {
+               STARTUPINFO startupInfo = {0};
+               startupInfo.cb = sizeof(startupInfo);
+               PROCESS_INFORMATION processInformation = {0};
+
+               startupInfo.hStdInput  = GetStdHandle(STD_INPUT_HANDLE);
+               startupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
+               startupInfo.hStdError  = GetStdHandle(STD_ERROR_HANDLE);
+
+               wchar_t commandLine[32768] = L"";
+
+               for (int i = 1; i < argc; i++) {
+                       if (wcscmp(argv[i], SKIP_ELEVATION_PARAM) != 0)
+                       {
+                               // add only original parameters
+                               appendArgument(commandLine, argv[i]);
+                       }
+               }
+
+               wprintf(L"Creating new process: %s\n", commandLine);
+               fflush(stdout);
+
+               if (!CreateProcess(
+                       NULL, /*LPCTSTR lpApplicationName*/
+                       commandLine,/* LPTSTR lpCommandLine*/
+                       NULL, /*LPSECURITY_ATTRIBUTES lpProcessAttributes*/
+                       NULL, /*LPSECURITY_ATTRIBUTES lpThreadAttributes*/
+                       TRUE, /*BOOL bInheritHandles,*/
+                       0,    /*DWORD dwCreationFlags*/
+                       NULL, /*LPVOID lpEnvironment*/
+                       NULL, /*LPCTSTR lpCurrentDirectory*/
+                       &startupInfo, /*LPSTARTUPINFO lpStartupInfo*/
+                       &processInformation /*LPPROCESS_INFORMATION lpProcessInformation*/))
+               {
                        wprintf(L"Cannot create process: %d\n", GetLastError());
                        return -1;
+               }
+               processHandle = processInformation.hProcess;
        }
 
-       WaitForSingleObject(processInformation.hProcess, INFINITE);
+       WaitForSingleObject(processHandle, INFINITE);
 
        DWORD exitCode = 0;
-       if (!GetExitCodeProcess(processInformation.hProcess, &exitCode))
+       if (!GetExitCodeProcess(processHandle, &exitCode))
        {
                wprintf(L"Cannot retrieve process exit code: %d\n", GetLastError());
                exitCode = -1;
        }
-       CloseHandle(processInformation.hProcess);
+       CloseHandle(processHandle);
 
        return exitCode;
 }
index 812e0baa4287bf6b964849ac516fbcbe1827b402..ec22af0099f8692ab23fdc54f84cc094e2ff0641 100644 (file)
@@ -7,7 +7,7 @@
 //
 // Generated from the TEXTINCLUDE 2 resource.
 //
-#include "afxres.h"
+#include "windows.h"
 
 /////////////////////////////////////////////////////////////////////////////
 #undef APSTUDIO_READONLY_SYMBOLS
@@ -27,18 +27,18 @@ LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT
 // TEXTINCLUDE
 //
 
-1 TEXTINCLUDE 
+1 TEXTINCLUDE
 BEGIN
     "resource.h\0"
 END
 
-2 TEXTINCLUDE 
+2 TEXTINCLUDE
 BEGIN
-    "#include ""afxres.h""\r\n"
+    "#include ""windows.h""\r\n"
     "\0"
 END
 
-3 TEXTINCLUDE 
+3 TEXTINCLUDE
 BEGIN
     "\r\n"
     "\0"
@@ -70,12 +70,12 @@ BEGIN
         BLOCK "041904b0"
         BEGIN
             VALUE "CompanyName", "JetBrains, Inc."
-            VALUE "FileDescription", "IntelliJ IDEA Automatic Update"
+            VALUE "FileDescription", "JetBrains Updater Launcher"
             VALUE "FileVersion", "1, 0, 0, 1"
-            VALUE "InternalName", "fsnotifier"
-            VALUE "LegalCopyright", "Copyright (C) 2009 JetBrains, Inc."
+            VALUE "InternalName", "vistalauncher"
+            VALUE "LegalCopyright", "Copyright (C) 2009-2011 JetBrains, Inc."
             VALUE "OriginalFilename", "vistalauncher.exe"
-            VALUE "ProductName", "IntelliJ IDEA"
+            VALUE "ProductName", "JetBrains Updater Launcher"
             VALUE "ProductVersion", "1, 0, 0, 1"
         END
     END