We weren't using multi-threading either, but your code still runs on a
thread (even if it's the only one in the process), so it could still be
causing a problem. Read the "Pitfalls to watch for" section at
http://pluralsight.com/wiki/default....sImpersonation.
Note that I'm fairly certain that the suggestion to use
createprocessasuser no longer works on SP2 - that's what caused me to
have to do a rewrite in the first place. This is documented on MSDN,
if you care to search for it.
OK...Here's the code that I'm 100% sure works. I've stripped out a lot
of the stuff that was implementation specific, but I think all the
pertinent parts are in here:
Impersonation class: This one does the actual installing and
uninstalling.
using System;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Windows.Forms;
using System.Management;
using System.Collections;
using ROOT.CIMV2.Win32;
namespace Extensions.Client.ApplicationUpdater
{
public class Impersonate
{
[DllImport("advapi32.dll", SetLastError=true)]
private static extern bool LogonUser(String lpszUsername, String
lpszDomain, String lpszPassword,
int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
public static void UninstallProduct(string productCode, string name,
string version)
{
//get error codes at
http://msdn.microsoft.com/library/de...rror_codes.asp
const int ERROR_SUCCESS_REBOOT_REQUIRED = 3010;
const int ERROR_SUCCESS_REBOOT_INITIATED = 1641;
const int ERROR_SUCCESS = 0;
uint retValue = 0;
try
{
ManagementScope scope = new ManagementScope();
ROOT.CIMV2.Win32.Product prod = new Product(productCode, name,
version);
prod.Scope = scope;
retValue = prod.Uninstall();
}
catch(Exception e)
{
MessageBox.Show("Uninstall failed. Verify that " + name + " is
installed for all users on this machine. Details: " + e.Message);
}
if (retValue != ERROR_SUCCESS_REBOOT_REQUIRED && retValue !=
ERROR_SUCCESS_REBOOT_INITIATED && retValue != ERROR_SUCCESS)
{
MessageBox.Show("Uninstall failed. Please contact helpdesk to get
updates. The returned error was: " + retValue);
}
}
public static void InstallProduct(string path, string options, bool
allUsers)
{
//get error codes at
http://msdn.microsoft.com/library/de...rror_codes.asp
uint retValue = 0;
const int ERROR_SUCCESS_REBOOT_REQUIRED = 3010;
const int ERROR_SUCCESS_REBOOT_INITIATED = 1641;
const int ERROR_SUCCESS = 0;
try
{
if (System.IO.File.Exists(path))
{
retValue = Product.Install(allUsers, options, path);
if (retValue != ERROR_SUCCESS_REBOOT_REQUIRED && retValue !=
ERROR_SUCCESS_REBOOT_INITIATED && retValue != ERROR_SUCCESS)
{
MessageBox.Show("Install failed. Please contact helpdesk to get
updates. The returned error was: " + retValue);
}
}
else
MessageBox.Show("Couldn't find file " + path + ". Please contact
helpdesk to get updates.");
}
catch(Exception e)
{
MessageBox.Show("Install failed. Please contact helpdesk to get
updates. Details: " + e.Message);
}
}
public static IntPtr LogonUser(string UserName, string Password,
string Domain)
{
IntPtr token = IntPtr.Zero;
try
{
// Call LogonUser to obtain a handle to an access token.
LogonUser(UserName, Domain, Password, 2, 1, ref token);
return token;
}
catch
{
return IntPtr.Zero;
}
}
}
}
And here how the impersonation class was used to actually install the
product:
private void Install()
{
try
{
if (!System.IO.File.Exists(this.msiPath))
{
MessageBox.Show("Could not find the installation file " +
this.msiPath + ". Please contact the helpdesk to get updates.", "New
version of " + this.applicationTitle + " not found.");
return;
}
StatusWindow statusForm = null;
statusForm = new StatusWindow();
statusForm.Status = "Preparing to install " + this.applicationTitle +
"...";
statusForm.Show();
statusForm.Status = "Logging On...";
statusForm.PerformStep();
IntPtr token = Impersonate.LogonUser(UserName, Password, Domain);
if (token != IntPtr.Zero)
{
WindowsIdentity newId = new WindowsIdentity(token);
WindowsImpersonationContext impersonatedUser = newId.Impersonate();
//EVERTHING YOU DO FROM HERE ON OUT IS IMPERSONATED
//You probably can ignore all this productCode stuff...It's a long
story. But if your MSI is set to remove previous versions, you don't
need this.
if (this.productCode != null && this.productCode.Length > 0)
{
statusForm.Status = "Uninstalling old version of " +
this.applicationTitle + " ...";
statusForm.PerformStep();
Impersonate.UninstallProduct(this.productCode,
this.applicationTitle, this.oldVersion);
}
statusForm.Status = "Installing new version of " +
this.applicationTitle + "...";
statusForm.PerformStep();
//The second parameter are the options sent to msiexec.
Impersonate.InstallProduct(this.msiPath, "REBOOT=R", true);
impersonatedUser.Undo();
//BACK TO RUNNING UNDER THE ORIGINAL USER
}
else
{
MessageBox.Show("Impersonate user failed. Please contact HelpDesk
to obtain the latest version.");
}
statusForm.Close();
}
catch(Exception e)
{
MessageBox.Show(this.applicationTitle + " update failed: " + e);
WriteEvent(e);
}
}
I'll look around for the example of doing it with a process.
Good luck,
Jared