how to write tests that impersonates different users? - c#

My Winforms app set permissions based on the group membership found in the current process.
I just made a unit test in MSTEST.
I'd like to run it as other users so I can verify the expected behavior.
Here's what I'm kind of shooting for:
[TestMethod]
public void SecuritySummaryTest1()
{
Impersonate(#"SomeDomain\AdminUser", password);
var target = new DirectAgentsSecurityManager();
string actual = target.SecuritySummary;
Assert.AreEqual(
#"Default=[no]AccountManagement=[no]MediaBuying=[no]AdSales=[no]Accounting=[no]Admin=[YES]", actual);
}
[TestMethod]
public void SecuritySummaryTest2()
{
Impersonate(#"SomeDomain\AccountantUser", password);
var target = new DirectAgentsSecurityManager();
string actual = target.SecuritySummary;
Assert.AreEqual(
#"Default=[no]AccountManagement=[YES]MediaBuying=[no]AdSales=[no]Accounting=[YES]Admin=[NO]", actual);
}

public class UserCredentials
{
private readonly string _domain;
private readonly string _password;
private readonly string _username;
public UserCredentials(string domain, string username, string password)
{
_domain = domain;
_username = username;
_password = password;
}
public string Domain { get { return _domain; } }
public string Username { get { return _username; } }
public string Password { get { return _password; } }
}
public class UserImpersonation : IDisposable
{
private readonly IntPtr _dupeTokenHandle = new IntPtr(0);
private readonly IntPtr _tokenHandle = new IntPtr(0);
private WindowsImpersonationContext _impersonatedUser;
public UserImpersonation(UserCredentials credentials)
{
const int logon32ProviderDefault = 0;
const int logon32LogonInteractive = 2;
const int securityImpersonation = 2;
_tokenHandle = IntPtr.Zero;
_dupeTokenHandle = IntPtr.Zero;
if (!Advapi32.LogonUser(credentials.Username, credentials.Domain, credentials.Password,
logon32LogonInteractive, logon32ProviderDefault, out _tokenHandle))
{
var win32ErrorNumber = Marshal.GetLastWin32Error();
// REVIEW: maybe ImpersonationException should inherit from win32exception
throw new ImpersonationException(win32ErrorNumber, new Win32Exception(win32ErrorNumber).Message,
credentials.Username, credentials.Domain);
}
if (!Advapi32.DuplicateToken(_tokenHandle, securityImpersonation, out _dupeTokenHandle))
{
var win32ErrorNumber = Marshal.GetLastWin32Error();
Kernel32.CloseHandle(_tokenHandle);
throw new ImpersonationException(win32ErrorNumber, "Unable to duplicate token!", credentials.Username,
credentials.Domain);
}
var newId = new WindowsIdentity(_dupeTokenHandle);
_impersonatedUser = newId.Impersonate();
}
public void Dispose()
{
if (_impersonatedUser != null)
{
_impersonatedUser.Undo();
_impersonatedUser = null;
if (_tokenHandle != IntPtr.Zero)
Kernel32.CloseHandle(_tokenHandle);
if (_dupeTokenHandle != IntPtr.Zero)
Kernel32.CloseHandle(_dupeTokenHandle);
}
}
}
internal static class Advapi32
{
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool DuplicateToken(IntPtr ExistingTokenHandle, int SECURITY_IMPERSONATION_LEVEL,
out IntPtr DuplicateTokenHandle);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword,
int dwLogonType, int dwLogonProvider, out IntPtr phToken);
}
internal static class Kernel32
{
[DllImport("kernel32.dll", SetLastError = true)]
[return : MarshalAs(UnmanagedType.Bool)]
public static extern bool CloseHandle(IntPtr hObject);
}
I didn't include the implementation of ImpersonationException but it's not important. It doesn't do anything special.

You can also set the current principal directly if that's sufficient for your use case:
System.Threading.Thread.CurrentPrincipal
= new WindowsPrincipal(new WindowsIdentity("testuser#contoso.com"));
The principal is restored after each test method according to this connect page.
Note that this method won't work if used with web service clients that check the principal (for this use case, Jim Bolla's solution works just fine).

You should use Mock objects to simulate dependent objects in different states.
See moq for an example of a mocking framework:
You would need to abstract out the bit that provides the current user behind an interface. And pass in a mock of that interface to the class under test.

Use SimpleImpersonation.
Run Install-Package SimpleImpersonation to install the nuget package.
Then
var credentials = new UserCredentials(domain, username, password);
Impersonation.RunAsUser(credentials, LogonType.NewCredentials, () =>
{
// Body of the unit test case.
});
This is the most simple and elegant solution.

Another thing to add to Markus's solution, you may also need to set HttpContext.Current.User to the Thread.CurrentPrincipal you are creating/impersonating for certain calls to the RoleManager (eg: Roles.GetRolesForUser(Identity.Name) ) If you use the parameterless version of the method this is not needed but I have an authorization infrastructure in place that requires a username to be passed.
Calling that method signature with an impersonated Thread.CurrentPrincipal will fail with "Method is only supported if the user name parameter matches the user name in the current Windows Identity". As the message suggests, there is an internal check in the WindowsTokenRoleProvider code against "HttpContext.Current.Identity.Name". The method fails if they don't match.
Here's sample code for an ApiController demonstrating authorization of an Action. I use impersonation for unit and integration testing so I can QA under different AD Roles to ensure security is working before deployment.
using System.Web
List<string> WhoIsAuthorized = new List<string>() {"ADGroup", "AdUser", "etc"};
public class MyController : ApiController {
public MyController() {
#if TEST
var myPrincipal = new WindowsPrincipal(new WindowsIdentity("testuser#contoso.com"));
System.Threading.Thread.CurrentPrincipal = myPrincipal;
HttpContext.Current.User = myPrincipal;
#endif
}
public HttpResponseMessage MyAction() {
var userRoles = Roles.GetRolesForUser(User.Identity.Name);
bool isAuthorized = userRoles.Any(role => WhoIsAuthorized.Contains(role));
}
}
Hope this helps someone else :)

Related

How can I use impersonation to manipulate files/directories on a remote machine with UNC?

I need to download files from a server to a shared drive directory, creating the directory if it doesn't exist. There are a few things making this more complicated:
I do not have write access (nor does the account that will run the job in UAT/Prod) to the shared drive directory.
The Service account that does have write access does not have any privileges anywhere but the shared drive directory.
I attempt to impersonate, as so:
class Impersonation
{
const int LOGON32_LOGON_NETWORK = 3;
const int LOGON_TYPE_NEW_CREDENTIALS = 9;
const int LOGON32_PROVIDER_WINNT50 = 3;
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool LogonUser(string pszUsername, string pszDomain, string pszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public extern static bool CloseHandle(IntPtr handle);
public static void Impersonate(string domain, string user, string password, Action act)
{
//if no user specified, don't impersonate
if (user.Trim() == "")
{
act();
return;
}
WindowsImpersonationContext impersonationContext = null;
IntPtr token = IntPtr.Zero;
try
{
//if no domain specified, default it to current machine
if (domain.Trim() == "")
{
domain = System.Environment.MachineName;
}
bool result = LogonUser(user, domain, password, LOGON_TYPE_NEW_CREDENTIALS, LOGON32_PROVIDER_WINNT50, ref token);
WindowsIdentity wi = new WindowsIdentity(token);
impersonationContext = WindowsIdentity.Impersonate(token);
act();
}
catch (Exception ex)
{
if (impersonationContext != null)
{
impersonationContext.Undo();
impersonationContext = null;
}
//if something went wrong, try it as the running user just in case
act();
}
finally
{
if (impersonationContext != null)
{
impersonationContext.Undo();
impersonationContext = null;
}
if (token != IntPtr.Zero)
{
CloseHandle(token);
token = IntPtr.Zero;
}
}
}
}
And a piece of the the actual calling code is (in another class):
private static void CreateDirectoryIfNotExist(string directory, string domain, string username, string password)
{
Impersonation.Impersonate(domain, username, password, () => CreateIfNotExist(directory));
}
private static void CreateIfNotExist(string dir)
{
if (!Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
}
}
If I run it with the proper login info for the service account, I get an Exception on the Directory.CreateDirectory(string) call:
{System.IO.IOException: This user isn't allowed to sign in to this computer.}
I'm guessing this means the service account isn't allowed to log in to the executing machine, which I already knew. But really, there's no reason it needs to log in to the executing machine. Is there a way I can use impersonation to log on to a remote machine and execute the commands from there?
You cannot do it with impersonation if the account cannot log on. Impersonation requires the thread to run under the user credentials. That is why the LogonUser fails.
You can use the WNetAddConnection2 function that is used to establish a connection to a network resource.
Here is a sample for your CreateDirectoryIfNotExist function using this approach:
public static void CreateDirectoryIfNotExists(string directory, string sharePath, string username, string password)
{
NETRESOURCE nr = new NETRESOURCE();
nr.dwType = ResourceType.RESOURCETYPE_DISK;
nr.lpLocalName = null;
nr.lpRemoteName = sharePath;
nr.lpProvider = null;
int result = WNetAddConnection2(nr, password, username, 0);
string directoryFullPath = Path.Combine(sharePath, directory);
if (!Directory.Exists(directoryFullPath))
{
Directory.CreateDirectory(directoryFullPath);
}
}
To be able to do the system call you need also the following definitions from pinvoke.net.
[StructLayout(LayoutKind.Sequential)]
private class NETRESOURCE
{
public ResourceScope dwScope = 0;
public ResourceType dwType = 0;
public ResourceDisplayType dwDisplayType = 0;
public ResourceUsage dwUsage = 0;
[MarshalAs(UnmanagedType.LPStr)] public string lpLocalName = null;
[MarshalAs(UnmanagedType.LPStr)] public string lpRemoteName = null;
[MarshalAs(UnmanagedType.LPStr)] public string lpComment = null;
[MarshalAs(UnmanagedType.LPStr)] public string lpProvider;
};
public enum ResourceScope
{
RESOURCE_CONNECTED = 1,
RESOURCE_GLOBALNET,
RESOURCE_REMEMBERED,
RESOURCE_RECENT,
RESOURCE_CONTEXT
};
public enum ResourceType
{
RESOURCETYPE_ANY,
RESOURCETYPE_DISK,
RESOURCETYPE_PRINT,
RESOURCETYPE_RESERVED
};
public enum ResourceUsage
{
RESOURCEUSAGE_CONNECTABLE = 0x00000001,
RESOURCEUSAGE_CONTAINER = 0x00000002,
RESOURCEUSAGE_NOLOCALDEVICE = 0x00000004,
RESOURCEUSAGE_SIBLING = 0x00000008,
RESOURCEUSAGE_ATTACHED = 0x00000010,
RESOURCEUSAGE_ALL = (RESOURCEUSAGE_CONNECTABLE | RESOURCEUSAGE_CONTAINER | RESOURCEUSAGE_ATTACHED),
};
public enum ResourceDisplayType
{
RESOURCEDISPLAYTYPE_GENERIC,
RESOURCEDISPLAYTYPE_DOMAIN,
RESOURCEDISPLAYTYPE_SERVER,
RESOURCEDISPLAYTYPE_SHARE,
RESOURCEDISPLAYTYPE_FILE,
RESOURCEDISPLAYTYPE_GROUP,
RESOURCEDISPLAYTYPE_NETWORK,
RESOURCEDISPLAYTYPE_ROOT,
RESOURCEDISPLAYTYPE_SHAREADMIN,
RESOURCEDISPLAYTYPE_DIRECTORY,
RESOURCEDISPLAYTYPE_TREE,
RESOURCEDISPLAYTYPE_NDSCONTAINER
};
public enum ResourceConnection
{
CONNECT_UPDATE_PROFILE = 1,
CONNECT_UPDATE_RECENT = 2,
CONNECT_TEMPORARY = 4,
CONNECT_INTERACTIVE = 8,
CONNECT_PROMPT = 0X10,
CONNECT_REDIRECT = 0X80,
CONNECT_CURRENT_MEDIA = 0X200,
CONNECT_COMMAND_LINE = 0X800,
CONNECT_CMD_SAVECRED = 0X1000,
CONNECT_CRED_RESET = 0X2000
};
[DllImport("mpr.dll", CharSet = System.Runtime.InteropServices.CharSet.Ansi)]
private static extern int WNetAddConnection2(NETRESOURCE lpNetResource,
[MarshalAs(UnmanagedType.LPStr)] string lpPassword,
[MarshalAs(UnmanagedType.LPStr)] string lpUserName, int dwFlags);
You can add this definitions to the same class as your function.
Here are also links to two older post which also use the same approach.
Accessing a Shared File (UNC) From a Remote, Non-Trusted Domain With Credentials
How do I access a file share programmatically
You can solve this problem like I solved it. Below are the steps:
Step 1: Open IIS. Then add a virtual directory in your web application.
Step 2: Then map your shared path with the virtual directory added in Step 1
Step 3: Now click on Connect as..
Step 4: Set the credential of your desired user by which you want to connect your shared path.
Step 5: Now the IIS configuration part is over and you need to start using src="Image/Image.png" in your html or start manipulating "SharedFolder/YourFile.ext".

Using Chromedriver with Developed Extension and Force Install Group Policy

I am working on a web application that has the following environment setup:
The website is designed to work in Chrome
The website depends on a Chrome Extension developed in-house and hosted on the Chrome Play Store
To make getting the extension easier, our systems team has setup the ExtensionInstallForcelist Group Policy (more information here http://dev.chromium.org/administrators/policy-list-3#ExtensionInstallForcelist). This makes it so Chrome can get the extension and update it as needed without manual user interaction
This is all setup on imaged devices (so separate devices, with separate browsers and group policies, that each get the extension on their own)
The problem I'm facing is when trying to use a Chromedriver instance with this group policy.
With the policy enabled, I am getting an error that says "Failed to load extension from: . (extension ID ) is blocked by the administrator." when running our tests.
This seems to happen because we add the extension to our Chromedriver through the Chrome Options (add Extension).
While I can manually turn off the Group Policy on our imaged devices to get around this, I was wondering if there is any setting I can add to the group policy to make it allow the installation of our extension in the Chromedriver?
I've tried a number of ways to get around this otherwise that didn't seem to work:
Command Line altering of the registry values set by the Group Policy - didn't work as, although altering the registry values worked, I still recevied the error on running tests
PowerShell altering of the group policy - won't work as it requires additional install of the Get Group Policy cmdlets
Using the GPMC C# libraries - the libraries seem very much out of date at this point (some only use .NET 2.0). I was able to setup some code to use the classes, but I receive a "reference missing" exception for creating a GPODomain object even though I have the reference in my solution
Thank you all in advance for any suggestions on getting making Chromedriver work with the force install group policy.
After a lot of looking at different answers around the web, I found this solution works.
Create a GroupPolicy class to handle interacting with it:
using Microsoft.Win32;
using Microsoft.Win32.SafeHandles;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Automation.Core
{
[ComImport, Guid("EA502722-A23D-11d1-A7D3-0000F87571E3")]
internal class GPClass
{
}
[ComImport, Guid("EA502723-A23D-11d1-A7D3-0000F87571E3"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IGroupPolicyObject
{
uint New([MarshalAs(UnmanagedType.LPWStr)] string domainName, [MarshalAs(UnmanagedType.LPWStr)] string displayName, uint flags);
uint OpenDSGPO([MarshalAs(UnmanagedType.LPWStr)] string path, uint flags);
uint OpenLocalMachineGPO(uint flags);
uint OpenRemoteMachineGPO([MarshalAs(UnmanagedType.LPWStr)] string computerName, uint flags);
uint Save([MarshalAs(UnmanagedType.Bool)] bool machine, [MarshalAs(UnmanagedType.Bool)] bool add, [MarshalAs(UnmanagedType.LPStruct)] Guid extension, [MarshalAs(UnmanagedType.LPStruct)] Guid app);
uint Delete();
uint GetName([MarshalAs(UnmanagedType.LPWStr)] StringBuilder name, int maxLength);
uint GetDisplayName([MarshalAs(UnmanagedType.LPWStr)] StringBuilder name, int maxLength);
uint SetDisplayName([MarshalAs(UnmanagedType.LPWStr)] string name);
uint GetPath([MarshalAs(UnmanagedType.LPWStr)] StringBuilder path, int maxPath);
uint GetDSPath(uint section, [MarshalAs(UnmanagedType.LPWStr)] StringBuilder path, int maxPath);
uint GetFileSysPath(uint section, [MarshalAs(UnmanagedType.LPWStr)] StringBuilder path, int maxPath);
uint GetRegistryKey(uint section, out IntPtr key);
uint GetOptions(out uint options);
uint SetOptions(uint options, uint mask);
uint GetType(out IntPtr gpoType);
uint GetMachineName([MarshalAs(UnmanagedType.LPWStr)] StringBuilder name, int maxLength);
uint GetPropertySheetPages(out IntPtr pages);
}
public enum GroupPolicySection
{
Root = 0,
User = 1,
Machine = 2,
}
public abstract class GroupPolicyObject
{
protected const int MaxLength = 1024;
/// <summary>
/// The snap-in that processes .pol files
/// </summary>
private static readonly Guid RegistryExtension = new Guid(0x35378EAC, 0x683F, 0x11D2, 0xA8, 0x9A, 0x00, 0xC0, 0x4F, 0xBB, 0xCF, 0xA2);
/// <summary>
/// This application
/// </summary>
private static readonly Guid LocalGuid = new Guid(GetAssemblyAttribute<GuidAttribute>(Assembly.GetExecutingAssembly()).Value);
protected IGroupPolicyObject Instance = (IGroupPolicyObject) new GPClass();
static T GetAssemblyAttribute<T>(ICustomAttributeProvider assembly) where T : Attribute
{
object[] attributes = assembly.GetCustomAttributes(typeof(T), true);
if (attributes.Length == 0)
return null;
return (T)attributes[0];
}
public void Save()
{
var result = Instance.Save(true, true, RegistryExtension, LocalGuid);
if (result != 0)
{
throw new Exception("Error saving machine settings");
}
result = Instance.Save(false, true, RegistryExtension, LocalGuid);
if (result != 0)
{
throw new Exception("Error saving user settings");
}
}
public RegistryKey GetRootRegistryKey(GroupPolicySection section)
{
IntPtr key;
var result = Instance.GetRegistryKey((uint)section, out key);
if (result != 0)
{
throw new Exception(string.Format("Unable to get section '{0}'", Enum.GetName(typeof(GroupPolicySection), section)));
}
var handle = new SafeRegistryHandle(key, true);
return RegistryKey.FromHandle(handle, RegistryView.Default);
}
}
public class GroupPolicyObjectSettings
{
public readonly bool LoadRegistryInformation;
public readonly bool Readonly;
public GroupPolicyObjectSettings(bool loadRegistryInfo = true, bool readOnly = false)
{
LoadRegistryInformation = loadRegistryInfo;
Readonly = readOnly;
}
private const uint RegistryFlag = 0x00000001;
private const uint ReadonlyFlag = 0x00000002;
internal uint Flag
{
get
{
uint flag = 0x00000000;
if (LoadRegistryInformation)
{
flag |= RegistryFlag;
}
if (Readonly)
{
flag |= ReadonlyFlag;
}
return flag;
}
}
}
public class ComputerGroupPolicyObject : GroupPolicyObject
{
public readonly bool IsLocal;
public ComputerGroupPolicyObject(GroupPolicyObjectSettings options = null)
{
options = options ?? new GroupPolicyObjectSettings();
var result = Instance.OpenLocalMachineGPO(options.Flag);
if (result != 0)
{
throw new Exception("Unable to open local machine GPO");
}
IsLocal = true;
}
public static void SetPolicySetting(string registryInformation, string settingValue, RegistryValueKind registryValueKind)
{
string valueName;
GroupPolicySection section;
string key = Key(registryInformation, out valueName, out section);
// Thread must be STA
Exception exception = null;
var t = new Thread(() =>
{
try
{
var gpo = new ComputerGroupPolicyObject();
using (RegistryKey rootRegistryKey = gpo.GetRootRegistryKey(section))
{
// Data can't be null so we can use this value to indicate key must be delete
if (settingValue == null)
{
using (RegistryKey subKey = rootRegistryKey.OpenSubKey(key, true))
{
if (subKey != null)
{
subKey.DeleteValue(valueName);
}
}
}
else
{
using (RegistryKey subKey = rootRegistryKey.CreateSubKey(key))
{
subKey.SetValue(valueName, settingValue, registryValueKind);
}
}
}
gpo.Save();
}
catch (Exception ex)
{
exception = ex;
}
});
t.SetApartmentState(ApartmentState.STA);
t.Start();
t.Join();
if (exception != null)
throw exception;
}
public static object GetPolicySetting(string registryInformation)
{
string valueName;
GroupPolicySection section;
string key = Key(registryInformation, out valueName, out section);
// Thread must be STA
object result = null;
var t = new Thread(() =>
{
var gpo = new ComputerGroupPolicyObject();
using (RegistryKey rootRegistryKey = gpo.GetRootRegistryKey(section))
{
// Data can't be null so we can use this value to indicate key must be delete
using (RegistryKey subKey = rootRegistryKey.OpenSubKey(key, true))
{
if (subKey == null)
{
result = null;
}
else
{
result = subKey.GetValue(valueName);
}
}
}
});
t.SetApartmentState(ApartmentState.STA);
t.Start();
t.Join();
return result;
}
public static string Key(string registryInformation, out string value, out GroupPolicySection section)
{
// Parse parameter of format HKCU\Software\Policies\Microsoft\Windows\Personalization!NoChangingSoundScheme
string[] split = registryInformation.Split('!');
string key = split[0];
string hive = key.Substring(0, key.IndexOf('\\'));
key = key.Substring(key.IndexOf('\\') + 1);
value = split[1];
if (hive.Equals(#"HKLM", StringComparison.OrdinalIgnoreCase)
|| hive.Equals(#"HKEY_LOCAL_MACHINE", StringComparison.OrdinalIgnoreCase))
{
section = GroupPolicySection.Machine;
}
else
{
section = GroupPolicySection.User;
}
return key;
}
}
}
Then turn off the Force Install Chrome Extension Group Policy with a call like this:
[STAThread]
private static bool DisabledChromeExtensionGPO()
{
var PolicyExists = ComputerGroupPolicyObject.GetPolicySetting(#"HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Google\Chrome\ExtensionInstallForcelist!1");
if (PolicyExists != null)
{
ComputerGroupPolicyObject.SetPolicySetting(#"HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Google\Chrome\ExtensionInstallForcelist!1", "null", RegistryValueKind.String);
}
return true;
}
While the policy will still have its key in regedit listed, the value of the key will be set to null.
Setting the value to null allowed the Chrome Driver to load our local Chrome Extension file without issue at this point.

How can I detect if another process is elevated? [duplicate]

I have an application that needs to detect whether or not it is running with elevated privileges or not. I currently have code set up like this:
static bool IsAdministrator()
{
WindowsIdentity identity = WindowsIdentity.GetCurrent();
WindowsPrincipal principal = new WindowsPrincipal(identity);
return principal.IsInRole (WindowsBuiltInRole.Administrator);
}
This works to detect if a user is an administrator or not, but doesn't work if running as an administrator without elevation. (For example in vshost.exe).
How can I determine whether or not elevation is [already in force or] possible?
Try this out:
using Microsoft.Win32;
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Principal;
public static class UacHelper
{
private const string uacRegistryKey = "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System";
private const string uacRegistryValue = "EnableLUA";
private static uint STANDARD_RIGHTS_READ = 0x00020000;
private static uint TOKEN_QUERY = 0x0008;
private static uint TOKEN_READ = (STANDARD_RIGHTS_READ | TOKEN_QUERY);
[DllImport("advapi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool OpenProcessToken(IntPtr ProcessHandle, UInt32 DesiredAccess, out IntPtr TokenHandle);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool GetTokenInformation(IntPtr TokenHandle, TOKEN_INFORMATION_CLASS TokenInformationClass, IntPtr TokenInformation, uint TokenInformationLength, out uint ReturnLength);
public enum TOKEN_INFORMATION_CLASS
{
TokenUser = 1,
TokenGroups,
TokenPrivileges,
TokenOwner,
TokenPrimaryGroup,
TokenDefaultDacl,
TokenSource,
TokenType,
TokenImpersonationLevel,
TokenStatistics,
TokenRestrictedSids,
TokenSessionId,
TokenGroupsAndPrivileges,
TokenSessionReference,
TokenSandBoxInert,
TokenAuditPolicy,
TokenOrigin,
TokenElevationType,
TokenLinkedToken,
TokenElevation,
TokenHasRestrictions,
TokenAccessInformation,
TokenVirtualizationAllowed,
TokenVirtualizationEnabled,
TokenIntegrityLevel,
TokenUIAccess,
TokenMandatoryPolicy,
TokenLogonSid,
MaxTokenInfoClass
}
public enum TOKEN_ELEVATION_TYPE
{
TokenElevationTypeDefault = 1,
TokenElevationTypeFull,
TokenElevationTypeLimited
}
public static bool IsUacEnabled
{
get
{
RegistryKey uacKey = Registry.LocalMachine.OpenSubKey(uacRegistryKey, false);
bool result = uacKey.GetValue(uacRegistryValue).Equals(1);
return result;
}
}
public static bool IsProcessElevated
{
get
{
if (IsUacEnabled)
{
IntPtr tokenHandle;
if (!OpenProcessToken(Process.GetCurrentProcess().Handle, TOKEN_READ, out tokenHandle))
{
throw new ApplicationException("Could not get process token. Win32 Error Code: " + Marshal.GetLastWin32Error());
}
TOKEN_ELEVATION_TYPE elevationResult = TOKEN_ELEVATION_TYPE.TokenElevationTypeDefault;
int elevationResultSize = Marshal.SizeOf((int)elevationResult);
uint returnedSize = 0;
IntPtr elevationTypePtr = Marshal.AllocHGlobal(elevationResultSize);
bool success = GetTokenInformation(tokenHandle, TOKEN_INFORMATION_CLASS.TokenElevationType, elevationTypePtr, (uint)elevationResultSize, out returnedSize);
if (success)
{
elevationResult = (TOKEN_ELEVATION_TYPE)Marshal.ReadInt32(elevationTypePtr);
bool isProcessAdmin = elevationResult == TOKEN_ELEVATION_TYPE.TokenElevationTypeFull;
return isProcessAdmin;
}
else
{
throw new ApplicationException("Unable to determine the current elevation.");
}
}
else
{
WindowsIdentity identity = WindowsIdentity.GetCurrent();
WindowsPrincipal principal = new WindowsPrincipal(identity);
bool result = principal.IsInRole(WindowsBuiltInRole.Administrator);
return result;
}
}
}
}
(new answer six years after the question was asked)
Disclaimer: This is just something that happened to work on my particular OS with my particular settings with my particular user:
using System.Security.Principal;
// ...
static bool IsElevated
{
get
{
return WindowsIdentity.GetCurrent().Owner
.IsWellKnown(WellKnownSidType.BuiltinAdministratorsSid);
}
}
So when I run this "Run as administrator", the property get accessor returns true. When running normally (even if my user "is" administrator, just not running this particular application "as administrator"), it returns false.
This seems much simpler than many other answers.
I have no idea if there are cases where this fails.
PS! This also seems OK:
static bool IsElevated
{
get
{
var id = WindowsIdentity.GetCurrent();
return id.Owner != id.User;
}
}
Here is a modified version of this answer to include things like the proper disposing of resources and handling of Domain Administrators.
public static class UacHelper
{
private const string uacRegistryKey = "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System";
private const string uacRegistryValue = "EnableLUA";
private static uint STANDARD_RIGHTS_READ = 0x00020000;
private static uint TOKEN_QUERY = 0x0008;
private static uint TOKEN_READ = (STANDARD_RIGHTS_READ | TOKEN_QUERY);
[DllImport("advapi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool OpenProcessToken(IntPtr ProcessHandle, UInt32 DesiredAccess, out IntPtr TokenHandle);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool CloseHandle(IntPtr hObject);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool GetTokenInformation(IntPtr TokenHandle, TOKEN_INFORMATION_CLASS TokenInformationClass, IntPtr TokenInformation, uint TokenInformationLength, out uint ReturnLength);
public enum TOKEN_INFORMATION_CLASS
{
TokenUser = 1,
TokenGroups,
TokenPrivileges,
TokenOwner,
TokenPrimaryGroup,
TokenDefaultDacl,
TokenSource,
TokenType,
TokenImpersonationLevel,
TokenStatistics,
TokenRestrictedSids,
TokenSessionId,
TokenGroupsAndPrivileges,
TokenSessionReference,
TokenSandBoxInert,
TokenAuditPolicy,
TokenOrigin,
TokenElevationType,
TokenLinkedToken,
TokenElevation,
TokenHasRestrictions,
TokenAccessInformation,
TokenVirtualizationAllowed,
TokenVirtualizationEnabled,
TokenIntegrityLevel,
TokenUIAccess,
TokenMandatoryPolicy,
TokenLogonSid,
MaxTokenInfoClass
}
public enum TOKEN_ELEVATION_TYPE
{
TokenElevationTypeDefault = 1,
TokenElevationTypeFull,
TokenElevationTypeLimited
}
public static bool IsUacEnabled
{
get
{
using (RegistryKey uacKey = Registry.LocalMachine.OpenSubKey(uacRegistryKey, false))
{
bool result = uacKey.GetValue(uacRegistryValue).Equals(1);
return result;
}
}
}
public static bool IsProcessElevated
{
get
{
if (IsUacEnabled)
{
IntPtr tokenHandle = IntPtr.Zero;
if (!OpenProcessToken(Process.GetCurrentProcess().Handle, TOKEN_READ, out tokenHandle))
{
throw new ApplicationException("Could not get process token. Win32 Error Code: " +
Marshal.GetLastWin32Error());
}
try
{
TOKEN_ELEVATION_TYPE elevationResult = TOKEN_ELEVATION_TYPE.TokenElevationTypeDefault;
int elevationResultSize = Marshal.SizeOf(typeof(TOKEN_ELEVATION_TYPE));
uint returnedSize = 0;
IntPtr elevationTypePtr = Marshal.AllocHGlobal(elevationResultSize);
try
{
bool success = GetTokenInformation(tokenHandle, TOKEN_INFORMATION_CLASS.TokenElevationType,
elevationTypePtr, (uint) elevationResultSize,
out returnedSize);
if (success)
{
elevationResult = (TOKEN_ELEVATION_TYPE) Marshal.ReadInt32(elevationTypePtr);
bool isProcessAdmin = elevationResult == TOKEN_ELEVATION_TYPE.TokenElevationTypeFull;
return isProcessAdmin;
}
else
{
throw new ApplicationException("Unable to determine the current elevation.");
}
}
finally
{
if (elevationTypePtr != IntPtr.Zero)
Marshal.FreeHGlobal(elevationTypePtr);
}
}
finally
{
if (tokenHandle != IntPtr.Zero)
CloseHandle(tokenHandle);
}
}
else
{
WindowsIdentity identity = WindowsIdentity.GetCurrent();
WindowsPrincipal principal = new WindowsPrincipal(identity);
bool result = principal.IsInRole(WindowsBuiltInRole.Administrator)
|| principal.IsInRole(0x200); //Domain Administrator
return result;
}
}
}
}
The CodePlex project UAChelper has code that checks on elevation in UserAccountControl.cpp UserAccountControl::IsUserAdmin, that checks if UAC is enabled and then checks if process is elevated.
bool UserAccountControl::IsCurrentProcessElevated::get()
{
return GetProcessTokenElevationType() == TokenElevationTypeFull; //elevated
}
from the function:
int UserAccountControl::GetProcessTokenElevationType()
{
HANDLE hToken;
try
{
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
throw gcnew Win32Exception(GetLastError());
TOKEN_ELEVATION_TYPE elevationType;
DWORD dwSize;
if (!GetTokenInformation(hToken, TokenElevationType, &elevationType, sizeof(elevationType), &dwSize))
throw gcnew Win32Exception(GetLastError());
return elevationType;
}
finally
{
CloseHandle(hToken);
}
}
In .net Framwork 4.5 I found another method that works for me.
In relation to the following script that can be found here here (in German)
rem --- Admintest.bat ---
whoami /groups | find "S-1-5-32-544" > nul
if errorlevel 1 goto ende
echo Benutzer %username% ist lokaler Administrator.
:ende
In C# it looks like this:
private bool IsAdmin
{
get
{
WindowsIdentity identity = WindowsIdentity.GetCurrent();
if (identity != null)
{
WindowsPrincipal principal = new WindowsPrincipal(identity);
List<Claim> list = new List<Claim>(principal.UserClaims);
Claim c = list.Find(p => p.Value.Contains("S-1-5-32-544"));
if (c != null)
return true;
}
return false;
}
}
But in .net < 4.5 the WindowsPrincipal class does not contain the UserClaims property
and I found no way to get this information.
Using TokenElevationType would work, but if you PInvoke CheckTokenMembership() against the admin group SID, your code would also work when UAC is off and on 2000/XP/2003 and will also handle deny SID's.
There is also a IsUserAnAdmin() function that does the CheckTokenMembership check for you, but MSDN says it might not be there forever
This Answer has a few problems. First, it doesn't get any System processes that run as Admin (for example under NT-Authority/SYSTEM).
The code example below fixes all problems (detects, LocalAdmins, DomainAdmins, and LocalSystemAdmins)
If you just want the current Process, replace pHandle with Process.GetCurrentProcess().Handle
NOTE:
You must have certain privileges to run it. (Every AdminProcess has them but
needs to active them first, Services have them activated by default)
internal static bool IsProcessElevatedEx(this IntPtr pHandle) {
var token = IntPtr.Zero;
if (!OpenProcessToken(pHandle, MAXIMUM_ALLOWED, ref token))
throw new Win32Exception(Marshal.GetLastWin32Error(), "OpenProcessToken failed");
WindowsIdentity identity = new WindowsIdentity(token);
WindowsPrincipal principal = new WindowsPrincipal(identity);
bool result = principal.IsInRole(WindowsBuiltInRole.Administrator)
|| principal.IsInRole(0x200); //Domain Administrator
CloseHandle(token);
return result;
}
I think there is one more issue. I checked solutions provided by you and have to say that in the installation of Windows 7 and logged on as an admin the check does not work. Windows never returns information that process runs in an elevated mode. So the sequence:
if (IsUacEnabled)
return IsProcessInElevatedMode();
return IsUserAdmin();
does not returns true when logged as an Administrator but the process has all privileges to perform system operations (e.g. stop system services).
The working sequence is:
if (IsUserAdmin())
return true;
if (IsUacEnabled)
return IsProcessInElevatedMode();
return false;
You should first check if the process is run in Administrator context.
Additional info:
IsUacEnabled() - checks if the UAC has been enabled in the system (Windows)
IsProcessInElevatedMode() - checks if the process is run in an elevated mode
IsUserAdmin() - checks if the current user has an Administrtor role
All of that methods has been described in previous posts.
Using UACHelper nuget package:
https://www.nuget.org/packages/UACHelper/
if (UACHelper.IsElevated)
// something
else
// something else
There are a lot of other properties that can be used to detect if the user is, in fact, an administrator, or if the process is running under UAC virtualization, or if the desktop owner is the process owner. (Run as from limited account)
Check the read me for more information.
I am using this code and it works well:
bool runningAsAdmin = WindowsIdentity.GetCurrent().Owner.IsWellKnown(WellKnownSidType.BuiltinAdministratorsSid);
*Admin is part of the Build-In Administrators group.
"A user account for the system administrator. This account is the first account created during operating system installation. The account cannot be deleted or locked out. It is a member of the Administrators group and cannot be removed from that group." -- https://ss64.com/nt/syntax-security_groups.html

Starting and stopping IIS Express programmatically

I am trying to build a small application in C# which should start/stop an IIS Express worker process. For this purpose I want to use the official "IIS Express API" which is documented on MSDN: http://msdn.microsoft.com/en-us/library/gg418415.aspx
As far as I understand, the API is based (only) on COM interfaces. To use this COM interfaces I've added a reference to the COM library in VS2010 via Add Reference -> COM -> "IIS Installed Versions Manager Interface":
So far so good, but what's next? There is an IIISExprProcessUtility interface available which includes the the two "methods" to start/stop an IIS process. Do I have to write a class which implements this interface?
public class test : IISVersionManagerLibrary.IIISExprProcessUtility
{
public string ConstructCommandLine(string bstrSite, string bstrApplication, string bstrApplicationPool, string bstrConfigPath)
{
throw new NotImplementedException();
}
public uint GetRunningProcessForSite(string bstrSite, string bstrApplication, string bstrApplicationPool, string bstrConfigPath)
{
throw new NotImplementedException();
}
public void StopProcess(uint dwPid)
{
throw new NotImplementedException();
}
}
As you can see, I'm not a professional developer. Can someone point me in the right direction.
Any help is greatly appreciated.
Update 1:
According to the suggestions I've tried the following code which unfortunately doesn't work:
Ok, it can be instantiated but I cannot see how to use this object...
IISVersionManagerLibrary.IIISExpressProcessUtility test3 = (IISVersionManagerLibrary.IIISExpressProcessUtility) Activator.CreateInstance(Type.GetTypeFromCLSID(new Guid("5A081F08-E4FA-45CC-A8EA-5C8A7B51727C")));
Exception: Retrieving the COM class factory for component with CLSID {5A081F08-E4FA-45CC-A8EA-5C8A7B51727C} failed due to the following error: 80040154 Class not registered (Exception from HRESULT: 0x80040154 (REGDB_E_CLASSNOTREG)).
I was trying to do similar thing. I concluded that the COM library provided by Microsoft is incomplete. I don't use it because the doc mentioned that "Note: This topic is pre-release documentation and is subject to change in future releases".
So, I decided to take a look at what IISExpressTray.exe is doing. It seems to be doing similar things.
I disassemble the IISExpressTray.dll and found that there is no magic in listing out all the IISexpress processes and stoping the IISexpress process.
It doesn't call that COM library. It doesn't lookup anything from registry.
So, the solution I ended up is very simple. To start an IIS express process, I just use Process.Start() and pass in all the parameters I need.
To stop an IIS express process, I copied the code from IISExpressTray.dll using reflector. I saw it simply sends a WM_QUIT message to the target IISExpress process.
Here is the class I wrote to start and stop an IIS express process. Hope this can help somebody else.
class IISExpress
{
internal class NativeMethods
{
// Methods
[DllImport("user32.dll", SetLastError = true)]
internal static extern IntPtr GetTopWindow(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
internal static extern IntPtr GetWindow(IntPtr hWnd, uint uCmd);
[DllImport("user32.dll", SetLastError = true)]
internal static extern uint GetWindowThreadProcessId(IntPtr hwnd, out uint lpdwProcessId);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool PostMessage(HandleRef hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
}
public static void SendStopMessageToProcess(int PID)
{
try
{
for (IntPtr ptr = NativeMethods.GetTopWindow(IntPtr.Zero); ptr != IntPtr.Zero; ptr = NativeMethods.GetWindow(ptr, 2))
{
uint num;
NativeMethods.GetWindowThreadProcessId(ptr, out num);
if (PID == num)
{
HandleRef hWnd = new HandleRef(null, ptr);
NativeMethods.PostMessage(hWnd, 0x12, IntPtr.Zero, IntPtr.Zero);
return;
}
}
}
catch (ArgumentException)
{
}
}
const string IIS_EXPRESS = #"C:\Program Files\IIS Express\iisexpress.exe";
const string CONFIG = "config";
const string SITE = "site";
const string APP_POOL = "apppool";
Process process;
IISExpress(string config, string site, string apppool)
{
Config = config;
Site = site;
AppPool = apppool;
StringBuilder arguments = new StringBuilder();
if (!string.IsNullOrEmpty(Config))
arguments.AppendFormat("/{0}:{1} ", CONFIG, Config);
if (!string.IsNullOrEmpty(Site))
arguments.AppendFormat("/{0}:{1} ", SITE, Site);
if (!string.IsNullOrEmpty(AppPool))
arguments.AppendFormat("/{0}:{1} ", APP_POOL, AppPool);
process = Process.Start(new ProcessStartInfo()
{
FileName = IIS_EXPRESS,
Arguments = arguments.ToString(),
RedirectStandardOutput = true,
UseShellExecute = false
});
}
public string Config { get; protected set; }
public string Site { get; protected set; }
public string AppPool { get; protected set; }
public static IISExpress Start(string config, string site, string apppool)
{
return new IISExpress(config, site, apppool);
}
public void Stop()
{
SendStopMessageToProcess(process.Id);
process.Close();
}
}
I don't need to list all the existing IIS express process. If you need that, from what I saw in the reflector, what IISExpressTray.dll does is to call Process.GetProcessByName("iisexpress", ".")
To use the class I provided, here is a sample program I used to test it.
class Program
{
static void Main(string[] args)
{
Console.Out.WriteLine("Launching IIS Express...");
IISExpress iis1 = IISExpress.Start(
#"C:\Users\Administrator\Documents\IISExpress\config\applicationhost.config",
#"WebSite1(1)",
#"Clr4IntegratedAppPool");
IISExpress iis2 = IISExpress.Start(
#"C:\Users\Administrator\Documents\IISExpress\config\applicationhost2.config",
#"WebSite1(1)",
#"Clr4IntegratedAppPool");
Console.Out.WriteLine("Press ENTER to kill");
Console.In.ReadLine();
iis1.Stop();
iis2.Stop();
}
}
This may not be an answer to your question but I think people interesting in your question may find my work useful. Feel free to improve the codes. There are some places that you might want to enhance.
Instead of hardcoding the iisexpress.exe location, you can fix my code to read from the registry.
I didn't include all the arguments supported by iisexpress.exe
I didn't do error handling. So, if the IISExpress process failed to start for some reasons (e.g. port is in used), I don't know. I think the easiest way to fix it is to monitor the StandardError stream and throw exception if I get anything from StandardError stream
Although, it's too late, I'll provide an answer to this question.
IISVersionManagerLibrary.IISVersionManager mgr = new IISVersionManagerLibrary.IISVersionManagerClass();
IISVersionManagerLibrary.IIISVersion ver = mgr.GetVersionObject("7.5", IISVersionManagerLibrary.IIS_PRODUCT_TYPE.IIS_PRODUCT_EXPRESS);
object obj1 = ver.GetPropertyValue("expressProcessHelper");
IISVersionManagerLibrary.IIISExpressProcessUtility util = obj1 as IISVersionManagerLibrary.IIISExpressProcessUtility;
That's it. Then you can call StopProcess method on util object.
However, you have to get notice from Microsoft.
" Version Manager API (IIS Express) ;
http://msdn.microsoft.com/en-us/library/gg418429(v=VS.90).aspx
Note: The IIS Version Manager API
supports the IIS Express
infrastructure and is not intended
to be used directly from your code.
"
This implementation works for starting/stopping IIS Express programmatically, can be used from tests.
public class IisExpress : IDisposable
{
private Boolean _isDisposed;
private Process _process;
public void Dispose()
{
Dispose(true);
}
public void Start(String directoryPath, Int32 port)
{
var iisExpressPath = DetermineIisExpressPath();
var arguments = String.Format(
CultureInfo.InvariantCulture, "/path:\"{0}\" /port:{1}", directoryPath, port);
var info = new ProcessStartInfo(iisExpressPath)
{
WindowStyle = ProcessWindowStyle.Normal,
ErrorDialog = true,
LoadUserProfile = true,
CreateNoWindow = false,
UseShellExecute = false,
Arguments = arguments
};
var startThread = new Thread(() => StartIisExpress(info))
{
IsBackground = true
};
startThread.Start();
}
protected virtual void Dispose(Boolean disposing)
{
if (_isDisposed)
{
return;
}
if (disposing)
{
if (_process.HasExited == false)
{
_process.Kill();
}
_process.Dispose();
}
_isDisposed = true;
}
private static String DetermineIisExpressPath()
{
String iisExpressPath;
iisExpressPath = Environment.GetFolderPath(Environment.Is64BitOperatingSystem
? Environment.SpecialFolder.ProgramFilesX86
: Environment.SpecialFolder.ProgramFiles);
iisExpressPath = Path.Combine(iisExpressPath, #"IIS Express\iisexpress.exe");
return iisExpressPath;
}
private void StartIisExpress(ProcessStartInfo info)
{
try
{
_process = Process.Start(info);
_process.WaitForExit();
}
catch (Exception)
{
Dispose();
}
}
}
I feel you are doing it in a hard way. Take a hint from this question Automatically stop/restart ASP.NET Development Server on Build and see if you can adopt the same process.
Answering your question, I think pinvoke.net might help you. They have lot of examples as well which can help you build your solution.
Harvey Kwok had provided a good hint, since I want to tear up and tear down the service when running integration test cases. But Harvey codes is too long with PInvoke and messaging.
Here's an alternative.
public class IisExpressAgent
{
public void Start(string arguments)
{
ProcessStartInfo info= new ProcessStartInfo(#"C:\Program Files (x86)\IIS Express\iisexpress.exe", arguments)
{
// WindowStyle= ProcessWindowStyle.Minimized,
};
process = Process.Start(info);
}
Process process;
public void Stop()
{
process.Kill();
}
}
And in my integration test suit with MS Test, I have
[ClassInitialize()]
public static void MyClassInitialize(TestContext testContext)
{
iis = new IisExpressAgent();
iis.Start("/site:\"WcfService1\" /apppool:\"Clr4IntegratedAppPool\"");
}
static IisExpressAgent iis;
//Use ClassCleanup to run code after all tests in a class have run
[ClassCleanup()]
public static void MyClassCleanup()
{
iis.Stop();
}
Here is my solution too. It runs IIS Express with hidden windows. Manager class controls several IIS Express instances.
class IISExpress
{
private const string IIS_EXPRESS = #"C:\Program Files\IIS Express\iisexpress.exe";
private Process process;
IISExpress(Dictionary<string, string> args)
{
this.Arguments = new ReadOnlyDictionary<string, string>(args);
string argumentsInString = args.Keys
.Where(key => !string.IsNullOrEmpty(key))
.Select(key => $"/{key}:{args[key]}")
.Aggregate((agregate, element) => $"{agregate} {element}");
this.process = Process.Start(new ProcessStartInfo()
{
FileName = IIS_EXPRESS,
Arguments = argumentsInString,
WindowStyle = ProcessWindowStyle.Hidden
});
}
public IReadOnlyDictionary<string, string> Arguments { get; protected set; }
public static IISExpress Start(Dictionary<string, string> args)
{
return new IISExpress(args);
}
public void Stop()
{
try
{
this.process.Kill();
this.process.WaitForExit();
}
finally
{
this.process.Close();
}
}
}
I need several instances. Designed manager class to control them.
static class IISExpressManager
{
/// <summary>
/// All started IIS Express hosts
/// </summary>
private static List<IISExpress> hosts = new List<IISExpress>();
/// <summary>
/// Start IIS Express hosts according to the config file
/// </summary>
public static void StartIfEnabled()
{
string enableIISExpress = ConfigurationManager.AppSettings["EnableIISExpress"]; // bool value from config file
string pathToConfigFile = ConfigurationManager.AppSettings["IISExpressConfigFile"]; // path string to iis configuration file
string quotedPathToConfigFile = '"' + pathToConfigFile + '"';
if (bool.TryParse(enableIISExpress, out bool isIISExpressEnabled)
&& isIISExpressEnabled && File.Exists(pathToConfigFile))
{
hosts.Add(IISExpress.Start(
new Dictionary<string, string> {
{"systray", "false"},
{"config", quotedPathToConfigFile},
{"site", "Site1" }
}));
hosts.Add(IISExpress.Start(
new Dictionary<string, string> {
{"systray", "false"},
{ "config", quotedPathToConfigFile},
{"site", "Site2" }
}));
}
}
/// <summary>
/// Stop all started hosts
/// </summary>
public static void Stop()
{
foreach(var h in hosts)
{
h.Stop();
}
}
}
Figure I'd throw my solution in here too. Derived from the SeongTae Jeong's solution and another post (Can't remember where now).
Install the Microsoft.Web.Administration nuget.
Reference the IIS Installed Versions Manager Interface COM type library as noted above.
Add the following class:
using System;
using System.Diagnostics;
using System.IO;
using System.Text.RegularExpressions;
using IISVersionManagerLibrary;
using Microsoft.Web.Administration;
public class Website
{
private const string DefaultAppPool = "Clr4IntegratedAppPool";
private const string DefaultIISVersion = "8.0";
private static readonly Random Random = new Random();
private readonly IIISExpressProcessUtility _iis;
private readonly string _name;
private readonly string _path;
private readonly int _port;
private readonly string _appPool;
private readonly string _iisPath;
private readonly string _iisArguments;
private readonly string _iisConfigPath;
private uint _iisHandle;
private Website(string path, string name, int port, string appPool, string iisVersion)
{
_path = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, path));
_name = name;
_port = port;
_appPool = appPool;
_iis = (IIISExpressProcessUtility)new IISVersionManager()
.GetVersionObject(iisVersion, IIS_PRODUCT_TYPE.IIS_PRODUCT_EXPRESS)
.GetPropertyValue("expressProcessHelper");
var commandLine = _iis.ConstructCommandLine(name, "", appPool, "");
var commandLineParts = new Regex("\\\"(.*?)\\\" (.*)").Match(commandLine);
_iisPath = commandLineParts.Groups[1].Value;
_iisArguments = commandLineParts.Groups[2].Value;
_iisConfigPath = new Regex("\\/config:\\\"(.*?)\\\"").Match(commandLine).Groups[1].Value;
Url = string.Format("http://localhost:{0}/", _port);
}
public static Website Create(string path,
string name = null, int? port = null,
string appPool = DefaultAppPool,
string iisVersion = DefaultIISVersion)
{
return new Website(path,
name ?? Guid.NewGuid().ToString("N"),
port ?? Random.Next(30000, 40000),
appPool, iisVersion);
}
public string Url { get; private set; }
public void Start()
{
using (var manager = new ServerManager(_iisConfigPath))
{
manager.Sites.Add(_name, "http", string.Format("*:{0}:localhost", _port), _path);
manager.CommitChanges();
}
Process.Start(new ProcessStartInfo
{
FileName = _iisPath,
Arguments = _iisArguments,
RedirectStandardOutput = true,
UseShellExecute = false
});
var startTime = DateTime.Now;
do
{
try
{
_iisHandle = _iis.GetRunningProcessForSite(_name, "", _appPool, "");
}
catch { }
if (_iisHandle != 0) break;
if ((DateTime.Now - startTime).Seconds >= 10)
throw new TimeoutException("Timeout starting IIS Express.");
} while (true);
}
public void Stop()
{
try
{
_iis.StopProcess(_iisHandle);
}
finally
{
using (var manager = new ServerManager(_iisConfigPath))
{
var site = manager.Sites[_name];
manager.Sites.Remove(site);
manager.CommitChanges();
}
}
}
}
Setup your test fixture as follows. The path is relative to the bin folder of your test suite.
[TestFixture]
public class Tests
{
private Website _website;
[TestFixtureSetUp]
public void Setup()
{
_website = Website.Create(#"..\..\..\TestHarness");
_website.Start();
}
[TestFixtureTearDown]
public void TearDown()
{
_website.Stop();
}
[Test]
public void should_serialize_with_bender()
{
new WebClient().UploadString(_website.Url, "hai").ShouldEqual("hai");
}
}
And one more point if this is going to also run on a build server. First you will need to install IIS Express on the build server. Second, you'll have to create an applicationhost.config on the build server. You can copy one from your dev box under C:\Users\<User>\Documents\IISExpress\config\. It needs to be copied to the corresponding path of the user your build server is running as. If it is running as system then the path would be C:\Windows\System32\config\systemprofile\Documents\IISExpress\config\.
No, you don't inherit the interface. You can create an instance of IISVersionManager with the new keyword. How that gets you a reference to an IIISExpressProcessUtility instance is completely unclear. The MSDN docs are awful. Maybe you can new one but it doesn't look like it supports that.
If you modify the web.config file of the web application, IIS (including Express) will restart the app pool. This will allow you to deploy updated assemblies.
One way to modify web.config is to copy it to a new file, and then move it back.
copy /Y path/web.config path/web_touch.config
move /Y path/web_touch.config path/web.config
You may want more control over IIS Express than simply restarting the app pool. But if that's all you need, this will work.
I have adopted a different solution. You can simply kill the process tree using "taskkill" and the name of the process.
This works perfectly locally and on TFS 2013
public static void FinalizeIis()
{
var startInfo = new ProcessStartInfo
{
UseShellExecute = false,
Arguments = string.Format("/F /IM iisexpress.exe"),
FileName = "taskkill"
};
Process.Start(startInfo);
}

Programmatically Set Proxy Address, Port, Username, Password

Hi I need to set proxy address of IE programmatically
Earlier I used to use this RedTomahawk.TORActivator
but it doesnot gives an option to set those proxies which requires username and password.
How can we set those proxies which needs username and passwords
Please provide examples like
void Setproxy(string ip,string port,string uname,string pwd)
{
///Code here
}
You could P/Invoke the WinHttpSetDefaultProxyConfiguration function.
UPDATE:
Including example as requested:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct WINHTTP_PROXY_INFO
{
public AccessType AccessType;
public string Proxy;
public string Bypass;
}
public enum AccessType
{
DefaultProxy = 0,
NamedProxy = 3,
NoProxy = 1
}
class Program
{
[DllImport("winhttp.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool WinHttpSetDefaultProxyConfiguration(ref WINHTTP_PROXY_INFO config);
static void Main()
{
var config = new WINHTTP_PROXY_INFO();
config.AccessType = AccessType.NamedProxy;
config.Proxy = "http://proxy.yourcompany.com:8080";
config.Bypass = "intranet.com";
var result = WinHttpSetDefaultProxyConfiguration(ref config);
if (!result)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
else
{
Console.WriteLine("Successfully modified proxy settings");
}
}
}

Categories

Resources