Using Vista’s UAC in SWT

UAC allows users to launch specific applications with elevated privileges, e.g. Changing some system settings, or launching an application that needs to have access to files not in your profile (e.g. C:/Program Files/ directory). To accomplish that we need to do two things.

First is to launch application with elevated privileges, it’s quite easy to do, we need to use OS.ShellExecuteEx and SHELLEXECUTEINFO in which there’s a field called lpVerb. This field needs to have runas string. Here’s the example code (it’s modified version of launch(String) method from Program class.

/**
 * Launches the operating system executable associated with the file or
 * URL (http:// or https://) with elevated privileges.  If the file is an
 * executable then the executable is launched.  Note that a
 * <code>Display</code> must already exist to guarantee that this method
 * returns an appropriate result. This method waits on OS.ShellExecuteEx
 * until user close UAC warning. This works only with Windows Vista and
 * higher. On lower versions of Windows there will be no visible difference.
 *
 * @param fileName the file or program name or URL (http:// or https://)
 * @return <code>true</code> if the file is launched, otherwise
 *         <code>false</code>
 *
 * @exception IllegalArgumentException <ul>
 *    <li>ERROR_NULL_ARGUMENT when fileName is null</li>
 * </ul>
 */

public static boolean launchElevated (String fileName) {
    if (fileName == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
   
    /* Use the character encoding for the default locale */
    int /*long*/ hHeap = OS.GetProcessHeap ();

    TCHAR buffer = new TCHAR (0, fileName, true);
    int byteCount = buffer.length () * TCHAR.sizeof;
    int /*long*/ lpFile = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount);
    OS.MoveMemory (lpFile, buffer, byteCount);

    TCHAR verbBuffer = new TCHAR (0, "runas", true);
    int verbByteCount = verbBuffer.length () * TCHAR.sizeof;
    int /*long*/ lpVerb = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, verbByteCount);
    OS.MoveMemory (lpVerb, verbBuffer, verbByteCount);

    SHELLEXECUTEINFO info = new SHELLEXECUTEINFO ();
    info.cbSize = SHELLEXECUTEINFO.sizeof;
    info.lpFile = lpFile;
    info.lpVerb = lpVerb;
    info.nShow = OS.SW_SHOW;
    boolean result = OS.ShellExecuteEx (info);

    if (lpFile != 0) OS.HeapFree (hHeap, 0, lpFile);
    if (lpVerb != 0) OS.HeapFree (hHeap, 0, lpVerb);

    return result;
}

Second thing is visual approach. All Vista’s applications that tries to elevate privileges have shield icon as button image. We don’t need to provide this icon by ourselves, we just simply send a message BCM_SETSHIELD to button widget with lParam 1 (true) or 0 (false).

OS.SendMessageW(button.handle, BCM_SETSHIELD, 0, 1);

Since BCM_SETSHIELD is not available in OS class (I wonder why?) we need to declare it:

public static final int BCM_SETSHIELD = OS.BCM_FIRST + 0xc;

Here’s obligatory screenshot of BCM_SETSHIELD message in use. In this screenshot there’s an example of retrieving icon from my previous post.

Launch notepad.exe elevated

Hope it will help, it did me :-)

Leave a Reply