PageRenderTime 319ms CodeModel.GetById 60ms app.highlight 217ms RepoModel.GetById 24ms app.codeStats 1ms

/admin/win/nsi/nsis_uac/uac.cpp

http://github.com/tomahawk-player/tomahawk
C++ | 1518 lines | 1438 code | 39 blank | 41 comment | 66 complexity | c61bd6a7e2029a603bf357d381fbdb94 MD5 | raw file
   1//Copyright (C) 2007 Anders Kjersem. Licensed under the zlib/libpng license, see License.txt for details.
   2/*
   3UAC plugin for NSIS
   4===================
   5Compiled with VC6+PlatformSDK (StdCall & MinimizeSize)
   6
   7
   8Todo:
   9-----
  10¤GetCurrentDir in elevated parent and pass along to outer process for Exec* (or somekind of ipc to request it if workingdir param is empty)
  11X¤Check if secondary logon service is running in SysElevationPresent() on NT5
  12¤Use IsUserAnAdmin? MAKEINTRESOURCE(680) export on 2k (it even exists on NT4?) //http://forums.winamp.com/showthread.php?s=&threadid=195020
  13¤AllowSetForegroundWindow
  14¤Use RPC instead of WM_COPYDATA for IPC
  15¤Autodetect "best" default admin user in MyRunAs
  16¤Use ChangeWindowMessageFilter so we can get >WM_USER msg success feedback and possibly fill the log with detailprints
  17¤Hide IPC window inside inner instance window? (Could this add unload problems?)
  18¤CREATE_PRESERVE_CODE_AUTHZ_LEVEL? http://msdn2.microsoft.com/en-us/library/ms684863.aspx
  19¤UpdateProcThreadAttribute?
  20¤Consent UI on XP ?
  21¤All langs in single file; [MyRunAsStrings]>LangSections != 0 then load strings from [langid] sections
  22¤BroadcastSystemMessage to help with SetForeground
  23¤UAC::StackPop
  24
  25
  26Notes:
  27------
  28Primary integrity levels:
  29Name					SID				RID 
  30Low Mandatory Level		S-1-16-4096		0x1000
  31Medium Mandatory Level	S-1-16-8192		0x2000
  32High Mandatory Level	S-1-16-12288	0x3000
  33System Mandatory Level	S-1-16-16384	0x4000
  34
  35*/
  36
  37#define UAC_HACK_Clammerz	//ugly messagebox focus hack for .onInit
  38#define UAC_HACK_FGWND1		//super ugly fullscreen invisible window for focus tricks
  39
  40#define UAC_INITIMPORTS

  41#include "UAC.h"

  42#include <objbase.h>//CoInitialize

  43#include "NSISUtil.h"

  44using namespace NSIS;
  45NSISUTIL_INIT();
  46
  47
  48#define ERRAPP_BADIPCSRV (0x20000000|1)

  49#define SW_INVALID ((WORD)-1)

  50#define IPCTOUT_DEF (1000*3) //default timeout for IPC messages
  51#define IPCTOUT_SHORT 1500

  52#define IPCTOUT_LONG (IPCTOUT_DEF*2)

  53
  54enum _IPCSRVWNDMSG 
  55{
  56	IPC_GETEXITCODE=WM_USER,	//Get exit-code of the process spawned by the last call to ExecWait/ShellExecWait
  57	IPC_ELEVATEAGAIN,
  58	IPC_GETSRVPID,				//Get PID of outer process
  59	IPC_GETSRVHWND,				//Get $HWNDParent of outer process
  60	IPC_EXECCODESEGMENT,		//wp:pos | lp:hwnd | returns ErrorCode+1
  61	IPC_GETOUTERPROCESSTOKEN,	
  62#ifdef UAC_HACK_FGWND1

  63	IPC_HACKFINDRUNAS,
  64#endif

  65};
  66
  67enum _COPYDATAID 
  68{
  69	CDI_SHEXEC=666,	//returns WindowsErrorCode+1
  70	CDI_SYNCVAR,
  71	CDI_STACKPUSH,
  72};
  73
  74typedef struct 
  75{
  76	UINT VarId;
  77	NSISCH buf[ANYSIZE_ARRAY];
  78} IPC_SYNCVAR;
  79
  80typedef struct 
  81{
  82	HWND hwnd;
  83	bool Wait;
  84	bool UseCreateProcess;
  85	WORD ShowMode;	
  86	NSISCH*strExec;
  87	NSISCH*strParams;
  88	NSISCH*strWorkDir;
  89	NSISCH*strVerb;
  90	NSISCH buf[ANYSIZE_ARRAY];
  91} IPC_SHEXEC;
  92
  93
  94typedef struct 
  95{
  96	HINSTANCE	hInstance;
  97	HWND		hSrvIPC;
  98	BYTE		DllRef;
  99	bool		UseIPC;
 100	bool		CheckedIPCParam;
 101	UINT NSISStrLen;
 102	//IPC Server stuff:
 103	HANDLE		hElevatedProcess;
 104	HANDLE		threadIPC;
 105	DWORD		LastWaitExitCode;//Exit code of process started from last call to ExecWait/ShellExecWait
 106	NSIS::extra_parameters*pXParams;
 107	bool		ElevateAgain;
 108	//DelayLoadedModules:
 109	HMODULE		hModAdvAPI;
 110} GLOBALS;
 111
 112
 113GLOBALS g = {0};
 114
 115
 116void StopIPCSrv();
 117DWORD _GetElevationType(TOKEN_ELEVATION_TYPE* pTokenElevType);
 118bool GetIPCSrvWndFromParams(HWND&hwndOut);
 119
 120
 121
 122#if _MSC_VER >= 1400 //MSVC 2005 wants to pull in the CRT, let's try to help it out
 123void* __cdecl memset(void*mem,int c,size_t len) 
 124{
 125	char *p=(char*)mem;
 126	while (len-- > 0){*p++=c;}
 127	return mem;
 128}
 129#endif

 130
 131
 132
 133FORCEINLINE NSISCH* GetIPCWndClass() { return _T("NSISUACIPC"); }
 134FORCEINLINE bool StrCmpI(LPCTSTR s1,LPCTSTR s2) {return 0==lstrcmpi(s1,s2);}
 135LPTSTR StrNextChar(LPCTSTR Str) { return CharNext(Str); }
 136bool StrContainsWhiteSpace(LPCTSTR s) { if (s) {while(*s && *s>_T(' '))s=StrNextChar(s);if (*s)return true;}return false; }
 137
 138DWORD GetSysVer(bool Major=true) 
 139{
 140	OSVERSIONINFO ovi = { sizeof(ovi) };
 141	if ( !GetVersionEx(&ovi) ) return 0;
 142	return Major ? ovi.dwMajorVersion : ovi.dwMinorVersion;
 143}
 144#define GetOSVerMaj() (GetSysVer(true))

 145#define GetOSVerMin() (GetSysVer(false))

 146
 147UINT_PTR StrToUInt(LPTSTR s,bool ForceHEX=false,BOOL*pFoundBadChar=0) 
 148{
 149	UINT_PTR v=0;
 150	BYTE base=ForceHEX?16:10;	
 151	if (pFoundBadChar)*pFoundBadChar=false;
 152	if ( !ForceHEX && *s=='0' && ((*(s=StrNextChar(s)))&~0x20)=='X' && (s=StrNextChar(s)) )base=16;
 153	for (TCHAR c=*s; c; c=*(s=StrNextChar(s)) ) 
 154	{
 155		if (c >= _T('0') && c <= _T('9')) c-='0';
 156		else if (base==16 && (c & ~0x20) >= 'A' && (c & ~0x20) <= 'F') c=(c & 7) +9;
 157		else 
 158		{
 159			if (pFoundBadChar /*&& c!=' '*/)*pFoundBadChar=true;
 160			break;
 161		}
 162		v*=base;v+=c;
 163	}
 164	return v;
 165}
 166
 167LPTSTR FindExePathEnd(LPTSTR p) 
 168{
 169	if ( *p=='"' && *(++p) ) 
 170	{
 171		while( *p && *p!='"' )p=StrNextChar(p);
 172		if (*p)
 173			p=StrNextChar(p);
 174		else 
 175			return 0;
 176	}
 177	else 
 178		if ( *p!='/' )while( *p && *p!=' ' )p=StrNextChar(p);
 179	return p;
 180}
 181
 182
 183#ifdef FEAT_MSRUNASDLGMODHACK

 184HHOOK g_MSRunAsHook;
 185void MSRunAsDlgMod_Unload(void*hook) 
 186{
 187	if (hook) 
 188	{
 189		//ASSERT(g_MSRunAsHook==hook);
 190		UnhookWindowsHookEx((HHOOK)hook);
 191		//g_MSRunAsHook=0;
 192	}
 193}
 194LRESULT CALLBACK MSRunAsDlgMod_ShellProc(int nCode,WPARAM wp,LPARAM lp) 
 195{
 196	CWPRETSTRUCT*pCWPS;
 197	if (nCode >= 0 && (pCWPS=(CWPRETSTRUCT*)lp) && WM_INITDIALOG==pCWPS->message)
 198	{
 199		TCHAR buf[30];
 200		GetClassName(pCWPS->hwnd,buf,COUNTOF(buf));
 201		if (!lstrcmpi(buf,_T("#32770"))) 
 202		{ 
 203			const UINT IDC_USRSAFER=0x106,IDC_OTHERUSER=0x104,IDC_SYSCRED=0x105;
 204			GetClassName(GetDlgItem(pCWPS->hwnd,IDC_SYSCRED),buf,COUNTOF(buf));
 205			if (!lstrcmpi(buf,_T("SysCredential"))) //make sure this is the run as dialog
 206			{
 207				MySndDlgItemMsg(pCWPS->hwnd,IDC_USRSAFER,BM_SETCHECK,BST_UNCHECKED);
 208				MySndDlgItemMsg(pCWPS->hwnd,IDC_OTHERUSER,BM_CLICK);
 209			}
 210		}
 211	}
 212	return CallNextHookEx(g_MSRunAsHook,nCode,wp,lp);
 213}
 214void* MSRunAsDlgMod_Init() 
 215{
 216	if(GetOSVerMaj()!=5 || GetOSVerMin()<1)return NULL;//only XP/2003
 217	return g_MSRunAsHook=SetWindowsHookEx(WH_CALLWNDPROCRET,MSRunAsDlgMod_ShellProc,0,GetCurrentThreadId());
 218}
 219#endif

 220
 221DWORD DllSelfAddRef() 
 222{
 223	NSISCH buf[MAX_PATH*5];//Lets hope $pluginsdir is shorter than this, only special builds could break this
 224	DWORD len=GetModuleFileName(g.hInstance,buf,MAX_PATH*5);
 225	if ( len && len<MAX_PATH*5 && LoadLibrary(buf) ) 
 226	{
 227		if (!g.DllRef)g.DllRef++;
 228		return NO_ERROR;
 229	}
 230	ASSERT(!"DllSelfAddRef failed!");
 231	return ERROR_BUFFER_OVERFLOW;
 232}
 233
 234FORCEINLINE DWORD MaintainDllSelfRef() //Call this from every exported function to prevent NSIS from unloading our plugin
 235{ 
 236	if(!g.CheckedIPCParam && !g.DllRef) 
 237	{
 238		HWND hSrv;
 239		g.CheckedIPCParam=true;
 240		g.UseIPC=GetIPCSrvWndFromParams(hSrv);
 241		if(g.UseIPC) 
 242		{
 243			g.DllRef++;
 244			g.hSrvIPC=hSrv;
 245		}
 246	}
 247	return (g.DllRef)?DllSelfAddRef():NO_ERROR;
 248}
 249
 250
 251DWORD SendIPCMsg(UINT Msg,WPARAM wp,LPARAM lp,DWORD_PTR&MsgRet,DWORD tout=IPCTOUT_DEF,const HWND hIPCSrv=g.hSrvIPC) 
 252{
 253	if (tout==INFINITE) //BUGFIX: SendMessageTimeout(...,INFINITE,...) seems to be broken, SendMessageTimeout(...,SMTO_NORMAL,0,..) seems to work but why take the chance
 254	{
 255		MsgRet=SendMessage(hIPCSrv,Msg,wp,lp);
 256		return NO_ERROR;
 257	}
 258	if ( SendMessageTimeout(hIPCSrv,Msg,wp,lp,SMTO_NORMAL,tout,&MsgRet) )return NO_ERROR;
 259	return (tout=GetLastError()) ? tout : ERROR_TIMEOUT; 
 260}
 261
 262void _Unload() 
 263{
 264	StopIPCSrv();
 265	if (g.DllRef) 
 266	{
 267		g.DllRef=0;
 268		FreeLibrary(g.hInstance);
 269		//Why bother?> FreeLibrary(g.hModAdvAPI);
 270	}
 271}
 272
 273DWORD DelayLoadGetProcAddr(void**ppProc,HMODULE hLib,LPCSTR Export) 
 274{
 275	ASSERT(ppProc && hLib && Export);
 276	if (!*ppProc) 
 277	{
 278		*ppProc=GetProcAddress(hLib,Export);
 279		if (!*ppProc)return GetLastError();
 280	}
 281	return NO_ERROR;
 282}
 283
 284DWORD DelayLoadDlls() 
 285{
 286
 287#ifdef UNICODE

 288#	define __DLD_FUNCSUFFIX "W"

 289#	else

 290#	define __DLD_FUNCSUFFIX "A"

 291#	endif

 292
 293	if (!g.hModAdvAPI) //using g.hModAdvAPI to test if this is the first time we have been called
 294	{
 295		struct 
 296		{
 297			HMODULE*pMod;
 298			LPCSTR DllName;//NOTE: Always using ANSI strings to save a couple of bytes
 299		} 
 300		dld[]=
 301		{ 
 302			{&g.hModAdvAPI,"AdvAPI32"},
 303			{0}
 304		};
 305		DWORD ec;
 306		UINT o;
 307
 308		for (o=0; dld[o].pMod; ++o)
 309			if ( !(*dld[o].pMod=LoadLibraryA(dld[o].DllName)) )
 310				return GetLastError();
 311
 312		struct 
 313		{
 314			HMODULE hMod;
 315			void**ppProc;
 316			LPCSTR Export;
 317			bool Optional;
 318		} 
 319		dldprocs[]=
 320		{
 321			{GetModuleHandle(_T("USER32")),(void**)&_AllowSetForegroundWindow,"AllowSetForegroundWindow",true},
 322			{g.hModAdvAPI,(void**)&_OpenProcessToken,			"OpenProcessToken"},
 323			{g.hModAdvAPI,(void**)&_OpenThreadToken,			"OpenThreadToken"},
 324			{g.hModAdvAPI,(void**)&_GetTokenInformation,		"GetTokenInformation"},
 325			{g.hModAdvAPI,(void**)&_AllocateAndInitializeSid,	"AllocateAndInitializeSid"},
 326			{g.hModAdvAPI,(void**)&_FreeSid,					"FreeSid"},
 327			{g.hModAdvAPI,(void**)&_EqualSid,					"EqualSid"},
 328			{g.hModAdvAPI,(void**)&_CheckTokenMembership,		"CheckTokenMembership",true},
 329			#ifdef FEAT_CUSTOMRUNASDLG
 330			{g.hModAdvAPI,(void**)&_GetUserName,			"GetUserName" __DLD_FUNCSUFFIX},
 331			{g.hModAdvAPI,(void**)&_CreateProcessWithLogonW,"CreateProcessWithLogonW",true},
 332			{LoadLibraryA("SECUR32"),(void**)&_GetUserNameEx,"GetUserNameEx" __DLD_FUNCSUFFIX,true},//We never free this library
 333			#endif

 334			{0}
 335		};
 336//#undef __DLD_FUNCSUFFIX
 337		for (o=0; dldprocs[o].hMod; ++o)
 338			if (ec=DelayLoadGetProcAddr(dldprocs[o].ppProc,dldprocs[o].hMod,dldprocs[o].Export) && !dldprocs[o].Optional) 
 339			{
 340				TRACEF("DelayLoadDlls failed to find %s in %X\n",dldprocs[o].Export,dldprocs[o].hMod);
 341				return ec;
 342			}
 343	}
 344	return NO_ERROR;
 345}
 346
 347void AllowOuterInstanceWindowFocus() 
 348{
 349	if (g.UseIPC) 
 350	{
 351		DWORD_PTR MsgRet;
 352		if (!SendIPCMsg(IPC_GETSRVPID,0,0,MsgRet,IPCTOUT_SHORT) && MsgRet && _AllowSetForegroundWindow)_AllowSetForegroundWindow(MsgRet);
 353	}
 354}
 355
 356DWORD SyncVars(HWND hwndNSIS) 
 357{
 358	DWORD i,ec=NO_ERROR;
 359	IPC_SYNCVAR*pSV=0;
 360	if (!g.UseIPC)return NO_ERROR;
 361	g.NSISStrLen=NSIS::StrSize;
 362	TRACEF("SyncVars: g.NSISStrLen=%d\n",g.NSISStrLen);ASSERT(g.NSISStrLen>10);
 363	DWORD cbStruct=FIELD_OFFSET(IPC_SYNCVAR,buf[g.NSISStrLen+1]);
 364	pSV=(IPC_SYNCVAR*)MemAlloc(cbStruct);
 365	if (!pSV)
 366		goto die_GLE;
 367	else 
 368	{
 369		COPYDATASTRUCT cds={CDI_SYNCVAR,cbStruct,pSV};
 370		for (i=0;i<__INST_LAST && !ec;++i) 
 371		{
 372			pSV->VarId=i;
 373			lstrcpyn(pSV->buf,GetVar(i),g.NSISStrLen);
 374			DWORD MsgRet;//TRACEF("SyncVars: (%d)%s|\n",i,pSV->buf);
 375			if (!(ec=SendIPCMsg(WM_COPYDATA,(WPARAM)hwndNSIS,(LPARAM)&cds,MsgRet,3000 )))ec=MsgRet-1;
 376		}	
 377	}
 378	return ec;
 379die_GLE:
 380	return GetLastError();
 381}
 382
 383DWORD _Exec(HWND hwnd,NSISCH*Verb,NSISCH*Exec,NSISCH*Params,NSISCH*WorkDir,WORD ShowWnd,bool Wait,bool UseCreateProcess) 
 384{
 385	DWORD ec;
 386	NSISCH*buf=0;
 387	SHELLEXECUTEINFO sei={sizeof(SHELLEXECUTEINFO)};
 388	sei.hwnd		=hwnd;
 389	sei.nShow		=(ShowWnd!=SW_INVALID)?ShowWnd:SW_NORMAL;
 390	sei.fMask		=SEE_MASK_FLAG_DDEWAIT;
 391	sei.lpFile		=(Exec&&*Exec)			?Exec:0;
 392	sei.lpParameters=(Params&&*Params)		?Params:0;
 393	sei.lpDirectory	=(WorkDir&&*WorkDir)	?WorkDir:0;
 394	sei.lpVerb		=(Verb&&*Verb)			?Verb:0;
 395	TRACEF("_Exec:%X|%s|%s|%s|wait=%d useCreateProc=%d ShowWnd=%d useShowWnd=%d\n",hwnd,Exec,Params,WorkDir,Wait,UseCreateProcess,ShowWnd,ShowWnd!=SW_INVALID);
 396	if (UseCreateProcess) 
 397	{
 398		STARTUPINFO si={sizeof(STARTUPINFO)};
 399		if (ShowWnd != SW_INVALID) 
 400		{
 401			si.dwFlags|=STARTF_USESHOWWINDOW;
 402			si.wShowWindow=sei.nShow;
 403		}
 404		PROCESS_INFORMATION pi;
 405		const NSISCH*Q=( (*Exec!='"') && (*Params) && StrContainsWhiteSpace(Exec)) ? _T("\"") : _T("");//Add extra quotes to program part of command-line?
 406		const DWORD len= ((*Q)?2:0) + lstrlen(Exec) + 1 + lstrlen(Params) + 1;
 407		buf=(NSISCH*)NSIS::MemAlloc(len*sizeof(NSISCH));
 408		if (!buf)return ERROR_OUTOFMEMORY;
 409		//Build string for CreateProcess, "[Q]<Exec>[Q][Space]<Params>"
 410		wsprintf(buf,_T("%s%s%s%s%s"),Q,Exec,Q,((*Params)?_T(" "):_T("")),Params);
 411		TRACEF("_Exec: calling CreateProcess>%s< in >%s< addedQ=%d show=%u\n",buf,sei.lpDirectory,*Q,sei.nShow);
 412		if ( !CreateProcess(0,buf,0,0,false,0,0,sei.lpDirectory,&si,&pi) ) goto die_GLE;
 413		CloseHandle(pi.hThread);
 414		sei.hProcess=pi.hProcess;
 415	}
 416	else 
 417	{
 418		sei.fMask|=SEE_MASK_NOCLOSEPROCESS;
 419		TRACEF("_Exec: calling ShellExecuteEx...\n");
 420		if ( !ShellExecuteEx(&sei) )goto die_GLE;
 421	}
 422	if (Wait) 
 423	{
 424		WaitForSingleObject(sei.hProcess,INFINITE);
 425		GetExitCodeProcess(sei.hProcess,&g.LastWaitExitCode);
 426	}
 427	else WaitForInputIdle(sei.hProcess,1500);//wait a little bit so the finish page window does not go away too fast and cause focus problems
 428
 429	CloseHandle(sei.hProcess);
 430	ec=NO_ERROR;
 431ret:
 432	if (buf)NSIS::MemFree(buf);
 433	return ec;
 434die_GLE:
 435	ec=GetLastError();
 436	TRACEF("_Exec>%s failed with error %u (%s)\n",UseCreateProcess?"CreateProcess":"ShExec",ec,buf);
 437	goto ret;
 438}
 439
 440WORD GetShowWndCmdFromStr(NSISCH*s) 
 441{
 442	//NOTE: Little used modes are still supported, just not with strings, you must use the actual number or ${SW_xx} defines from WinMessages.h
 443	struct {NSISCH*id;WORD cmd;} swcm[] = {
 444		{_T("SW_HIDE"),				SW_HIDE},
 445		{_T("SW_SHOW"),				SW_SHOW},
 446		{_T("SW_RESTORE"),			SW_RESTORE},
 447		{_T("SW_MAXIMIZE"),			SW_MAXIMIZE},
 448		{_T("SW_MINIMIZE"),			SW_MINIMIZE},
 449		//	{_T("SW_MAX"),			SW_MAXIMIZE},
 450		//	{_T("SW_MIN"),			SW_MINIMIZE},
 451		{_T("SW_SHOWNORMAL"),		SW_SHOWNORMAL},
 452		//{_T("SW_NORMAL"),			SW_NORMAL},
 453		//{_T("SW_SHOWMINIMIZED"),	SW_SHOWMINIMIZED},
 454		//{_T("SW_SHOWMAXIMIZED"),	SW_SHOWMAXIMIZED},
 455		//{_T("SW_SHOWNOACTIVATE"),	SW_SHOWNOACTIVATE},
 456		//{_T("SW_SHOWNA"),			SW_SHOWNA},
 457		//{_T("SW_SHOWMINNOACTIVE"),	SW_SHOWMINNOACTIVE},
 458		//{_T("SW_SHOWDEFAULT"),		SW_SHOWDEFAULT},
 459		//{_T("SW_FORCEMINIMIZE"),	SW_FORCEMINIMIZE},
 460		{0}
 461	};
 462	for (int i=0; swcm[i].id; ++i) if (StrCmpI(s,swcm[i].id)) return swcm[i].cmd;
 463	return SW_INVALID;
 464}
 465
 466#define HasIPCServer() (g.UseIPC!=NULL)

 467
 468void HandleExecExport(bool CreateProc,bool Wait,HWND&hwndNSIS,int&StrSize,NSISCH*&Vars,stack_t**&StackTop,NSIS::extra_parameters*pXParams) 
 469{
 470	DWORD ec=NO_ERROR,ForkExitCode=ERROR_INVALID_FUNCTION;
 471	UINT cch=0,cbStruct;
 472	WORD ShowWnd;
 473	IPC_SHEXEC*pISE=0;
 474	stack_t* const pSIVerb=CreateProc?0:StackPop();//Only ShellExec supports verb's
 475	stack_t* const pSIShowWnd	=StackPop();
 476	stack_t* const pSIExec		=StackPop();
 477	stack_t* const pSIParams	=StackPop();
 478	stack_t* const pSIWorkDir	=StackPop();
 479
 480	if (ec=MaintainDllSelfRef())goto ret;
 481	if (!pSIExec || !pSIParams || !pSIWorkDir || !pSIShowWnd || (!pSIVerb && !CreateProc)) 
 482	{
 483		TRACE("If you see this you probably forgot that all parameters are required!\n");
 484		ec=ERROR_INVALID_PARAMETER;
 485		goto ret;
 486	}
 487	ShowWnd=GetShowWndCmdFromStr(pSIShowWnd->text);
 488	if (ShowWnd==SW_INVALID && *pSIShowWnd->text) 
 489	{
 490		BOOL BadCh;
 491		ShowWnd=StrToUInt(pSIShowWnd->text,false,&BadCh);
 492		if (BadCh)ShowWnd=SW_INVALID;
 493	}
 494	TRACEF("HandleExecExport: ipc=%X (%X)\n",g.UseIPC,g.hSrvIPC);
 495	SyncVars(hwndNSIS);
 496	if (!g.UseIPC) //No IPC Server, we are not elevated with UAC
 497	{
 498		ec=_Exec(hwndNSIS,pSIVerb?pSIVerb->text:0,pSIExec->text,pSIParams->text,pSIWorkDir->text,ShowWnd,Wait,CreateProc);
 499		if (Wait)ForkExitCode=g.LastWaitExitCode;
 500		goto ret;
 501	}	
 502	cch+=lstrlen(pSIExec->text)+1;
 503	cch+=lstrlen(pSIParams->text)+1;
 504	cch+=lstrlen(pSIWorkDir->text)+1;
 505	if (pSIVerb)cch+=lstrlen(pSIVerb->text)+1;
 506	cbStruct=FIELD_OFFSET( IPC_SHEXEC, buf[cch*sizeof(TCHAR)] );
 507	pISE=(IPC_SHEXEC*)NSIS::MemAlloc(cbStruct);
 508	if (!pISE)ec=GetLastError();
 509	if (!ec) 
 510	{
 511		DWORD_PTR MsgRet;
 512		pISE->hwnd		=hwndNSIS;
 513		pISE->Wait		=Wait;
 514		pISE->ShowMode	=ShowWnd;
 515		pISE->UseCreateProcess=CreateProc;
 516		//Just offsets at this point
 517		pISE->strExec	=(NSISCH*)0;
 518		pISE->strParams	=(NSISCH*)(lstrlen(pSIExec->text)	+pISE->strExec+1);
 519		pISE->strWorkDir=(NSISCH*)(lstrlen(pSIParams->text)	+pISE->strParams+1);
 520		pISE->strVerb=	 (NSISCH*)(lstrlen(pSIWorkDir->text)+pISE->strWorkDir+1);
 521		lstrcpy(pISE->buf,pSIExec->text);
 522		lstrcpy(&pISE->buf[(DWORD_PTR)pISE->strParams],	pSIParams->text);
 523		lstrcpy(&pISE->buf[(DWORD_PTR)pISE->strWorkDir],pSIWorkDir->text);
 524		if (pSIVerb)lstrcpy(&pISE->buf[(DWORD_PTR)pISE->strVerb],	pSIVerb->text);
 525		COPYDATASTRUCT cds;
 526		cds.dwData=CDI_SHEXEC;
 527		cds.cbData=cbStruct;
 528		cds.lpData=pISE;
 529		AllowOuterInstanceWindowFocus();
 530		if (!(ec=SendIPCMsg(WM_COPYDATA,(WPARAM)hwndNSIS,(LPARAM)&cds,MsgRet,Wait?(INFINITE):(IPCTOUT_LONG) )))ec=MsgRet-1;
 531		TRACEF("HandleExecExport: IPC returned %X, ec=%d\n",MsgRet,ec);
 532		if (Wait && NO_ERROR==ec) 
 533		{
 534			ec=SendIPCMsg(IPC_GETEXITCODE,0,0,ForkExitCode);
 535			TRACEF("HandleExecExport(Wait): Spawned process exit code=%d",ForkExitCode);
 536		}
 537	}
 538ret:
 539	NSIS::MemFree(pISE);
 540	StackFreeItem(pSIShowWnd);
 541	StackFreeItem(pSIExec);
 542	StackFreeItem(pSIParams);
 543	StackFreeItem(pSIWorkDir);
 544	StackFreeItem(pSIVerb);
 545	SetVarUINT(INST_0,ec);
 546	if (ec)SetErrorFlag(pXParams);
 547	if (Wait)SetVarUINT(INST_1,ForkExitCode);
 548}
 549
 550
 551bool _SupportsUAC(bool VersionTestOnly=false) 
 552{
 553	TOKEN_ELEVATION_TYPE tet;
 554	OSVERSIONINFO ovi={sizeof(ovi)};
 555	if (!GetVersionEx(&ovi)) 
 556	{
 557		ASSERT(!"_SupportsUAC>GetVersionEx");
 558		return false;
 559	}
 560	if (VersionTestOnly)return ovi.dwMajorVersion>=6;
 561	if (ovi.dwMajorVersion>=6 && _GetElevationType(&tet)==NO_ERROR) 
 562	{
 563		const bool ret=tet!=TokenElevationTypeDefault && tet!=NULL;
 564		TRACEF("_SupportsUAC tet=%d, returning %d\n",tet,ret);
 565		return ret;
 566	}
 567	DBGONLY(TRACEF("_SupportsUAC returning false! ver=%d _GetElevationType.ret=%u\n",ovi.dwMajorVersion,_GetElevationType(&tet)));
 568	return false;
 569}
 570
 571DWORD _GetElevationType(TOKEN_ELEVATION_TYPE*pTokenElevType) 
 572{
 573	DWORD ec=ERROR_ACCESS_DENIED;
 574	HANDLE hToken=0;
 575	DWORD RetLen;
 576	if (!pTokenElevType)return ERROR_INVALID_PARAMETER;
 577	if (ec=DelayLoadDlls())return ec;
 578	*pTokenElevType=(TOKEN_ELEVATION_TYPE)NULL;
 579	if (!_SupportsUAC(true))return NO_ERROR;
 580	if (!_OpenProcessToken(GetCurrentProcess(),TOKEN_QUERY,&hToken))goto dieLastErr;
 581	if (!_GetTokenInformation(hToken,(_TOKEN_INFORMATION_CLASS)TokenElevationType,pTokenElevType,sizeof(TOKEN_ELEVATION_TYPE),&RetLen))goto dieLastErr;
 582	SetLastError(NO_ERROR);
 583dieLastErr:
 584	ec=GetLastError();
 585	CloseHandle(hToken);
 586	TRACEF("_GetElevationType ec=%u type=%d\n",ec,*pTokenElevType);
 587	return ec;
 588}
 589
 590
 591bool _IsUACEnabled() 
 592{
 593	HKEY hKey;
 594	bool ret=false;
 595	if (GetSysVer()>=6 && NO_ERROR==RegOpenKeyEx(HKEY_LOCAL_MACHINE,_T("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System"),0,KEY_READ,&hKey)) 
 596	{
 597		//Check must be !=0, see http://codereview.chromium.org/3110 & http://src.chromium.org/viewvc/chrome?view=rev&revision=2330
 598		//Apparently, Vista treats !=0 as UAC on, and some machines have EnableLUA=2 !!
 599		DWORD val,type,size=sizeof(DWORD);
 600		if (NO_ERROR==RegQueryValueEx(hKey,_T("EnableLUA"),0,&type,(LPBYTE)&val,&size) && type==REG_DWORD && val!=0) ret=true;
 601		RegCloseKey(hKey);
 602	}
 603	return ret;
 604}
 605
 606bool SysQuery_IsServiceRunning(LPCTSTR servicename) 
 607{
 608	bool retval=false;
 609	SC_HANDLE scdbh=NULL,hsvc;
 610	scdbh=OpenSCManager(NULL,NULL,GENERIC_READ);
 611	if (scdbh) 
 612	{
 613		if (hsvc=OpenService(scdbh,servicename,SERVICE_QUERY_STATUS)) 
 614		{
 615			SERVICE_STATUS ss;
 616			if (QueryServiceStatus(hsvc,&ss))retval=(ss.dwCurrentState==SERVICE_RUNNING);
 617			CloseServiceHandle(hsvc);
 618		}
 619	}
 620	CloseServiceHandle(scdbh);
 621	return retval;
 622}
 623
 624inline bool SysNT5IsSecondaryLogonSvcRunning() 
 625{
 626	return SysQuery_IsServiceRunning(_T("seclogon"));
 627}
 628
 629bool SysElevationPresent() //Will return false on Vista if UAC is off
 630{ 
 631	const DWORD vmaj=GetSysVer();
 632	ASSERT(vmaj<=6 && vmaj>=4);
 633	if (vmaj==5) return true; //TODO:Check if secondary logon service is running?
 634	if (vmaj>=6) return _IsUACEnabled();
 635	return false;
 636}
 637
 638FORCEINLINE bool SysSupportsRunAs() 
 639{ 
 640	return GetSysVer()>=5;
 641}
 642
 643
 644
 645
 646
 647bool _IsAdmin() 
 648{
 649	
 650#ifdef BUILD_XPTEST

 651	static int _dbgOld=-1;
 652	unsigned _dbg=(unsigned)FindExePathEnd(GetCommandLine());
 653	if (_dbgOld==-1){_dbg=(_dbg && *((TCHAR*)_dbg))?MessageBoxA(0,"Debug: Pretend to be admin?",GetCommandLine(),MB_YESNOCANCEL):IDCANCEL;} else _dbg=_dbgOld;_dbgOld=_dbg;TRACEF("_IsAdmin=%d|%d\n",_dbg,_dbg==IDYES);
 654	if (_dbg!=IDCANCEL){SetLastError(0);return _dbg==IDYES;}
 655#endif

 656
 657	BOOL isAdmin=false;
 658	DWORD ec;
 659	OSVERSIONINFO ovi={sizeof(ovi)};	
 660	if (!GetVersionEx(&ovi))return false;
 661	if (VER_PLATFORM_WIN32_NT != ovi.dwPlatformId) //Not NT
 662	{
 663		SetLastError(NO_ERROR);
 664		return true;
 665	}
 666	if (ec=DelayLoadDlls()) 
 667	{
 668		TRACEF("DelayLoadDlls failed in _IsAdmin() with err x%X\n",ec);
 669		SetLastError(ec);
 670		return false;
 671	}
 672
 673	ASSERT(_OpenThreadToken && _OpenProcessToken && _AllocateAndInitializeSid && _EqualSid && _FreeSid);
 674	HANDLE hToken;
 675	if (_OpenThreadToken(GetCurrentThread(),TOKEN_QUERY,FALSE,&hToken) || _OpenProcessToken(GetCurrentProcess(),TOKEN_QUERY,&hToken)) 
 676	{
 677		SID_IDENTIFIER_AUTHORITY SystemSidAuthority = SECURITY_NT_AUTHORITY;
 678		PSID psid=0;
 679		if (_AllocateAndInitializeSid(&SystemSidAuthority,2,SECURITY_BUILTIN_DOMAIN_RID,DOMAIN_ALIAS_RID_ADMINS,0,0,0,0,0,0,&psid)) 
 680		{
 681			if (_CheckTokenMembership) 
 682			{
 683				if (!_CheckTokenMembership(0,psid,&isAdmin))isAdmin=false;
 684			}
 685			else 
 686			{
 687				DWORD cbTokenGrps;
 688				if (!_GetTokenInformation(hToken,TokenGroups,0,0,&cbTokenGrps)&&GetLastError()==ERROR_INSUFFICIENT_BUFFER) 
 689				{
 690					TOKEN_GROUPS*ptg=0;
 691					if (ptg=(TOKEN_GROUPS*)NSIS::MemAlloc(cbTokenGrps)) 
 692					{
 693						if (_GetTokenInformation(hToken,TokenGroups,ptg,cbTokenGrps,&cbTokenGrps)) 
 694						{
 695							for (UINT i=0; i<ptg->GroupCount;i++) 
 696							{								
 697								if (_EqualSid(ptg->Groups[i].Sid,psid))isAdmin=true;
 698							}
 699						}
 700						NSIS::MemFree(ptg);
 701					}
 702				}
 703			}			
 704			_FreeSid(psid);
 705		}
 706		CloseHandle(hToken);
 707	}
 708	if (isAdmin) //UAC Admin with split token check
 709	{
 710		if (_SupportsUAC()) 
 711		{
 712			TOKEN_ELEVATION_TYPE tet;
 713			if (_GetElevationType(&tet) || tet==TokenElevationTypeLimited)isAdmin=false;
 714		}
 715		else SetLastError(NO_ERROR);
 716	}
 717	return FALSE != isAdmin;
 718}
 719
 720
 721LRESULT CALLBACK IPCSrvWndProc(HWND hwnd,UINT uMsg,WPARAM wp,LPARAM lp) 
 722{
 723	switch(uMsg) 
 724	{
 725	case WM_DESTROY:
 726		PostQuitMessage(0);
 727		break;
 728	case WM_CLOSE:
 729		return DestroyWindow(hwnd);
 730	case WM_COPYDATA:
 731		if (lp) 
 732		{
 733			const COPYDATASTRUCT*pCDS=(COPYDATASTRUCT*)lp;
 734			if (pCDS->dwData==CDI_SHEXEC && pCDS->lpData) 
 735			{
 736				if ( pCDS->cbData < sizeof(IPC_SHEXEC) )break;
 737				g.LastWaitExitCode=ERROR_INVALID_FUNCTION;
 738				IPC_SHEXEC& ise=*((IPC_SHEXEC*)pCDS->lpData);
 739				SetForegroundWindow(ise.hwnd);				
 740				DWORD ec=_Exec(
 741					ise.hwnd,
 742					&ise.buf[(DWORD_PTR)ise.strVerb],
 743					&ise.buf[(DWORD_PTR)ise.strExec],
 744					&ise.buf[(DWORD_PTR)ise.strParams],
 745					&ise.buf[(DWORD_PTR)ise.strWorkDir],
 746					ise.ShowMode,ise.Wait,ise.UseCreateProcess
 747					);
 748				TRACEF("IPCSrvWndProc>IPC_SHEXEC>_ShExec=%d\n",ec);
 749				return ec+1;
 750			}
 751			else if (pCDS->dwData==CDI_SYNCVAR && pCDS->lpData && pCDS->cbData>1) 
 752			{
 753				IPC_SYNCVAR*pSV=(IPC_SYNCVAR*)pCDS->lpData;
 754				if (pSV->VarId>=__INST_LAST)return 1+ERROR_INVALID_PARAMETER;
 755				TRACEF("WM_COPYDATA: CDI_SYNCVAR:%d=%s|\n",pSV->VarId,&pSV->buf[0]);
 756				lstrcpy(GetVar(pSV->VarId),&pSV->buf[0]);
 757				return NO_ERROR+1;
 758			}
 759			else if (pCDS->dwData==CDI_STACKPUSH && pCDS->lpData && pCDS->cbData>=1) 
 760			{
 761				TRACEF("WM_COPYDATA: CDI_STACKPUSH:%s|\n",pCDS->lpData);
 762				return StackPush((NSISCH*)pCDS->lpData)+1;
 763			}
 764		}
 765		break;
 766	case IPC_GETEXITCODE:
 767		return g.LastWaitExitCode;
 768	case IPC_ELEVATEAGAIN:
 769		TRACE("IPCSrvWndProc>IPC_ELEVATEAGAIN\n");
 770		return (g.ElevateAgain=true);
 771	case IPC_GETSRVPID:
 772		return GetCurrentProcessId();
 773	case IPC_GETSRVHWND:
 774		return GetWindowLongPtr(hwnd,GWLP_USERDATA);
 775	case IPC_EXECCODESEGMENT:
 776		return 1+(g.pXParams ? ExecuteCodeSegment(g.pXParams,wp,(HWND)lp) : ERROR_INVALID_FUNCTION);
 777	case IPC_GETOUTERPROCESSTOKEN:
 778		if (_OpenProcessToken)
 779		{
 780			HANDLE hToken,hOutToken;
 781			if (_OpenProcessToken(GetCurrentProcess(),TOKEN_ALL_ACCESS,&hToken)) 
 782			{
 783				TRACEF("IPC_GETOUTERPROCESSTOKEN: hToken=%X targetProcess=%X\n",hToken,g.hElevatedProcess);
 784				if (DuplicateHandle(GetCurrentProcess(),hToken,g.hElevatedProcess,&hOutToken,lp,false,DUPLICATE_CLOSE_SOURCE))
 785				{
 786					TRACEF("IPC_GETOUTERPROCESSTOKEN: %X(%X) > %X(%X)\n",hToken,-1,hOutToken,g.hElevatedProcess);
 787					return (LRESULT)hOutToken;
 788				}
 789			}
 790		}
 791		return NULL;
 792
 793#ifdef UAC_HACK_FGWND1

 794	case IPC_HACKFINDRUNAS: //super ugly hack to get title of run as dialog
 795		if (wp<200) 
 796		{
 797			HWND hRA=GetLastActivePopup((HWND)lp);
 798			TRACEF("IPC_HACKFINDRUNAS:%d %X %X\n",wp,lp,hRA);
 799			if (hRA && hRA !=(HWND)lp ) return PostMessage((HWND)lp,WM_APP,0,(LONG_PTR)hRA);
 800			Sleep(10);PostMessage(hwnd,uMsg,wp+1,lp);
 801		}
 802		break;
 803#endif

 804	}
 805	return DefWindowProc(hwnd,uMsg,wp,lp);
 806}
 807
 808
 809DWORD WINAPI IPCSrvThread(LPVOID lpParameter) 
 810{
 811	CoInitialize(0);
 812	const DWORD WStyle=WS_VISIBLE DBGONLY(|(WS_CAPTION));
 813	const int PosOffset=32700;
 814	MSG msg;
 815	WNDCLASS wc={0};
 816	wc.lpszClassName=GetIPCWndClass();
 817	wc.lpfnWndProc=IPCSrvWndProc;
 818	wc.hInstance=g.hInstance;
 819	if (!RegisterClass(&wc))goto dieLastErr;	
 820	if (!(g.hSrvIPC=CreateWindowEx(WS_EX_TOOLWINDOW DBGONLY(&~WS_EX_TOOLWINDOW),
 821		GetIPCWndClass(),
 822		DBGONLY(_T("Debug: NSIS.UAC")+)0,
 823		WStyle,
 824		-PosOffset DBGONLY(+PosOffset),-PosOffset DBGONLY(+PosOffset),DBGONLY(150+)1,DBGONLY(10+)1,
 825		0,0,wc.hInstance,0
 826		)))goto dieLastErr;		
 827	SetWindowLongPtr(g.hSrvIPC,GWLP_USERDATA,(LONG_PTR)lpParameter);
 828	TRACEF("IPCSrv=%X server created...\n",g.hSrvIPC);
 829	while (GetMessage(&msg,0,0,0)>0)DispatchMessage(&msg);
 830	SetLastError(NO_ERROR);
 831dieLastErr:
 832	CoUninitialize();
 833	return g.LastWaitExitCode=GetLastError();
 834}
 835
 836DWORD InitIPC(HWND hwndNSIS,NSIS::extra_parameters*pXParams,UINT NSISStrLen) 
 837{
 838	if (g.threadIPC)return NO_ERROR;
 839	TRACEF("InitIPC StrSize=%u vs %u\n",NSIS::StrSize,NSISStrLen);
 840	DWORD tid;
 841	ASSERT(!g.pXParams && pXParams);
 842	ASSERT(NSISStrLen>0 && NSISStrLen==NSIS::StrSize);
 843	g.NSISStrLen=NSISStrLen;
 844	g.pXParams=pXParams;
 845	g.threadIPC=CreateThread(0,0,IPCSrvThread,hwndNSIS,0,&tid);
 846	if (g.threadIPC) 
 847	{
 848		while(!g.hSrvIPC && !g.LastWaitExitCode)Sleep(20);
 849		return g.hSrvIPC ? NO_ERROR : g.LastWaitExitCode;
 850	}
 851	return GetLastError();
 852}
 853
 854void StopIPCSrv() 
 855{
 856	if (g.threadIPC) 
 857	{
 858		TRACEF("StopIPCSrv h=%X \n",g.hSrvIPC);
 859#ifdef UAC_HACK_Clammerz

 860		if ( GetSysVer()>=5 )
 861		{
 862			//WINBUGFIX: This ugly thing supposedly solves the problem of messagebox'es in .OnInit appearing behind other windows in Vista
 863			HWND hack=CreateWindowEx(WS_EX_TRANSPARENT|WS_EX_LAYERED,_T("Button"),NULL,NULL,0,0,0,0,NULL,NULL,NULL,0);
 864			ShowWindow(hack,SW_SHOW);
 865			SetForegroundWindow(hack);
 866			DestroyWindow(hack);		
 867		}
 868#endif

 869		PostMessage(g.hSrvIPC,WM_CLOSE,0,0);
 870		WaitForSingleObject(g.threadIPC,INFINITE);
 871		CloseHandle(g.threadIPC);
 872		UnregisterClass(GetIPCWndClass(),g.hInstance);//DLL can be loaded more than once, so make sure RegisterClass doesn't fail
 873		g.hSrvIPC=0;
 874		g.threadIPC=0;
 875	}
 876}
 877
 878#ifdef UAC_HACK_FGWND1 

 879LRESULT CALLBACK HackWndSubProc(HWND hwnd,UINT Msg,WPARAM wp,LPARAM lp) 
 880{
 881	switch(Msg) 
 882	{
 883	case WM_APP:
 884		GetWindowText((HWND)lp,GetVar(0),NSIS::StrSize);
 885		if (*GetVar(0))SendMessage(hwnd,WM_SETTEXT,0,(LONG_PTR)GetVar(0));
 886		break;
 887	}
 888	return DefWindowProc(hwnd,Msg,wp,lp);
 889}
 890#endif

 891
 892inline bool MustUseInternalRunAs() 
 893{ 
 894#ifdef BUILD_DBGSELECTELVMODE

 895	TCHAR dbgb[MAX_PATH*4];wsprintf(dbgb,_T("%s.ini"),GetVar(VIDX_EXEPATH));
 896	static int dbg_answer=GetPrivateProfileInt(_T("UACDBG"),_T("MustUseInternalRunAs"),2,dbgb);
 897	if (dbg_answer<2)return !!dbg_answer;WritePrivateProfileString(_T("UACDBG"),_T("MustUseInternalRunAs"),"",dbgb);
 898	if (MessageBox(GetActiveWindow(),"MustUseInternalRunAs?",dbgb,MB_YESNO)==IDYES)return true;
 899#endif

 900	return GetSysVer()>=6 && !SysElevationPresent(); 
 901}
 902
 903DWORD ForkSelf(HWND hParent,DWORD&ForkExitCode,NSIS::extra_parameters*pXParams,UINT NSISStrLen) 
 904{
 905	DWORD ec=ERROR_ACCESS_DENIED;
 906	SHELLEXECUTEINFO sei={sizeof(sei)};
 907	//STARTUPINFO startInfo={sizeof(STARTUPINFO)};
 908	LPTSTR pszExePathBuf=0;
 909	LPTSTR pszParamBuf=0;
 910	LPTSTR p,pCL=GetCommandLine();
 911	UINT len;
 912	const OSVerMaj=GetOSVerMaj();
 913#ifdef UAC_HACK_FGWND1

 914	HWND hHack=0;
 915#endif

 916	ASSERT(pXParams);
 917	
 918	//GetStartupInfo(&startInfo);
 919	if (ec=InitIPC(hParent,pXParams,NSISStrLen))goto ret;
 920	ASSERT(IsWindow(g.hSrvIPC));
 921	sei.hwnd=hParent;
 922	sei.nShow=/*(startInfo.dwFlags&STARTF_USESHOWWINDOW) ? startInfo.wShowWindow :*/ SW_SHOWNORMAL;
 923	sei.fMask=SEE_MASK_NOCLOSEPROCESS|SEE_MASK_NOZONECHECKS;
 924	sei.lpVerb=_T("runas");
 925	p=FindExePathEnd(pCL);
 926	len=p-pCL;
 927	if (!p || !len) 
 928	{
 929		ec=ERROR_FILE_NOT_FOUND;
 930		goto ret;
 931	}
 932	for (;;) 
 933	{
 934		NSIS::MemFree(pszExePathBuf);
 935		if (!(pszExePathBuf=(LPTSTR)NSIS::MemAlloc((++len)*sizeof(TCHAR))))goto dieOOM;
 936		if ( GetModuleFileName(0,pszExePathBuf,len) < len )break; //FUCKO: what if GetModuleFileName returns 0?
 937		len+=MAX_PATH;
 938	}
 939	sei.lpFile=pszExePathBuf;	
 940	len=lstrlen(p);
 941	len+=20;//space for "/UAC:xxxxxx /NCRC\0"
 942	if (!(pszParamBuf=(LPTSTR)NSIS::MemAlloc(len*sizeof(TCHAR))))goto dieOOM;
 943	wsprintf(pszParamBuf,_T("/UAC:%X /NCRC%s"),g.hSrvIPC,p);//NOTE: The argument parser depends on our special flag appearing first
 944	sei.lpParameters=pszParamBuf;
 945
 946
 947	if (OSVerMaj==5) 
 948	{
 949		bool hasseclogon=SysNT5IsSecondaryLogonSvcRunning();
 950		TRACEF("SysNT5IsSecondaryLogonSvcRunning=%d\n",hasseclogon);
 951		if (!hasseclogon) 
 952		{
 953			ec=ERROR_SERVICE_NOT_ACTIVE;
 954			goto ret;
 955		}
 956	}
 957	
 958
 959
 960
 961#ifdef UAC_HACK_FGWND1

 962	if ( OSVerMaj>=5 && !sei.hwnd ) 
 963	{
 964		//sei.nShow=SW_SHOW;//forced, do we HAVE to do this?
 965		hHack=CreateWindowEx(WS_EX_TRANSPARENT|WS_EX_LAYERED|WS_EX_TOOLWINDOW|WS_EX_APPWINDOW,_T("Button"),GetVar(VIDX_EXEFILENAME),0|WS_MAXIMIZE,0,0,0,0,NULL,NULL,NULL,0);
 966		
 967		SetWindowLongPtr(hHack,GWLP_WNDPROC,(LONG_PTR)HackWndSubProc);
 968		if (GetSysVer()<6 || MustUseInternalRunAs())
 969			PostMessage(g.hSrvIPC,IPC_HACKFINDRUNAS,0,(LONG_PTR)hHack);
 970		else
 971			SetWindowLongPtr(hHack,GWL_EXSTYLE,GetWindowLongPtr(hHack,GWL_EXSTYLE)&~WS_EX_APPWINDOW);//kill taskbar btn on vista
 972		HICON hIcon=(HICON)LoadImage(GetModuleHandle(0),MAKEINTRESOURCE(103),IMAGE_ICON,0,0,LR_DEFAULTSIZE|LR_SHARED);	
 973		SendMessage(hHack,WM_SETICON,ICON_BIG,(LONG_PTR)hIcon);
 974		ShowWindow(hHack,SW_SHOW);
 975		SetForegroundWindow(hHack);
 976		sei.hwnd=hHack;
 977	}
 978#endif

 979
 980	if (hParent)SetForegroundWindow(hParent);//try to force taskbar button active (for RunElevatedAndProcessMessages, really important on Vista)
 981
 982#ifdef FEAT_CUSTOMRUNASDLG

 983	if (MustUseInternalRunAs()) 
 984	{ 
 985		ec=MyRunAs(g.hInstance,sei);
 986	}
 987	else 
 988#endif

 989	{
 990		if (GetSysVer()>=6) 
 991		{
 992			////////sei.nShow=SW_SHOW;
 993			//if ( _SupportsUAC() )sei.hwnd=0; //Vista does not like it when we provide a HWND
 994			//if (_AllowSetForegroundWindow) _AllowSetForegroundWindow(ASFW_ANY);//TODO: is GrantClientWindowInput() enough?
 995		}
 996#ifdef FEAT_MSRUNASDLGMODHACK

 997		void* hook=MSRunAsDlgMod_Init();
 998#endif

 999		TRACEF("ForkSelf:calling ShExec:app=%s|params=%s|vrb=%s|hwnd=%X\n",sei.lpFile,sei.lpParameters,sei.lpVerb,sei.hwnd);
1000		ec=(ShellExecuteEx(&sei)?NO_ERROR:GetLastError());
1001		TRACEF("ForkSelf: ShExec->Runas returned %d hInstApp=%d\n",ec,sei.hInstApp);
1002#ifdef FEAT_MSRUNASDLGMODHACK

1003		MSRunAsDlgMod_Unload(hook);
1004#endif

1005	}
1006#ifdef UAC_HACK_FGWND1

1007	DestroyWindow(hHack);
1008#endif

1009	if (ec)goto ret;
1010	TRACEF("ForkSelf: waiting for process %X (%s|%s|%s)sei.hwnd=%X\n",(sei.hProcess),sei.lpFile,sei.lpParameters,sei.lpVerb,sei.hwnd);
1011	ASSERT(sei.hProcess);
1012	ASSERT(NO_ERROR==ec);
1013	ShowWindow(g.hSrvIPC,SW_HIDE);
1014
1015	g.hElevatedProcess=sei.hProcess;
1016
1017	if (!IsWindow(sei.hwnd))
1018	{
1019		DWORD w=WaitForSingleObject(sei.hProcess,INFINITE);
1020		if (w==WAIT_OBJECT_0)
1021			VERIFY(GetExitCodeProcess(sei.hProcess,&ForkExitCode));
1022		else 
1023		{
1024			ec=GetLastError();
1025			TRACEF("ForkSelf:WaitForSingleObject failed ec=%d w=%d\n",ec,w);ASSERT(!"ForkSelf:WaitForSingleObject");
1026		}
1027	}
1028	else 
1029	{
1030		bool abortWait=false;
1031		const DWORD waitCount=1;
1032		const HANDLE handles[waitCount]={sei.hProcess};
1033		do 
1034		{
1035			DWORD w=MsgWaitForMultipleObjects(waitCount,handles,false,INFINITE,QS_ALLEVENTS|QS_ALLINPUT);
1036			switch(w)
1037			{
1038			case WAIT_OBJECT_0:
1039				VERIFY(GetExitCodeProcess(sei.hProcess,&ForkExitCode));
1040				abortWait=true;
1041				break;
1042			case WAIT_OBJECT_0+waitCount:
1043				{
1044					const HWND hwnd=sei.hwnd;
1045					MSG msg;
1046					while( !ec && PeekMessage(&msg,hwnd,0,0,PM_REMOVE) ) 
1047					{
1048						if (msg.message==WM_QUIT) 
1049						{
1050							ASSERT(0);
1051							ec=ERROR_CANCELLED;
1052							break;
1053						}
1054						if (!IsDialogMessage(hwnd,&msg)) 
1055						{
1056							TranslateMessage(&msg);
1057							DispatchMessage(&msg);
1058						}
1059					}
1060				}
1061				break;
1062			default:
1063				abortWait=true;
1064				ec=GetLastError();
1065				TRACEF("ForkSelf:MsgWaitForMultipleObjects failed, ec=%u w=%u\n",ec,w);
1066			}
1067		} while( NO_ERROR==ec && !abortWait );
1068	} 
1069	
1070	TRACEF("ForkSelf: wait complete, ec=%d forkexitcode=%u\n",ec,ForkExitCode);
1071	goto ret;
1072dieOOM:
1073	ec=ERROR_OUTOFMEMORY;
1074ret:
1075	StopIPCSrv();
1076	CloseHandle(sei.hProcess);
1077	NSIS::MemFree(pszExePathBuf);
1078	NSIS::MemFree(pszParamBuf);
1079	return ec;
1080}
1081
1082bool GetIPCSrvWndFromParams(HWND&hwndOut) 
1083{
1084	LPTSTR p=FindExePathEnd(GetCommandLine());
1085	while(p && *p==' ')p=CharNext(p);TRACEF("GetIPCSrvWndFromParams:%s|\n",p);
1086	if (p && *p++=='/'&&*p++=='U'&&*p++=='A'&&*p++=='C'&&*p++==':') 
1087	{
1088		hwndOut=(HWND)StrToUInt(p,true);
1089		return !!IsWindow(hwndOut);
1090	}
1091	return false;
1092}
1093
1094
1095/*** RunElevated
1096Return:	r0:	Windows error code (0 on success, 1223 if user aborted elevation dialog, anything else should be treated as a fatal error)	
1097		r1: If r0==0, r1 is:
1098				0 if UAC is not supported by the OS, 
1099				1 if UAC was used to elevate and the current process should act like a wrapper (Call Quit in .OnInit without any further processing), 
1100				2 if the process is (already?) running @ HighIL (Member of admin group on other systems),
1101				3 if you should call RunElevated again (This can happen if a user without admin priv. is used in the runas dialog),
1102		r2: If r0==0 && r1==1: r2 is the ExitCode of the elevated fork process (The NSIS errlvl is also set to the ExitCode)
1103		r3: If r0==0: r3 is 1 if the user is a member of the admin group or 0 otherwise
1104*/
1105EXPORTNSISFUNC RunElevated(HWND hwndNSIS,int StrSize,NSISCH*Vars,NSIS::stack_t **StackTop,NSIS::extra_parameters* XParams) 
1106{
1107	NSISFUNCSTART(hwndNSIS,StrSize,Vars,StackTop,XParams);
1108
1109	BYTE UACMode=0;
1110	bool UserIsAdmin=false;
1111	DWORD ec=ERROR_ACCESS_DENIED,ForkExitCode;
1112	TOKEN_ELEVATION_TYPE tet;
1113	
1114	UserIsAdmin=_IsAdmin();
1115	TRACEF("RunElevated:Init: IsAdmin=%d\n",UserIsAdmin);
1116#ifdef BUILD_XPTEST

1117	if (!UserIsAdmin)goto DbgXPAsUAC;//Skip UAC detection for special debug builds and force a call to runas on <NT6 systems
1118#endif

1119	if (!_SupportsUAC() && !SysSupportsRunAs())goto noUAC;
1120	if ((ec=DelayLoadDlls()))goto ret;
1121		
1122	if (GetIPCSrvWndFromParams(g.hSrvIPC)) 
1123	{
1124		if (ec=DllSelfAddRef())goto ret;
1125		if (!IsWindow(g.hSrvIPC))ec=ERRAPP_BADIPCSRV;
1126		UACMode=2;
1127		g.UseIPC=true;
1128		if (!UserIsAdmin) //Elevation used, but we are not Admin, let the wrapper know so it can try again...
1129		{ 		
1130			UACMode=0xFF;//Special invalid mode
1131			DWORD_PTR MsgRet;
1132			if (SendIPCMsg(IPC_ELEVATEAGAIN,0,0,MsgRet) || !MsgRet)ec=ERRAPP_BADIPCSRV;//if we could not notify the need for re-elevation, the IPCSrv must be bad
1133		}
1134		goto ret;
1135	}
1136	
1137	if ( (ec=DllSelfAddRef()) || (ec=_GetElevationType(&tet)) )goto ret;
1138	if ( tet==TokenElevationTypeFull || UserIsAdmin ) 
1139	{
1140		UserIsAdmin=true;
1141		UACMode=2;
1142		goto ret;
1143	}
1144
1145	DBGONLY(DBG_RESETDBGVIEW());
1146	
1147#ifdef BUILD_XPTEST

1148DbgXPAsUAC:VERIFY(!DllSelfAddRef());
1149#endif

1150	//OS supports UAC and we need to elevate...
1151	ASSERT(!UserIsAdmin);
1152	UACMode=1;
1153	
1154	ec=ForkSelf(hwndNSIS,ForkExitCode,XParams,StrSize);
1155	if (!ec && !g.ElevateAgain) 
1156	{
1157		SetVarUINT(INST_2,ForkExitCode);
1158		SetErrLvl(XParams,ForkExitCode);
1159	}
1160	goto ret;
1161noUAC:
1162	ec=NO_ERROR;ASSERT(UACMode==0);
1163ret:
1164	if (ec==ERROR_CANCELLED) 
1165	{
1166		if (UACMode!=1)ec=ERROR_INVALID_FUNCTION;
1167		if (UACMode<2)g.UseIPC=false;
1168	}
1169	if (UACMode==0xFF && !ec) //We called IPC_ELEVATEAGAIN, so we need to quit so the wrapper gains control
1170	{
1171		ASSERT(g.UseIPC);
1172		UACMode=1;//We pretend to be the wrapper so Quit gets called in .OnInit
1173		SetErrLvl(XParams,0);
1174		_Unload();
1175	}
1176	if (g.ElevateAgain) 
1177	{
1178		ASSERT(!g.UseIPC);
1179		UACMode=3;//Fork called IPC_ELEVATEAGAIN, we need to change our UACMode so the wrapper(our instance) can try to elevate again if it wants to
1180	}
1181	if (!g.UseIPC)
1182		_Unload();//The wrapper can call quit in .OnInit without calling UAC::Unload, so we do it here
1183	
1184	SetVarUINT(INST_0,ec);
1185	SetVarUINT(INST_1,UACMode);
1186	SetVarUINT(INST_3,UserIsAdmin);
1187	TRACEF("RunElevated returning ec=%X UACMode=%d g.UseIPC=%d g.ElevateAgain=%d IsAdmin=%d\n",ec,UACMode,g.UseIPC,g.ElevateAgain,UserIsAdmin);
1188
1189	NSISFUNCEND();
1190}
1191
1192
1193
1194/*** Exec
1195Notes:
1196		¤ ErrorFlag is also set on error
1197STACK:	<ShowWindow> <File> <Parameters> <WorkingDir>
1198Return:	windows error code in r0, 0 on success
1199*/
1200EXPORTNSISFUNC Exec(HWND hwndNSIS,int StrSize,NSISCH*Vars,NSIS::stack_t **StackTop,NSIS::extra_parameters* XParams) 
1201{
1202	NSISFUNCSTART(hwndNSIS,StrSize,Vars,StackTop,XParams);
1203	HandleExecExport(true,false,hwndNSIS,StrSize,Vars,StackTop,XParams);
1204	NSISFUNCEND();
1205}
1206
1207/*** ExecWait
1208Notes:
1209		¤ ErrorFlag is also set on error
1210STACK:	<ShowWindow> <File> <Parameters> <WorkingDir>
1211Return:	
1212		r0: windows error code, 0 on success
1213		r1: exitcode of new process 
1214*/
1215EXPORTNSISFUNC ExecWait(HWND hwndNSIS,int StrSize,NSISCH*Vars,NSIS::stack_t **StackTop,NSIS::extra_parameters* XParams) 
1216{
1217	NSISFUNCSTART(hwndNSIS,StrSize,Vars,StackTop,XParams);
1218	HandleExecExport(true,true,hwndNSIS,StrSize,Vars,StackTop,XParams);
1219	NSISFUNCEND();
1220}
1221
1222/*** ShellExec
1223Notes:
1224		¤ ErrorFlag is also set on error
1225STACK:	<Verb> <ShowWindow> <File> <Parameters> <WorkingDir>
1226Return:	windows error code in r0, 0 on success
1227*/
1228EXPORTNSISFUNC ShellExec(HWND hwndNSIS,int StrSize,NSISCH*Vars,NSIS::stack_t **StackTop,NSIS::extra_parameters* XParams) 
1229{
1230	NSISFUNCSTART(hwndNSIS,StrSize,Vars,StackTop,XParams);
1231	HandleExecExport(false,false,hwndNSIS,StrSize,Vars,StackTop,XParams);
1232	NSISFUNCEND();
1233}
1234
1235/*** ShellExecWait
1236Notes:
1237		¤ ErrorFlag is also set on error
1238STACK:	<Verb> <ShowWindow> <File> <Parameters> <WorkingDir>
1239Return:	
1240		r0: windows error code, 0 on success
1241		r1: exitcode of new process 
1242*/
1243EXPORTNSISFUNC ShellExecWait(HWND hwndNSIS,int StrSize,NSISCH*Vars,NSIS::stack_t **StackTop,NSIS::extra_parameters* XParams) 
1244{
1245	NSISFUNCSTART(hwndNSIS,StrSize,Vars,StackTop,XParams);
1246	HandleExecExport(false,true,hwndNSIS,StrSize,Vars,StackTop,XParams);
1247	NSISFUNCEND();
1248}
1249
1250
1251/*** GetElevationType
1252Notes:
1253		TokenElevationTypeDefault=1	:User is not using a split token (UAC disabled)
1254		TokenElevationTypeFull=2	:UAC enabled, the process is elevated
1255		TokenElevationTypeLimited=3	:UAC enabled, the process is not elevated
1256Return:	r0:	(TOKEN_ELEVATION_TYPE)TokenType, The error flag is set if the function fails and r0==0
1257*/
1258EXPORTNSISFUNC GetElevationType(HWND hwndNSIS,int StrSize,NSISCH*Vars,NSIS::stack_t **StackTop,NSIS::extra_parameters* XParams) 
1259{
1260	NSISFUNCSTART(hwndNSIS,StrSize,Vars,StackTop,XParams);
1261	TOKEN_ELEVATION_TYPE tet=(TOKEN_ELEVATION_TYPE)NULL; //Default to invalid value
1262	if (MaintainDllSelfRef() || /*!_SupportsUAC() ||*/ _GetElevationType(&tet)) SetErrorFlag(XParams);
1263	SetVarUINT(INST_0,tet);
1264	NSISFUNCEND();
1265}
1266
1267
1268
1269/*** SupportsUAC
1270Notes:	Check if the OS supports UAC (And the user has UAC turned on)
1271Return:	r0:	(BOOL)Result 
1272*/
1273EXPORTNSISFUNC SupportsUAC(HWND hwndNSIS,int StrSize,NSISCH*Vars,NSIS::stack_t **StackTop,NSIS::extra_parameters* XParams) 
1274{
1275	NSISFUNCSTART(hwndNSIS,StrSize,Vars,StackTop,XParams);
1276	BOOL present=false;
1277	MaintainDllSelfRef();
1278	if (_SupportsUAC())present=true;	
1279	SetVarUINT(INST_0,present);
1280	NSISFUNCEND();
1281}
1282
1283
1284/*** IsAdmin
1285Return:	r0:	(BOOL)Result 
1286*/
1287EXPORTNSISFUNC IsAdmin(HWND hwndNSIS,int StrSize,NSISCH*Vars,NSIS::stack_t **StackTop,NSIS::extra_parameters* XParams) 
1288{
1289	NSISFUNCSTART(hwndNSIS,StrSize,Vars,StackTop,XParams);
1290	bool Admin=false;
1291	DWORD ec;
1292	if ( !(ec=MaintainDllSelfRef()) ) 
1293	{
1294		Admin=_IsAdmin();
1295		if ( ec=GetLastError() ) 
1296		{
1297			TRACEF("IsAdmin failed with %d\n",ec);
1298			SetErrorFlag(XParams);
1299			Admin=false;
1300		}
1301	}
1302	SetVarUINT(INST_0,Admin);
1303	NSISFUNCEND();
1304}
1305
1306
1307
1308/*** ExecCodeSegment
1309Notes:	Sets error flag on error
1310		There is currently no way to transfer state to/from the executed code segment!
1311		If you use instructions that alter the UI or the stack/variables in the code segment (StrCpy,Push/Pop/Exch,DetailPrint,HideWindow etc.) they will affect the hidden wrapper installer and not "your" installer instance!
1312*/
1313EXPORTNSISFUNC ExecCodeSegment(HWND hwndNSIS,int StrSize,NSISCH*Vars,NSIS::stack_t **StackTop,NSIS::extra_parameters* XParams) 
1314{
1315	NSISFUNCSTART(hwndNSIS,StrSize,Vars,StackTop,XParams);
1316
1317	DWORD ec;
1318	if (!(ec=DllSelfAddRef())) //force AddRef since our plugin could be called inside the executed code segment!
1319	{
1320		ec=ERROR_INVALID_PARAMETER;
1321		stack_t* pSI=StackPop();
1322		if (pSI) 
1323		{
1324			BOOL badCh;
1325			UINT pos=StrToUInt(pSI->text,false,&badCh);
1326			TRACEF("ExecCodeSegment %d (%s) badinput=%d\n",pos-1,pSI->text,badCh);
1327			if (!badCh && pos--) 
1328			{
1329				if (!g.UseIPC)
1330					ec=NSIS::ExecuteCodeSegment(XParams,pos);
1331				else 
1332				{
1333					SyncVars(hwndNSIS);
1334					DWORD_PTR MsgRet;
1335					AllowOuterInstanceWindowFocus();
1336					if (!(ec=SendIPCMsg(IPC_EXECCODESEGMENT,pos,0,MsgRet,INFINITE)))ec=MsgRet-1;
1337				}
1338			}
1339			StackFreeItem(pSI);
1340		}
1341	}
1342	if (ec)SetErrorFlag(XParams);
1343
1344	NSISFUNCEND();
1345}
1346
1347
1348
1349/*** StackPush   */
1350EXPORTNSISFUNC StackPush(HWND hwndNSIS,int StrSize,NSISCH*Vars,NSIS::stack_t **StackTop,NSIS::extra_parameters* XParams) 
1351{
1352	NSISFUNCSTART(hwndNSIS,StrSize,Vars,StackTop,XParams);
1353
1354	DWORD ec;
1355	if (!(ec=DllSelfAddRef()) && g.UseIPC) 
1356	{
1357		stack_t* pSI=StackPop();
1358		ec=ERROR_INVALID_PARAMETER;
1359		if (pSI) 
1360		{
1361			DWORD_PTR MsgRet;
1362			COPYDATASTRUCT cds={CDI_STACKPUSH,(lstrlen(pSI->text)+1)*sizeof(NSISCH),pSI->text};
1363			if (!(ec=SendIPCMsg(WM_COPYDATA,(WPARAM)hwndNSIS,(LPARAM)&cds,MsgRet,5000 )))ec=MsgRet-1;
1364			StackFreeItem(pSI);
1365		}
1366	}
1367	if (ec)SetErrorFlag(XParams);
1368
1369	NSISFUNCEND();
1370}
1371
1372
1373
1374/*** GetOuterHwnd   */
1375EXPORTNSISFUNC GetOuterHwnd(HWND hwndNSIS,int StrSize,NSISCH*Vars,NSIS::stack_t **StackTop,NSIS::extra_parameters* XParams) 
1376{
1377	NSISFUNCSTART(hwndNSIS,StrSize,Vars,StackTop,XParams);
1378	MaintainDllSelfRef();
1379	DWORD_PTR MsgRet;HWND hSrvIPC;
1380	if (!GetIPCSrvWndFromParams(hSrvIPC)||!IsWindow(hSrvIPC)||SendIPCMsg(IPC_GETSRVHWND,0,0,MsgRet,IPCTOUT_DEF,hSrvIPC))MsgRet=0;
1381	SetVarUINT(INST_0,MsgRet);
1382	NSISFUNCEND();
1383}
1384
1385
1386DWORD SetPrivilege(LPCSTR PrivName,bool Enable)
1387{
1388	DWORD r=NO_ERROR;
1389	HANDLE hToken=NULL;
1390	TOKEN_PRIVILEGES TokenPrivs;
1391	if (!LookupPrivilegeValueA(NULL,PrivName,&TokenPrivs.Privileges[0].Luid))goto dieGLE;
1392	if (!_OpenProcessToken(GetCurrentProcess (),TOKEN_ADJUST_PRIVILEGES,&hToken))goto dieGLE;
1393	TokenPrivs.Privileges[0].Attributes = Enable ? SE_PRIVILEGE_ENABLED : 0 ;
1394	TokenPrivs.PrivilegeCount=1;
1395	if (AdjustTokenPrivileges(hToken,FALSE,&TokenPrivs,0,NULL,NULL))goto ret;
1396dieGLE:
1397	r=GetLastError();
1398ret:
1399	CloseHandle(hToken);
1400	return r;
1401}
1402
1403
1404#include <shlobj.h>

1405/*** GetShellFolderPath   */
1406EXPORTNSISFUNC GetShellFolderPath(HWND hwndNSIS,int StrSize,NSISCH*Vars,NSIS::stack_t **StackTop,NSIS::extra_parameters* XParams) 
1407{
1408	/*
1409	WINBUG: 
1410	For some reason, even with debug priv. enabled, a call to OpenProcessToken(TOKEN_READ|TOKEN_IMPERSONATE)
1411	will fail, even if we have a PROCESS_ALL_ACCESS handle on XP when running as a member of the Power Users Group.
1412	So we have to ask the outer process to give us the handle.
1413	*/
1414
1415	NSISFUNCSTART(hwndNSIS,StrSize,Vars,StackTop,XParams);
1416	MaintainDllSelfRef();
1417
1418	const unsigned TokenAccessRights=TOKEN_READ|TOKEN_IMPERSONATE;
1419	unsigned clsidFolder;
1420	HWND hSrvIPC;
1421	HANDLE hToken=NULL,hOuterProcess=NULL;
1422	DWORD SrvPID;
1423	HRESULT hr;
1424	FARPROC pfnSHGetFolderPath;
1425	LPTSTR buf=GetVar(INST_0);
1426	stack_t*pssCLSID=StackPop();
1427	stack_t*pssFallback=NULL;
1428	BOOL ConvBadCh;
1429	if (!pssCLSID || !(pssFallback=StackPop())) goto fail;
1430	clsidFolder=StrToUInt(pssCLSID->text,false,&ConvBadCh);
1431	if (ConvBadCh)goto fail;
1432	TRACEF("GetShellFolderPath HasIPCServer=%X param=%s>%X fallback=%s|\n",HasIPCServer(),pssCLSID->text,clsidFolder,pssFallback->text);
1433
1434	pfnSHGetFolderPath=GetProcAddress(GetModuleHandle(_T("SHELL32")),"SHGetFolderPath"__DLD_FUNCSUFFIX);
1435	if (!pfnSHGetFolderPath)goto fail;
1436
1437	if (GetIPCSrvWndFromParams(hSrvIPC) && 0 != GetWindowThreadProcessId(hSrvIPC,&SrvPID)) 
1438	{
1439		hOuterProcess=OpenProcess(PROCESS_QUERY_INFORMATION,false,SrvPID);
1440		if (hOuterProcess)
1441		{
1442			BOOL bOk=OpenProcessToken(hOuterProcess,TokenAccessRights,&hToken);
1443			CloseHandle(hOuterProcess);
1444			if (bOk)goto gotToken;
1445
1446		}
1447/*		SetPrivilege(SE_DEBUG_NAME,true);
1448		hOuterProcess=OpenProcess(PROCESS_DUP_HANDLE,false,SrvPID);
1449		SetPrivilege(SE_DEBUG_NAME,false);
1450		if (!hOuterProcess)goto fail;
1451		TRACEF("hOuterProcess=%X\n",hOuterProcess);
1452*/		SendIPCMsg(IPC_GETOUTERPROCESSTOKEN,0,TokenAccessRights,(DWORD_PTR&)hToken,IPCTOUT_DEF,hSrvIPC);
1453		TRACEF("IPC_GETOUTERPROCESSTOKEN=%X\n",hToken);//*/
1454	}
1455gotToken:
1456	if (HasIPCServer() && !hToken)goto fail;
1457
1458	hr=((HRESULT(WINAPI*)(HWND,int,HANDLE,DWORD,LPTSTR))pfnSHGetFolderPath)(hwndNSIS,clsidFolder,hToken,SHGFP_TYPE_CURRENT,buf);
1459	TRACEF("SHGetFolderPath hr=%X with token=%X, clsidFolder=%X|%s\n",hr,hToken,clsidFolder,buf);
1460	if (FAILED(hr)) goto fail; else goto ret;
1461fail:
1462	TRACEF("GetShellFolderPath GLE=%X\n",GetLastError());
1463	lstrcpy(buf,pssFallback->text);
1464	TRACEF("%s|%s\n",buf,pssFallback->text);
1465ret:
1466//	CloseHandle(hOuterProcess);
1467	CloseHandle(hToken);
1468	StackFreeItem(pssFallback);
1469	StackFreeItem(pssCLSID);
1470	NSISFUNCEND();
1471}
1472
1473
1474
1475/*** GetOuterPID   * /
1476EXPORTNSISFUNC GetOuterPID(HWND hwndNSIS,int StrSize,NSISCH*Vars,NSIS::stack_t **StackTop,NSIS::extra_parameters* XParams) 
1477{
1478	NSISFUNCSTART(hwndNSIS,StrSize,Vars,StackTop,XParams);
1479	MaintainDllSelfRef();
1480	DWORD_PTR Ret=0;
1481	HWND hSrvIPC;
1482	if (GetIPCSrvWndFromParams(hSrvIPC))
1483		if (0==GetWindowThreadProcessId(hSrvIPC,&Ret))Ret=0;
1484	SetVarUINT(INST_0,Ret);
1485	NSISFUNCEND();
1486}//*/
1487
1488
1489
1490/*** Unload
1491Notes:	Call in .OnInstFailed AND .OnInstSuccess !
1492*/
1493EXPORTNSISFUNC Unload(HWND hwndNSIS,int StrSize,NSISCH*Vars,NSIS::stack_t **StackTop) 
1494{
1495	NSISFUNCSTART4(hwndNSIS,StrSize,Vars,StackTop);
1496	if (!MaintainDllSelfRef())_Unload(); else ASSERT(!"MaintainDllSelfRef failed in Unload!");
1497	NSISFUNCEND();
1498}
1499
1500
1501
1502#ifdef _DEBUG

1503BOOL WINAPI DllMain(HINSTANCE hInst,DWORD Event,LPVOID lpReserved) 
1504#else

1505extern "C" BOOL WINAPI _DllMainCRTStartup(HINSTANCE hInst,ULONG Event,LPVOID lpReserved) 
1506#endif

1507{
1508	if (Event==DLL_PROCESS_ATTACH) 
1509	{
1510		TRACEF("************************************ DllMain %u\n",GetCurrentProcessId());
1511		ASSERT(!_OpenProcessToken && !_EqualSid);
1512		g.hInstance=hInst;
1513	}
1514//	DBGONLY( if (Event==DLL_PROCESS_DETACH){ASSERT(g.DllRef==0);}TRACE("DLL_PROCESS_DETACH\n"); );//Make sure we unloaded so we don't lock $pluginsdir
1515	return TRUE;
1516}
1517
1518