Old 14th July 2014, 21:45   #1
sledgehammer_999
Junior Member
 
Join Date: Jan 2013
Posts: 27
Questions about UAC plugin

I read the wiki page and the included *.nsi files but I still have questions.
First of all I am no nsis guru.

I want my installer to run some code from the user process and some other code from the admin process. However I cannot understand how I tell it to run function A as user and function B as root...

Also I cannot understand how the program flow works between the user and admin process. Can someone explain it to me? eg based on the wiki example found here: http://nsis.sourceforge.net/UAC_plug-in

Thank you.
sledgehammer_999 is offline   Reply With Quote
Old 14th July 2014, 23:02   #2
Anders
Moderator
 
Anders's Avatar
 
Join Date: Jun 2002
Location: ${NSISDIR}
Posts: 4,911
I'd rather people don't use the plugin and I regret creating it in the first place. If you think you need it then you might be doing something wrong.

The general program flow is that most of the installer runs elevated (possibly as another user) and you can execute one of your functions as the user that started the installer: !insertmacro UAC_AsUser_Call Function MyFunction ${UAC_SYNCREGISTERS}

IntOp $PostCount $PostCount + 1
Anders is offline   Reply With Quote
Old 15th July 2014, 07:12   #3
sledgehammer_999
Junior Member
 
Join Date: Jan 2013
Posts: 27
Why do you regret it? Is there something alternative?

So the "!insertmacro UAC_RunElevated" in .onInit make the whole installer run in admin mode and I need to explicitly tell which functions to run in user mode, right?

I need this in order to make correct file associations in Windows Vista+ , my current method seems to work only on Windows XP.
sledgehammer_999 is offline   Reply With Quote
Old 15th July 2014, 12:56   #4
Anders
Moderator
 
Anders's Avatar
 
Join Date: Jun 2002
Location: ${NSISDIR}
Posts: 4,911
This is the wrong approach, if you are installing to $ProgramFiles then you should write to HKLM/HKCR and not HKCU because you are doing a "all users" install. If you write to HKCU then other users will not see those registry entries.

A very basic file association is just:
Quote:
WriteRegStr HKCR ".myext" "" "myprogid"
WriteRegStr HKCR "myprogid\shell\open\command" "" '"$instdir\myapp.exe" "%1"'
and should work on all versions of Windows. If .myext is a common type it might already be assigned to some other application and every new version of Windows makes it harder for you to take over those associations (In Windows 8 the UserChoice value is now encrypted to prevent you from taking it over). You should therefore add yourself to HKLM\Software\RegisteredApplications so that the user can use Default Programs/Set Program Access and Computer Defaults to select your application as the default for a popular type. Finally, if you want you can add yourself to HKCR\.myext\OpenWithProgids to list yourself in the openwith menu...

IntOp $PostCount $PostCount + 1
Anders is offline   Reply With Quote
Old 16th July 2014, 00:15   #5
sledgehammer_999
Junior Member
 
Join Date: Jan 2013
Posts: 27
OK. I'll take a look into your suggestions. But I have other problems unrelated to file associations.

Currently my installer requests admin privileges before startup. This means that if a user with restricted rights launches it, give UAC credentials my installer ends up using the admin's start menu instead of the user's (assume that I want to use per user installation).
sledgehammer_999 is offline   Reply With Quote
Old 16th July 2014, 01:19   #6
Anders
Moderator
 
Anders's Avatar
 
Join Date: Jun 2002
Location: ${NSISDIR}
Posts: 4,911
Use "SetShellVarContext all" and $smprograms will point to the shared startmenu.

If you want to do per-user installs like Chrome you need to use "RequestExecutionLevel user" and install your app under $localappdata\Programs. This of course means you cannot write to $programfiles/$windir/HKLM/HKCR...

IntOp $PostCount $PostCount + 1
Anders is offline   Reply With Quote
Old 16th July 2014, 01:41   #7
sledgehammer_999
Junior Member
 
Join Date: Jan 2013
Posts: 27
I don't know if there is any MS guideline on this, but IMO "program files" is the de facto folder of installing applications. I don't like using $localappdata

So it seems to me, if I want to achieve this kind of behavior the UAC plugin is the way to go.

Thanks for your time explaining things here. (I'll still visit this thread if you want to add something else).
sledgehammer_999 is offline   Reply With Quote
Old 16th July 2014, 09:49   #8
Afrow UK
Moderator
 
Afrow UK's Avatar
 
Join Date: Nov 2002
Location: Surrey, England
Posts: 8,434
What do you need to run under the current user that cannot be ran for all users? You really should try to avoid using the UAC plug-in if you are doing an all users install.

Stu
Afrow UK is offline   Reply With Quote
Old 16th July 2014, 12:16   #9
Anders
Moderator
 
Anders's Avatar
 
Join Date: Jun 2002
Location: ${NSISDIR}
Posts: 4,911
Quote:
Originally Posted by sledgehammer_999 View Post
I don't like using $localappdata
$localappdata\Programs is the default location for FOLDERID_UserProgramFiles

IntOp $PostCount $PostCount + 1
Anders is offline   Reply With Quote
Old 17th July 2014, 22:15   #10
sledgehammer_999
Junior Member
 
Join Date: Jan 2013
Posts: 27
I am going to dig up the relevant issues from bugtracker and report bug. But until then I remembered something else.

What happens with the feature "launch program after installtion is done"? I still need to launch the program with non-admin rights.
sledgehammer_999 is offline   Reply With Quote
Old 18th July 2014, 00:04   #11
Anders
Moderator
 
Anders's Avatar
 
Join Date: Jun 2002
Location: ${NSISDIR}
Posts: 4,911
Quote:
Originally Posted by sledgehammer_999 View Post
I am going to dig up the relevant issues from bugtracker and report bug.
Report a bug about what? This is how UAC is designed, there is not much anyone can do. It is better to just play ball instead of trying to fight it.


Quote:
Originally Posted by sledgehammer_999 View Post
What happens with the feature "launch program after installtion is done"? I still need to launch the program with non-admin rights.
Yes, this is the only issue where the UAC plugin is a legitimate solution but I would personally recommend that you just remove the run box from the finish page. There is also the stdutils plugin that can start any app as the same user as explorer.exe's taskbar.

IntOp $PostCount $PostCount + 1
Anders is offline   Reply With Quote
Old 18th July 2014, 00:59   #12
sledgehammer_999
Junior Member
 
Join Date: Jan 2013
Posts: 27
Quote:
Originally Posted by Anders View Post
Report a bug about what? This is how UAC is designed, there is not much anyone can do. It is better to just play ball instead of trying to fight it.

Sorry wrong wording. I meant MY bugtracker. Currently my script doesn't use UAC, it requests admin elevation and runs everything under that, which causes some problems in newer OS's(I designed it on my Windows XP box.). Those issues are reported by users in my bug tracker but I was too lazy to act upon them till now.
sledgehammer_999 is offline   Reply With Quote
Old 18th July 2014, 01:00   #13
sledgehammer_999
Junior Member
 
Join Date: Jan 2013
Posts: 27
Quote:
I am going to dig up the relevant issues from bugtracker and report bug.
Sometimes my brains wonders off. The above should read:

Quote:
I am going to dig up the relevant issues from my bugtracker and report back.
sledgehammer_999 is offline   Reply With Quote
Old 18th July 2014, 01:08   #14
Anders
Moderator
 
Anders's Avatar
 
Join Date: Jun 2002
Location: ${NSISDIR}
Posts: 4,911
Exclamation

Quote:
Originally Posted by sledgehammer_999 View Post
Currently my script doesn't use UAC, it requests admin elevation and runs everything under that, which causes some problems in newer OS's(I designed it on my Windows XP box.).
These issues are not really new to Vista, they apply to all versions of NT but most people fail to test as non-admin on the older versions which is why all of these bad practices are so common. (RunAs was added in Win2000 so from then on, a installer could be running as a "different user" than explorer/desktop/taskbar etc)

IntOp $PostCount $PostCount + 1
Anders is offline   Reply With Quote
Old 21st July 2014, 19:44   #15
sledgehammer_999
Junior Member
 
Join Date: Jan 2013
Posts: 27
What do I make the installer run on finish the program as user?
Is this sufficient?

code:
!define MUI_FINISHPAGE_RUN !insertmacro UAC_AsUser_Call blah blah


What should I put in the "blah blah" section? I downloaded the zip but UAC_AsUser_Call doesn't seem to be documented...
sledgehammer_999 is offline   Reply With Quote
Old 21st July 2014, 19:46   #16
sledgehammer_999
Junior Member
 
Join Date: Jan 2013
Posts: 27
Correction yes it seems that it is documented in UAC.nsh, but I still can't get to grasp my head around its parameters. Possibly because I am not good with the nsis syntax/terminology.
sledgehammer_999 is offline   Reply With Quote
Old 21st July 2014, 19:52   #17
sledgehammer_999
Junior Member
 
Join Date: Jan 2013
Posts: 27
Hmmm, the parameters need more documenting. This is what I got from reading it:

code:
UAC_AsUser_Call <Function|Label> <NSISAddressName> <UAC_* flags>


1. The first parameter is a string and can be either "Function" or "Label". It is used to indicate the type of the second parameter
2. The second parameter is the ACTUAL name of the function/label in your script that you want to be called
3. The third parameter are flags. They are enumerated in the code below the code comment in UAC.nsh. They aren't explained what each does.

So I suppose I should
code:
!define MUI_FINISHPAGE_RUN !insertmacro UAC_AsUser_Call Function MyFunction ${UAC_SYNCREGISTERS}


And inside "MyFunction" I should launch my exe.
Is that correct?
sledgehammer_999 is offline   Reply With Quote
Old 21st July 2014, 20:51   #18
Anders
Moderator
 
Anders's Avatar
 
Join Date: Jun 2002
Location: ${NSISDIR}
Posts: 4,911
MUI_FINISHPAGE_RUN* has to be a function, see UAC_Basic.nsi

IntOp $PostCount $PostCount + 1
Anders is offline   Reply With Quote
Old 21st July 2014, 21:44   #19
sledgehammer_999
Junior Member
 
Join Date: Jan 2013
Posts: 27
Quote:
Originally Posted by Anders View Post
MUI_FINISHPAGE_RUN* has to be a function, see UAC_Basic.nsi
Wow. That seems simple enough.

I was confused because my script (without UAC) currently uses:
code:
!define MUI_FINISHPAGE_RUN "$INSTDIR\myprogram.exe"
sledgehammer_999 is offline   Reply With Quote
Old 22nd July 2014, 07:38   #20
sledgehammer_999
Junior Member
 
Join Date: Jan 2013
Posts: 27
I think I also need to use the UAC_SYNCINSTDIR flag, judging from its name.
And probably UAC_AsUser_ExecShell is probably more suited here to launch the program.
sledgehammer_999 is offline   Reply With Quote
Old 22nd July 2014, 10:47   #21
Anders
Moderator
 
Anders's Avatar
 
Join Date: Jun 2002
Location: ${NSISDIR}
Posts: 4,911
UAC_AsUser_ExecShell already uses UAC_SYNCINSTDIR...

IntOp $PostCount $PostCount + 1
Anders is offline   Reply With Quote
Old 24th July 2014, 23:01   #22
sledgehammer_999
Junior Member
 
Join Date: Jan 2013
Posts: 27
Another question:

In my current .onInit I have
code:
!insertmacro MUI_LANGDLL_DISPLAY

Where do I put
code:
!insertmacro Init "installer"
? Before or after that?

(I am refering to the Init macro defined in UAC_Basic.nsi)
sledgehammer_999 is offline   Reply With Quote
Old 25th July 2014, 02:46   #23
Anders
Moderator
 
Anders's Avatar
 
Join Date: Jun 2002
Location: ${NSISDIR}
Posts: 4,911
That probably depends on if you want the language dialog before or after the UAC prompt. If the answer is before you will probably have to manually sync $language somehow. The language in the elevated part is not going to change either way...

IntOp $PostCount $PostCount + 1
Anders is offline   Reply With Quote
Old 28th July 2014, 17:48   #24
sledgehammer_999
Junior Member
 
Join Date: Jan 2013
Posts: 27
The final question is really practical:
How do I use this with the 3.0 NSIS? Is there a unicode .dll of this plugin somewhere or am I stuck with the ansi version?
sledgehammer_999 is offline   Reply With Quote
Old 28th July 2014, 19:30   #25
Anders
Moderator
 
Anders's Avatar
 
Join Date: Jun 2002
Location: ${NSISDIR}
Posts: 4,911
It can probably be compiled as Unicode if you have the tools or you can try to find one of the Unicode versions that were uploaded at some point.

IntOp $PostCount $PostCount + 1
Anders is offline   Reply With Quote
Old 28th July 2014, 20:05   #26
sledgehammer_999
Junior Member
 
Join Date: Jan 2013
Posts: 27
I have set up msvc2008 and mingw 4.9.0 on my desktop. I am not stranger on compiling things.
Are the sources included in the .zip enough? Or do I need some extra environment from the nsis sources?
Any link to docs/forum posts would be appreciated.
sledgehammer_999 is offline   Reply With Quote
Old 28th July 2014, 20:42   #27
Anders
Moderator
 
Anders's Avatar
 
Join Date: Jun 2002
Location: ${NSISDIR}
Posts: 4,911
No NSIS bits are required. You probably have to set some UNICODE define to create the Unicode version though...

IntOp $PostCount $PostCount + 1
Anders is offline   Reply With Quote
Old 28th July 2014, 21:01   #28
sledgehammer_999
Junior Member
 
Join Date: Jan 2013
Posts: 27
Since this is C++ code shouldn't I compile with the same compiler as NSIS is compiled or am I missing something here?

I see that the .zip file doesn't have a README with basic compilation instuctions, nor a makefile (or mvc project file).

Are you the author of this plugin? If I am not asking too much can you give an outline on how to compile this? Just feed all the .cpp files into gcc(mingw) and use the correct switch to produce a shared lib(dll)?
sledgehammer_999 is offline   Reply With Quote
Old 28th July 2014, 21:32   #29
sledgehammer_999
Junior Member
 
Join Date: Jan 2013
Posts: 27
Probably I am missing something here:

gcc:
code:
g++ -c -fpic -DUNICODE RunAs.cpp uac.cpp util.cpp


Produces errors during compilation for runas.cpp and uac.cpp but I get a util.o (object file)

msvc2008:
code:
cl.exe /D_USRDLL /D_WINDLL /DUNICODE RunAs.cpp uac.cpp util.cpp /link /DLL /OUT:uac.dll


Produces .obj files for all 3 files but it errors out during likning.

I am not 100% that I use the correct commands/switches.
If I am using the right commands, which compiler's error output should I provide here?
sledgehammer_999 is offline   Reply With Quote
Old 28th July 2014, 22:38   #30
Anders
Moderator
 
Anders's Avatar
 
Join Date: Jun 2002
Location: ${NSISDIR}
Posts: 4,911
Only tested with MSVC so don't use GCC. You don't need those _* defines but you need at least "/O1s /GS- /GR- /EHs-c- /Zl /LD /DUNICODE" and "/link kernel32.lib user32.lib shell32.lib advapi32.lib ole32.lib"

IntOp $PostCount $PostCount + 1
Anders is offline   Reply With Quote
Old 28th July 2014, 22:49   #31
sledgehammer_999
Junior Member
 
Join Date: Jan 2013
Posts: 27
Thanks. This is some progress but I still see 3 errors.

Quote:
RunAs.cpp
uac.cpp
util.cpp
Generating Code...
Microsoft (R) Incremental Linker Version 9.00.30729.01
Copyright (C) Microsoft Corporation. All rights reserved.

/out:RunAs.dll
/dll
/implib:RunAs.lib
kernel32.lib
user32.lib
shell32.lib
advapi32.lib
ole32.lib
/DLL
/OUT:uac.dll
RunAs.obj
uac.obj
util.obj
RunAs.obj : warning LNK4229: invalid directive '/opt:nowin98' encountered; ignored
RunAs.obj : warning LNK4229: invalid directive '/ignore:4078' encountered; ignored
uac.obj : warning LNK4229: invalid directive '/opt:nowin98' encountered; ignored
uac.obj : warning LNK4229: invalid directive '/ignore:4078' encountered; ignored
Creating library RunAs.lib and object RunAs.exp
RunAs.obj : error LNK2019: unresolved external symbol _memset referenced in function "int __stdcall MyRunAsDlgProc(struct HWND__ *,unsigned int,unsigned int,long)" (?MyRunAsDlgProc@@YGHPAUHWND__@@IIJ@Z)
uac.obj : error LNK2001: unresolved external symbol _memset
util.obj : error LNK2001: unresolved external symbol _memset
uac.dll : fatal error LNK1120: 1 unresolved externals
Unfortunately google didn't turn up something useful. Any ideas?

PS: Commmandline used:

Quote:
cl.exe /O1s /GS- /GR- /EHs-c- /Zl /LD /DUNICODE RunAs.cpp uac.cpp util.cpp /link kernel32.lib user32.lib shell32.lib advapi32.lib ole32.lib /DLL /OUT:uac.dll
sledgehammer_999 is offline   Reply With Quote
Old 28th July 2014, 22:58   #32
Anders
Moderator
 
Anders's Avatar
 
Join Date: Jun 2002
Location: ${NSISDIR}
Posts: 4,911
I uploaded a new version that tries to not use memset. You could also specify /MD or one of the other CRT switches (and remove /Zl) but then the plugin might not work on older versions of Windows.

IntOp $PostCount $PostCount + 1
Anders is offline   Reply With Quote
Old 28th July 2014, 23:06   #33
sledgehammer_999
Junior Member
 
Join Date: Jan 2013
Posts: 27
I still get one memset error in util.cpp

Quote:
util.obj : error LNK2019: unresolved external symbol _memset referenced in function "void __stdcall MemSet(void *,unsigned long,unsigned char)" (?MemSet@@YGXPAXKE@Z)
But removing /Zl and using /MD produces a dll. I am going to test it now and probably go to sleep.

Thanks for the help and I hope you'll solve the remaining memset error.
sledgehammer_999 is offline   Reply With Quote
Old 28th July 2014, 23:12   #34
Anders
Moderator
 
Anders's Avatar
 
Join Date: Jun 2002
Location: ${NSISDIR}
Posts: 4,911
Actually, that error is not really possible to solve, the only way around it is to use a pragma to disable optimizations for that function.

This is a bug in the compiler IMHO. What actually happens is that the compiler sees that the MemSet function looks like it can be replaced by a call to memset and so the compiler does exactly that without even checking if the memset function "exists"...

IntOp $PostCount $PostCount + 1
Anders is offline   Reply With Quote
Old 28th July 2014, 23:28   #35
sledgehammer_999
Junior Member
 
Join Date: Jan 2013
Posts: 27
OK.

So does using /MD have any real disadvantage? My minimum OS req is Windows XP.

And of course I have another problem now that I am able to compile the script.
NSIS reports that function PageFinishRun isn't referenced and it zeroes it out. The funny thing is that I use it in MUI_FINISHPAGE_RUN_FUNCTION.

I might find what is wrong tomorrow when I won't be sleepy...
sledgehammer_999 is offline   Reply With Quote
Old 28th July 2014, 23:55   #36
Anders
Moderator
 
Anders's Avatar
 
Join Date: Jun 2002
Location: ${NSISDIR}
Posts: 4,911
/MD will import from msvcrt*.dll. Which exact dll that is depends on the compiler version. If it is msvcrt.dll then it will work on all versions except Win95 RTM, if it is one with a number in the name then it will only work if the CRT redist. for that exact version is installed on the machine. You can try /MT or one of the other CRT switches...

IntOp $PostCount $PostCount + 1
Anders is offline   Reply With Quote
Old 29th July 2014, 09:10   #37
Afrow UK
Moderator
 
Afrow UK's Avatar
 
Join Date: Nov 2002
Location: Surrey, England
Posts: 8,434
Are you explicitly using memset or ZeroMemory? Something like this can also force the compiler to use memset: char a[32] = "";

If you can't stop the compiler from linking with memset (or you'd rather not) you can include your own memset function (CRTReplica.cpp in LockedList or crt.cpp in inetc has it).

This is a blog post you may like to read if you want to get rid of any MSVCRT dependencies: http://afrowsoft.blogspot.co.uk/2011...ual-c-run.html.

Stu
Afrow UK is offline   Reply With Quote
Old 29th July 2014, 11:26   #38
Anders
Moderator
 
Anders's Avatar
 
Join Date: Jun 2002
Location: ${NSISDIR}
Posts: 4,911
Quote:
Originally Posted by Afrow UK View Post
If you can't get rid of the memset reference, you can include your own memset function (CRTReplica.cpp in LockedList or crt.cpp in inetc has it).
There is no memset reference, it is the compiler itself that makes the reference internally.

It will turn "void MemSet(char*p,int c,int v) { for(..) .. }" into a call to memset even when compiling with /Zl and no CRT. The compiler just assumes that the CRT is always available.

IntOp $PostCount $PostCount + 1
Anders is offline   Reply With Quote
Old 29th July 2014, 12:02   #39
Afrow UK
Moderator
 
Afrow UK's Avatar
 
Join Date: Nov 2002
Location: Surrey, England
Posts: 8,434
Quote:
Originally Posted by Anders View Post
There is no memset reference, it is the compiler itself that makes the reference internally.
My first sentence made this quite clear that this is the case and one way to stop it. I have edited the 2nd sentence for clarity.

Quote:
Originally Posted by Anders View Post
It will turn "void MemSet(char*p,int c,int v) { for(..) .. }" into a call to memset even when compiling with /Zl and no CRT. The compiler just assumes that the CRT is always available.
Not if you use /NODEFAULTLIB.

Stu
Afrow UK is offline   Reply With Quote
Old 29th July 2014, 12:42   #40
sledgehammer_999
Junior Member
 
Join Date: Jan 2013
Posts: 27
I opened the produced dll in dependency walker and there isn't a reference to msvcrt.dll in the first level of the dependency tree. (compiled with /MD).

I then compiled it using /MT (statically link the runtime). The filesize increased ~2KB. This means that something is used from the runtime, but why doesn't dependcy walker show the dependency of msvcrt in the /MD compiled dll?

Quote:
Not if you use /NODEFAULTLIB
Tried with /Zl and /NODEFAULTLIB but I get the same error.
sledgehammer_999 is offline   Reply With Quote
Reply
Go Back   Winamp & SHOUTcast Forums > Developer Center > NSIS Discussion

Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump