How to get a pipelist with C#? I know about the next trick:
Directory.GetFile(#"\\.\\pipe\")
but I want to get number of instances of each pipe. I've got a code:
using System;
using System.IO;
using System.ComponentModel;
using Microsoft.Win32.SafeHandles;
using System.Runtime.InteropServices;
using System.Diagnostics.CodeAnalysis;
namespace PipeList {
[StructLayout(LayoutKind.Explicit, Size = 8)]
internal struct LARGE_INTEGER {
[SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
[FieldOffset(0)]
internal Int64 QuadPart;
[FieldOffset(0)]
internal Int32 LowPart;
[SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
[FieldOffset(4)]
internal UInt32 HighPart;
} //LARGE_INTEGER
[StructLayout(LayoutKind.Sequential)]
internal struct IO_STATUS_BLOCK {
internal UInt32 Status;
internal UInt64 Information;
} //IO_STATUS_BLOCK
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal struct FILE_DIRECTORY_INFORMATION {
internal UInt32 NextEntryOffset;
internal UInt32 FileIndex;
internal LARGE_INTEGER CreationTime;
internal LARGE_INTEGER LastAccessTime;
internal LARGE_INTEGER LastWriteTime;
internal LARGE_INTEGER ChangeTime;
internal LARGE_INTEGER EndOfFile;
internal LARGE_INTEGER AllocationSize;
internal UInt32 FileAttributes;
internal UInt32 FileNameLength;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
internal Byte[] FileName;
} //FILE_DIRECTORY_INFORMATION
internal static class NativeMethods {
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern SafeFileHandle CreateFile(
String lpFileName,
UInt32 dwDesiredAccess,
FileShare dwShareMode,
IntPtr lpSecurityAttributes,
FileMode dwCreationDisposition,
UInt32 dwFlagsAndAttributes,
IntPtr hTemplateFile
);
[DllImport("ntdll.dll")]
internal static extern Int32 NtQueryDirectoryFile(
SafeFileHandle FileHandle,
IntPtr Event,
IntPtr ApcRoutine,
IntPtr ApcContext,
out IO_STATUS_BLOCK IoStatusBlock,
[Out] IntPtr FileInformation,
UInt32 Length,
UInt32 FileInformationClass,
[MarshalAs(UnmanagedType.Bool)]
Boolean ReturnSingleEntry,
IntPtr FileName,
[MarshalAs(UnmanagedType.Bool)]
Boolean RestartScan
);
} //NativeMethods
internal sealed class Program {
const UInt32 FileDirectoryInformation = 1;
const UInt32 GENERIC_READ = 0x80000000;
const Int32 STATUS_SUCCESS = 0x00000000;
static T PtrToStruct<T>(IntPtr p) {
return (T)Marshal.PtrToStructure(p, typeof(T));
}
static void GetLastError() {
Console.WriteLine(new Win32Exception(Marshal.GetLastWin32Error()).Message);
}
static void Main() {
SafeFileHandle pipes;
IntPtr ptr, ofs;
IO_STATUS_BLOCK isb;
Int32 ntstatus;
Boolean query = true;
pipes = NativeMethods.CreateFile(
#"\\.\pipe\", GENERIC_READ, FileShare.ReadWrite | FileShare.Delete,
IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero
);
if (pipes.IsInvalid) {
GetLastError();
return;
}
Console.WriteLine("{0,-40}{1,14}{2,20}", "Pipe Name", "Instances", "Max Instances");
Console.WriteLine("{0,-40}{1,14}{2,20}", "---------", "---------", "-------------");
ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(FILE_DIRECTORY_INFORMATION)));
try {
ofs = ptr;
while (true) {
ntstatus = NativeMethods.NtQueryDirectoryFile(
pipes, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, out isb, ofs, 1024,
FileDirectoryInformation, false, IntPtr.Zero, query
);
if (ntstatus != STATUS_SUCCESS) break;
while (true) {
FILE_DIRECTORY_INFORMATION fdi = PtrToStruct<FILE_DIRECTORY_INFORMATION>(ofs);
IntPtr name = (IntPtr)(
Marshal.OffsetOf(typeof(FILE_DIRECTORY_INFORMATION), "FileName").ToInt64() + ofs.ToInt64()
);
Console.WriteLine("{0,-40}{1,14}{2,20}",
Marshal.PtrToStringUni(name, (Int32)(fdi.FileNameLength / 2)),
fdi.EndOfFile.LowPart,
fdi.AllocationSize.LowPart
);
if (fdi.NextEntryOffset == 0) break;
ofs = (IntPtr)(ofs.ToInt64() + fdi.NextEntryOffset);
} //while
query = false;
} //while
}
catch (Exception e) { Console.WriteLine(e.Message); }
finally {
Marshal.FreeHGlobal(ptr);
}
pipes.Close();
}
} //Program
}
but it works well only on CLR 2. Could someone explain me an issue of a code above? Why it does not correctly work on CLR 4?
Related
I wrote code that can write and read data from physical disk.
I open the physical disk using pInvoke CreateFile and use FileStream to perform read and write data.
When physical disk is online, every thing works great.
In case the physical disk is offline and I try to write to disk, I get an error
System.IO.IOException: 'The media is write protected.'
How can I detect if disk is offline without trying to write to disk.
Here is the code that create the FileStream and perform writes to disk
private const uint GENERIC_READ = 0x80000000;
private const uint GENERIC_WRITE = 0x40000000;
private const uint OPEN_EXISTING = 3;
private const uint FILE_FLAG_NO_BUFFERING = 0x20000000;
private const uint FILE_FLAG_WRITE_THROUGH = 0x80000000;
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern SafeFileHandle CreateFile(string lpFileName, uint dwDesiredAccess,
uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition,
uint dwFlagsAndAttributes, IntPtr hTemplateFile);
public void OpenFile()
{
m_handleValue = CreateFile(DevicePath, GENERIC_WRITE | GENERIC_READ,
0x3, IntPtr.Zero, OPEN_EXISTING,
FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH
, IntPtr.Zero);
m_fileStream = new FileStream(m_handleValue, FileAccess.ReadWrite, 512);
}
public void WriteData(int offset, byte[] data)
{
m_fileStream.Seek(offset, SeekOrigin.Begin);
m_fileStream.Write(data, 0, data.Length);
}
Using DeviceIoControl pInvoke we can review attribute DISK_ATTRIBUTE_OFFLINE
public static class RawDeviceInfoProvider
{
private const int IOCTL_DISK_GET_DISK_ATTRIBUTES = 0x000700F0;
private const uint DISK_ATTRIBUTE_OFFLINE = 0x1;
[StructLayout(LayoutKind.Sequential)]
public struct GetDiskAttributes
{
public uint Version;
public uint Reserved;
public ulong Attributes;
}
[DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool DeviceIoControl(SafeHandle hDevice, uint dwIoControlCode, IntPtr lpInBuffer,
uint nInBufferSize, IntPtr lpOutBuffer, uint nOutBufferSize, out uint lpBytesReturned, IntPtr lpOverlapped);
public static bool GetOnlineStatus(SafeFileHandle hDevice)
{
uint dummy;
GetDiskAttributes attributes = new GetDiskAttributes();
IntPtr lpOutBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(attributes));
bool success = DeviceIoControl(hDevice, IOCTL_DISK_GET_DISK_ATTRIBUTES, IntPtr.Zero, 0,
lpOutBuffer, (uint)Marshal.SizeOf(typeof(GetDiskAttributes)), out dummy, IntPtr.Zero);
attributes = (GetDiskAttributes)Marshal.PtrToStructure(lpOutBuffer, typeof(GetDiskAttributes));
Marshal.FreeHGlobal(lpOutBuffer);
if (!success)
{
int errorCode = Marshal.GetLastWin32Error();
throw new IOException("Unable to retrieve disk attributes, Error: " + errorCode);
}
bool isOffline = (attributes.Attributes & DISK_ATTRIBUTE_OFFLINE) > 0;
return !isOffline;
}
}
I'm creating a windows service app in visual studio and I want to get the currently active window title.
below is the code I have tried but the function GetForegroundWindow() returns 0 every time.
Is it okay to use win32api in windows services?
public partial class My_Service: ServiceBase
{
Timer timer = new Timer();
[DllImport("User32.dll")]
static extern IntPtr GetForegroundWindow();
[DllImport("User32.dll")]
static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
public My_Service()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
WriteToFile("Service is started at " + DateTime.Now);
timer.Elapsed += new ElapsedEventHandler(OnElapsedTime);
timer.Interval = 10000; //number in milisecinds
timer.Enabled = true;
}
protected override void OnStop()
{
WriteToFile("Service is stopped at " + DateTime.Now);
}
private void OnElapsedTime(object source, ElapsedEventArgs e)
{
string title = GetActivewindow();
WriteToFile("Service is recall at " + title + DateTime.Now);
}
public void WriteToFile(string Message)
{
....
}
public string GetActivewindow()
{
const int nChars = 256;
StringBuilder buff = new StringBuilder(nChars);
string title = "- ";
IntPtr handle;
handle = GetForegroundWindow();
if( GetWindowText(handle,buff,nChars) > 0)
{
title = buff.ToString();
}
return title;
}
}
As comments, and also according to the document: About Window Stations and Desktops - Window Station and Desktop Creation.
Your service may be running under Service-0x0-3e7$\default, but the foreground window you want to retrieve is in the default desktop of the interactive window station (Winsta0\default)
The interactive window station is the only window station that can
display a user interface or receive user input. It is assigned to the
logon session of the interactive user, and contains the keyboard,
mouse, and display device. It is always named "WinSta0". All other
window stations are noninteractive, which means they cannot display a
user interface or receive user input.
That means you cannot use GetForegroundWindow directly from the service. You could create a child process in Winsta0\default and redirect its output. Then call GetForegroundWindow in the child process and output.
Sample(removed error checking):
Child:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.IO;
using System.Threading;
namespace ConsoleApp1
{
class Program
{
[DllImport("User32.dll")]
static extern IntPtr GetForegroundWindow();
[DllImport("User32.dll")]
static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
static void Main(string[] args)
{
const int nChars = 256;
StringBuilder buff = new StringBuilder(nChars);
string title = "- ";
IntPtr handle;
handle = GetForegroundWindow();
if (GetWindowText(handle, buff, nChars) > 0)
{
title = buff.ToString();
}
Console.WriteLine(title);
return;
}
}
}
Service:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.IO;
using System.Diagnostics.Tracing;
namespace WindowsService3
{
public partial class MyNewService : ServiceBase
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal 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 dwYSize;
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, CharSet = CharSet.Unicode)]
internal struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public Int32 dwProcessId;
public Int32 dwThreadId;
}
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public int nLength;
public IntPtr lpSecurityDescriptor;
public int bInheritHandle;
}
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern bool CreateProcessAsUser(
IntPtr hToken,
string lpApplicationName,
string lpCommandLine,
IntPtr lpProcessAttributes,
IntPtr lpThreadAttributes,
bool bInheritHandles,
uint dwCreationFlags,
IntPtr lpEnvironment,
IntPtr lpCurrentDirectory,
ref STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern bool CreatePipe(
ref IntPtr hReadPipe,
ref IntPtr hWritePipe,
ref SECURITY_ATTRIBUTES lpPipeAttributes,
uint nSize);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern bool ReadFile(
IntPtr hFile,
byte[] lpBuffer,
uint nNumberOfBytesToRead,
out uint lpNumberOfBytesRead,
IntPtr lpOverlapped);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern bool CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern uint WTSGetActiveConsoleSessionId();
[DllImport("Wtsapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern bool WTSQueryUserToken(UInt32 SessionId, out IntPtr hToken);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern uint WaitForSingleObject(IntPtr hProcess, uint dwMilliseconds);
public MyNewService()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
System.Diagnostics.Debugger.Launch();
using (StreamWriter sw = File.CreateText("Path\\Log.txt"))
{
IntPtr read = new IntPtr();
IntPtr write = new IntPtr();
IntPtr read2 = new IntPtr();
IntPtr write2 = new IntPtr();
SECURITY_ATTRIBUTES saAttr = new SECURITY_ATTRIBUTES();
saAttr.nLength = Marshal.SizeOf(typeof(SECURITY_ATTRIBUTES));
saAttr.bInheritHandle = 1;
saAttr.lpSecurityDescriptor = IntPtr.Zero;
CreatePipe(ref read, ref write, ref saAttr, 0);
CreatePipe(ref read2, ref write2, ref saAttr, 0);
uint CREATE_NO_WINDOW = 0x08000000;
int STARTF_USESTDHANDLES = 0x00000100;
STARTUPINFO si = new STARTUPINFO();
si.cb = Marshal.SizeOf(typeof(STARTUPINFO));
si.hStdOutput = write;
si.hStdError = write;
si.hStdInput = read2;
si.lpDesktop = "Winsta0\\default";
si.dwFlags = STARTF_USESTDHANDLES;
PROCESS_INFORMATION pi;
IntPtr hToken;
bool err = WTSQueryUserToken(WTSGetActiveConsoleSessionId(), out hToken);
string path = "Path\\ConsoleApp1.exe";
if (CreateProcessAsUser(hToken, path, null, IntPtr.Zero, IntPtr.Zero, true, CREATE_NO_WINDOW, IntPtr.Zero, IntPtr.Zero, ref si, out pi))
{
uint ret = WaitForSingleObject(pi.hProcess, 2000); //wait for the child process exit.
if (ret == 0)
{
byte[] title = new byte[200];
uint reads = 0;
CloseHandle(write);
err = ReadFile(read, title, 200, out reads, IntPtr.Zero);
string result = System.Text.Encoding.UTF8.GetString(title).Replace("\0","").Replace("\r", "").Replace("\n", "");
sw.WriteLine(result);
}
}
CloseHandle(read2);
CloseHandle(write2);
CloseHandle(read);
}
}
protected override void OnStop()
{
}
}
}
I am trying to call a WinAPI function DeviceIoControl in C# with code IOCTL_DISK_SET_DISK_ATTRIBUTES and pass struct SET_DISK_ATTRIBUTES. I am trying do it with this code:
const uint GENERIC_READ = 0x80000000;
const uint GENERIC_WRITE = 0x40000000;
const int FILE_SHARE_READ = 0x1;
const int FILE_SHARE_WRITE = 0x2;
const uint IOCTL_DISK_SET_DISK_ATTRIBUTES = 0x0007c0f4;
const ulong DISK_ATTRIBUTE_READ_ONLY = 0x0000000000000002;
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern IntPtr CreateFile(
string lpFileName,
uint dwDesiredAccess,
uint dwShareMode,
IntPtr SecurityAttributes,
uint dwCreationDisposition,
uint dwFlagsAndAttributes,
IntPtr hTemplateFile
);
[DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)]
private static extern bool DeviceIoControl(
IntPtr hDevice,
uint dwIoControlCode,
IntPtr lpInBuffer,
uint nInBufferSize,
IntPtr lpOutBuffer,
uint nOutBufferSize,
out uint lpBytesReturned,
IntPtr lpOverlapped
);
struct SET_DISK_ATTRIBUTES
{
public uint Version;
public bool Persist;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public byte[] Reserved1;
public ulong Attributes;
public ulong AttributesMask;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public uint[] Reserved2;
};
private bool SetReadonly(IntPtr handle)
{
var sda = new SET_DISK_ATTRIBUTES();
sda.AttributesMask = DISK_ATTRIBUTE_READ_ONLY;
sda.Attributes = DISK_ATTRIBUTE_READ_ONLY;
int nPtrQryBytes = Marshal.SizeOf(sda);
sda.Version = (uint)nPtrQryBytes;
IntPtr ptrQuery = Marshal.AllocHGlobal(nPtrQryBytes);
Marshal.StructureToPtr(sda, ptrQuery, false);
uint byteReturned;
var res = DeviceIoControl(handle, IOCTL_DISK_SET_DISK_ATTRIBUTES, ptrQuery, (uint)nPtrQryBytes, IntPtr.Zero, 0, out byteReturned, IntPtr.Zero);
var ex = new Win32Exception(Marshal.GetLastWin32Error());
MessageBox.Show(ex.Message);
return res;
}
I receive error "Parameter incorrect". What is the right way to call DeviceIoControl function passing structure SET_DISK_ATTRIBUTES?
The original definition of SET_DISK_ATTRIBUTES:
typedef struct _SET_DISK_ATTRIBUTES {
DWORD Version;
BOOLEAN Persist;
BYTE Reserved1[3];
DWORDLONG Attributes;
DWORDLONG AttributesMask;
DWORD Reserved2[4];
} SET_DISK_ATTRIBUTES, *PSET_DISK_ATTRIBUTES;
makes use of BOOLEAN data type, which is defined as a synonym of unsigned char (1 byte), as opposed to BOOL that is a synonym of int (4 bytes).
C#'s bool is marshaled as BOOL by default.
You need to force it into one byte:
{
...
[MarshalAs(UnmanagedType.I1)]
public bool Persist;
...
}
Finally code to set disk readonly
const uint GENERIC_READ = 0x80000000;
const uint GENERIC_WRITE = 0x40000000;
const int FILE_SHARE_READ = 0x1;
const int FILE_SHARE_WRITE = 0x2;
const uint FILE_FLAG_WRITE_THROUGH = 0x80000000;
const uint FILE_FLAG_NO_BUFFERING = 0x20000000;
const uint IOCTL_DISK_SET_DISK_ATTRIBUTES = 0x0007c0f4;
const ulong DISK_ATTRIBUTE_READ_ONLY = 0x0000000000000002;
const uint IOCTL_DISK_UPDATE_PROPERTIES = 459072;
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern IntPtr CreateFile(
string lpFileName,
uint dwDesiredAccess,
uint dwShareMode,
IntPtr SecurityAttributes,
uint dwCreationDisposition,
uint dwFlagsAndAttributes,
IntPtr hTemplateFile
);
[DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)]
private static extern bool DeviceIoControl(
IntPtr hDevice,
uint dwIoControlCode,
IntPtr lpInBuffer,
uint nInBufferSize,
IntPtr lpOutBuffer,
uint nOutBufferSize,
out uint lpBytesReturned,
IntPtr lpOverlapped
);
struct SET_DISK_ATTRIBUTES
{
public uint Version;
[MarshalAs(UnmanagedType.I1)]
public bool Persist;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public byte[] Reserved1;
public ulong Attributes;
public ulong AttributesMask;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public uint[] Reserved2;
};
public IntPtr CreateHandle(string driveLetter)
{
string filename = #"\\.\" + driveLetter[0] + ":";
return CreateFile(filename, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, 0x3, FILE_FLAG_WRITE_THROUGH, IntPtr.Zero);
}
private void SetReadonly(IntPtr handle)
{
var sda = new SET_DISK_ATTRIBUTES();
sda.Persist = true;
sda.AttributesMask = DISK_ATTRIBUTE_READ_ONLY;
sda.Attributes = DISK_ATTRIBUTE_READ_ONLY;
sda.Reserved1 = new byte[3] { 0, 0, 0 };
sda.Reserved2 = new uint[4] { 0, 0, 0, 0 };
int nPtrQryBytes = Marshal.SizeOf(sda);
sda.Version = (uint)nPtrQryBytes;
IntPtr ptrQuery = Marshal.AllocHGlobal(nPtrQryBytes);
Marshal.StructureToPtr(sda, ptrQuery, false);
uint byteReturned;
bool res = DeviceIoControl(handle, IOCTL_DISK_SET_DISK_ATTRIBUTES, ptrQuery, (uint)nPtrQryBytes, IntPtr.Zero, 0, out byteReturned, IntPtr.Zero);
bool res2 = DeviceIoControl(handle, IOCTL_DISK_UPDATE_PROPERTIES, IntPtr.Zero, 0, IntPtr.Zero, 0, out byteReturned, IntPtr.Zero);
var mess = new List<string>();
mess.Add(new Win32Exception(Marshal.GetLastWin32Error()).Message);
mess.Add(new Win32Exception(Marshal.GetLastWin32Error()).Message);
MessageBox.Show(string.Join(" ", mess));
}
private void button1_Click(object sender, EventArgs e)
{
SetReadonly(CreateHandle(textBox1.Text));
}
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);
I need to create a new process but so that it is a "child" of another process not the current process eg re-parent the new process.
The following have got me almost there .NET : How to call CreateProcessAsUser() with STARTUPINFOEX from C# and .NET : How to PInvoke UpdateProcThreadAttribute and http://winprogger.com/launching-a-non-child-process/
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
public class ProcessCreator
{
[DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
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", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UpdateProcThreadAttribute(
out 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(
out IntPtr lpAttributeList, int dwAttributeCount, int dwFlags, ref IntPtr lpSize);
public static bool CreateProcess(int parentProcessId)
{
const uint EXTENDED_STARTUPINFO_PRESENT = 0x00080000;
const int PROC_THREAD_ATTRIBUTE_PARENT_PROCESS = 0x00020000;
var pInfo = new PROCESS_INFORMATION();
var sInfoEx = new STARTUPINFOEX();
sInfoEx.StartupInfo = new STARTUPINFO();
if (parentProcessId > 0)
{
var lpSize = IntPtr.Zero;
IntPtr dummyPtr;
var success = InitializeProcThreadAttributeList(out dummyPtr, 1, 0, ref lpSize);
if (success || lpSize == IntPtr.Zero)
{
return false;
}
sInfoEx.lpAttributeList = Marshal.AllocHGlobal(lpSize);
if (sInfoEx.lpAttributeList == IntPtr.Zero)
{
return false;
}
success = InitializeProcThreadAttributeList(out sInfoEx.lpAttributeList, 1, 0, ref lpSize);
if (!success)
{
return false;
}
var parentHandle = Process.GetProcessById(parentProcessId).Handle;
success = UpdateProcThreadAttribute(
out sInfoEx.lpAttributeList,
0,
(IntPtr)PROC_THREAD_ATTRIBUTE_PARENT_PROCESS,
parentHandle,
(IntPtr)IntPtr.Size,
IntPtr.Zero,
IntPtr.Zero);
if (!success)
{
return false;
}
sInfoEx.StartupInfo.cb = Marshal.SizeOf(sInfoEx);
}
var pSec = new SECURITY_ATTRIBUTES();
var tSec = new SECURITY_ATTRIBUTES();
pSec.nLength = Marshal.SizeOf(pSec);
tSec.nLength = Marshal.SizeOf(tSec);
var lpApplicationName = Path.Combine(Environment.SystemDirectory, "notepad.exe");
return CreateProcess(lpApplicationName, null, ref pSec, ref tSec, false, EXTENDED_STARTUPINFO_PRESENT, IntPtr.Zero, null, ref sInfoEx, out pInfo);
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct STARTUPINFOEX
{
public STARTUPINFO StartupInfo;
public IntPtr lpAttributeList;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
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 dwYSize;
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)]
internal struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public int dwProcessId;
public int dwThreadId;
}
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public int nLength;
public IntPtr lpSecurityDescriptor;
public int bInheritHandle;
}
}
ProcessCreator.CreateProcess(0) starts Notepad as a child of current process which is the default behaviour. So far so good.
If the value passed in is non 0 the code attempts to start Notepad as a child of the process whose process ID matches the input value (I am assuming that process exists for now).
Unfortunately that part does not work and throws the following exception:
FatalExecutionEngineError was detected
Message: The runtime has encountered a fatal error. The address of the error was at 0x69a2c7ad, on thread 0x1de0. The error code is 0xc0000005. This error may be a bug in the CLR or in the unsafe or non-verifiable portions of user code. Common sources of this bug include user marshaling errors for COM-interop or PInvoke, which may corrupt the stack.
Any pointers much appreciated.
There are two issues with your code. First, the lpAttributeList parameter of the InitializeProcThreadAttributeList and UpdateProcThreadAttribute functions must be typed as IntPtr with no out modifier. Second, the lpValue parameter of the UpdateProcThreadAttribute function must be a pointer to the attribute value (in your case, parentHandle), not the value itself. Below is the fixed code.
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
public class ProcessCreator
{
[DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
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", 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);
public static bool CreateProcess(int parentProcessId)
{
const uint EXTENDED_STARTUPINFO_PRESENT = 0x00080000;
const int PROC_THREAD_ATTRIBUTE_PARENT_PROCESS = 0x00020000;
var pInfo = new PROCESS_INFORMATION();
var sInfoEx = new STARTUPINFOEX();
sInfoEx.StartupInfo.cb = Marshal.SizeOf(sInfoEx);
IntPtr lpValue = IntPtr.Zero;
try
{
if (parentProcessId > 0)
{
var lpSize = IntPtr.Zero;
var success = InitializeProcThreadAttributeList(IntPtr.Zero, 1, 0, ref lpSize);
if (success || lpSize == IntPtr.Zero)
{
return false;
}
sInfoEx.lpAttributeList = Marshal.AllocHGlobal(lpSize);
success = InitializeProcThreadAttributeList(sInfoEx.lpAttributeList, 1, 0, ref lpSize);
if (!success)
{
return false;
}
var parentHandle = Process.GetProcessById(parentProcessId).Handle;
// This value should persist until the attribute list is destroyed using the DeleteProcThreadAttributeList function
lpValue = Marshal.AllocHGlobal(IntPtr.Size);
Marshal.WriteIntPtr(lpValue, parentHandle);
success = UpdateProcThreadAttribute(
sInfoEx.lpAttributeList,
0,
(IntPtr)PROC_THREAD_ATTRIBUTE_PARENT_PROCESS,
lpValue,
(IntPtr)IntPtr.Size,
IntPtr.Zero,
IntPtr.Zero);
if (!success)
{
return false;
}
}
var pSec = new SECURITY_ATTRIBUTES();
var tSec = new SECURITY_ATTRIBUTES();
pSec.nLength = Marshal.SizeOf(pSec);
tSec.nLength = Marshal.SizeOf(tSec);
var lpApplicationName = Path.Combine(Environment.SystemDirectory, "notepad.exe");
return CreateProcess(lpApplicationName, null, ref pSec, ref tSec, false, EXTENDED_STARTUPINFO_PRESENT, IntPtr.Zero, null, ref sInfoEx, out pInfo);
}
finally
{
// Free the attribute list
if (sInfoEx.lpAttributeList != IntPtr.Zero)
{
DeleteProcThreadAttributeList(sInfoEx.lpAttributeList);
Marshal.FreeHGlobal(sInfoEx.lpAttributeList);
}
Marshal.FreeHGlobal(lpValue);
// Close process and thread handles
if (pInfo.hProcess != IntPtr.Zero)
{
CloseHandle(pInfo.hProcess);
}
if (pInfo.hThread != IntPtr.Zero)
{
CloseHandle(pInfo.hThread);
}
}
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct STARTUPINFOEX
{
public STARTUPINFO StartupInfo;
public IntPtr lpAttributeList;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
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 dwYSize;
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)]
internal struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public int dwProcessId;
public int dwThreadId;
}
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public int nLength;
public IntPtr lpSecurityDescriptor;
public int bInheritHandle;
}
}