I have a service that runs and accepts remote commands. I am trying to send a remote command to uninstall. This works and executes the msiexec. The msiexec gets to the point where it tries to stop the service and then hangs and eventually fails saying no privileges to stop the service.
If I run the same command from a command window it works fine.
I have tried this running the service as local system (with admin privs) as well as administrator (I enabled the administrator account)
I just can't seem to figure out why I am getting permission errors stopping the service running the msiexec
added information
We have a single service - it does required work and sleeps periodically
the service typically runs as local system
we have the installer set using custom actions - and all cases work fine - installing and uninstalling from command line - but running msiexec uninstaller initiated within the C# code has this issue
When a remote request is made to uninstall the desire is to remove the service, registry entries, and files on disk
The request is handled by the running service - it goes to the registry, finds the product entry and retrieves the uninstall command
when the uninstall is performed I use C# process and include in the StartInfo parameters the program name, arguments, and verb (defined as run as) - among other parameters
the uninstaller starts properly, does some actions, and then gets to the point of stopping the service
stopping the service sits until it times out after which it writes in the uninstall log that there may not be sufficient privileges to stop the service
The note from Quercus triggered the solution to my problem. It turns out changing to not run the uninstall as a child of the service allowed the uninstaller to perform the msiexec without issue. To accomplish this I had to change UseShellExecute from false to true.
Related
All the solutions I can find on this topic are very old and none of them appear to answer my question...
I am trying to create a windows service that can self update (or auto update by some external trigger). In the past, I had created a windows service that was installed with InstallShield and we were able to update auto update the service in a hacky way by making the service write a batch script to the local machine and then run the batch script, which would stop the service, overwrite the service executable and other files with the new ones, and restart the service. This surprisingly worked.
However, I have updated the service to use InstallUtil.exe and this auto update script no longer works... I assume it's something to do with the way InstallShield handles the service install vs how InstallUtil does it... but I can only make guesses as I don't fully understand what each is doing to the registry.
Since I can't just overwrite the files and restart the service with the InstallUtil method, I thought I'd write a batch script that runs sc.exe to stop the service, uninstall it entirely, write the new files, install the new service files, and then start it... unfortunately, I can't seem to get sc.exe to run from a windows service automatically because it requires admin permissions... I tried to force it to self-elevate to admin using this snippet, but it doesn't appear to work as a service (it works fine if I run it from command line not as a service)
if not "%1"=="am_admin" (powershell start -verb runas '%0' am_admin & exit /b)
Does anyone know how I can cause a windows service to self update? I can look into updating to a .NET Core Worker service if there is some method of self update in .NET Core that I'm unaware of... Any ideas are much appreciated... it really shouldn't be this hard to accomplish...
For reference, here is the batch script I am currently using (ignore odd variables and such as I am dynamically replacing some of them, it works great when launched manually, just doesn't work when the service tries to run it):
#echo off
setlocal enableextensions enabledelayedexpansion
::make sure to run whole script as admin (this restarts scripts as admin if not already in admin mode)
if not "%1"=="am_admin" (powershell start -verb runas '%0' am_admin & exit /b)
pushd %networkDirectory%
::stop running service
for /F "tokens=3 delims=: " %%H in ('sc query %serviceName% ^| findstr " STATE"') do (
if /I "%%H" NEQ "STOPPED" (
net stop %serviceName%
if errorlevel 1 goto :stop
)
::delete existing service after stopping
sc delete %serviceName%
)
:: install updated service files
set "releaseDir=%networkDirectory%\Release"
set "programFilesCopyDir=%ProgramFiles%\{_companyDirectory}\%serviceName%\Release"
:: copy service Release dir to local system program files
xcopy "%releaseDir%" "%programFilesCopyDir%" /S /Y /Q
::execute the install
pushd "%programFilesCopyDir%"
CALL %serviceName%.exe --install
::start service
sc start %serviceName%
For anyone else trying to accomplish this that stumbles on this... I ended up finding a solution. I use the same script posted in my question above, but I wrote code to set up a scheduled task with Windows Task Scheduler. The scheduled task runs the above script as a one time scheduled task. This works like a charm.
I used this NuGet package to write the Task Scheduler code I needed:
https://www.nuget.org/packages/TaskScheduler/2.8.20?_src=template
I have an installer with InstallPrivileges="elevated". I include inside the file table an app.EXE
<Component Id="myapp" Guid="*">
<File Id="myapp" Source="myapp.exe"/>
</Component>
using these CA to run it in an elevcated state:
<CustomAction Id="SetProp" Property="Launch" Value =""[INSTALLDIR]myapp.exe""/>
<CustomAction Id="Launch" BinaryKey="WixCA" DllEntry="CAQuietExec64" Execute="deferred" Return="ignore" Impersonate="no"/>
scheduled like this:
<Custom Action="SetProp" Before="Launch">NOT Installed</Custom>
<Custom Action="Launch" Before="InstallFinalize">NOT Installed</Custom>
but according to Uachelper class (c#) and the missing result this process is not elevated BUT verbose log and myapp.exe log shows no error.
I also tried to use a type 2 custom action (binarykey etc.), a type 18 custom action (filekey etc.) and to include an app.manifest into myapp.exe with requiredadministrator.
nothing worked to elevate myapp.exe.. during installation.
Workaround I found is to start myapp.exe manually after installation completed (go to [INSTALLDIR] and doubleclick myapp.exe, will prompt uac etc.) but I want to avoid that.
I also thought about trying to use runas but am not sure if this would work or if this is feasible.
All of this is based on http://wixtoolset.org/documentation/manual/v3/customactions/qtexec.html , https://stackoverflow.com/a/10028939/4096653 and many more questions here on SO.
What am I missing or what else can I try?
By default a custom action will be elevated only if it is deferred, not impersonated, and sequenced between InstallInitialize and InstallFinalize, and if it's a per machine install, and then it will run elevated with the system account. Whether it will actually work or not depends on whether the code is prepared to deal with being aware that it's not running as the interactive user with access to HKCU, user profile items like PersonalFolder, and the code knows where its data is. When a custom action executable gets fired off it is via the CreateProcess() type of call (NOT a shell execute call) and so there is no UAC dialog/manifest checking stuff going on. So your WiX posted looks correct if you are deferred and before InstallFinalize. I have no idea what a UACHelper class thinks if are running as system and elevated. ProcessMonitor/Explorer might tell you what your rights/tokens are.
It should work if you create an impersonated launcher custom action that does an explicit Shell Execute of your exe, because that behaves like a run from Explorer by the interactive user. It will run the app which will then ask for elevation as long as the user doing the install is admin. In code that's something like ProcessStartInfo.UseShellExecute=true, ShellExecute() Win32.
If you need an impersonated CA to be elevated the MSI needs to be running as administrator or started from a program with an elevation manifest that then runs the MSI.
You'll see lots of advice (I hope) saying you should arrange to run this automatically the first time your app runs, just have it launched and remember whether that it has been run. Running as elevated user from an MSI is too complicated unless you have an elevated launching bootstrapper.
I assume you want this to run in Execute sequence and have scheduled your custom actions in InstallExecuteSequence
It might be so that your app is not able to run as Local system user so try to switch the Impersonate to yes Impersonate="Yes"
The scenario:
I have a Java based installer.
The Java based installer calls a C# program, whose job is to create a shortcut.
The shortcut location depends on if the installer is running as administrator or as a regular user. When running as admin, I'm trying to create a shortcut to "%ALLUSERSPROFILE%\Desktop", else I write to "%USERPROFILE%\Desktop".
My impression is that the issue seems to be a loss of administrative privilege when my Java program calls my C# shortcut maker program.
Notes:
I run my Java based installer as administrator (right click, run as administrator).
I'm able to verify the installer is running with administrator privileged because I can read registry keys that require administrative privilege.
I'm calling my C# program via 'Process process = Runtime.getRuntime().exec(command);'
When running the command manually through an administrative command prompt, the command works fine. (When outputting to "%ALLUSERSPROFILE%\Desktop")
When running the the same command manually, from a normal command prompt, I get System.UnauthorizedAccessException. (Which is to be expected). The program crashes in a similar was that it does when run from the installer.
The Exception:
Unhandled Exception: System.UnauthorizedAccessException: Access is
denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED)) at
IWshRuntimeLibrary.IWshShortcut.Save()
Any thoughts on what I'm missing? The installer needs to be flexible to run as both a normal user and as an administrator. How can I ensure this behavior?
Update 1
I attached a debugger to the C# program at runtime. It is throwing a:
DirectoryNoFoundException was unhandled
The system cannot find the path specified. (Exception from HRESULT: 0x80070003)
I added "mkdir" commands before my shortcutmaker commands. The mkdir commands, just ensure that the directories exist before trying to write to them.
Rebuilt the installer, ran it and when trying to mkdir "%ALLUSERSPROFILE%\Desktop", java throws an exception of
java.io.IOException: Cannot run program "mkdir": CreateProcess error=2, The system cannot find the file specified
java.io.IOException: Cannot run program "mkdir": CreateProcess error=2, The system cannot find the file specified
at java.lang.ProcessBuilder.start(Unknown Source)
at java.lang.Runtime.exec(Unknown Source)
at java.lang.Runtime.exec(Unknown Source)
at java.lang.Runtime.exec(Unknown Source)
At this point, it looks like my process isn't getting admin access
Process process = Runtime.getRuntime().exec(command);
I'm going to see if I can find anything else.
Update 2
The following gave me some info that I tried: Enterprise Logging not translating environment variables in XML Trace Listener fileName specification
It suggested that %ALLUSERSPROFILE% was not getting translated.
Instead of %ALLUSERSPROFILE%, I got the environment variable values via:
String allUsersProfile = System.getenv("ALLUSERSPROFILE");
String userProfile = System.getenv("USERPROFILE");
I was then able to supply the actual values to the C# program. But I'm still having issues.
From a admin console I can navigate to "c:\ProgramData\Start Menu", but if I run "explorer" with administrative priviledges, I can navigate to "c:\ProgramData" but not see anything past that... Through some looking, I found out that "c:\ProgramData\Start Menu" is a protected operating system file. So I turned on the setting to see it. So now I can see it, but not go into it.
Using system internals, I elevated an explorer.exe to "system" access and still can't go into the folder (System internal elevation reference: http://verbalprocessor.com/2007/12/05/running-a-cmd-prompt-as-local-system/)
I right clicked on the folder and checked out the security tab. It looks like even my "System" user has limited access. I find this a bit baffling, that I can run the command from an admin command prompt that will write the shortcut to the desktop, but going through this other process I cannot... I also find the access to be a bit inconsistent.
I have a batch file that I have been using to install my C# Windows Services for awhile now, never had a problem until Windows 7. I have attempted to run the batch file with Administrator privileges. I have attempted to run the command prompt with admin privs, navigate to the windows service EXE and run InstallUtil there. Still doesn't work.
After reading some other suggestions I tried moving my files out of the /bin folder and running them from another location but that also didn't work.
The batch file looks like this
#ECHO OFF
REM The following directory is for .NET 2.0
set DOTNETFX2=%SystemRoot%\Microsoft.NET\Framework\v2.0.50727
set PATH=%PATH%;%DOTNETFX2%
echo Installing IEPPAMS Win Service...
echo ---------------------------------------------------
InstallUtil /i IEPPAMS_WinService1.exe
echo ---------------------------------------------------
echo Done.
and I have a install log file that I dump info to. If I just double click the .bat file I get
Running a transacted installation.
Beginning the Install phase of the
installation. See the contents of the
log file for the
C:\Users\Justin\Desktop\service
test\IEPPAMS_WinService1.exe
assembly's progress. The file is
located at
C:\Users\Justin\Desktop\service
test\IEPPAMS_WinService1.InstallLog.
An exception occurred during the
Install phase.
System.InvalidOperationException:
Cannot open Service Control Manager on
computer '.'. This operation might
require other privileges. The inner
exception
System.ComponentModel.Win32Exception
was thrown with the following error
message: Access is denied.
The Rollback phase of the installation
is beginning. See the contents of the
log file for the
C:\Users\Justin\Desktop\service
test\IEPPAMS_WinService1.exe
assembly's progress. The file is
located at
C:\Users\Justin\Desktop\service
test\IEPPAMS_WinService1.InstallLog.
The Rollback phase completed
successfully.
The transacted install has completed.
When I run the .bat file with admin privileges nothing is written to the log file, and the service is still not installed.
Any thoughts? Is there a new way to install services in Windows 7?
Right click on the batch file and run it as Administrator.
You are most likely running into battle with the new security model (User Account Control) from Windows Vista and Windows 7. Even if you are running as an account that has Admin rights you will still need to elevate to do some (most) administrative activities. (Yes it is possible to disable this feature, but don't)
UAC (MSDN)
UAC (Wikipedia)
InstallUtil (MSDN)
Edit... The correct commandline is InstallUtil YourApp.exe. The /i does not look to be a vaild switch for InstallUtil.
So I was able to fix the problem by typing in the command line the entire path to InstallUtil and it worked. So after navigating to the folder that had my EXE I typed the following:
C:\Windows\Microsoft.NET\Framework\v4.0.21006\installutil.exe
IEPPAMS_WinService1.exe
Not sure why I have to do that in Windows 7 now when I never had to in XP, but oh well. Thanks for all the suggestions!
When I run the .bat file with admin privileges nothing is written to the log file, and the service is still not installed.
First off, you HAVE to run as admin permissions.
Second, when you "Run as Administrator", it actually changes the directory to c:\windows\system32 as the initial directory ( no idea why ), which would probably explain why running as admin causes no log file. Manually change to the path IEPPAMS_WinService1.exe resides in that the start of your script.
I'm able to successfully uninstall a third-party application via the command line and via a custom Inno Setup installer.
Command line Execution:
MSIEXEC.exe /x {14D74337-01C2-4F8F-B44B-67FC613E5B1F} /qn
Inno Setup Command:
[Run]
Filename: msiexec.exe; Flags: runhidden waituntilterminated;
Parameters: "/x {{14D74337-01C2-4F8F-B44B-67FC613E5B1F} /qn";
StatusMsg: "Uninstalling Service...";
I am also able to uninstall the application programmatically when executing the following C# code in debug mode.
C# Code:
string fileName = "MSIEXEC.exe";
string arguments = "/x {14D74337-01C2-4F8F-B44B-67FC613E5B1F} /qn";
ProcessStartInfo psi = new ProcessStartInfo(fileName, arguments)
{
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardOutput = true
};
Process process = Process.Start(psi);
string errorMsg = process.StandardOutput.ReadToEnd();
process.WaitForExit();
The same C# code, however, produces the following failure output when run as a compiled, deployed Windows Service:
"This action is only valid for products that are currently installed."
Additional Comments:
The Windows Service which is issuing
the uninstall command is running on
the same machine as the code being
tested in Debug Mode. The Windows
Service is running/logged on as the
Local system account.
I have consulted my application logs
and I have validated that the
executed command arguments are thhe
same in both debug and release mode.
I have consulted the Event Viewer
but it doesn't offer any clues.
Thoughts? Any help would be greatly appreciated. Thanks.
Step 1: Check the MSI error log files
I'm suspicious that your problem is due to running as LocalSystem.
The Local System account is not the same as a normal user account which happens to have admin rights. It has no access to the network, and its interaction with the registry and file system is quite different.
From memory any requests to read/write to your 'home directory' or HKCU under the registry actually go into either the default user profile, or in the case of temp dirs, c:\windows\temp
I've come across similar problems in the past with installation, a customer was using the SYSTEM account to install and this was causing all sorts of permission problems for non-administrative users.
MSI log files aren't really going to help if the application doesn't appear "installed", I'd suggest starting with capturing the output of MSIINV.EXE under the system account, that will get you an "Inventory" of the currently installed programs (or what that user sees installed) http://blogs.msdn.com/brada/archive/2005/06/24/432209.aspx
I think you probably need to go back to the drawing board and see if you really need the windows service to do the uninstall. You'll probably come across all sorts of Vista UAC issues if you haven't already...
Thanks to those offering help. This appears to be a permissions issue. I have updated my service to run under an Administrator account and it was able to successfully uninstall the third-party application. To Orion's point, though the Local System account is a powerful account that has full access to the system -- http://technet.microsoft.com/en-us/library/cc782435.aspx -- it doesn't seem to have the necessary rights to perform the uninstall.
[See additional comments for full story regarding the LocalSystem being able to uninstall application for which it installed.]
This is bizarre. LocalSystem definitely has the privileges to install applications (that's how Windows Update and software deployment in Active Directory work), so it should be able to uninstall as well.
Perhaps the application is initially installed per-user instead of per-machine?
#Paul Lalonde
The app's installer is wrapped within a custom InnoSetup Installer. The InnoSetup installer, in turn, is manually executed by the logged in user. That said, the uninstall is trigged by a service running under the Local System account.
Apparently, you were on to something. I put together a quick test which had the service running under the LocalSystem account install as well as uninstall the application and everything worked flawlessly. You were correct. The LocalSystem account has required uninstall permissions for applications in which it installs. You saved the day. Thanks for the feedback!