How to launch detached process through cmd.exe with no console? - c#

I want to launch an external program from C# to be completely detached.
I use CreateProcess through pinvoke because Process.Start doesn't allow me to use DETACHED_PROCESS. Also I want this application to redirect it's output to some file.
Here's the sample code:
var processInformation = new ProcessUtility.PROCESS_INFORMATION();
var securityInfo = new ProcessUtility.STARTUPINFO();
var sa = new ProcessUtility.SECURITY_ATTRIBUTES();
sa.Length = Marshal.SizeOf(sa);
// Create process with no window and totally detached
var result = ProcessUtility.CreateProcess(Path.Combine(Environment.SystemDirectory, "cmd.exe"), commandLineArguments, ref sa, ref sa, false,
ProcessUtility.DETACHED_PROCESS, IntPtr.Zero, null, ref securityInfo, out processInformation);
CommandLineArguments are something like this : "/c Foo.bat > Foo.log 2>&1"
Everything works fine and Foo.log is populated by Foo.bat. No additional console window is visible. PERFECT.
CommandLineArguments are something like this : "/c Foo.exe > Foo.log 2>&1"
Foo.exe is .NET Console Application.
Foo.log is not populated and Foo.exe is launched in visible console window. STRANGE. Why behavior is different from 1. ?
Just for your information. CommandLineArguments are something like this : "/c Foo.exe > Foo.log 2>&1"
Foo.exe is .NET Windows Application.
Everything works fine but when I launch this application simply from command prompt I see no output because no console is allocated.
I want 2. work the same as 1. Why there's a difference ?
UPDATE: I don't want to write the Foo.log for myself because launching application will be killed.
UPDATE: Ok, I wrote some code to specify that only one handle is inherited but CreateProcess gives me error 87 when calling with EXTENDED_STARTUPINFO_PRESENT ( even if it's present and empty ).
Can you please help me why?
public class ProcessUtility
{
// Process creation flags
const uint ZERO_FLAG = 0x00000000;
const uint CREATE_BREAKAWAY_FROM_JOB = 0x01000000;
const uint CREATE_DEFAULT_ERROR_MODE = 0x04000000;
const uint CREATE_NEW_CONSOLE = 0x00000010;
const uint CREATE_NEW_PROCESS_GROUP = 0x00000200;
const uint CREATE_NO_WINDOW = 0x08000000;
const uint CREATE_PROTECTED_PROCESS = 0x00040000;
const uint CREATE_PRESERVE_CODE_AUTHZ_LEVEL = 0x02000000;
const uint CREATE_SEPARATE_WOW_VDM = 0x00001000;
const uint CREATE_SHARED_WOW_VDM = 0x00001000;
const uint CREATE_SUSPENDED = 0x00000004;
const uint CREATE_UNICODE_ENVIRONMENT = 0x00000400;
const uint DEBUG_ONLY_THIS_PROCESS = 0x00000002;
const uint DEBUG_PROCESS = 0x00000001;
const uint DETACHED_PROCESS = 0x00000008;
const uint EXTENDED_STARTUPINFO_PRESENT = 0x00080000;
const uint INHERIT_PARENT_AFFINITY = 0x00010000;
// Thread attributes flags
const uint PROC_THREAD_ATTRIBUTE_HANDLE_LIST = 0x00020002;
const uint PROC_THREAD_ATTRIBUTE_PARENT_PROCESS = 0x00020000;
// File creation flags
const uint FILE_ACCESS_WRITE = 0x40000000;
// StartupInfo flags
const int STARTF_USESTDHANDLES = 0x00000100;
[StructLayout(LayoutKind.Sequential)]
struct STARTUPINFO
{
public Int32 cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public Int32 dwX;
public Int32 dwY;
public Int32 dwXSize;
public Int32 dwXCountChars;
public Int32 dwYCountChars;
public Int32 dwFillAttribute;
public Int32 dwFlags;
public Int16 wShowWindow;
public Int16 cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential)]
struct STARTUPINFOEX
{
public STARTUPINFO StartupInfo;
public IntPtr lpAttributeList;
};
[StructLayout(LayoutKind.Sequential)]
struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public Int32 dwProcessID;
public Int32 dwThreadID;
}
[StructLayout(LayoutKind.Sequential)]
struct SECURITY_ATTRIBUTES
{
public Int32 Length;
public IntPtr lpSecurityDescriptor;
public bool bInheritHandle;
}
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern bool CreateProcess(
string lpApplicationName,
string lpCommandLine,
ref SECURITY_ATTRIBUTES lpProcessAttributes,
ref SECURITY_ATTRIBUTES lpThreadAttributes,
bool bInheritHandles,
uint dwCreationFlags,
IntPtr lpEnvironment,
string lpCurrentDirectory,
[In] ref STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern bool CreateProcess(
string lpApplicationName,
string lpCommandLine,
ref SECURITY_ATTRIBUTES lpProcessAttributes,
ref SECURITY_ATTRIBUTES lpThreadAttributes,
bool bInheritHandles,
uint dwCreationFlags,
IntPtr lpEnvironment,
string lpCurrentDirectory,
[In] ref STARTUPINFOEX lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation);
[DllImport("kernel32.dll")]
public static extern uint GetLastError();
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UpdateProcThreadAttribute(
IntPtr lpAttributeList, uint dwFlags, IntPtr Attribute, IntPtr lpValue,
IntPtr cbSize, IntPtr lpPreviousValue, IntPtr lpReturnSize);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool InitializeProcThreadAttributeList(
IntPtr lpAttributeList, int dwAttributeCount, int dwFlags, ref IntPtr lpSize);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool DeleteProcThreadAttributeList(IntPtr lpAttributeList);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll", SetLastError = true)]
static extern SafeFileHandle CreateFile(
string lpFileName,
uint fileAccess,
[MarshalAs(UnmanagedType.U4)] FileShare fileShare,
SECURITY_ATTRIBUTES securityAttributes,
[MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
uint dwFlagsAndAttributes,
IntPtr hTemplateFile);
public static bool CreateProcessWithStdHandlesRedirect(string lpApplicationName, string lpCommandLine, string logFilename)
{
var startupInfo = new STARTUPINFOEX();
startupInfo.StartupInfo.cb = Marshal.SizeOf(startupInfo);
try
{
var lpSize = IntPtr.Zero;
if (InitializeProcThreadAttributeList(IntPtr.Zero, 1, 0, ref lpSize) || lpSize == IntPtr.Zero)
return false;
startupInfo.lpAttributeList = Marshal.AllocHGlobal(lpSize);
// Here startupInfo.lpAttributeList is initialized to hold 1 value
if (!InitializeProcThreadAttributeList(startupInfo.lpAttributeList, 1, 0, ref lpSize))
return false;
var fileSecurityAttributes = new SECURITY_ATTRIBUTES();
fileSecurityAttributes.Length = Marshal.SizeOf(fileSecurityAttributes);
// Create inheritable file handle
fileSecurityAttributes.bInheritHandle = true;
// Open log file for writing
using (var handle = CreateFile(logFilename, FILE_ACCESS_WRITE, FileShare.ReadWrite,
fileSecurityAttributes, FileMode.Create, 0, IntPtr.Zero))
{
var fileHandle = handle.DangerousGetHandle();
// Add filehandle to proc thread attribute list
if (!UpdateProcThreadAttribute(startupInfo.lpAttributeList, 0, (IntPtr)PROC_THREAD_ATTRIBUTE_HANDLE_LIST, fileHandle,
(IntPtr)IntPtr.Size, IntPtr.Zero, IntPtr.Zero))
return false;
startupInfo.StartupInfo.hStdError = fileHandle;
startupInfo.StartupInfo.hStdOutput = fileHandle;
// startupInfo.StartupInfo.hStdInput = ?;
startupInfo.StartupInfo.dwFlags = STARTF_USESTDHANDLES;
var processInformation = new PROCESS_INFORMATION();
var securityAttributes = new SECURITY_ATTRIBUTES();
securityAttributes.Length = Marshal.SizeOf(securityAttributes);
securityAttributes.bInheritHandle = true;
// Create process with no window and totally detached
return ProcessUtility.CreateProcess(lpApplicationName, lpCommandLine, ref securityAttributes, ref securityAttributes, true,
DETACHED_PROCESS | EXTENDED_STARTUPINFO_PRESENT, IntPtr.Zero, null, ref startupInfo, out processInformation);
}
}
finally
{
if (startupInfo.lpAttributeList != IntPtr.Zero)
{
DeleteProcThreadAttributeList(startupInfo.lpAttributeList);
Marshal.FreeHGlobal(startupInfo.lpAttributeList);
}
}
}
}

In case 1, the instance of cmd.exe that you're launching can run the batch file itself. No child process is created.
In case 2, the instance of cmd.exe that you're launching has to run the console application as a child process. It has no way of knowing that you wanted the application to not be given a console window, so when it calls CreateProcess it does not use the DETACHED_PROCESS flag, and Windows creates a new console as normal.
In case 3, the child process is not a console application, so Windows does not create a console for it even though DETACHED_PROCESS was not specified.
The usual solution is to open the foo.log file yourself, launch the console application directly (rather than via cmd.exe) and use the STARTUP_INFO structure to pass the log file handle as the standard output and standard error for the new process. Once CreateProcess has returned you can close the file handle. The duplicated handle in the child process will not be affected when your process closes.
However, I'm not sure how you would properly go about that in .NET. It's a bit tricky at the best of times, because you have to make the child process inherit the log file handle without having it inherit other handles inappropriately - that's probably the reason Process.Start is causing you problems. The recommended practice is to use a process/thread attribute list (InitializeProcThreadAttributeList) with a PROC_THREAD_ATTRIBUTE_HANDLE_LIST entry. (But the log handle still needs to be inheritable.)

Create a vb script, NoWindow.vbs, perhaps programmatically on the fly, as follows
CreateObject("Wscript.Shell").Run WScript.Arguments(0), 0, False
From you main app, call cscript simply with Process.Start
Process.Start("cscript", "NoWindow.vbs \"cmd /c Foo.exe > Foo.log 2>&1 \" ");
The vbs script itself will detach the process. No window is visible.
My testing was limited to using Procexp to confirm that the proceses Foo.exe was detached - Win7.

Chekout this answer to hide console:
How to Run a C# console application with the console hidden
And this answer to launch detached process :
Process Tree

Related

GetFinalPathNameByHandle returns path of link file

I have the following directory structure:
D:\LinkTest
├─Links
│ ├─LinkToTargetDirectory.lnk
│ └─LinkToTargetFile.lnk
└─Targets
├─TargetFile.txt
└─TargetDirectory
└─FileInTargetDirectory.txt
.lnk files are symbolic links.
They show up in Windows Explorer without the .lnk extension, with the respective target's icon, and double-clicking LinkToTargetDirectory shows the contents of D:\LinkTest\Targets\TargetDirectory, and double-clicking LinkToTargetFile opens the file D:\LinkTest\Targets\TargetFile.txt in notepad.
.txt files are text files.
Everything else are directories.
I would like to use the following code taken from this answer to get the path of the file/directory pointed to:
public static class NativeMethods
{
private static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
private const uint FILE_READ_EA = 0x0008;
private const uint FILE_FLAG_BACKUP_SEMANTICS = 0x2000000;
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern uint GetFinalPathNameByHandle(IntPtr hFile, [MarshalAs(UnmanagedType.LPTStr)] StringBuilder lpszFilePath, uint cchFilePath, uint dwFlags);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr CreateFile(
[MarshalAs(UnmanagedType.LPTStr)] string filename,
[MarshalAs(UnmanagedType.U4)] uint access,
[MarshalAs(UnmanagedType.U4)] FileShare share,
IntPtr securityAttributes, // optional SECURITY_ATTRIBUTES struct or IntPtr.Zero
[MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
[MarshalAs(UnmanagedType.U4)] uint flagsAndAttributes,
IntPtr templateFile);
public static string GetFinalPathName(string path)
{
var h = CreateFile(path,
FILE_READ_EA,
FileShare.ReadWrite | FileShare.Delete,
IntPtr.Zero,
FileMode.Open,
FILE_FLAG_BACKUP_SEMANTICS,
IntPtr.Zero);
if (h == INVALID_HANDLE_VALUE)
throw new Win32Exception();
try
{
var sb = new StringBuilder(1024);
var res = GetFinalPathNameByHandle(h, sb, 1024, 0);
if (res == 0)
throw new Win32Exception();
return sb.ToString();
}
finally
{
CloseHandle(h);
}
}
}
What should happen:
Console.WriteLine(NativeMethods.GetFinalPathName(#"D:\LinkTest\Links\LinkToTargetDirectory.lnk"));
should print \\?\D:\LinkTest\Targets\TargetDirectory
Console.WriteLine(NativeMethods.GetFinalPathName(#"D:\LinkTest\Links\LinkToTargetFile.lnk"));
should print \\?\D:\LinkTest\Targets\TargetFile.txt
What actually happens:
Console.WriteLine(NativeMethods.GetFinalPathName(#"D:\LinkTest\Links\LinkToTargetDirectory.lnk"));
prints \\?\D:\LinkTest\Links\LinkToTargetDirectory.lnk
Console.WriteLine(NativeMethods.GetFinalPathName(#"D:\LinkTest\Links\LinkToTargetFile.lnk"));
prints \\?\D:\LinkTest\Links\LinkToTargetFile.lnk
So instead of getting the target's path, it just returns the path of the link file.
What is wrong with this code and how can I fix it?
I already found this: https://social.msdn.microsoft.com/forums/windowsdesktop/en-US/d8a26a7f-6661-48ce-b183-8e476dfffecd/am-i-using-getfinalpathnamebyhandle-incorrectly?forum=windowsgeneraldevelopmentissues
Passing 8 instead of 0 as the last argument to GetFinalPathNameByHandle seems to help that user, but it does not change the behavior for me.

Binding static IP settings

I'm trying to configure a device's TCP/IP settings via registries but i can't seem to get it to work without a soft reboot.
I've been scouring Google for an answer and found that the way to do it is to pinvoke DeviceIoControl().
I've looked at the registries and they are definitely changing to the ones i want, I can't get DeviceIoControl() to return anything except 0 (fail) and I'm not sure what I'm doing wrong.
[DllImport("coredll.dll", EntryPoint = "CreateFile", SetLastError = true)]
private static extern IntPtr CreateFile(string lpFileName, uint dwDesiredAccess, uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition, uint dwFLagsAndAttributes, IntPtr hTemplateFile);
[DllImport("coredll.dll", EntryPoint = "DeviceIoControl", SetLastError = true)]
private static extern int DeviceIoControl(IntPtr hDevice, uint dwIoControlCode, ref string lpInBuffer, int nInBufferSize, out string lpOutBuffer, int nOutBufferSize, ref int lpBytesReturned, IntPtr lpOverlapped);
[DllImport("coredll.dll", EntryPoint = "CloseHandle", SetLastError = true)]
private static extern bool CloseHandle(IntPtr hObject);
public static void SetTCPIP()
{
RegistryKey baseKey = Registry.LocalMachine;
string subKey = #"Comm\FEC1\Parms\TcpIp";
string enableDhcpKey = "EnableDHCP";
string gatewayKey = "DefaultGateway";
string ipAddressKey = "IpAddress";
string subnetMaskKey = "Subnetmask";
string ipAddress = "192.168.8.100";
string subnetMask = "255.255.255.0";
string gateway = "192.168.8.1";
WriteReg(baseKey, subKey, enableDhcpKey, 0);
WriteRegMultiString(baseKey, subKey, ipAddressKey, ipAddress);
WriteRegMultiString(baseKey, subKey, gatewayKey, gateway);
WriteRegMultiString(baseKey, subKey, subnetMaskKey, subnetMask);
CommitLocalMachineRegistry(); //RegFlushKey()
BindAdapter();
}
private static void BindAdapter()
{
const uint genericRead = 0x80000000;
const uint genericWrite = 0x40000000;
const int IOCTL_NDIS_REBIND_ADAPTER = 0x17002E;
const int OPEN_EXISTING = 3;
const string NI = #"NDS0:";
//Open the Handle to Rebind the Network Adapter
IntPtr hNdis = CreateFile(NI, genericRead | genericWrite, 0, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
if (hNdis == IntPtr.Zero)
{
Debug.WriteLine("!!!!!!!!!!!!!!!!!"+hNdis);
}
string Adapter = "FEC1\0";
string OutBuffer = "";
int bytesReturned = 0;
//Rebind the Network Adapter
int check = DeviceIoControl(hNdis, IOCTL_NDIS_REBIND_ADAPTER, ref Adapter, Adapter.Length,
out OutBuffer, 0, ref bytesReturned, IntPtr.Zero);
if (check==0)
{
Debug.WriteLine("!!!!!!!!!!!!!!!!!!");
Debug.WriteLine(Marshal.GetLastWin32Error());
}
//Close the Handle
CloseHandle(hNdis);
}
I've tried getting more info on the issue using GetLastWin32Error() but it's returning 0 as well
Edit (couldn't add comment)
i've tried having Adapter = "FEC1\0\0" and I've also tried marshalling as a StringBuilder with no luck
Edit 2
Registries are HIVE based, so changes persist after a reboot.

Call kernel32.dll from website project

I have created website project, and am using IIS version 6 as the web server when developing. When I try to debug this method (createfile) bellow in order to read from USB .
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern SafeFileHandle CreateFile(string lpFileName, uint dwDesiredAccess, uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile);
const uint OPEN_EXISTING = 3;
const uint GENERIC_WRITE = (0x40000000);
const uint FSCTL_LOCK_VOLUME = 0x00090018;
const uint FSCTL_UNLOCK_VOLUME = 0x0009001c;
const uint FSCTL_DISMOUNT_VOLUME = 0x00090020;
bool success = false;
int intOut;
string deviceId = #"\\.\H:";
long DiskSize = 2056320000;
SafeFileHandle diskHandle = CreateFile(deviceId, GENERIC_WRITE, 0, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
if (diskHandle.IsInvalid)
{
//Console.WriteLine(deviceId + " open error.");
Debug.WriteLine(Marshal.GetLastWin32Error());
return 0;
}
I get this error num (5=access denied), but it works fine in other types of projects like (windows forms , console application , asp project ).
How can this be solved?
thank you in advance
the solution has been found, we just need to give permissions to the application pool by changing the identity property to (Local system)

Getting Disk geometry Information

I need to obtain disk geometry info, but something wrong and DeviceIoControl returning false. Any ideas how to fix it? Or Other examples using C# and kernel32 appreciated.
[DllImport("kernel32.dll")]
public static extern IntPtr CreateFile(
string lpFileName, int dwDesiredAccess, int dwShareMode,
IntPtr lpSecurityAttributes, int dwCreationDisposition,
int dwFlagsAndAttributes, IntPtr hTemplateFile);
private const int FILE_SHARE_READ = 1;
private const int OPEN_ALWAYS = 4;
private const int INVALID_HANDLE_VALUE = -1;
[DllImport("kernel32.dll", ExactSpelling = true)]
internal static extern bool DeviceIoControl(
IntPtr hDevice, int dwIoControlCode, IntPtr lpInBuffer, int nInBufferSize,
IntPtr lpOutBuffer, int nOutBufferSize, ref int lpBytesReturned, IntPtr lpOverlapped);
private const int IOCTL_DISK_GET_MEDIA_TYPES = 0x00070c00;
static void Main(string[] args)
{
IntPtr hflp = CreateFile(#""\\.\C:", 0, FILE_SHARE_READ, IntPtr.Zero, OPEN_ALWAYS, 0, IntPtr.Zero);
if ((int)hflp == INVALID_HANDLE_VALUE)
{ Console.WriteLine("CreateFile failed"); return; }
Type ts = typeof(DISK_GEOMETRY);
int ss = Marshal.SizeOf(ts);
int ssa = ss * 20;
IntPtr mptr = Marshal.AllocHGlobal(ssa);
int byret = 0;
bool ok = DeviceIoControl(hflp, IOCTL_DISK_GET_MEDIA_TYPES, IntPtr.Zero, 0,
mptr, ssa, ref byret, IntPtr.Zero);
if (!ok)
{ Console.WriteLine("DeviceIoControl failed"); return; }
int count = byret / ss;
int run = (int)mptr;
DISK_GEOMETRY gem;
for (int i = 0; i < count; i++)
{
gem = (DISK_GEOMETRY)Marshal.PtrToStructure((IntPtr)run, ts);
Console.WriteLine("MediaType={0} SectorsPerTrack={1}", gem.MediaType, gem.SectorsPerTrack);
run += ss;
}
Marshal.FreeHGlobal(mptr);
}
P.S I've already read msdn help on this.
IOCTL_DISK_GET_MEDIA_TYPES appears to be legacy and no longer supported. At least that's the case on my OS (Win7 x64). Attempting to call DeviceIoControl with IOCTL_DISK_GET_MEDIA_TYPES results in error code 1, ERROR_INVALID_FUNCTION.
I believe that you will need to use IOCTL_STORAGE_GET_MEDIA_TYPES_EX instead.
My advice in this situation is to attempt to call the API functions from C++ first. That way you don't have to struggle with p/invoke and you know that all the structures and function prototypes are correct. Once you have worked out how to call the particular API function then translate into p/invoke.
As an aside, you should be a little more careful about your p/invokes. Take care to use uint to match DWORD, and make sure you use SetLastError=true so that you can query the error code with Marshal.GetLastWin32Error().
Something like this:
[DllImport("kernel32.dll", SetLastError=true)]
public static extern IntPtr CreateFile(
string lpFileName, uint dwDesiredAccess, uint dwShareMode,
IntPtr lpSecurityAttributes, uint dwCreationDisposition,
uint dwFlagsAndAttributes, IntPtr hTemplateFile);
[DllImport("kernel32.dll", SetLastError=true)]
internal static extern bool DeviceIoControl(
IntPtr hDevice, uint dwIoControlCode, IntPtr lpInBuffer,
uint nInBufferSize, IntPtr lpOutBuffer, uint nOutBufferSize,
ref uint lpBytesReturned, IntPtr lpOverlapped);

Process.Start with different credentials with UAC on

I am trying to start another process with Process.Start running under different credentials with the UAC turned on. I get the following error:
System.ComponentModel.Win32Exception:
Logon failure: user account
restriction. Possible reasons are
blank passwords not allowed, logon
hour restrictions, or a policy
restriction has been enforced at
System.Diagnostics.Process.StartWithCreateProcess(ProcessStartInfo
startInfo) at
System.Diagnostics.Process.Start()
at
System.Diagnostics.Process.Start(ProcessStartInfo
startInfo)
If I turn off UAC, it works fine. If I start the process without specifying username and password, it works fine. Below is a copy of the code:
ProcessStartInfo processInfo = new ProcessStartInfo();
processInfo.Verb = "runas";
processInfo.FileName = command;
processInfo.Arguments = parameters;
processInfo.UserName = txtUserName.Text;
processInfo.Password = password;
processInfo.Domain = "myDomain";
processInfo.UseShellExecute = false;
processInfo.LoadUserProfile = true;
Process.Start(processInfo);
I believe this has something to do with the desktop session, but am not sure why it works when UAC is off.
Any ideas on how to get this to work without turning off UAC, modifying the registry, or changing local/group policies would greatly be appreciated.
Thanks
EDIT
The Process that has the code above is started as an admin:
static void Main(string[] args)
{
//make sure we are running with admin privileges
if (VerifyIsAdmin())
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MyApp(args));
}
}
private static bool VerifyIsAdmin()
{
WindowsPrincipal principal = new WindowsPrincipal(WindowsIdentity.GetCurrent());
bool isAdmin = principal.IsInRole(WindowsBuiltInRole.Administrator);
if (!isAdmin)
{
ProcessStartInfo processInfo = new ProcessStartInfo();
processInfo.Verb = "runas";
processInfo.FileName = Application.ExecutablePath;
Process.Start(processInfo);
}
return isAdmin;
}
EDIT
Tried CreateProcessWithLogonW ... Had same problem.
After further searching, looks like it is a problem with the Group Policy requiring smart card logon (scforceoption). That being said, I still don't understand why it works when UAC is off. It should still require smart card on logon.
It doesn't look like I am going to be able to get around this. Any insight/advice would be appreciated.
You have to do following things:
1. Get who is logged in and in this user has an active desktop
2. Get process' handle started by this user
3. "Steal" credentials from this process
4. Start process with those credentials
Following code should do the trick:
using System;
using System.Text;
using System.Security;
using System.Management;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace Win32
{
public class Win32API
{
#region WMI Constants
private const String cstrScope = "root\\CIMV2";
private const String cstrLoggenInUser = "SELECT * FROM Win32_ComputerSystem";
#endregion
#region Win32 API routines
[StructLayout(LayoutKind.Sequential)]
struct SECURITY_ATTRIBUTES
{
public Int32 Length;
public IntPtr lpSecurityDescriptor;
public Boolean bInheritHandle;
}
enum TOKEN_TYPE
{
TokenPrimary = 1,
TokenImpersonation = 2
}
enum TOKEN_INFORMATION_CLASS
{
TokenUser = 1,
TokenGroups,
TokenPrivileges,
TokenOwner,
TokenPrimaryGroup,
TokenDefaultDacl,
TokenSource,
TokenType,
TokenImpersonationLevel,
TokenStatistics,
TokenRestrictedSids,
TokenSessionId,
TokenGroupsAndPrivileges,
TokenSessionReference,
TokenSandBoxInert,
TokenAuditPolicy,
TokenOrigin,
MaxTokenInfoClass // MaxTokenInfoClass should always be the last enum
}
[StructLayout(LayoutKind.Sequential)]
struct STARTUPINFO
{
public Int32 cb;
public String lpReserved;
public String lpDesktop;
public String lpTitle;
public UInt32 dwX;
public UInt32 dwY;
public UInt32 dwXSize;
public UInt32 dwYSize;
public UInt32 dwXCountChars;
public UInt32 dwYCountChars;
public UInt32 dwFillAttribute;
public UInt32 dwFlags;
public short wShowWindow;
public short cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential)]
struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public UInt32 dwProcessId;
public UInt32 dwThreadId;
}
enum SECURITY_IMPERSONATION_LEVEL
{
SecurityAnonymous = 0,
SecurityIdentification = 1,
SecurityImpersonation = 2,
SecurityDelegation = 3,
}
[StructLayout(LayoutKind.Sequential)]
struct LUID
{
public Int32 LowPart;
public Int32 HighPart;
}
[StructLayout(LayoutKind.Sequential)]
struct LUID_AND_ATRIBUTES
{
LUID Luid;
Int32 Attributes;
}
[StructLayout(LayoutKind.Sequential)]
struct TOKEN_PRIVILEGES
{
public Int32 PrivilegeCount;
//LUID_AND_ATRIBUTES
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public Int32[] Privileges;
}
const Int32 READ_CONTROL = 0x00020000;
const Int32 STANDARD_RIGHTS_REQUIRED = 0x000F0000;
const Int32 STANDARD_RIGHTS_READ = READ_CONTROL;
const Int32 STANDARD_RIGHTS_WRITE = READ_CONTROL;
const Int32 STANDARD_RIGHTS_EXECUTE = READ_CONTROL;
const Int32 STANDARD_RIGHTS_ALL = 0x001F0000;
const Int32 SPECIFIC_RIGHTS_ALL = 0x0000FFFF;
const Int32 TOKEN_ASSIGN_PRIMARY = 0x0001;
const Int32 TOKEN_DUPLICATE = 0x0002;
const Int32 TOKEN_IMPERSONATE = 0x0004;
const Int32 TOKEN_QUERY = 0x0008;
const Int32 TOKEN_QUERY_SOURCE = 0x0010;
const Int32 TOKEN_ADJUST_PRIVILEGES = 0x0020;
const Int32 TOKEN_ADJUST_GROUPS = 0x0040;
const Int32 TOKEN_ADJUST_DEFAULT = 0x0080;
const Int32 TOKEN_ADJUST_SESSIONID = 0x0100;
const Int32 TOKEN_ALL_ACCESS_P = (
STANDARD_RIGHTS_REQUIRED |
TOKEN_ASSIGN_PRIMARY |
TOKEN_DUPLICATE |
TOKEN_IMPERSONATE |
TOKEN_QUERY |
TOKEN_QUERY_SOURCE |
TOKEN_ADJUST_PRIVILEGES |
TOKEN_ADJUST_GROUPS |
TOKEN_ADJUST_DEFAULT);
const Int32 TOKEN_ALL_ACCESS = TOKEN_ALL_ACCESS_P | TOKEN_ADJUST_SESSIONID;
const Int32 TOKEN_READ = STANDARD_RIGHTS_READ | TOKEN_QUERY;
const Int32 TOKEN_WRITE = STANDARD_RIGHTS_WRITE |
TOKEN_ADJUST_PRIVILEGES |
TOKEN_ADJUST_GROUPS |
TOKEN_ADJUST_DEFAULT;
const Int32 TOKEN_EXECUTE = STANDARD_RIGHTS_EXECUTE;
const UInt32 MAXIMUM_ALLOWED = 0x2000000;
const Int32 CREATE_NEW_PROCESS_GROUP = 0x00000200;
const Int32 CREATE_UNICODE_ENVIRONMENT = 0x00000400;
const Int32 IDLE_PRIORITY_CLASS = 0x40;
const Int32 NORMAL_PRIORITY_CLASS = 0x20;
const Int32 HIGH_PRIORITY_CLASS = 0x80;
const Int32 REALTIME_PRIORITY_CLASS = 0x100;
const Int32 CREATE_NEW_CONSOLE = 0x00000010;
const string SE_DEBUG_NAME = "SeDebugPrivilege";
const string SE_RESTORE_NAME = "SeRestorePrivilege";
const string SE_BACKUP_NAME = "SeBackupPrivilege";
const Int32 SE_PRIVILEGE_ENABLED = 0x0002;
const Int32 ERROR_NOT_ALL_ASSIGNED = 1300;
[StructLayout(LayoutKind.Sequential)]
struct PROCESSENTRY32
{
UInt32 dwSize;
UInt32 cntUsage;
UInt32 th32ProcessID;
IntPtr th32DefaultHeapID;
UInt32 th32ModuleID;
UInt32 cntThreads;
UInt32 th32ParentProcessID;
Int32 pcPriClassBase;
UInt32 dwFlags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
string szExeFile;
}
const UInt32 TH32CS_SNAPPROCESS = 0x00000002;
const Int32 INVALID_HANDLE_VALUE = -1;
[DllImport("kernel32.dll", SetLastError = true)]
static extern Boolean CloseHandle(IntPtr hSnapshot);
[DllImport("kernel32.dll")]
public static extern UInt32 WTSGetActiveConsoleSessionId();
[DllImport("Wtsapi32.dll")]
static extern UInt32 WTSQueryUserToken(UInt32 SessionId, ref IntPtr phToken);
[DllImport("advapi32.dll", SetLastError = true)]
static extern Boolean LookupPrivilegeValue(IntPtr lpSystemName, string lpname, [MarshalAs(UnmanagedType.Struct)] ref LUID lpLuid);
[DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUser", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
extern static Boolean CreateProcessAsUser(IntPtr hToken, String lpApplicationName, String lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes,
ref SECURITY_ATTRIBUTES lpThreadAttributes, Boolean bInheritHandle, Int32 dwCreationFlags, IntPtr lpEnvironment,
String lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);
[DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx")]
extern static Boolean DuplicateTokenEx(IntPtr ExistingTokenHandle, UInt32 dwDesiredAccess,
ref SECURITY_ATTRIBUTES lpThreadAttributes, Int32 TokenType,
Int32 ImpersonationLevel, ref IntPtr DuplicateTokenHandle);
[DllImport("kernel32.dll")]
static extern IntPtr OpenProcess(UInt32 dwDesiredAccess, Boolean bInheritHandle, UInt32 dwProcessId);
[DllImport("advapi32", SetLastError = true), SuppressUnmanagedCodeSecurityAttribute]
static extern Boolean OpenProcessToken(IntPtr ProcessHandle, // handle to process
Int32 DesiredAccess, // desired access to process
ref IntPtr TokenHandle); // handle to open access token
[DllImport("advapi32.dll", SetLastError = true)]
static extern Boolean AdjustTokenPrivileges(IntPtr TokenHandle, Boolean DisableAllPrivileges, ref TOKEN_PRIVILEGES NewState, Int32 BufferLength, IntPtr PreviousState, IntPtr ReturnLength);
[DllImport("advapi32.dll", SetLastError = true)]
static extern Boolean SetTokenInformation(IntPtr TokenHandle, TOKEN_INFORMATION_CLASS TokenInformationClass, ref UInt32 TokenInformation, UInt32 TokenInformationLength);
[DllImport("userenv.dll", SetLastError = true)]
static extern Boolean CreateEnvironmentBlock(ref IntPtr lpEnvironment, IntPtr hToken, Boolean bInherit);
#endregion
#region Methods
/// <summary>
/// Method returns name of the user that logged in on workstation
/// </summary>
public static String GetLoggedInUserName()
{
String userName = String.Empty;
try
{
ManagementObjectSearcher searcher =
new ManagementObjectSearcher(cstrScope, cstrLoggenInUser);
foreach (ManagementObject queryObj in searcher.Get())
{
userName = queryObj["UserName"].ToString();
break;
}
}
catch
{
userName = String.Empty;
}
return userName;
}
/// <summary>
/// Creates the process in the interactive desktop with credentials of the logged in user.
/// </summary>
public static Boolean CreateProcessAsUser(String commandLine,
String workingDirectory,
String userAppName,
out StringBuilder output)
{
Boolean processStarted = false;
output = new StringBuilder();
try
{
UInt32 dwSessionId = WTSGetActiveConsoleSessionId();
output.AppendLine(String.Format("Active console session Id: {0}", dwSessionId));
IntPtr hUserToken = IntPtr.Zero;
WTSQueryUserToken(dwSessionId, ref hUserToken);
if (hUserToken != IntPtr.Zero)
{
output.AppendLine(String.Format("WTSQueryUserToken() OK (hUserToken:{0})", hUserToken));
Process[] processes = Process.GetProcessesByName(userAppName);
if (processes.Length == 0)
{
output.AppendLine(String.Format("Application '{0}' can not be found in the running processes", userAppName));
return false;
}
Int32 userAppProcessId = -1;
for (Int32 k = 0; k < processes.Length; k++)
{
output.AppendLine(String.Format("Process: '{0}', PID: {1}, Handle: {2}, Session: {3}",
processes[k].ProcessName, processes[k].Id, processes[k].Handle, processes[k].SessionId));
if ((UInt32)processes[k].SessionId == dwSessionId)
{
userAppProcessId = processes[k].Id;
}
}
if (userAppProcessId == -1)
{
output.AppendLine(String.Format("Application '{0}' is not found in the processes of the current session", userAppName));
return false;
}
IntPtr hProcess = OpenProcess((Int32)MAXIMUM_ALLOWED, false, (UInt32)userAppProcessId);
IntPtr hPToken = IntPtr.Zero;
OpenProcessToken(hProcess,
TOKEN_ADJUST_PRIVILEGES
| TOKEN_QUERY
| TOKEN_DUPLICATE
| TOKEN_ASSIGN_PRIMARY
| TOKEN_ADJUST_SESSIONID
| TOKEN_READ
| TOKEN_WRITE,
ref hPToken);
if (hPToken != IntPtr.Zero)
{
output.AppendLine(String.Format("OpenProcessToken() OK (Token: {0})", hPToken));
LUID luid = new LUID();
if (LookupPrivilegeValue(IntPtr.Zero, SE_DEBUG_NAME, ref luid))
{
output.AppendLine(String.Format("LookupPrivilegeValue() OK (High: {0}, Low: {1})", luid.HighPart, luid.LowPart));
SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
sa.Length = Marshal.SizeOf(sa);
IntPtr hUserTokenDup = IntPtr.Zero;
DuplicateTokenEx(hPToken,
(Int32)MAXIMUM_ALLOWED,
ref sa,
(Int32)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification,
(Int32)TOKEN_TYPE.TokenPrimary,
ref hUserTokenDup);
if (hUserTokenDup != IntPtr.Zero)
{
output.AppendLine(String.Format("DuplicateTokenEx() OK (hToken: {0})", hUserTokenDup));
TOKEN_PRIVILEGES tp = new TOKEN_PRIVILEGES
{
PrivilegeCount = 1,
Privileges = new Int32[3]
};
tp.Privileges[1] = luid.HighPart;
tp.Privileges[0] = luid.LowPart;
tp.Privileges[2] = SE_PRIVILEGE_ENABLED;
//Adjust Token privilege
if (SetTokenInformation(hUserTokenDup,
TOKEN_INFORMATION_CLASS.TokenSessionId,
ref dwSessionId,
(UInt32)IntPtr.Size))
{
output.AppendLine(String.Format("SetTokenInformation() OK"));
if (AdjustTokenPrivileges(hUserTokenDup,
false, ref tp, Marshal.SizeOf(tp),
IntPtr.Zero, IntPtr.Zero))
{
output.AppendLine("AdjustTokenPrivileges() OK");
Int32 dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE;
IntPtr pEnv = IntPtr.Zero;
if (CreateEnvironmentBlock(ref pEnv, hUserTokenDup, true))
{
dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
}
else
{
output.AppendLine(String.Format("CreateEnvironmentBlock() FAILED (Last Error: {0})", Marshal.GetLastWin32Error()));
pEnv = IntPtr.Zero;
}
// Launch the process in the client's logon session.
PROCESS_INFORMATION pi;
STARTUPINFO si = new STARTUPINFO();
si.cb = Marshal.SizeOf(si);
si.lpDesktop = "winsta0\\default";
output.AppendLine(String.Format("CreateProcess (Path:{0}, CurrDir:{1})", commandLine, workingDirectory));
if (CreateProcessAsUser(hUserTokenDup, // client's access token
null, // file to execute
commandLine, // command line
ref sa, // pointer to process SECURITY_ATTRIBUTES
ref sa, // pointer to thread SECURITY_ATTRIBUTES
false, // handles are not inheritable
dwCreationFlags, // creation flags
pEnv, // pointer to new environment block
workingDirectory, // name of current directory
ref si, // pointer to STARTUPINFO structure
out pi // receives information about new process
))
{
processStarted = true;
output.AppendLine(String.Format("CreateProcessAsUser() OK (PID: {0})", pi.dwProcessId));
}
else
{
output.AppendLine(String.Format("CreateProcessAsUser() failed (Last Error: {0})", Marshal.GetLastWin32Error()));
}
}
else
{
output.AppendLine(String.Format("AdjustTokenPrivileges() failed (Last Error: {0})", Marshal.GetLastWin32Error()));
}
}
else
{
output.AppendLine(String.Format("SetTokenInformation() failed (Last Error: {0})", Marshal.GetLastWin32Error()));
}
CloseHandle(hUserTokenDup);
}
else
{
output.AppendLine(String.Format("DuplicateTokenEx() failed (Last Error: {0})", Marshal.GetLastWin32Error()));
}
}
else
{
output.AppendLine(String.Format("LookupPrivilegeValue() failed (Last Error: {0})", Marshal.GetLastWin32Error()));
}
CloseHandle(hPToken);
}
else
{
output.AppendLine(String.Format("OpenProcessToken() failed (Last Error: {0})", Marshal.GetLastWin32Error()));
}
CloseHandle(hUserToken);
}
else
{
output.AppendLine(String.Format("WTSQueryUserToken failed: {0}", Marshal.GetLastWin32Error()));
}
}
catch (Exception ex)
{
output.AppendLine("Exception occurred: " + ex.Message);
}
return processStarted;
}
#endregion
}
}
Usage:
try
{
StringBuilder output = new StringBuilder();
if (!Win32API.CreateProcessAsUser("C:\\WINDOWS\\notepad.exe", "C:\\WINDOWS\\", "winlogon", out output))
throw new Win32Exception(output.ToString());
else
throw new Win32Exception("Process RUN!!!");
}
catch (Win32Exception ex)
{
File.WriteAllText("c:\\hello!.txt", ex.Message + " " + ex.ErrorCode.ToString());
}
Credits for the code goes to Forcas from rsdn.ru
I found some old project where I used code below and it worked at the time (looks similar to yours), maybe it won't help but still it's worth a try :
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.UseShellExecute = true;
startInfo.WorkingDirectory = Environment.CurrentDirectory;
startInfo.FileName = Application.ExecutablePath;
startInfo.Verb = "runas";
try
{
Process p = Process.Start(startInfo);
}
catch(System.ComponentModel.Win32Exception ex)
{
return;
}
It looks like you aren't alone with this issue, and it may not be possible as you are trying to do:
http://www.icodefactory.com/lab/post/UAC-Revealed-7e-elevation-of-rights-from-NET-as-commonly-misunderstood.aspx
It's just a guess, but maybe you can request admin privileges and then impersonate the user and then finally then start the process while impersonating?
Edit: You can also try PInvoke which based on a comment on this page is working for starting a process under a different user:
http://blogs.msdn.com/thottams/archive/2006/08/11/696013.aspx
Try adding manifest file for that other process that you want to start.
Windows will analyze manifest file and present you with UAC dialog before process start.
Here is the info how to embed manifest file link1 and link2
HTH

Categories