locked
API to set "Run as administrator" flag on a shortcut file? RRS feed

  • Question

  • I need my installer to create some shortcuts with "Run as administrator" checked in the shortcuts' Advanced Properties.

    How is this flag stored in the file system? Some kind of file attribute? What API could I use to access it?

     

    Thanks for any advice / suggestions.

     

    Thursday, April 5, 2007 6:39 PM

Answers

  • David,

    Thanks for the tips. In addition to Save, I needed to do SaveCompleted. This code worked:

    Code Snippet

    #include "stdafx.h" // includes <tchar.h>

    #include <windows.h>
    #include <shobjidl.h> // ShellLink
    #include <shlobj.h> // IShellLinkDataList (build on Vista use shobjidl.h)
    #include <objbase.h> // CoInitialize, CoInitializeEx, CoUninitialize

    int _tmain(int argc, _TCHAR* argv[])
    {
      HRESULT       result;
      WCHAR         wbuf[MAX_PATH];
      IShellLink*   link;
      IPersistFile* file;
      char    szFile[MAX_PATH];
      char    szProgramMenuFolder[MAX_PATH];

      // SHOULD use CoInitializeEx, but compiler can't find it.
      result = CoInitialize(NULL); // For Ex: 2nd param: COINIT_APARTMENTTHREADED
     
      // Create IShellLink object
      result = CoCreateInstance(CLSID_ShellLink,
          NULL,
          CLSCTX_INPROC_SERVER,
          IID_IShellLink,
          (void**)&link);
      if (result != S_OK) {
        CoUninitialize();
        return -1;
      }

      // Retreive the IPersistFile
      result = link->QueryInterface(IID_IPersistFile, (void**)&file);
      if (result != S_OK) {
        link->Release();
        CoUninitialize();
        return -2;
      }
      // SHOULD get from registry.
      strcpy(szProgramMenuFolder, "C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\");

      // DEBUG
      strcpy(szFile, szProgramMenuFolder);
      // SHOULD use your folder and shortcut name.
      strcat(szFile, "Program Shortcut Folder\\Shortcut.lnk");
     
      // Convert the filename
      MultiByteToWideChar(CP_ACP, 0, szFile, -1, wbuf, sizeof(wbuf)-1);

      // Load the link data from the file
      result = file->Load(wbuf, STGM_READ);
      if (result != S_OK) {
        file->Release();
        link->Release();
        CoUninitialize();
        return -3;
      }

      // Look for IShellLinkDataList interface
      IShellLinkDataList* pdl;

      result = link->QueryInterface(IID_IShellLinkDataList, (void**)&pdl);
      if (result != S_OK) {
        file->Release();
        link->Release();
        CoUninitialize();
        return -4; // Where did IShellLinkDataList go?
      }

      DWORD dwFlags = 0;

      result = pdl->GetFlags(&dwFlags);
      if (result != S_OK) {
        pdl->Release();
        file->Release();
        link->Release();
        CoUninitialize();
        return -5;
      }

      // Only set SLDF_RUNAS_USER if it's not set, otherwise
      // SetFlags returns an error.
      if ((SLDF_RUNAS_USER & dwFlags) != SLDF_RUNAS_USER) {
        result = pdl->SetFlags(SLDF_RUNAS_USER | dwFlags);
        if (result != S_OK) {
          pdl->Release();
          file->Release();
          link->Release();
          CoUninitialize();
          return -6;
        }
      }
      else {
        pdl->Release();
        file->Release();
        link->Release();
        CoUninitialize();
        return 0;
      }

      result = file->Save(NULL, true);
      if (result != S_OK) {
        pdl->Release();
        file->Release();
        link->Release();
        CoUninitialize();
        return -8;
      }
      result = file->SaveCompleted(NULL);
      if (result != S_OK) {
        pdl->Release();
        file->Release();
        link->Release();
        CoUninitialize();
        return -9;
      }

      pdl->Release();
      file->Release();
      link->Release();
      CoUninitialize();
      return ERROR_SUCCESS;
    }





    Thursday, April 26, 2007 5:15 PM

All replies

  • You should mark the target executables as requiring Administrator rights by embedding a Vista Manifest rather than changing the setting in the shortcut file.
    Thursday, April 5, 2007 7:50 PM
  • I already embedded a manifest in my application but it does not serve all my needs.

    It is a console application and I need the calling command prompt to be running elevated

    before the application.

    If I can check the "Run as administrator" box on shortcuts then it must be a supported feature.

    All I need is a programmatic way to do it in my installer so that our customers are not required

    to check that flag manually when they install our products.

    Thursday, April 5, 2007 9:56 PM
  • I'm pretty sure the shortcut flags are for application compatibility only and thus not really exposed programmatically. How about creating a stub executable with an elevation manifest that just launches cmd.exe and pointing the shortcut to that?
    Friday, April 6, 2007 11:40 AM
  • If you play around with the run as administrator setting for both the current user and all users on an exe's properties you will see the following keys being set:

    HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers

    HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers

    Note that not all shortcuts point directly at an exe, depending upon whether an application is installed as an advertised application.

    Setting a shortcut to run an app as admin, when possible (i.e. the shortcut actually points at an exe) is the same as going into the apps exe properties box and selecting run as admin, thus if it is cmd.exe then you would be setting cmd.exe to run as admin no matter how it were started, so you would need to possibly be resetting it back after it were started to avoid making it run as admin thereafter.
    Friday, April 6, 2007 2:54 PM

  • > Setting a shortcut to run an app as admin, when possible (i.e. the shortcut actually points at an exe) is the same as going into the apps exe properties box and selecting run as admin, thus if it is cmd.exe then you would be setting cmd.exe to run as admin no matter how it were started, so you would need to possibly be resetting it back after it were started to avoid making it run as admin thereafter.


     This is incorrect.

     

    See MSDN for:

    IShellLinkDataList::SetFlags

     

    if you want to play with shortcut flags.  They apply to just the shortcut you modify (so if you create your own shortcut, that should impact nobody but yourself.)

    Friday, April 6, 2007 6:26 PM
  • David,

    I tried IShellLinkDataList:: SetFlags(SLDF_RUNAS_USER), and though it returns S_OK, the shortcut is not set to run as admin.

    How should that function be used?
    Wednesday, April 25, 2007 6:51 PM
  • first off, you want to getFlags and then OR in SLDF_RUNAS_USER, I'm guessing?

    second, did you save the shortcut with the IPersistFile interface?

    Wednesday, April 25, 2007 7:54 PM
  • David,

    I didn't do GetFlags, and OR in SLDF_RUNAS_USER. You're guessing that will work? How do I set the shortcut to Run as administrator? The documentation for SLDF_RUNAS_USER just says, "Run this link as a different user.
    " How do I set the different user to be Administrator?

    I didn't save the shortcut with the IPersistFile interface. I'll try that.

    Wednesday, April 25, 2007 8:28 PM
  • David,

    Thanks for the tips. In addition to Save, I needed to do SaveCompleted. This code worked:

    Code Snippet

    #include "stdafx.h" // includes <tchar.h>

    #include <windows.h>
    #include <shobjidl.h> // ShellLink
    #include <shlobj.h> // IShellLinkDataList (build on Vista use shobjidl.h)
    #include <objbase.h> // CoInitialize, CoInitializeEx, CoUninitialize

    int _tmain(int argc, _TCHAR* argv[])
    {
      HRESULT       result;
      WCHAR         wbuf[MAX_PATH];
      IShellLink*   link;
      IPersistFile* file;
      char    szFile[MAX_PATH];
      char    szProgramMenuFolder[MAX_PATH];

      // SHOULD use CoInitializeEx, but compiler can't find it.
      result = CoInitialize(NULL); // For Ex: 2nd param: COINIT_APARTMENTTHREADED
     
      // Create IShellLink object
      result = CoCreateInstance(CLSID_ShellLink,
          NULL,
          CLSCTX_INPROC_SERVER,
          IID_IShellLink,
          (void**)&link);
      if (result != S_OK) {
        CoUninitialize();
        return -1;
      }

      // Retreive the IPersistFile
      result = link->QueryInterface(IID_IPersistFile, (void**)&file);
      if (result != S_OK) {
        link->Release();
        CoUninitialize();
        return -2;
      }
      // SHOULD get from registry.
      strcpy(szProgramMenuFolder, "C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\");

      // DEBUG
      strcpy(szFile, szProgramMenuFolder);
      // SHOULD use your folder and shortcut name.
      strcat(szFile, "Program Shortcut Folder\\Shortcut.lnk");
     
      // Convert the filename
      MultiByteToWideChar(CP_ACP, 0, szFile, -1, wbuf, sizeof(wbuf)-1);

      // Load the link data from the file
      result = file->Load(wbuf, STGM_READ);
      if (result != S_OK) {
        file->Release();
        link->Release();
        CoUninitialize();
        return -3;
      }

      // Look for IShellLinkDataList interface
      IShellLinkDataList* pdl;

      result = link->QueryInterface(IID_IShellLinkDataList, (void**)&pdl);
      if (result != S_OK) {
        file->Release();
        link->Release();
        CoUninitialize();
        return -4; // Where did IShellLinkDataList go?
      }

      DWORD dwFlags = 0;

      result = pdl->GetFlags(&dwFlags);
      if (result != S_OK) {
        pdl->Release();
        file->Release();
        link->Release();
        CoUninitialize();
        return -5;
      }

      // Only set SLDF_RUNAS_USER if it's not set, otherwise
      // SetFlags returns an error.
      if ((SLDF_RUNAS_USER & dwFlags) != SLDF_RUNAS_USER) {
        result = pdl->SetFlags(SLDF_RUNAS_USER | dwFlags);
        if (result != S_OK) {
          pdl->Release();
          file->Release();
          link->Release();
          CoUninitialize();
          return -6;
        }
      }
      else {
        pdl->Release();
        file->Release();
        link->Release();
        CoUninitialize();
        return 0;
      }

      result = file->Save(NULL, true);
      if (result != S_OK) {
        pdl->Release();
        file->Release();
        link->Release();
        CoUninitialize();
        return -8;
      }
      result = file->SaveCompleted(NULL);
      if (result != S_OK) {
        pdl->Release();
        file->Release();
        link->Release();
        CoUninitialize();
        return -9;
      }

      pdl->Release();
      file->Release();
      link->Release();
      CoUninitialize();
      return ERROR_SUCCESS;
    }





    Thursday, April 26, 2007 5:15 PM
  • This code worked great for me as well.

     

    Charles, David, many thanks to you both.

     

    Jean-Philippe

     

    Monday, May 21, 2007 7:36 PM
  •  

    how to get szProgramMenuFolder path? from which registry value?

    Monday, January 21, 2008 9:24 AM
  • Hi Charles / David / jpm,

    Can someone tell me how to use this code to integrate the same with my install script?

     

    I am using Installshield 2008 to build the .msi though new to windows and installer world Sad.

    we are able to install app on XP and VISTA (UAC enabled/disabled) both but not able to launch when we double click the shortcut desktop/system tray icon neither as an admin  nor as a standard user when UAC is enabled.

    We are using executables built by VS 2005 and have never used or embedded any manifest for creating this .msi file.

    Even my script is UI based and i dont see any script function when I open the same in Installshield Sad

     

    It would be great if you can help me to resolve this issue.

    many thanks.

     

     

     

    Tuesday, June 24, 2008 9:31 AM