C# USB driver from C++ - c#

I've a problem trying to call SetupDiEnumDeviceInterfaces from C#. It always returns 1784 error code ("The supplied user buffer is not valid for the requested operation").
On the same machine if I execute the corrisponding C++ code the function is successful.
This is my C# code:
Guid classGuid = GUID_DEVINTERFACE_DFU; // Guid(0x3fe809ab, 0xfb91, 0x4cb5, 0xa6, 0x43, 0x69, 0x67, 0x0d, 0x52,0x36,0x6e)
IntPtr hDevInfo = Win32.SetupDiGetClassDevs(ref classGuid, IntPtr.Zero, IntPtr.Zero, Win32.DIGCF_DEVICEINTERFACE | Win32.DIGCF_PRESENT);
if (hDevInfo.ToInt32() == Win32.INVALID_HANDLE_VALUE)
{
Console.WriteLine("read hardware information error");
}
else
{
uint i = 0;
SP_DEVINFO_DATA devInfoData = new SP_DEVINFO_DATA();
devInfoData.classGuid = Guid.Empty;
devInfoData.devInst = 0;
devInfoData.reserved = IntPtr.Zero;
bool result = Win32.SetupDiEnumDeviceInfo(hDevInfo, i, devInfoData);
if (false == result)
{
int error = Marshal.GetLastWin32Error();
if (error != Win32.ERROR_NO_MORE_ITEMS)
throw new Win32Exception(error);
}
SP_DEVICE_INTERFACE_DATA ifData = new SP_DEVICE_INTERFACE_DATA();
ifData.cbSize = 50;
ifData.Flags = 0;
ifData.InterfaceClassGuid = Guid.Empty;
ifData.Reserved = IntPtr.Zero;
bool result2 = Win32.SetupDiEnumDeviceInterfaces(hDevInfo, IntPtr.Zero, ref classGuid, i, ref ifData);
if(result2 == false)
{
int error = Marshal.GetLastWin32Error();
if (error != Win32.ERROR_NO_MORE_ITEMS)
throw new Win32Exception(error);
}
}
[StructLayout(LayoutKind.Sequential)]
public class SP_DEVINFO_DATA
{
public uint cbSize = (uint)Marshal.SizeOf(typeof(SP_DEVINFO_DATA));
public Guid classGuid;
public uint devInst;
public IntPtr reserved;
};
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
public class SP_DEVICE_INTERFACE_DATA
{
public uint cbSize = (uint)Marshal.SizeOf(typeof(SP_DEVICE_INTERFACE_DATA));
public Guid InterfaceClassGuid;
public uint Flags;
public IntPtr Reserved;
}
public class Win32
{
[DllImport("setupapi.dll", SetLastError = true)]
public static extern IntPtr SetupDiGetClassDevs(ref Guid ClassGuid, IntPtr Enumerator, IntPtr hwndParent, uint Flags);
[DllImport("setupapi.dll", SetLastError = true)]
public static extern Boolean SetupDiEnumDeviceInfo(IntPtr lpInfoSet, UInt32 dwIndex, SP_DEVINFO_DATA devInfoData);
public const int DIGCF_PRESENT = 0x02;
public const int DIGCF_DEVICEINTERFACE = 0x10;
public const long ERROR_NO_MORE_ITEMS = 259L;
}
And this is my C++ code:
GUID Guid=GUID_DFU; // { 0x3fe809ab, 0xfb91, 0x4cb5, { 0xa6, 0x43, 0x69, 0x67, 0x0d, 0x52,0x36,0x6e } }
HDEVINFO info = SetupDiGetClassDevs(&Guid, NULL, NULL, DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);
if (info!=INVALID_HANDLE_VALUE)
{
SP_INTERFACE_DEVICE_DATA ifData;
ifData.cbSize=sizeof(ifData);
bool result = SetupDiEnumDeviceInterfaces(info, NULL, &Guid, devIndex, &ifData);
}
The code is running on Windows 8.1 x64 and the C# application platform is x86.

You declare SP_DEVICE_INTERFACE_DATA to be a class in the C# code. That means that it is already a reference. You then pass the SP_DEVICE_INTERFACE_DATA instance by reference to SetupDiEnumDeviceInfo. Which means that you are passing SP_DEVICE_INTERFACE_DATA** rather than SP_DEVICE_INTERFACE_DATA*, in C++ terms.
Either:
Change SP_DEVICE_INTERFACE_DATA to be a struct, or
Leave SP_DEVICE_INTERFACE_DATA as a class, but pass it by value. That is, as you did with SP_DEVINFO_DATA and SetupDiEnumDeviceInfo.
You are setting cbSize incorrectly. It should be:
ifData.cbSize = Marshal.SizeOf(typeof(SP_DEVICE_INTERFACE_DATA));
There's no point in setting the other members of SP_DEVICE_INTERFACE_DATA since they are ignored by the function that you call. That function's job is to populate those members.
There may be other problems. It's hard to say because you have not posted your actual code. The C++ code does not compile, and the C# code is incomplete.

Related

GetEffectiveRightsFromAcl throws AccessViolationException

I am writing a module in C# which needs to retrieve the effective rights on a resource for a given Active Directory user account. I'm attempting to pinvoke the GetEffectiveRightsFromAcl C function to do this. The function is returning an exception:
System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
From my extremely limited knowledge of unmanaged programming, I'm lead to believe that maybe one of the pointers I'm passing into the function (or the TRUSTEE struct) isn't actually pointing to the place in memory that I think it does.
Here's my code:
class Program {
const Int32 NO_MULTIPLE_TRUSTEE = 0;
const Int32 TRUSTEE_IS_SID = 0;
const Int32 TRUSTEE_IS_USER = 1;
[DllImport("advapi32.dll", SetLastError = true)]
static extern UInt32 GetEffectiveRightsFromAcl(
IntPtr pAcl,
ref TRUSTEE pTrustee,
ref Int32 pAclRights);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 4)]
struct TRUSTEE {
public IntPtr pMultipleTrustee;
public Int32 MultipleTrusteeOperation;
public Int32 TrusteeForm;
public Int32 TrusteeType;
[MarshalAs(UnmanagedType.LPStr)]
public String ptstrName;
}
static void Main(string[] args) {
var SID = new WindowsIdentity("company\user1").user ?? throw new ArgumentException("User does not exist");
IntPtr fileACLHandle = getFileSecurityHandle("C:\temp\test.txt"); //Confirmed working via the pinvoked GetNamedSecurityInfo C function
var trustee = new TRUSTEE {
pMultipleTrustee = IntPtr.Zero,
MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE,
TrusteeForm = TRUSTEE_IS_SID,
TrusteeType = TRUSTEE_IS_USER,
ptstrName = SID.Value
};
Int32 pAclRights = 0;
UInt32 result = GetEffectiveRightsFromAcl(fileACLHandle, ref trustee, ref pAclRights);
if (result != 0) {
Int32 hResult = Marshal.GetLastWin32Error();
var ex = new Win32Exception(hResult);
Console.WriteLine(ex.ToString());
return;
}
Console.WriteLine($"Rights: {pAclRights}");
}
}
Thanks in advance for any help!
The problem was my lack of understanding about marshalling pointers between managed and unmanaged memory. I had a method, which I didn't post because I didn't think it was relevant, that was returning an IntPtr handle, however, I was destroying the handle in that same method!
static IntPtr getHandle(Byte[] bytes) {
IntPtr handle = Marshal.AllocHGlobal(bytes.Length);
Marshal.Copy(bytes, 0, handle, bytes.Length);
//Marshal.FreeHGlobal(handle); <-- Destroys the pointer! Don't do this yet.
return handle;
}
This explains the Attempted to read or write protected memory. message: I had freed up the memory that my handle was pointing to before I actually used it!
In terms of P/Invoke, my declaration was a bit off as well. Here is what worked:
const Int32 NO_MULTIPLE_TRUSTEE = 0;
const Int32 TRUSTEE_IS_NAME = 1;
const Int32 TRUSTEE_IS_USER = 1;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
struct TRUSTEE {
public IntPtr pMultipleTrustee;
public Int32 MultipleTrusteeOperation;
public Int32 TrusteeForm;
public Int32 TrusteeType;
[MarshalAs(UnmanagedType.LPTStr)]
public String ptstrName;
}
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern Int32 GetEffectiveRightsFromAcl(
IntPtr pAcl,
ref TRUSTEE pTrustee,
out Int32 pAclRights);
//-----And later on-----//
Int32 pAclRights = 0;
try {
var trustee = new TRUSTEE {
pMultipleTrustee = IntPtr.Zero,
MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE,
TrusteeForm = TRUSTEE_IS_NAME,
TrusteeType = TRUSTEE_IS_USER,
ptstrName = userName
};
Int32 hResult = GetEffectiveRightsFromAcl(keySecurityHandle, ref trustee, out pAclRights);
if (hResult != 0) {
throw new Win32Exception(Marshal.GetLastWin32Error());
}
} catch (Exception ex) {
//do something with the exception
} finally {
//Lastly, deallocate the unmanaged pointer
Marshal.FreeHGlobal(keySecurityHandle);
}
The final thing I should point out is that I was doing extra work to translate a user ID into a SID. It is far easier to pass a userID, which the GetEffectiveRightsFromAcl function is able to resolve in virtually any format.
There are quite a number of issues with your code.
Structure packing (in general, you should not specify it, defaults are consistent between .NET and Win32)
Ansi vs Unicode
Error handling (GetLastError is not used by these APIs)
Here's a version that seems to work:
var identity = WindowsIdentity.GetCurrent(); // get some identity
var status = GetNamedSecurityInfo(#"c:\temp\file.txt",
SE_OBJECT_TYPE.SE_FILE_OBJECT,
SECURITY_INFORMATION.DACL_SECURITY_INFORMATION | SECURITY_INFORMATION.UNPROTECTED_DACL_SECURITY_INFORMATION,
IntPtr.Zero,
IntPtr.Zero,
out var fileACLHandle,
IntPtr.Zero,
IntPtr.Zero);
if (status != 0)
throw new Win32Exception(status);
var nameTrustee = new TRUSTEE_WITH_NAME
{
TrusteeForm = TRUSTEE_FORM.TRUSTEE_IS_NAME,
TrusteeType = TRUSTEE_TYPE.TRUSTEE_IS_USER,
ptstrName = identity.Name
};
status = GetEffectiveRightsFromAcl(fileACLHandle, ref nameTrustee, out var accessMask);
if (status != 0)
throw new Win32Exception(status);
Console.WriteLine($"Rights: {accessMask}");
var sid = new byte[identity.User.BinaryLength];
identity.User.GetBinaryForm(sid, 0);
var sidTrustee = new TRUSTEE_WITH_SID
{
TrusteeForm = TRUSTEE_FORM.TRUSTEE_IS_SID,
TrusteeType = TRUSTEE_TYPE.TRUSTEE_IS_USER,
pSid = Marshal.UnsafeAddrOfPinnedArrayElement(sid, 0)
};
status = GetEffectiveRightsFromAcl(fileACLHandle, ref sidTrustee, out accessMask);
if (status != 0)
throw new Win32Exception(status);
Console.WriteLine($"Rights: {accessMask}");
...
public enum SE_OBJECT_TYPE
{
SE_UNKNOWN_OBJECT_TYPE,
SE_FILE_OBJECT,
SE_SERVICE,
SE_PRINTER,
SE_REGISTRY_KEY,
SE_LMSHARE,
SE_KERNEL_OBJECT,
SE_WINDOW_OBJECT,
SE_DS_OBJECT,
SE_DS_OBJECT_ALL,
SE_PROVIDER_DEFINED_OBJECT,
SE_WMIGUID_OBJECT,
SE_REGISTRY_WOW64_32KEY
}
[Flags]
public enum SECURITY_INFORMATION
{
OWNER_SECURITY_INFORMATION = 0x00000001,
GROUP_SECURITY_INFORMATION = 0x00000002,
DACL_SECURITY_INFORMATION = 0x00000004,
SACL_SECURITY_INFORMATION = 0x00000008,
UNPROTECTED_SACL_SECURITY_INFORMATION = 0x10000000,
UNPROTECTED_DACL_SECURITY_INFORMATION = 0x20000000,
PROTECTED_SACL_SECURITY_INFORMATION = 0x40000000,
PROTECTED_DACL_SECURITY_INFORMATION = unchecked((int)0x80000000)
}
public enum MULTIPLE_TRUSTEE_OPERATION
{
NO_MULTIPLE_TRUSTEE,
TRUSTEE_IS_IMPERSONATE
}
public enum TRUSTEE_FORM
{
TRUSTEE_IS_SID,
TRUSTEE_IS_NAME,
TRUSTEE_BAD_FORM,
TRUSTEE_IS_OBJECTS_AND_SID,
TRUSTEE_IS_OBJECTS_AND_NAME
}
public enum TRUSTEE_TYPE
{
TRUSTEE_IS_UNKNOWN,
TRUSTEE_IS_USER,
TRUSTEE_IS_GROUP,
TRUSTEE_IS_DOMAIN,
TRUSTEE_IS_ALIAS,
TRUSTEE_IS_WELL_KNOWN_GROUP,
TRUSTEE_IS_DELETED,
TRUSTEE_IS_INVALID,
TRUSTEE_IS_COMPUTER
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct TRUSTEE_WITH_NAME
{
public IntPtr pMultipleTrustee;
public MULTIPLE_TRUSTEE_OPERATION MultipleTrusteeOperation;
public TRUSTEE_FORM TrusteeForm;
public TRUSTEE_TYPE TrusteeType;
public string ptstrName;
}
[StructLayout(LayoutKind.Sequential)]
public struct TRUSTEE_WITH_SID
{
public IntPtr pMultipleTrustee;
public MULTIPLE_TRUSTEE_OPERATION MultipleTrusteeOperation;
public TRUSTEE_FORM TrusteeForm;
public TRUSTEE_TYPE TrusteeType;
public IntPtr pSid;
}
[DllImport("advapi32", CharSet = CharSet.Unicode)]
public static extern int GetNamedSecurityInfo(string pObjectName, SE_OBJECT_TYPE ObjectType, SECURITY_INFORMATION SecurityInfo, IntPtr pSidOwner, IntPtr pSidGroup, out IntPtr pDacl, IntPtr pSacl, IntPtr pSecurityDescriptor);
[DllImport("advapi32", CharSet = CharSet.Unicode)]
public static extern int GetEffectiveRightsFromAcl(IntPtr pacl, ref TRUSTEE_WITH_NAME pTrustee, out int pAccessRights);
[DllImport("advapi32", CharSet = CharSet.Unicode)]
public static extern int GetEffectiveRightsFromAcl(IntPtr pacl, ref TRUSTEE_WITH_SID pTrustee, out int pAccessRights);

Using Setup Api to enumerate monitors pnp device ids

For my program I need to get detailed information about the current displays. In my research I came across this post with talks about linking the System.Windows.Forms.Screen class and its EDID information. At first I tried copying and pasting the code found with using p/invoke to supply all the required native methods and structs, but it did not work and only gave me a string of ? for the InstanceID. So instead I tried to use the MSDN resources and again p/invoke to create the code myself. This is what I came up with:
private static void Foo()
{
Guid DisplayGUID = new Guid(Bar.GUID_DEVINTERFACE_MONITOR);
IntPtr DisplaysHandle = Bar.SetupDiGetClassDevs(ref DisplayGUID, null, IntPtr.Zero, (uint)(Win32.DIGCF_PRESENT | Win32.DIGCF_DEVICEINTERFACE));
Bar.SP_DEVICE_INTERFACE_DATA Data = new Bar.SP_DEVICE_INTERFACE_DATA();
Data.cbSize = Marshal.SizeOf(Data);
for (uint id = 0; Bar.SetupDiEnumDeviceInterfaces(DisplaysHandle, IntPtr.Zero, ref DisplayGUID, id, ref Data); id++)
{
Bar.SP_DEVINFO_DATA SPDID = new Bar.SP_DEVINFO_DATA();
SPDID.cbSize = (uint)Marshal.SizeOf(SPDID);
Bar.SP_DEVICE_INTERFACE_DETAIL_DATA NDIDD = new Bar.SP_DEVICE_INTERFACE_DETAIL_DATA();
if (IntPtr.Size == 8) //64 bit
NDIDD.cbSize = 8;
else //32 bit
NDIDD.cbSize = 4 + Marshal.SystemDefaultCharSize;
uint requiredsize = 0;
uint buffer = Bar.BUFFER_SIZE;
if (Bar.SetupDiGetDeviceInterfaceDetail(DisplaysHandle, ref Data, ref NDIDD, buffer, ref requiredsize, ref SPDID))
{
uint size = 0;
Bar.CM_Get_Device_ID_Size(out size, SPDID.DevInst);
IntPtr ptrInstanceBuf = Marshal.AllocHGlobal((int)size);
Bar.CM_Get_Device_ID(SPDID.DevInst, ref ptrInstanceBuf, size);
string InstanceID = Marshal.PtrToStringAuto(ptrInstanceBuf);
Console.WriteLine("InstanceID: {0}", InstanceID);
Marshal.FreeHGlobal(ptrInstanceBuf);
Console.WriteLine("DevicePath: {0}\n", NDIDD.DevicePath);
}
}
Bar.SetupDiDestroyDeviceInfoList(DisplaysHandle);
}
private class Bar
{
[DllImport("setupapi.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SetupDiGetClassDevs(ref Guid ClassGuid, [MarshalAs(UnmanagedType.LPTStr)] string Enumerator, IntPtr hwndParent, uint Flags);
[DllImport("setupapi.dll", SetLastError = true)]
public static extern bool SetupDiDestroyDeviceInfoList(IntPtr DeviceInfoSet);
[DllImport(#"setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern Boolean SetupDiEnumDeviceInterfaces(IntPtr hDevInfo, IntPtr devInfo, ref Guid interfaceClassGuid, UInt32 memberIndex, ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData);
[DllImport(#"setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern Boolean SetupDiGetDeviceInterfaceDetail(IntPtr hDevInfo, ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData, ref SP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData, UInt32 deviceInterfaceDetailDataSize, ref UInt32 requiredSize, ref SP_DEVINFO_DATA deviceInfoData);
[DllImport("setupapi.dll", SetLastError = true)]
public static extern int CM_Get_Device_ID_Size(out uint pulLen, UInt32 dnDevInst, int flags = 0);
[DllImport("setupapi.dll", SetLastError = true)]
public static extern int CM_Get_Device_ID(uint dnDevInst, ref IntPtr Buffer, uint BufferLen, int ulFlags = 0);
public const int BUFFER_SIZE = 168; //guess
public const string GUID_DEVINTERFACE_MONITOR = "{E6F07B5F-EE97-4a90-B076-33F57BF4EAA7}";
[Flags]
public enum DiGetClassFlags : uint
{
DIGCF_DEFAULT = 0x00000001, // only valid with DIGCF_DEVICEINTERFACE
DIGCF_PRESENT = 0x00000002,
DIGCF_ALLCLASSES = 0x00000004,
DIGCF_PROFILE = 0x00000008,
DIGCF_DEVICEINTERFACE = 0x00000010,
}
[StructLayout(LayoutKind.Sequential)]
public struct SP_DEVICE_INTERFACE_DATA
{
public Int32 cbSize;
public Guid interfaceClassGuid;
public Int32 flags;
private UIntPtr reserved;
}
[StructLayout(LayoutKind.Sequential)]
public struct SP_DEVINFO_DATA
{
public uint cbSize;
public Guid classGuid;
public uint DevInst;
public IntPtr reserved;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct SP_DEVICE_INTERFACE_DETAIL_DATA
{
public int cbSize;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = BUFFER_SIZE)]
public string DevicePath;
}
}
My code compiles and runs, but it does not give me the output I am looking for.
The output that I am looking for is:
InstanceID: DISPLAY\DELA00B\5&786e6ca&0&UID1048832
DevicePath: \\?\display#dela00b#5&786e6ca&0&uid1048832#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}
But this is the output I am receiving:
InstanceID: l
DevicePath: \\?\display#dela00b#5&786e6ca&0&uid1048832#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}
My question comes in the form of what is the problem causing the InstanceID to not output correctly.
Turns out I was using the wrong p/invoke signature. CM_Get_Device_ID should look like this:
[DllImport("setupapi.dll", SetLastError = true)]
public static extern int CM_Get_Device_ID(uint dnDevInst, StringBuilder Buffer, int BufferLen, int ulFlags = 0);
Also Updated Usage Code:
StringBuilder IDBuffer = new StringBuilder((int)buffer);
Bar.CM_Get_Device_ID(SPDID.DevInst, IDBuffer, (int)buffer);
Console.WriteLine("InstanceID: {0}", IDBuffer.ToString());
Console.WriteLine("DevicePath: {0}\n", NDIDD.DevicePath);

open external .exe over LAN without the run message c#

Hi Im trying to open a external .exe in c#.
If i try to open the .exe file in my computer like "C:\programs\test.exe" it works well. but if I try to open it in another machine I have to map the drive
S:\programs\test.exe , is ok but it prompts a message if I wanna run this .exe
I want to avoid this message.
ProcessStartInfo psi = new ProcessStartInfo();
psi.FileName = "S:\\dev_express\\calendario_indar.exe";
psi.Arguments = "arguments"
var p = Process.Start(psi);
I had similar problems about running executable files. I found a class from MSDN and I always use it. But I'm not sure if it hides that window too. But give it a try. After adding this class to your Project you can launch your application like that:
NativeMethods.LaunchProcess("S:\\dev_express\\calendario_indar.exe");
Class is here:
class NativeMethods
{
[StructLayout(LayoutKind.Sequential)]
public struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public System.UInt32 dwProcessId;
public System.UInt32 dwThreadId;
}
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public System.UInt32 nLength;
public IntPtr lpSecurityDescriptor;
public bool bInheritHandle;
}
[StructLayout(LayoutKind.Sequential)]
public struct STARTUPINFO
{
public System.UInt32 cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public System.UInt32 dwX;
public System.UInt32 dwY;
public System.UInt32 dwXSize;
public System.UInt32 dwYSize;
public System.UInt32 dwXCountChars;
public System.UInt32 dwYCountChars;
public System.UInt32 dwFillAttribute;
public System.UInt32 dwFlags;
public short wShowWindow;
public short cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential)]
public struct PROFILEINFO
{
public int dwSize;
public int dwFlags;
[MarshalAs(UnmanagedType.LPTStr)]
public string lpUserName;
[MarshalAs(UnmanagedType.LPTStr)]
public string lpProfilePath;
[MarshalAs(UnmanagedType.LPTStr)]
public string lpDefaultPath;
[MarshalAs(UnmanagedType.LPTStr)]
public string lpServerName;
[MarshalAs(UnmanagedType.LPTStr)]
public string lpPolicyPath;
public IntPtr hProfile;
}
internal enum SECURITY_IMPERSONATION_LEVEL
{
SecurityAnonymous = 0,
SecurityIdentification = 1,
SecurityImpersonation = 2,
SecurityDelegation = 3
}
internal enum TOKEN_TYPE
{
TokenPrimary = 1,
TokenImpersonation = 2
}
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern bool CreateProcessAsUser(IntPtr hToken, string lpApplicationName, string lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes, ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandles, uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, ref PROCESS_INFORMATION lpProcessInformation);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool DuplicateTokenEx(IntPtr hExistingToken, uint dwDesiredAccess, ref SECURITY_ATTRIBUTES lpTokenAttributes, SECURITY_IMPERSONATION_LEVEL ImpersonationLevel, TOKEN_TYPE TokenType, ref IntPtr phNewToken);
[DllImport("advapi32.dll", SetLastError = true)]
private static extern bool OpenProcessToken(IntPtr ProcessHandle, int DesiredAccess, ref IntPtr TokenHandle);
[DllImport("userenv.dll", SetLastError = true)]
private static extern bool CreateEnvironmentBlock(ref IntPtr lpEnvironment, IntPtr hToken, bool bInherit);
[DllImport("userenv.dll", SetLastError = true)]
private static extern bool DestroyEnvironmentBlock(IntPtr lpEnvironment);
private const short SW_SHOW = 1;
private const short SW_SHOWMAXIMIZED = 7;
private const int TOKEN_QUERY = 8;
private const int TOKEN_DUPLICATE = 2;
private const int TOKEN_ASSIGN_PRIMARY = 1;
private const int GENERIC_ALL_ACCESS = 268435456;
private const int STARTF_USESHOWWINDOW = 1;
private const int STARTF_FORCEONFEEDBACK = 64;
private const int CREATE_UNICODE_ENVIRONMENT = 0x00000400;
private const string gs_EXPLORER = "explorer";
public static void LaunchProcess(string Ps_CmdLine)
{
IntPtr li_Token = default(IntPtr);
IntPtr li_EnvBlock = default(IntPtr);
Process[] lObj_Processes = Process.GetProcessesByName(gs_EXPLORER);
// Get explorer.exe id
// If process not found
if (lObj_Processes.Length == 0)
{
// Exit routine
return;
}
// Get primary token for the user currently logged in
li_Token = GetPrimaryToken(lObj_Processes[0].Id);
// If token is nothing
if (li_Token.Equals(IntPtr.Zero))
{
// Exit routine
return;
}
// Get environment block
li_EnvBlock = GetEnvironmentBlock(li_Token);
// Launch the process using the environment block and primary token
LaunchProcessAsUser(Ps_CmdLine, li_Token, li_EnvBlock);
// If no valid enviroment block found
if (li_EnvBlock.Equals(IntPtr.Zero))
{
// Exit routine
return;
}
// Destroy environment block. Free environment variables created by the
// CreateEnvironmentBlock function.
DestroyEnvironmentBlock(li_EnvBlock);
}
private static IntPtr GetPrimaryToken(int Pi_ProcessId)
{
IntPtr li_Token = IntPtr.Zero;
IntPtr li_PrimaryToken = IntPtr.Zero;
bool lb_ReturnValue = false;
Process lObj_Process = Process.GetProcessById(Pi_ProcessId);
SECURITY_ATTRIBUTES lObj_SecurityAttributes = default(SECURITY_ATTRIBUTES);
// Get process by id
// Open a handle to the access token associated with a process. The access token
// is a runtime object that represents a user account.
lb_ReturnValue = OpenProcessToken(lObj_Process.Handle, TOKEN_DUPLICATE, ref li_Token);
// If successfull in opening handle to token associated with process
if (lb_ReturnValue)
{
// Create security attributes to pass to the DuplicateTokenEx function
lObj_SecurityAttributes = new SECURITY_ATTRIBUTES();
lObj_SecurityAttributes.nLength = Convert.ToUInt32(Marshal.SizeOf(lObj_SecurityAttributes));
// Create a new access token that duplicates an existing token. This function
// can create either a primary token or an impersonation token.
lb_ReturnValue = DuplicateTokenEx(li_Token, Convert.ToUInt32(TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_QUERY), ref lObj_SecurityAttributes, SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, TOKEN_TYPE.TokenPrimary, ref li_PrimaryToken);
// If un-successful in duplication of the token
if (!lb_ReturnValue)
{
// Throw exception
throw new Exception(string.Format("DuplicateTokenEx Error: {0}", Marshal.GetLastWin32Error()));
}
}
else
{
// If un-successful in opening handle for token then throw exception
throw new Exception(string.Format("OpenProcessToken Error: {0}", Marshal.GetLastWin32Error()));
}
// Return primary token
return li_PrimaryToken;
}
private static IntPtr GetEnvironmentBlock(IntPtr Pi_Token)
{
IntPtr li_EnvBlock = IntPtr.Zero;
bool lb_ReturnValue = CreateEnvironmentBlock(ref li_EnvBlock, Pi_Token, false);
// Retrieve the environment variables for the specified user.
// This block can then be passed to the CreateProcessAsUser function.
// If not successful in creation of environment block then
if (!lb_ReturnValue)
{
// Throw exception
throw new Exception(string.Format("CreateEnvironmentBlock Error: {0}", Marshal.GetLastWin32Error()));
}
// Return the retrieved environment block
return li_EnvBlock;
}
private static void LaunchProcessAsUser(string Ps_CmdLine, IntPtr Pi_Token, IntPtr Pi_EnvBlock)
{
bool lb_Result = false;
PROCESS_INFORMATION lObj_ProcessInformation = default(PROCESS_INFORMATION);
SECURITY_ATTRIBUTES lObj_ProcessAttributes = default(SECURITY_ATTRIBUTES);
SECURITY_ATTRIBUTES lObj_ThreadAttributes = default(SECURITY_ATTRIBUTES);
STARTUPINFO lObj_StartupInfo = default(STARTUPINFO);
// Information about the newly created process and its primary thread.
lObj_ProcessInformation = new PROCESS_INFORMATION();
// Create security attributes to pass to the CreateProcessAsUser function
lObj_ProcessAttributes = new SECURITY_ATTRIBUTES();
lObj_ProcessAttributes.nLength = Convert.ToUInt32(Marshal.SizeOf(lObj_ProcessAttributes));
lObj_ThreadAttributes = new SECURITY_ATTRIBUTES();
lObj_ThreadAttributes.nLength = Convert.ToUInt32(Marshal.SizeOf(lObj_ThreadAttributes));
// To specify the window station, desktop, standard handles, and appearance of the
// main window for the new process.
lObj_StartupInfo = new STARTUPINFO();
lObj_StartupInfo.cb = Convert.ToUInt32(Marshal.SizeOf(lObj_StartupInfo));
lObj_StartupInfo.lpDesktop = null;
lObj_StartupInfo.dwFlags = Convert.ToUInt32(STARTF_USESHOWWINDOW | STARTF_FORCEONFEEDBACK);
lObj_StartupInfo.wShowWindow = SW_SHOW;
// Creates a new process and its primary thread. The new process runs in the
// security context of the user represented by the specified token.
lb_Result = CreateProcessAsUser(Pi_Token, null, Ps_CmdLine, ref lObj_ProcessAttributes, ref lObj_ThreadAttributes, true, CREATE_UNICODE_ENVIRONMENT, Pi_EnvBlock, null, ref lObj_StartupInfo, ref lObj_ProcessInformation);
// If create process return false then
if (!lb_Result)
{
// Throw exception
throw new Exception(string.Format("CreateProcessAsUser Error: {0}", Marshal.GetLastWin32Error()));
}
}
}

CryptoAPI's SignerTimeStampEx2 using PInvoke

I'm trying to use CryptoAPI from C# code to add SHA256 timestamps to signed assemblies. Here is the code I'm using:
Signer.TimestampSignedAssembly("MyAssembly.exe", "http://tsa.starfieldtech.com");
Signer class:
public static class Signer
{
[StructLayoutAttribute(LayoutKind.Sequential)]
struct SIGNER_SUBJECT_INFO
{
public uint cbSize;
public IntPtr pdwIndex;
public uint dwSubjectChoice;
public SubjectChoiceUnion Union1;
[StructLayoutAttribute(LayoutKind.Explicit)]
internal struct SubjectChoiceUnion
{
[FieldOffsetAttribute(0)]
public IntPtr pSignerFileInfo;
[FieldOffsetAttribute(0)]
public IntPtr pSignerBlobInfo;
}
}
[StructLayoutAttribute(LayoutKind.Sequential)]
struct SIGNER_FILE_INFO
{
public uint cbSize;
public IntPtr pwszFileName;
public IntPtr hFile;
}
[DllImport("Mssign32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern int SignerTimeStampEx2(
uint dwFlags, // DWORD
IntPtr pSubjectInfo, // SIGNER_SUBJECT_INFO
string pwszHttpTimeStamp, // LPCWSTR
uint dwAlgId, // ALG_ID
IntPtr psRequest, // PCRYPT_ATTRIBUTES
IntPtr pSipData, // LPVOID
out IntPtr ppSignerContext // SIGNER_CONTEXT
);
public static void TimestampSignedAssembly(string appPath, string tsaServer)
{
if (tsaServer == null) throw new ArgumentNullException("tsaServer");
var pSubjectInfo = IntPtr.Zero;
try
{
pSubjectInfo = CreateSignerSubjectInfo(appPath);
TimestampSignedAssembly(pSubjectInfo, tsaServer);
}
finally
{
if (pSubjectInfo != IntPtr.Zero)
{
Marshal.DestroyStructure(pSubjectInfo, typeof(SIGNER_SUBJECT_INFO));
}
}
}
private static IntPtr CreateSignerSubjectInfo(string pathToAssembly)
{
var info = new SIGNER_SUBJECT_INFO
{
cbSize = (uint)Marshal.SizeOf(typeof(SIGNER_SUBJECT_INFO)),
pdwIndex = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(uint)))
};
var index = 0;
Marshal.StructureToPtr(index, info.pdwIndex, false);
info.dwSubjectChoice = 0x1; //SIGNER_SUBJECT_FILE
var assemblyFilePtr = Marshal.StringToHGlobalUni(pathToAssembly);
var fileInfo = new SIGNER_FILE_INFO
{
cbSize = (uint)Marshal.SizeOf(typeof(SIGNER_FILE_INFO)),
pwszFileName = assemblyFilePtr,
hFile = IntPtr.Zero
};
info.Union1 = new SIGNER_SUBJECT_INFO.SubjectChoiceUnion
{
pSignerFileInfo = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SIGNER_FILE_INFO)))
};
Marshal.StructureToPtr(fileInfo, info.Union1.pSignerFileInfo, false);
IntPtr pSubjectInfo = Marshal.AllocHGlobal(Marshal.SizeOf(info));
Marshal.StructureToPtr(info, pSubjectInfo, false);
return pSubjectInfo;
}
/*
Here CryptoAPI function SignerTimeStampEx2 called.
*/
private static void TimestampSignedAssembly(IntPtr pSubjectInfo, string tsaServer)
{
IntPtr context;
var hResult = SignerTimeStampEx2(
0x1, // I have not found anywhere what value should have this parameter!
pSubjectInfo,
tsaServer,
0x0000800c, // 256 bit SHA hashing algorithm. This value taken form here: http://msdn.microsoft.com/en-us/library/windows/desktop/aa375549(v=vs.85).aspx
IntPtr.Zero,
IntPtr.Zero,
out context
);
if (hResult != 0)
{
throw new Exception(string.Format("Error occured when adding timestamp - Error code: 0x{0:X}", hResult));
}
}
}
Despite the fact that I pass to SignerTimeStampEx2 function an argument (dwAlgId), indicating that it is necessary to add SHA256 timestamp (0x0000800c), SHA1 timestamp is always generated.
Has anyone encountered with this problem? What I'm doing wrong? What values should I set for dwFlags and dwAlgId parameters?
Thanks in advance!
dwFlags needs to be SIGNER_TIMESTAMP_RFC3161 (2). The reason you get an access violation is that SignerTimeStampEx2() is documented incorrectly. It expects the algorithm as a PCSTR rather than a DWORD. If you pass 0x800C it'll try to dereference that as a pointer, leading to the AV. So replace ALG_ID dwAlgId in the function declaration with PCSTR pszTimeStampAlgorithmOid. Pass szOID_NIST_sha256 to it, which should be defined as "2.16.840.1.101.3.4.2.1".
SignerTimeStampEx3() is also incorrectly incorrectly documented. pszTimeStampAlgorithmOid should be declared as PCSTR rather than as PCWSTR.
In my experience, code signing and time stamping are more reliable if you specify both the file name and an open Win32 file handle in the SIGNER_FILE_INFO structure.
Whether you will actually get an SHA-256 time stamp also depends on the time stamping service you're using. http://tsa.starfieldtech.com, http://timestamp.globalsign.com/ and http://timestamp.comodoca.com/rfc3161 issue SHA-256 timestamps. Other services may issue SHA-1 time stamps even when requesting an SHA-256 time stamp.
I got it working finally. Here is the complete code of the Timestamper class:
public static class Timestamper
{
[StructLayout(LayoutKind.Sequential)]
struct SIGNER_SUBJECT_INFO
{
public uint cbSize;
public IntPtr pdwIndex;
public uint dwSubjectChoice;
public SubjectChoiceUnion Union1;
[StructLayoutAttribute(LayoutKind.Explicit)]
internal struct SubjectChoiceUnion
{
[FieldOffsetAttribute(0)]
public IntPtr pSignerFileInfo;
[FieldOffsetAttribute(0)]
public IntPtr pSignerBlobInfo;
}
}
[StructLayoutAttribute(LayoutKind.Sequential)]
struct SIGNER_FILE_INFO
{
public uint cbSize;
public IntPtr pwszFileName;
public IntPtr hFile;
}
[DllImport("Mssign32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern int SignerTimeStampEx2(
uint dwFlags, // DWORD
IntPtr pSubjectInfo, // SIGNER_SUBJECT_INFO
string pwszHttpTimeStamp, // LPCWSTR
IntPtr pszTimeStampAlgorithmOid, // PCSTR
IntPtr psRequest, // PCRYPT_ATTRIBUTES
IntPtr pSipData, // LPVOID
out IntPtr ppSignerContext // SIGNER_CONTEXT
);
public static void TimestampSignedAssembly(string appPath, string tsaServer)
{
if (tsaServer == null) throw new ArgumentNullException("tsaServer");
IntPtr pSubjectInfo = IntPtr.Zero;
try
{
pSubjectInfo = CreateSignerSubjectInfo(appPath);
TimestampSignedAssembly(pSubjectInfo, tsaServer);
}
finally
{
if (pSubjectInfo != IntPtr.Zero)
{
Marshal.DestroyStructure(pSubjectInfo, typeof(SIGNER_SUBJECT_INFO));
}
}
}
private static IntPtr CreateSignerSubjectInfo(string pathToAssembly)
{
SIGNER_SUBJECT_INFO info = new SIGNER_SUBJECT_INFO
{
cbSize = (uint)Marshal.SizeOf(typeof(SIGNER_SUBJECT_INFO)),
pdwIndex = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(uint)))
};
int index = 0;
Marshal.StructureToPtr(index, info.pdwIndex, false);
info.dwSubjectChoice = 0x1; //SIGNER_SUBJECT_FILE
IntPtr assemblyFilePtr = Marshal.StringToHGlobalUni(pathToAssembly);
SIGNER_FILE_INFO fileInfo = new SIGNER_FILE_INFO
{
cbSize = (uint)Marshal.SizeOf(typeof(SIGNER_FILE_INFO)),
pwszFileName = assemblyFilePtr,
hFile = IntPtr.Zero
};
info.Union1 = new SIGNER_SUBJECT_INFO.SubjectChoiceUnion
{
pSignerFileInfo = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SIGNER_FILE_INFO)))
};
Marshal.StructureToPtr(fileInfo, info.Union1.pSignerFileInfo, false);
IntPtr pSubjectInfo = Marshal.AllocHGlobal(Marshal.SizeOf(info));
Marshal.StructureToPtr(info, pSubjectInfo, false);
return pSubjectInfo;
}
/*
Here CryptoAPI function SignerTimeStampEx2 called.
*/
private static void TimestampSignedAssembly(IntPtr pSubjectInfo, string tsaServer)
{
IntPtr context;
int hResult = SignerTimeStampEx2(
0x2, // SIGNER_TIMESTAMP_RFC3161
pSubjectInfo,
tsaServer,
Marshal.StringToHGlobalAnsi("2.16.840.1.101.3.4.2.1"), // szOID_NIST_sha256 constant, SHA256 hashing algorithm.
IntPtr.Zero,
IntPtr.Zero,
out context
);
if (hResult != 0)
{
throw new Exception(string.Format("Error occured when adding timestamp - Error code: 0x{0:X}", hResult));
}
}
}
Usage example:
Timestamper.TimestampSignedAssembly("Assembly.exe", "http://timestamp.comodoca.com/?td=sha256");

Starting a new user session from a service

I have the following problem:
From a service I need to start an application in a user session. No human user log on that machine, since it is a server. Launched application must have a session != 0.
Current "solution"
I used a scheduled task at machine startup, that task launch ( in session 0, of course ) an application launching a Remote Desktop logon on the same machine: this creates a user session > 0 and in the user startup the is the final application to launch. It works, but too tricky.
Is there some smartest way? It is critical that I can reuse a user session already on since there is potentially no user logged on.
MAJOR UPDATE
Well after a lot of research and partial successes, and also thanks to some SysAdmin inflexibility about creating an user for a specific pourpose, I decided to use OpenGL instead of WPF for render the 3d portion broken in Session 0.
Surprisingly it took less than expected. I think having this question as a reference could be useful to other who want try to render a Viewport3D from a service.
I'm not sure if this will work, but maybe this answer helps in your case.
Use the class from the answer I link i provided and the following method (with the appropriate values):
public static void EnableVideoDrivers(bool enable)
{
// every type of device has a hard-coded GUID, put here the one for
// video drivers
Guid videoGuid = new Guid("{device GUID}");
// get this from the properties dialog box of this device in Device Manager
string instancePath = #"Device Instance Path";
DeviceHelper.SetDeviceEnabled(videoGuid, instancePath, enable);
}
Here's a list of Popular Device Class GUIDs.
I'm not sure I understand correctly your needs, but maybe just starting process with given credentials and redirect input and output is what you need. Starting process with given credentials:
Process p = new Process();
p.StartInfo = new ProcessStartInfo(fileName, args);
p.StartInfo.UserName = userName;
p.StartInfo.Password = pass;
p.Start();
You may also need to redirect input and output of the application. That problem is well described on CodeProjecgt in this artice.
This is how I start a process for a particular usersession from a Local windows service.
It uses C#, with some DLL imports from kernel32.dll, wtsaspi.dll, userev.dll, and advapi32.dll.
For context, my code will search all user sessions. In my scenario, my service is running on a Windows Terminal server and wants to keep a particular app "alive" in each user's session. Meaning, if we check and its not running anymore, we restart it.
Here is the program logic (abbreviated), this is how you call the method that starts the user process:
foreach(var sesh in ProcessExtensions.GetSessions().Where(r => r.State == "Active").ToList())
{
var running = procs.Any(r => r.ProcessName == filename && r.SessionId == sesh.SessionId);
if (!running)
{
try
{
ProcessExtensions.StartProcessForSession(sesh.SessionId, (string)item, "/restart", System.IO.Path.GetDirectoryName((string)item), true);
}
catch (Exception ex)
{
Trace.TraceWarning("Error: {0}", ex);
}
}
}
Here is the implementation of ProcessExtensions where all of the good stuff is.
Disclaimer - I did not write this code, This is an example I found online and adjusted it to my needs. If you authored the original post. Apologies for the lack of footnote.
ProcessExtensions.cs
public static class ProcessExtensions
{
#region Win32 Constants
private const int CREATE_UNICODE_ENVIRONMENT = 0x00000400;
private const int CREATE_NO_WINDOW = 0x08000000;
private const int CREATE_NEW_CONSOLE = 0x00000010;
private const uint INVALID_SESSION_ID = 0xFFFFFFFF;
private static readonly IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero;
#endregion
#region DllImports
[DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUser", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
private static extern bool CreateProcessAsUser(
IntPtr hToken,
String lpApplicationName,
String lpCommandLine,
IntPtr lpProcessAttributes,
IntPtr lpThreadAttributes,
bool bInheritHandle,
uint dwCreationFlags,
IntPtr lpEnvironment,
String lpCurrentDirectory,
ref STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation);
[DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx")]
private static extern bool DuplicateTokenEx(
IntPtr ExistingTokenHandle,
uint dwDesiredAccess,
IntPtr lpThreadAttributes,
int TokenType,
int ImpersonationLevel,
ref IntPtr DuplicateTokenHandle);
[DllImport("userenv.dll", SetLastError = true)]
private static extern bool CreateEnvironmentBlock(ref IntPtr lpEnvironment, IntPtr hToken, bool bInherit);
[DllImport("userenv.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool DestroyEnvironmentBlock(IntPtr lpEnvironment);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool CloseHandle(IntPtr hSnapshot);
[DllImport("kernel32.dll")]
private static extern uint WTSGetActiveConsoleSessionId();
[DllImport("Wtsapi32.dll")]
private static extern uint WTSQueryUserToken(uint SessionId, ref IntPtr phToken);
[DllImport("wtsapi32.dll", SetLastError = true)]
private static extern int WTSEnumerateSessions(
IntPtr hServer,
int Reserved,
int Version,
ref IntPtr ppSessionInfo,
ref int pCount);
#endregion
#region Win32 Structs
private enum SW
{
SW_HIDE = 0,
SW_SHOWNORMAL = 1,
SW_NORMAL = 1,
SW_SHOWMINIMIZED = 2,
SW_SHOWMAXIMIZED = 3,
SW_MAXIMIZE = 3,
SW_SHOWNOACTIVATE = 4,
SW_SHOW = 5,
SW_MINIMIZE = 6,
SW_SHOWMINNOACTIVE = 7,
SW_SHOWNA = 8,
SW_RESTORE = 9,
SW_SHOWDEFAULT = 10,
SW_MAX = 10
}
private enum WTS_CONNECTSTATE_CLASS
{
WTSActive,
WTSConnected,
WTSConnectQuery,
WTSShadow,
WTSDisconnected,
WTSIdle,
WTSListen,
WTSReset,
WTSDown,
WTSInit
}
[StructLayout(LayoutKind.Sequential)]
private struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public uint dwProcessId;
public uint dwThreadId;
}
private enum SECURITY_IMPERSONATION_LEVEL
{
SecurityAnonymous = 0,
SecurityIdentification = 1,
SecurityImpersonation = 2,
SecurityDelegation = 3,
}
[StructLayout(LayoutKind.Sequential)]
private struct STARTUPINFO
{
public int cb;
public String lpReserved;
public String lpDesktop;
public String lpTitle;
public uint dwX;
public uint dwY;
public uint dwXSize;
public uint dwYSize;
public uint dwXCountChars;
public uint dwYCountChars;
public uint dwFillAttribute;
public uint dwFlags;
public short wShowWindow;
public short cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
private enum TOKEN_TYPE
{
TokenPrimary = 1,
TokenImpersonation = 2
}
[StructLayout(LayoutKind.Sequential)]
private struct WTS_SESSION_INFO
{
public readonly UInt32 SessionID;
[MarshalAs(UnmanagedType.LPStr)]
public readonly String pWinStationName;
public readonly WTS_CONNECTSTATE_CLASS State;
}
#endregion
public static IEnumerable<UserSessionData> GetSessions()
{
//var bResult = false;
var hImpersonationToken = IntPtr.Zero;
//var activeSessionId = INVALID_SESSION_ID;
var pSessionInfo = IntPtr.Zero;
var sessionCount = 0;
if (WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, ref pSessionInfo, ref sessionCount) != 0)
{
var arrayElementSize = Marshal.SizeOf(typeof(WTS_SESSION_INFO));
var current = pSessionInfo;
for (var i = 0; i < sessionCount; i++)
{
var si = (WTS_SESSION_INFO)Marshal.PtrToStructure((IntPtr)current, typeof(WTS_SESSION_INFO));
current += arrayElementSize;
var data = new UserSessionData
{
SessionId = (int)si.SessionID,
State = si.State.ToString().Substring(3),
Name = si.pWinStationName
};
yield return data;
}
}
}
private static bool GetUserTokenForSession(int sessionId, ref IntPtr phUserToken)
{
var bResult = false;
var hImpersonationToken = IntPtr.Zero;
var pSessionInfo = IntPtr.Zero;
if (WTSQueryUserToken((uint)sessionId, ref hImpersonationToken) != 0)
{
// Convert the impersonation token to a primary token
bResult = DuplicateTokenEx(hImpersonationToken, 0, IntPtr.Zero,
(int)SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, (int)TOKEN_TYPE.TokenPrimary,
ref phUserToken);
CloseHandle(hImpersonationToken);
}
return bResult;
}
private static bool GetCurrentUserSessionToken(ref IntPtr phUserToken)
{
var bResult = false;
var hImpersonationToken = IntPtr.Zero;
var activeSessionId = INVALID_SESSION_ID;
var pSessionInfo = IntPtr.Zero;
var sessionCount = 0;
// Get a handle to the user access token for the current active session.
if (WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, ref pSessionInfo, ref sessionCount) != 0)
{
var arrayElementSize = Marshal.SizeOf(typeof(WTS_SESSION_INFO));
var current = pSessionInfo;
for (var i = 0; i < sessionCount; i++)
{
var si = (WTS_SESSION_INFO)Marshal.PtrToStructure((IntPtr)current, typeof(WTS_SESSION_INFO));
current += arrayElementSize;
if (si.State == WTS_CONNECTSTATE_CLASS.WTSActive)
{
activeSessionId = si.SessionID;
}
}
}
// If enumerating did not work, fall back to the old method
if (activeSessionId == INVALID_SESSION_ID)
{
activeSessionId = WTSGetActiveConsoleSessionId();
}
if (WTSQueryUserToken(activeSessionId, ref hImpersonationToken) != 0)
{
// Convert the impersonation token to a primary token
bResult = DuplicateTokenEx(hImpersonationToken, 0, IntPtr.Zero,
(int)SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, (int)TOKEN_TYPE.TokenPrimary,
ref phUserToken);
CloseHandle(hImpersonationToken);
}
return bResult;
}
public static bool StartProcessForSession(int sessionId, string appPath, string cmdLine = null, string workDir = null, bool visible = true)
{
var hUserToken = IntPtr.Zero;
var startInfo = new STARTUPINFO();
var procInfo = new PROCESS_INFORMATION();
var pEnv = IntPtr.Zero;
int iResultOfCreateProcessAsUser;
startInfo.cb = Marshal.SizeOf(typeof(STARTUPINFO));
try
{
if (!GetUserTokenForSession(sessionId, ref hUserToken))
{
throw new Exception("StartProcessAsCurrentUser: GetSessionUserToken failed.");
}
uint dwCreationFlags = CREATE_UNICODE_ENVIRONMENT | (uint)(visible ? CREATE_NEW_CONSOLE : CREATE_NO_WINDOW);
startInfo.wShowWindow = (short)(visible ? SW.SW_SHOW : SW.SW_HIDE);
startInfo.lpDesktop = "winsta0\\default";
if (!CreateEnvironmentBlock(ref pEnv, hUserToken, false))
{
throw new Exception("StartProcessInSession: CreateEnvironmentBlock failed.");
}
if (!CreateProcessAsUser(hUserToken,
appPath, // Application Name
cmdLine, // Command Line
IntPtr.Zero,
IntPtr.Zero,
false,
dwCreationFlags,
pEnv,
workDir, // Working directory
ref startInfo,
out procInfo))
{
iResultOfCreateProcessAsUser = Marshal.GetLastWin32Error();
throw new Exception("StartProcessAsCurrentUser: CreateProcessAsUser failed. Error Code -" + iResultOfCreateProcessAsUser);
}
iResultOfCreateProcessAsUser = Marshal.GetLastWin32Error();
}
finally
{
CloseHandle(hUserToken);
if (pEnv != IntPtr.Zero)
{
DestroyEnvironmentBlock(pEnv);
}
CloseHandle(procInfo.hThread);
CloseHandle(procInfo.hProcess);
}
return true;
}
public static bool StartProcessAsCurrentUser(string appPath, string cmdLine = null, string workDir = null, bool visible = true)
{
var hUserToken = IntPtr.Zero;
var startInfo = new STARTUPINFO();
var procInfo = new PROCESS_INFORMATION();
var pEnv = IntPtr.Zero;
int iResultOfCreateProcessAsUser;
startInfo.cb = Marshal.SizeOf(typeof(STARTUPINFO));
try
{
if (!GetCurrentUserSessionToken(ref hUserToken))
{
throw new Exception("StartProcessAsCurrentUser: GetSessionUserToken failed.");
}
uint dwCreationFlags = CREATE_UNICODE_ENVIRONMENT | (uint)(visible ? CREATE_NEW_CONSOLE : CREATE_NO_WINDOW);
startInfo.wShowWindow = (short)(visible ? SW.SW_SHOW : SW.SW_HIDE);
startInfo.lpDesktop = "winsta0\\default";
if (!CreateEnvironmentBlock(ref pEnv, hUserToken, false))
{
throw new Exception("StartProcessAsCurrentUser: CreateEnvironmentBlock failed.");
}
if (!CreateProcessAsUser(hUserToken,
appPath, // Application Name
cmdLine, // Command Line
IntPtr.Zero,
IntPtr.Zero,
false,
dwCreationFlags,
pEnv,
workDir, // Working directory
ref startInfo,
out procInfo))
{
iResultOfCreateProcessAsUser = Marshal.GetLastWin32Error();
throw new Exception("StartProcessAsCurrentUser: CreateProcessAsUser failed. Error Code -" + iResultOfCreateProcessAsUser);
}
iResultOfCreateProcessAsUser = Marshal.GetLastWin32Error();
}
finally
{
CloseHandle(hUserToken);
if (pEnv != IntPtr.Zero)
{
DestroyEnvironmentBlock(pEnv);
}
CloseHandle(procInfo.hThread);
CloseHandle(procInfo.hProcess);
}
return true;
}
}

Categories