管理者として起動するショートカットを作る

EXEファイルを実行する際に自動的に「管理者として実行」するには、そのEXEのプロパティ>互換性>特権レベルを設定すればよく、

それをプログラムで設定するには、上記設定に対応するレジストリである

  • HKCU\SOFTWARE\Microsoft\\Windows NT\CurrentVersion\AppCompatFlags\Layers (現在のユーザ)

または

  • HKLM\SOFTWARE\Microsoft\\Windows NT\CurrentVersion\AppCompatFlags\Layers (全てのユーザ)

に"RUNASADMIN"の値を持つエントリを作ればよいのですが、そうして設定済のEXEファイルをショートカット(.LNK)から実行すると何故か「管理者として実行」されません。

何で?と思って調べたら実は、
「ショートカットファイルは独自に『管理者として実行』のフラグを内部に持っている」
というオチでした。

このショートカットファイルの「管理者として実行」フラグをプログラムで設定する方法を探してみたところ、

にそのものズバリの答えがありましたが、両者の良いところをマージして、かつATLが無いVisual Studio Expressの環境でもビルドできるようにスマートポインタを_com_ptr_tで書き直してみました。

しかし「管理者として実行」(RUNASADMIN)なのに"SLDF_RUNAS_USER"フラグを立てるというのが何とも…

[stdafx.h]

#pragma once

#include "targetver.h"

#include <stdio.h>
#include <windows.h>
#include <shlobj.h>
#include <comdef.h>
#include <locale.h>

//typedef _com_ptr_t<_com_IIID<IPersistFile, &IID_IPersistFile>>  IPersistFilePtr;
typedef _com_ptr_t<_com_IIID<IShellLinkDataList, &IID_IShellLinkDataList>>  IShellLinkDataListPtr;

[MarkShortcutRunAs.cpp]

#include "stdafx.h"

bool SetLinkAttributes(LPCWSTR pszShortcut, DWORD flags)
{
	IPersistFilePtr sppf;
	if (FAILED(sppf.CreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER))) return false;
	if (FAILED(sppf->Load(pszShortcut, STGM_READWRITE))) return false;

	IShellLinkDataListPtr spdl = sppf;
	if (!spdl) return false;   

	DWORD dwFlags;
	if (FAILED(spdl->GetFlags(&dwFlags))) return false;
	if ((dwFlags & flags) == flags) return true;

	if (FAILED(spdl->SetFlags(dwFlags | flags))) return false;
	if (FAILED(sppf->Save(NULL, TRUE))) return false;

	return true;
}

int wmain(int argc, wchar_t *argv[])
{
	int rc = 1;
	// Only Vista or later
	if (LOBYTE(LOWORD(GetVersion())) < 6) return rc;

	_wsetlocale(LC_CTYPE, L"");
	if (argc > 1 && SUCCEEDED(CoInitialize(NULL))) {
		rc = 0;
		for (int i = 1; i < argc; ++i) {
			if (!SetLinkAttributes(argv[i], SLDF_RUNAS_USER)) {
				fwprintf(stderr, L"%ls: An error occurred.\n", argv[i]);
				rc = 1;
			}
		}

		if (0 == rc) wprintf(L"Succeeded.\n");
		CoUninitialize();
	}

	return rc;
}