Run As - Preset
Main window of the application
Presets list
Shortcut Icons
Users list
New record entry - before validation of user done
All needed details filled - User is valid
Preset name prompt after clicking "Save Preset an..." button
Prompt for location to save the shortcut
Note 1
Before running the application, change Project->Setting to suit your network.
Note 2
I haven't been able to create this application functionality in non domain network environment because it is using Domain Controller - which (probably) does not exist on home network...
Introduction
As a programmer (and especially Web programmer), I've found myself in need to frequently use the RunAs; but the basic RunAs that comes with Windows is neither sufficient nor efficient...
It is always a pain using it, simply because when you need it, you have to go on to the command prompt, write its name with the command and parameters, hit enter, type the password, then hit Enter again - hoping you haven't written the password wrong...
Now, for my work, this process has to be down several times (I'm developing an application which uses different permissions for Managers, Workers and some of the between - basically four levels - as starting point).
The meaning of this was that for a single round of testing the application, I have to open the application four times using different user credentials.
This was becoming even worse when I was required to add a new level of management between the existing two...
Then was the time I started digging in the .NET Framework and windows internals to find out how to do it myself.
Background
The first incarnation of the software was a simple command line program, after a while, it has evolved - pretty much the way it is shown here - when my manager thought it can be used for some other uses (like giving worker - not a manager - that replaced a manager on leave) the ability to use a specific application - WITH THE MANAGER ACCOUNT - but without giving out the manager's password to the worker.
Using the Application
The application has two command line switches:
/config
- to create, edit and/or run a preset. From the UI, you can save a shortcut to wherever you want (Desktop,
Program Files, etc.)
Example:RunAsPreset.exe /config
/profile, /p, /pr
- to use a command line arguments to specific (saved) profile.
Example:RunAsPreset.exe /profile:"My application as TheUser"
Using the Code
The two most important classes in the code are:
RegistryPreset Class
/// <summary>
/// Class representing Preset from registry
/// </summary>
public class RegistryPreset : IComparable, IEquatable<RegistryPreset>
which contains one important internal
function:
/// <summary>
/// Initialize the instance from predefined RegistryKey
/// </summary>
/// <param name="rkPreset">Preset RegistryKey</param>
internal void Initialize(RegistryKey rkPreset);
Six important public functions:
/// <summary>
/// Load a preset from the specified preset name
/// </summary>
/// <param name="sPresetName">Preset name to be load</param>
/// <returns>New instance of the class for this preset and sets IsLoaded</returns>
public static RegistryPreset LoadPreset(string sPresetName);
/// <summary>
/// Save preset to registry key and sets IsSaved on success
/// </summary>
/// <param name="sPresetName">Preset name to save</param>
public void SavePreset(string sPresetName);
/// <summary>
/// Save preset to registry and sets IsSaved on success
/// </summary>
public void SavePreset()
/// <summary>
/// Run the selected preset
/// </summary>
public void Run()
/// <summary>
/// Load and run a selected preset
/// </summary>
/// <param name="sPresetName">Preset name to load</param>
/// <returns>New instance of the class for this preset
/// (in case the preset is required for other processing later)</returns>
public static RegistryPreset LoadAndRun(string sPresetName)
/// <summary>
/// Manually reload a preset (when modified).
/// </summary>
public void Reload()
and the properties:
/*******************************************************************
* Properties for Preset *
*******************************************************************/
/// <summary>
/// Preset name (Registry subkey) (REQUIRED)
/// </summary>
public string PresetName { get; set; }
/// <summary>
/// Target executable path and name (for Run() - used later) (REQUIRED)
/// </summary>
public string ExecutableTarget { get; set; }
/// <summary>
/// Credentials: Login (REQUIRED)
/// </summary>
public string Login { get; set; }
/// <summary>
/// Credentials: Password (OPTIONAL)
/// </summary>
public string Password { get; set; }
/// <summary>
/// Command line arguments to be passed to executable (OPTIONAL)
/// </summary>
public string Arguments { get; set; }
/**************************************************************************
* Properties for shortcut (if created) and for the Run() function *
**************************************************************************/
/// <summary>
/// Working Directory (OPTIONAL)
/// </summary>
public string WorkingDirectory { get; set; }
/// <summary>
/// Icon file path (OPTIONAL)
/// </summary>
public string IconFile { get; set; }
/// <summary>
/// Icon index for selected shortcut (OPTIONAL)
/// </summary>
public int? IconIndex { get; set; }
/// <summary>
/// Shortcut description (OPTIONAL)
/// </summary> public string Description { get; set; }
/// <summary>
/// Window style (OPTIONAL)
/// </summary>
public ProcessWindowStyle? WindowStyle { get; set; }
/*******************************************************************
* Generic usage Properties *
*******************************************************************/
/// <summary>
/// True if preset was successfully loaded, otherwise False
/// </summary>
public bool IsLoaded { get; }
/// <summary>
/// True if preset was successfully loaded, otherwise False
/// </summary> public bool IsSaved { get; }
/// <summary>
/// Logon domain
/// </summary>
public string Domain { get; }
Note: The properties marked (REQUIRED) throw an exception if set incorrectly.
ActiveDirectoryPerson Class
/// <summary>
/// Class representing User from ActiveDirectory
/// </summary>
public class ActiveDirectoryPerson : IComparable, IEquatable<ActiveDirectoryPerson>
which contains one public enum
:
/// <summary>
/// Login result
/// </summary>
public enum LoginResult
{
/// <summary>
/// Failed login - unknown reason
/// </summary>
Login_Fail = 0,
/// <summary>
/// Login and password OK
/// </summary>
Login_OK,
/// <summary>
/// Administrator account unavailable
/// </summary>
Administrator_Account_Unavailible,
/// <summary>
/// User account unavailable
/// </summary>
User_Account_Unavailible,
/// <summary>
/// Invalid login
/// </summary>
Invalid_Login,
/// <summary>
/// Invalid password
/// </summary>
Invalid_Password
}
Important internal
function:
/// <summary>
/// Check if login exists on ActiveDirectory and load the user's properties
/// </summary>
/// <returns>True if login exist, otherwise False</returns>
internal bool AuthenticateAndLoad()
One important public
function:
/// <summary>
/// Validate the user with the password against the ActiveDirectory Server
/// This function is the one needs an Administrator credentials to function correctly
/// (as a way of preventing misuse of it)
/// </summary>
/// <param name="sLogin">Login</param>
/// <param name="sPassword">Plain text password</param>
/// <returns>LoginResult (mentioned above)</returns>
public LoginResult Authenticate(string sLogin, string sPassword)
and the properties:
/// <summary>
/// Logon domain
/// </summary>
public string Domain { get; }
/// <summary>
/// User Login
/// </summary>
public string Login { get; set; }
/// <summary>
/// User's full name
/// </summary>
public string Name { get; set; }
/// <summary>
/// User entry on the ActiveDirectory as LDap path ("LDAP://...")
/// </summary>
public string LDapPath { get; }
/// <summary>
/// Property Description
/// </summary>
public Hashtable Properties { get; }
/// <summary>
/// Login exists in the ActiveDirectory
/// </summary>
public bool LoginExists { get; }
There is one - but not so important - class worth mentioning:
/// <summary>
/// Assembly information class
/// </summary>
public static class CallingAssemblyInfo
This class is used as a wrapper for the Assembly
class to display some information - like assembly name and version in a convenient way which contains one public static
properties:
/// <summary>
/// Return the assembly as "Assembly" object
/// </summary>
public static Assembly TheAssembly { get; }
/// <summary>
/// Return the assembly name as "AssemblyName" object
/// </summary>
public static AssemblyName TheAssemblyName { get; }
/// <summary>
/// Return assembly version as "Version" object
/// </summary>
public static Version TheAssemblyVersion { get; }
/// <summary>
/// Return assembly last written date as "DateTime" object
/// </summary> public static DateTime TheAssemblyModificationDate { get; }
/// <summary>
/// Assembly file (EXE) modification (last write) date as String
/// </summary>
public static string TheAssemblyModificationDateString { get; }
/// <summary>
/// Major version as integer
/// </summary>
public static int Major { get; }
/// <summary>
/// Minor version as integer
/// </summary> public static int Minor { get; }
/// <summary>
/// Build number as integer
/// </summary>
public static int Build { get; }
/// <summary>
/// Revision number as integer
/// </summary>
public static int Revision { get; }
/// <summary>
/// Return assembly version as formatted string (1.22.333.4444)
/// </summary>
public static string TheAssemblyVersionString { get; }
/// <summary>
/// Return assembly name and version format as "name,
/// vVersion" (= "MyAsm, v1.22.333.4444")
/// </summary>
public static string NameAndVersion { get; }
Points of Interest
The nicest thing I've got from this application (other than its usefulness - at least in my opinion) is the knowledge of "messing around" :-) with process information to force it to my will.
Thanks
First of all:
- To all other contributors whose code examples and highlights I have used and not mentioned here
Second:
- Andrew Robinson on www.SourceForge.com, for his
CommandLine.OptParse
library - Hannes du Preez on www.codeguru.com, for his
ShellLinkManipulate
library - Unknown, for his
IconHelpers
library - Unknown, for his
ShellLinkManipulate
library
History
- v4.2.4332.15211 - My first (at least for now - and hopefully not the last) publicly available code contribution to the developers community
发表评论
ct2aii Major thankies for the article post.Thanks Again. Really Great.