how to determine CPU cache size in .NET? - c#

I would like to know if there is a way to determine CPU cache size in managed code?
I am writing a Strassen's algorithm for matrix multiplication in C# and would like to know how many elements of the matrices I could fit into cache to improve computational speed.

You can use WMI to retrieve cache information.
You will first need to add a reference to System.Management.dll to your project, then you can use the following code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Management;
namespace Scratch
{
public enum CacheLevel : ushort
{
Level1 = 3,
Level2 = 4,
Level3 = 5,
}
public static class CPUInfo
{
public static List<uint> GetCacheSizes(CacheLevel level)
{
ManagementClass mc = new ManagementClass("Win32_CacheMemory");
ManagementObjectCollection moc = mc.GetInstances();
List<uint> cacheSizes = new List<uint>(moc.Count);
cacheSizes.AddRange(moc
.Cast<ManagementObject>()
.Where(p => (ushort)(p.Properties["Level"].Value) == (ushort)level)
.Select(p => (uint)(p.Properties["MaxCacheSize"].Value)));
return cacheSizes;
}
}
}
Full details of the Win32_CacheMemory WMI class is available at:
http://msdn.microsoft.com/en-us/library/aa394080(v=vs.85).aspx

is this what you are looking for? The Win32_Processor class features L2CacheSize and L3CacheSize members.

using System;
using System.Runtime.InteropServices;
class Processor
{
[DllImport("kernel32.dll")]
public static extern int GetCurrentThreadId();
//[DllImport("kernel32.dll")]
//public static extern int GetCurrentProcessorNumber();
[StructLayout(LayoutKind.Sequential, Pack = 4)]
private struct GROUP_AFFINITY
{
public UIntPtr Mask;
[MarshalAs(UnmanagedType.U2)]
public ushort Group;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3, ArraySubType = UnmanagedType.U2)]
public ushort[] Reserved;
}
[DllImport("kernel32", SetLastError = true)]
private static extern Boolean SetThreadGroupAffinity(IntPtr hThread, ref GROUP_AFFINITY GroupAffinity, ref GROUP_AFFINITY PreviousGroupAffinity);
[StructLayout(LayoutKind.Sequential)]
public struct PROCESSORCORE
{
public byte Flags;
};
[StructLayout(LayoutKind.Sequential)]
public struct NUMANODE
{
public uint NodeNumber;
}
public enum PROCESSOR_CACHE_TYPE
{
CacheUnified,
CacheInstruction,
CacheData,
CacheTrace
}
[StructLayout(LayoutKind.Sequential)]
public struct CACHE_DESCRIPTOR
{
public byte Level;
public byte Associativity;
public ushort LineSize;
public uint Size;
public PROCESSOR_CACHE_TYPE Type;
}
[StructLayout(LayoutKind.Explicit)]
public struct SYSTEM_LOGICAL_PROCESSOR_INFORMATION_UNION
{
[FieldOffset(0)]
public PROCESSORCORE ProcessorCore;
[FieldOffset(0)]
public NUMANODE NumaNode;
[FieldOffset(0)]
public CACHE_DESCRIPTOR Cache;
[FieldOffset(0)]
private UInt64 Reserved1;
[FieldOffset(8)]
private UInt64 Reserved2;
}
public enum LOGICAL_PROCESSOR_RELATIONSHIP
{
RelationProcessorCore,
RelationNumaNode,
RelationCache,
RelationProcessorPackage,
RelationGroup,
RelationAll = 0xffff
}
public struct SYSTEM_LOGICAL_PROCESSOR_INFORMATION
{
#pragma warning disable 0649
public UIntPtr ProcessorMask;
public LOGICAL_PROCESSOR_RELATIONSHIP Relationship;
public SYSTEM_LOGICAL_PROCESSOR_INFORMATION_UNION ProcessorInformation;
#pragma warning restore 0649
}
[DllImport(#"kernel32.dll", SetLastError = true)]
public static extern bool GetLogicalProcessorInformation(IntPtr Buffer, ref uint ReturnLength);
private const int ERROR_INSUFFICIENT_BUFFER = 122;
private static SYSTEM_LOGICAL_PROCESSOR_INFORMATION[] _logicalProcessorInformation = null;
public static SYSTEM_LOGICAL_PROCESSOR_INFORMATION[] LogicalProcessorInformation
{
get
{
if (_logicalProcessorInformation != null)
return _logicalProcessorInformation;
uint ReturnLength = 0;
GetLogicalProcessorInformation(IntPtr.Zero, ref ReturnLength);
if (Marshal.GetLastWin32Error() == ERROR_INSUFFICIENT_BUFFER)
{
IntPtr Ptr = Marshal.AllocHGlobal((int)ReturnLength);
try
{
if (GetLogicalProcessorInformation(Ptr, ref ReturnLength))
{
int size = Marshal.SizeOf(typeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION));
int len = (int)ReturnLength / size;
_logicalProcessorInformation = new SYSTEM_LOGICAL_PROCESSOR_INFORMATION[len];
IntPtr Item = Ptr;
for (int i = 0; i < len; i++)
{
_logicalProcessorInformation[i] = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION)Marshal.PtrToStructure(Item, typeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION));
Item += size;
}
return _logicalProcessorInformation;
}
}
finally
{
Marshal.FreeHGlobal(Ptr);
}
}
return null;
}
}
}
Handy helper function:
public static void GetPerCoreCacheSizes(out Int64 L1, out Int64 L2, out Int64 L3)
{
L1 = 0;
L2 = 0;
L3 = 0;
var info = Processor.LogicalProcessorInformation;
foreach (var entry in info)
{
if (entry.Relationship != Processor.LOGICAL_PROCESSOR_RELATIONSHIP.RelationCache)
continue;
Int64 mask = (Int64)entry.ProcessorMask;
if ((mask & (Int64)1) == 0)
continue;
var cache = entry.ProcessorInformation.Cache;
switch (cache.Level)
{
case 1: L1 = L1 + cache.Size; break;
case 2: L2 = L2 + cache.Size; break;
case 3: L3 = L3 + cache.Size; break;
default:
break;
}
}
And call it:
static void Main(string[] args)
{
long l1, l2, l3;
GetPerCoreCacheSizes(out l1, out l2, out l3);
String s = String.Format("Single-core memory cache: L1={0} KB, L2={1} KB, L3={2} KB", l1 / 1024, l2 / 1024, l3 / 1024);
Console.WriteLine(s);
Console.ReadLine();
}
Output:
Single-core memory cache: L1=64 KB, L2=256 KB, L3=6144 KB

Try this code
using System.Management;
uint32 cachsize;
public void CPUSpeed()
{
using(ManagementObject Mo = new ManagementObject("Win32_Processor.DeviceID='CPU0'"))
{
cachsize = (uint)(Mo["L2CacheSize"]);
}
}
I get it from Here

Related

LAB to RGB using Lcms2 gives odd result

I am doing a project where I need to do lab->rgb.
I am using a color management library called lcms2, link: https://github.com/mm2/Little-CMS
My project is in c#, I use interop to communicate with the dll. The project will have to run on linux Ubuntu Core 16, which isn't supported by most c# colour management libraries, which is why I'm going with lcms2.
When trying to convert the LAB L:99, A:-122, B:-66 I get really strange results, an RGB with values:
R: -3769.14044380188
G: 304.88466560840607
B: 378.99470329284668
This is the (simplified) data classes, and the extension method used in the test, as well as the test of course:
[Test()]
public void LabThatGaveWeirdResultDoesntDoItAnymore()
{
var lab = new LabColor(99, -112, -66);
var rgb = lab.ToRgb();
Assert.IsTrue(new[] { rgb.R, rgb.G, rgb.B }.All(v => v >= 0 && v <= 255));
}
public class LabColor
{
public double L { get; }
public double A { get; }
public double B { get; }
public LabColor(int l, int a, int b)
{
L = l;
A = a;
B = b;
}
}
public class RgbColor
{
public double R { get; }
public double G { get; }
public double B { get; }
public RgbColor(double r, double g, double b)
{
R = r;
G = g;
B = b;
}
}
public static RgbColor ToRgb(this LabColor lab, Intent intent = Intent.Perceptual)
{
var labProfile = Lcms2.GetLabProfile();
var hRgbProfile = Lcms2.GetRgbProfile();
var transform = Lcms2.GetTransform(labProfile, hRgbProfile, intent);
var rgbBuffer = new double[3];
var labBuffer = new[]
{
lab.L,
lab.A,
lab.B
};
Lcms2.Transform(transform, labBuffer, ref rgbBuffer, 1);
Lcms2.FreeMemory(labProfile);
Lcms2.FreeMemory(hRgbProfile);
Lcms2.FreeMemory(transform);
return new RgbColor(rgbBuffer[0] * 255, rgbBuffer[1] * 255, rgbBuffer[2] * 255);
}
The lcms2 wrapper code:
internal enum ProfileFormat
{
Rgb = 4456472,
Cmyk = 4587552,
Lab = 4849688
}
public enum Intent
{
Perceptual = 0,
RelativeColorimetric = 1,
Saturation = 2,
AbsoluteColorimetric = 3
}
internal sealed class Profile
{
public Profile(IntPtr pointer, ProfileFormat format)
{
Pointer = pointer;
Format = format;
}
internal ProfileFormat Format { get; }
internal IntPtr Pointer { get; }
}
internal sealed class Transform
{
public Transform(IntPtr pointer)
{
Pointer = pointer;
}
internal IntPtr Pointer { get; }
}
internal sealed class Lcms2
{
[DllImport("lcms2.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "cmsCreate_sRGBProfile")]
static extern IntPtr cmsCreate_sRGBProfile();
public static Profile GetRgbProfile()
{
return new Profile(cmsCreate_sRGBProfile(), ProfileFormat.Rgb);
}
[DllImport("lcms2.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "cmsCreateLab4Profile")]
static extern IntPtr cmsCreateLab4Profile();
public static Profile GetLabProfile()
{
return new Profile(cmsCreateLab4Profile(), ProfileFormat.Lab);
}
[DllImport("lcms2.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "cmsCreateTransform")]
static extern IntPtr cmsCreateTransform(IntPtr inProfilePtr, uint inputFormat, IntPtr outPutProfilePtr, uint outputFormat, uint intent, uint dword);
public static Transform GetTransform(Profile inProfile, Profile outProfile, Intent intent)
{
return new Transform(cmsCreateTransform(inProfile.Pointer, (uint)inProfile.Format, outProfile.Pointer, (uint)outProfile.Format, (uint)intent, 0));
}
[DllImport("lcms2.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "cmsDoTransform")]
static extern void cmsDoTransform(IntPtr transformPtr, double[] inputBuffer, double[] outputBuffer, uint size);
public static void Transform(Transform transform, double[] inputBuffer, ref double[] outputBuffer, uint size)
{
cmsDoTransform(transform.Pointer, inputBuffer, outputBuffer, size);
}
[DllImport("lcms2.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "cmsDeleteTransform")]
static extern void cmsDeleteTransform(IntPtr transformPtr);
public static void FreeMemory(Transform transform)
{
cmsDeleteTransform(transform.Pointer);
}
[DllImport("lcms2.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "cmsCloseProfile")]
static extern void cmsCloseProfile(IntPtr profilePtr);
public static void FreeMemory(Profile profile)
{
cmsCloseProfile(profile.Pointer);
}
I calculated the profileformat integers by recreating the code in the c++ lcms2 project.
They were defined as
#define TYPE_RGB_DBL (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(0))
#define TYPE_Lab_DBL (FLOAT_SH(1)|COLORSPACE_SH(PT_Lab)|CHANNELS_SH(3)|BYTES_SH(0))
I mimicked the code, creating:
uint FLOAT_SH(uint i) => (i) << 22;
uint COLORSPACE_SH(uint i) => (i) << 16;
uint PT_Lab = 10;
uint PT_RGB = 4;
uint CHANNELS_SH(uint i) => (i) << 3;
uint BYTES_SH(uint i) => (i);
var TYPE_RGB_DBL = (FLOAT_SH(1) | COLORSPACE_SH(PT_RGB) | CHANNELS_SH(3) | BYTES_SH(0)); //4456472
var TYPE_Lab_DBL = (FLOAT_SH(1) | COLORSPACE_SH(PT_Lab) | CHANNELS_SH(3) | BYTES_SH(0)); //4849688
It just happens that Lab (99, -122, -66) is out of sRGB gamut. You cannot represent this color in sRGB. If you clip the values it is RGB=(0, 255, 255), but this is not the color you asked for.
Otherwise the unbounded values for sRGB are:
C:\github\Little-CMS\bin>transicc -t1 -i*Lab
LittleCMS ColorSpace conversion calculator - 4.3 [LittleCMS 2.08]
Enter values, 'q' to quit
L*? 99
a*? -122
b*? -66
R=-4111.8278 G=307.7362 B=378.8372

C# generate Self Signed Cert with Subject Alternative Name?

We have an app that generates a Self Signed Cert but now with Chrome 58 we need to add the Subject Alternative Name. The Cert is generated using C# but invoking the CertCreateSelfSignCertificate function in win32. So far all the examples I am finding are not passing the extensions param and I'm finding it hard to create an extension to pass to generate the SAN.
Note I will be cleaning this up once I get it working
This is what I am using to create an Entry and then the Extension:
CERT_ALT_NAME_ENTRY entry = new CERT_ALT_NAME_ENTRY {
dwAltNameChoice = AlternativeNameType.Dns, // 3
Name = Marshal.StringToHGlobalUni("127.0.0.1")
};
IntPtr entryBlob Marshal.AllocHGlobal(Marshal.SizeOf(typeof(CERT_ALT_NAME_ENTRY)));
var pvStructInfo = new CERT_ALT_NAME_INFO { cAltEntry = 1, rgAltEntry = entryBlob };
IntPtr pvEncoded = IntPtr.Zero;
int pcbEncoded = 0;
var status = InvokeMethods.CryptEncodeObjectEx(
CertEncodingType.X509_ASN_ENCODING | CertEncodingType.PKCS_7_ASN_ENCODING, // 1 | 0x10000
new IntPtr(12),
ref pvStructInfo,
EncodeObjectFlags.CRYPT_ENCODE_ALLOC_FLAG, // 0x8000
IntPtr.Zero,
ref pvEncoded,
ref pcbEncoded);
Marshal.FreeHGlobal(entryBlob);
if (!status)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
var extension = new CERT_EXTENSION
{
ExtensionOid = OidSubjectAltName, //2.5.29.17
IsCritical = false,
Value = new CRYPTOAPI_BLOB
{
Length = (uint)pcbEncoded,
Data = pvEncoded
}
};
var result = new CertExtensions
{
cExtension = 1,
rgExtension = extension
};
Structs Used
internal struct CertExtensions
{
public uint cExtension;
public CERT_EXTENSION rgExtension;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
internal struct CRYPTOAPI_BLOB
{
public uint Length;
public IntPtr Data;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
internal class CERT_EXTENSION
{
[MarshalAs(UnmanagedType.LPWStr)]
public string ExtensionOid;
public bool IsCritical;
public CRYPTOAPI_BLOB Value;
}
[StructLayoutAttribute(LayoutKind.Sequential)]
internal struct CERT_ALT_NAME_INFO
{
/// DWORD->unsigned int
public uint cAltEntry;
public IntPtr rgAltEntry;
}
[StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
internal struct CERT_ALT_NAME_ENTRY
{
public AlternativeNameType dwAltNameChoice;
public IntPtr Name;
}
Full Code - Pieced together from a few examples I found
private static CertExtensions CreateExtensions(IList<CERT_ALT_NAME_ENTRY> items)
{
IntPtr itemBlob = Marshal.AllocHGlobal(items.Count * Marshal.SizeOf(typeof(CERT_ALT_NAME_ENTRY)));
for (int i = 0; i < items.Count; i++)
{
var offset = (IntPtr)((long)itemBlob + i * Marshal.SizeOf(typeof(CERT_ALT_NAME_ENTRY)));
Marshal.StructureToPtr(items[i], offset, false);
}
var pvStructInfo = new CERT_ALT_NAME_INFO { cAltEntry = (uint)items.Count, rgAltEntry = itemBlob };
IntPtr pvEncoded = IntPtr.Zero;
int pcbEncoded = 0;
var status = InvokeMethods.CryptEncodeObjectEx(
CertEncodingType.X509_ASN_ENCODING | CertEncodingType.PKCS_7_ASN_ENCODING,
new IntPtr(12),
ref pvStructInfo,
EncodeObjectFlags.CRYPT_ENCODE_ALLOC_FLAG,
IntPtr.Zero,
ref pvEncoded,
ref pcbEncoded);
Marshal.FreeHGlobal(itemBlob);
if (!status)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
var extension = new CERT_EXTENSION
{
ExtensionOid = OidSubjectAltName,
IsCritical = false,
Value = new CRYPTOAPI_BLOB
{
Length = (uint)pcbEncoded,
Data = pvEncoded
}
};
var result = new CertExtensions
{
cExtension = 1,
rgExtension = extension
};
return result;
}
Well, this going to be a hell of C#/C++ interop and is hard to understand without knowing how pointers, structs and C-like arrays work in C++ and how marshalong works in interop.
You have incorrectly defined CERT_ALT_NAME_ENTRY structure. It used union in C++ definition. When translating unions to C# they must be aligned to the largest struct size in union (which is CRYPTOAPI_BLOB and which is 8 bytes) and plus other field size: 8 + 4 = 12 bytes. Your struct signature is only 8 bytes.
rgExtension member in CERT_EXTENSIONS structure doesn't accept single CERT_EXTENSION struct, actually it is a pointer to an array of CERT_EXTENSION structs. This means that rgExtension member must be defined as IntPtr.
Solution:
Drop your entire code and use examples below.
Correct struct signature definitions:
using System;
using System.Runtime.InteropServices;
namespace TestApp {
static class Wincrypt {
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct CRYPTOAPI_BLOB {
public UInt32 cbData;
public IntPtr pbData;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct CERT_EXTENSION {
[MarshalAs(UnmanagedType.LPStr)]
public String pszObjId;
public Boolean fCritical;
public CRYPTOAPI_BLOB Value;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct CERT_EXTENSIONS {
public UInt32 cExtension;
public IntPtr rgExtension;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct CERT_ALT_NAME_INFO {
public UInt32 cAltEntry;
public IntPtr rgAltEntry;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct CERT_ALT_NAME_ENTRY {
public UInt32 dwAltNameChoice;
// since there is no direct translation from C-like unions in C#
// make additional struct to represent union options.
public CERT_ALT_NAME_UNION Value;
}
// create mapping to dwAltNameChoice
public const UInt32 CERT_ALT_NAME_OTHER_NAME = 1;
public const UInt32 CERT_ALT_NAME_RFC822_NAME = 2;
public const UInt32 CERT_ALT_NAME_DNS_NAME = 3;
public const UInt32 CERT_ALT_NAME_X400_ADDRESS = 4;
public const UInt32 CERT_ALT_NAME_DIRECTORY_NAME = 5;
public const UInt32 CERT_ALT_NAME_EDI_PARTY_NAME = 6;
public const UInt32 CERT_ALT_NAME_URL = 7;
public const UInt32 CERT_ALT_NAME_IP_ADDRESS = 8;
public const UInt32 CERT_ALT_NAME_REGISTERED_ID = 9;
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Auto)]
public struct CERT_ALT_NAME_UNION {
[FieldOffset(0)]
public IntPtr pOtherName;
[FieldOffset(0)]
public IntPtr pwszRfc822Name;
[FieldOffset(0)]
public IntPtr pwszDNSName;
[FieldOffset(0)]
public CRYPTOAPI_BLOB DirectoryName;
[FieldOffset(0)]
public IntPtr pwszURL;
[FieldOffset(0)]
public IntPtr IPAddress;
[FieldOffset(0)]
public IntPtr pszRegisteredID;
}
// not really used in this scenario, but is necessary when want to add
// UPN alt name, for example.
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct CERT_OTHER_NAME {
[MarshalAs(UnmanagedType.LPStr)]
public String pszObjId;
public CRYPTOAPI_BLOB Value;
}
}
}
CryptEncodeObject signature (haven't tried Ex version):
using System;
using System.Runtime.InteropServices;
namespace TestApp {
static class Crypt32 {
[DllImport("Crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern Boolean CryptEncodeObject(
[In] UInt32 CertEncodingType,
[In] UInt32 lpszStructType,
[In, Out]ref Wincrypt.CERT_ALT_NAME_INFO pvStructInfo,
[Out] Byte[] pbEncoded,
[In, Out] ref UInt32 cbEncoded);
}
}
and the whole story with comments:
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
namespace TestApp {
class Program {
static void Main(String[] args) {
//return;
// suppose we want to add three alternative DNS names to SAN extension
String[] dnsNames = { "contoso.com", "www.contoso.com", "mail.contoso.com" };
// calculate size of CERT_ALT_NAME_ENTRY structure. Since it is C-like
// struct, use Marshal.SizeOf(), not C# sizeof().
var altEntrySize = Marshal.SizeOf(typeof(Wincrypt.CERT_ALT_NAME_ENTRY));
// create CERT_ALT_NAME_INFO structure and set initial data:
// cAltEntry -- number of alt names in the extension
// rgAltEntry -- starting pointer in unmanaged memory to an array of alt names
// the size is calculated as: CERT_ALT_NAME_ENTRY size * alt name count
var altInfo = new Wincrypt.CERT_ALT_NAME_INFO {
cAltEntry = (UInt32)dnsNames.Length,
rgAltEntry = Marshal.AllocHGlobal(altEntrySize * dnsNames.Length)
};
// now create CERT_ALT_NAME_ENTRY for each alt name and copy structure to
// a pointer allocated in altInfo structure with a shift.
// Create a loop to save some coding
for (Int32 i = 0; i < dnsNames.Length; i++) {
var altEntry = new Wincrypt.CERT_ALT_NAME_ENTRY {
dwAltNameChoice = Wincrypt.CERT_ALT_NAME_DNS_NAME,
// use Uni, because the pwszDNSName is defined as LPWStr (unicode)
Value = { pwszDNSName = Marshal.StringToHGlobalUni(dnsNames[i]) },
};
// copy alt name entry to altInfo.rgAltEntry at the specified index.
// In unmanaged memory you have to calculate shift based on managed
// index and structure size
Marshal.StructureToPtr(altEntry, altInfo.rgAltEntry + i * altEntrySize, false);
}
// encode CERT_ALT_NAME_INFO to ASN.1 DER byte array
UInt32 pcbEncoded = 0;
if (Crypt32.CryptEncodeObject(1, 12, ref altInfo, null, ref pcbEncoded)) {
Byte[] encodedSANvalue = new Byte[pcbEncoded];
Crypt32.CryptEncodeObject(1, 12, ref altInfo, encodedSANvalue, ref pcbEncoded);
// create certificate extension array:
var extensions = new Wincrypt.CERT_EXTENSIONS {
cExtension = 1,
rgExtension = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Wincrypt.CERT_EXTENSION)))
};
// create SAN extension:
var san = new Wincrypt.CERT_EXTENSION {
fCritical = false,
pszObjId = "2.5.29.17",
Value = { cbData = (UInt32)encodedSANvalue.Length, pbData = Marshal.AllocHGlobal(encodedSANvalue.Length) }
};
// copy SAN bytes to SAN extension:
Marshal.Copy(encodedSANvalue,0,san.Value.pbData, encodedSANvalue.Length);
// copy CERT_EXTENSION structure to extensions:
Marshal.StructureToPtr(san, extensions.rgExtension, false);
// use 'extensions' variable in CertCreateSelfSignCertificate call.
} else {
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
}
}
One note: the provided code do not release unmanaged resources. You have to release them after calling CertCreateSelfSignCertificate function.

Detect the presence of multiple CPU kGroups or NUMA nodes

I would like to write a tool in C# which can detect the presence of multiple CPU groups in Windows. I have seen this referred to as kGroups, and also as NUMA nodes.
This need has grown out of multiple performance related issues where we discovered the customer was running HP servers which often have their NUMA Group Size Optimization in the BIOS set to "Clustered" instead of "Flat" which can result in multiple CPU groups in Windows. Any one process, unless otherwise designed to operate across kGroups, will only be able to use logical processors within the kGroup the process affinity is set to.
I have found a number of resources that can detect information about number of physical/logical processors, but I'm unable to find information on if/how those CPU's might be logically grouped. I'm open to getting this info through p/invoke or WMI among other methods.
Edit: Found the following post with a full example of the GetLogicalProcessorInformationEx call via p/invoke. Will update when I can confirm how to test numa node configuration.
https://stackoverflow.com/a/6972620/3736007
references: http://h17007.www1.hpe.com/docs/iss/proliant_uefi/UEFI_Gen9_060216/s_set_NUMA_group.html
Solved (I think). I was able to use David Heffernan's c# implementation, wrapped it up in a class and added some properties to return some of the information I'm after. Unsafe code is still a bit of black magic to me so I know it could be done better, but it's working so far.
public static class LogicalProcessorInformation
{
public static int CpuPackages
{
get
{
if (_buffer == null)
_buffer = MyGetLogicalProcessorInformation();
return _buffer.Count(b => b.Relationship == LOGICAL_PROCESSOR_RELATIONSHIP.RelationProcessorPackage);
}
}
public static int CpuCores
{
get
{
if (_buffer == null)
_buffer = MyGetLogicalProcessorInformation();
return _buffer.Count(b => b.Relationship == LOGICAL_PROCESSOR_RELATIONSHIP.RelationProcessorCore);
}
}
public static int LogicalProcessors
{
get
{
if (_buffer == null)
_buffer = MyGetLogicalProcessorInformation();
var count = 0;
foreach (var obj in _buffer.Where(b => b.Relationship == LOGICAL_PROCESSOR_RELATIONSHIP.RelationProcessorCore))
{
count += CountSetBits(obj.ProcessorMask);
}
return count;
}
}
public static int CpuGroups
{
get
{
if (_buffer == null)
_buffer = MyGetLogicalProcessorInformation();
return _buffer.Count(b => b.Relationship == LOGICAL_PROCESSOR_RELATIONSHIP.RelationGroup);
}
}
public static int NumaNodes
{
get
{
if (_buffer == null)
_buffer = MyGetLogicalProcessorInformation();
return _buffer.Count(b => b.Relationship == LOGICAL_PROCESSOR_RELATIONSHIP.RelationNumaNode);
}
}
private static int CountSetBits(UIntPtr bitMask)
{
// todo: get rid of tostring and figure out the right way.
var bitMaskuint = uint.Parse(bitMask.ToString());
uint count = 0;
while (bitMaskuint != 0)
{
count += bitMaskuint & 1;
bitMaskuint >>= 1;
}
return (int)count;
}
private static SYSTEM_LOGICAL_PROCESSOR_INFORMATION[] _buffer;
[StructLayout(LayoutKind.Sequential)]
private struct PROCESSORCORE
{
public byte Flags;
};
[StructLayout(LayoutKind.Sequential)]
private struct NUMANODE
{
public uint NodeNumber;
}
private enum PROCESSOR_CACHE_TYPE
{
CacheUnified,
CacheInstruction,
CacheData,
CacheTrace
}
[StructLayout(LayoutKind.Sequential)]
private struct CACHE_DESCRIPTOR
{
public byte Level;
public byte Associativity;
public ushort LineSize;
public uint Size;
public PROCESSOR_CACHE_TYPE Type;
}
[StructLayout(LayoutKind.Explicit)]
private struct SYSTEM_LOGICAL_PROCESSOR_INFORMATION_UNION
{
[FieldOffset(0)]
public PROCESSORCORE ProcessorCore;
[FieldOffset(0)]
public NUMANODE NumaNode;
[FieldOffset(0)]
public CACHE_DESCRIPTOR Cache;
[FieldOffset(0)]
private UInt64 Reserved1;
[FieldOffset(8)]
private UInt64 Reserved2;
}
private enum LOGICAL_PROCESSOR_RELATIONSHIP
{
RelationProcessorCore,
RelationNumaNode,
RelationCache,
RelationProcessorPackage,
RelationGroup,
RelationAll = 0xffff
}
private struct SYSTEM_LOGICAL_PROCESSOR_INFORMATION
{
public UIntPtr ProcessorMask;
public LOGICAL_PROCESSOR_RELATIONSHIP Relationship;
public SYSTEM_LOGICAL_PROCESSOR_INFORMATION_UNION ProcessorInformation;
}
[DllImport(#"kernel32.dll", SetLastError = true)]
private static extern bool GetLogicalProcessorInformation(
IntPtr Buffer,
ref uint ReturnLength
);
private const int ERROR_INSUFFICIENT_BUFFER = 122;
private static SYSTEM_LOGICAL_PROCESSOR_INFORMATION[] MyGetLogicalProcessorInformation()
{
uint ReturnLength = 0;
GetLogicalProcessorInformation(IntPtr.Zero, ref ReturnLength);
if (Marshal.GetLastWin32Error() == ERROR_INSUFFICIENT_BUFFER)
{
IntPtr Ptr = Marshal.AllocHGlobal((int)ReturnLength);
try
{
if (GetLogicalProcessorInformation(Ptr, ref ReturnLength))
{
int size = Marshal.SizeOf(typeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION));
int len = (int)ReturnLength / size;
SYSTEM_LOGICAL_PROCESSOR_INFORMATION[] Buffer = new SYSTEM_LOGICAL_PROCESSOR_INFORMATION[len];
IntPtr Item = Ptr;
for (int i = 0; i < len; i++)
{
Buffer[i] = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION)Marshal.PtrToStructure(Item, typeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION));
Item += size;
}
return Buffer;
}
}
finally
{
Marshal.FreeHGlobal(Ptr);
}
}
return null;
}
}

DS9097E and DS18B20 temperature reading in C#

I have built an adapter DS9097E and connected to it DS18B20 digital thermometer. On the internet you can find examples of applications in Delphi and C that reads the temperature from this device. I'm trying to write it in C#. At the beginning I was relying on .NET but it didn't work. I had problems with RESET AND PRESENCE PULSES, so I tried with kernel32.dll. Yesterday I found this library: OpenNETCF. Unfortunately there is no documentation for it. In the meantime, I tried to write my own library:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Activities;
using Microsoft.Win32.SafeHandles;
using System.IO;
using System.Collections.Specialized;
using System.IO.Ports;
namespace COMAPI
{
public class COMutils
{
private int hCOM = -1;
private DCB dcb = new DCB();
private COMMTIMEOUTS commtime = new COMMTIMEOUTS();
private int bufferSize = 128;
private byte[] ReceiveBytes;
#region STALE
private const int PURGE_TXABORT = 0x01;
private const int PURGE_RXABORT = 0x02;
private const int PURGE_TXCLEAR = 0x04;
private const int PURGE_RXCLEAR = 0x08;
private const int INVALID_HANDLE_VALUE = -1;
private const uint GENERIC_READ = 0x80000000;
private const uint GENERIC_WRITE = 0x40000000;
#endregion
#region KERNEL32
[DllImport("kernel32.dll")]
static extern bool PurgeComm(int hFile, int dwFlags);
[DllImport("kernel32.dll", SetLastError=true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool CloseHandle(int hObject);
[DllImport("kernel32.dll")]
static extern bool FlushFileBuffers(int hFile);
[DllImport("kernel32.dll")]
static extern bool TransmitCommChar(int hFile, char cChar);
[DllImport("kernel32.dll")]
static extern bool WriteFile(int hFile, byte [] lpBuffer, int nNumberOfBytesToWrite, ref int lpNumberOfBytesWritten, int lpOverlapped);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool ReadFile(int hFile, [Out] byte[] lpBuffer, int nNumberOfBytesToRead, ref int lpNumberOfBytesRead, int lpOverlapped);
[DllImport("kernel32.dll")]
static extern bool GetCommState(int hFile, ref DCB lpDCB);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool GetCommTimeouts(int hFile, ref COMMTIMEOUTS lpCommTimeouts);
[DllImport("kernel32.dll")]
static extern bool SetCommState(int hFile, [In] ref DCB lpDCB);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern int CreateFile(
string lpFileName,
[MarshalAs(UnmanagedType.U4)] uint dwDesiredAccess,
[MarshalAs(UnmanagedType.U4)] int dwShareMode,
int lpSecurityAttributes,
[MarshalAs(UnmanagedType.U4)] int dwCreationDisposition,
[MarshalAs(UnmanagedType.U4)] int dwFlagsAndAttributes,
int hTemplateFile);
[DllImport("kernel32.dll")]
static extern int GetTickCount();
#endregion
#region DCB
[StructLayout(LayoutKind.Sequential)]
internal struct DCB
{
internal uint DCBLength;
internal uint BaudRate;
private BitVector32 Flags;
private ushort wReserved; // not currently used
internal ushort XonLim; // transmit XON threshold
internal ushort XoffLim; // transmit XOFF threshold
internal byte ByteSize;
internal Parity Parity;
internal StopBits StopBits;
internal sbyte XonChar; // Tx and Rx XON character
internal sbyte XoffChar; // Tx and Rx XOFF character
internal sbyte ErrorChar; // error replacement character
internal sbyte EofChar; // end of input character
internal sbyte EvtChar; // received event character
private ushort wReserved1; // reserved; do not use
private static readonly int fBinary;
private static readonly int fParity;
private static readonly int fOutxCtsFlow;
private static readonly int fOutxDsrFlow;
private static readonly BitVector32.Section fDtrControl;
private static readonly int fDsrSensitivity;
private static readonly int fTXContinueOnXoff;
private static readonly int fOutX;
private static readonly int fInX;
private static readonly int fErrorChar;
private static readonly int fNull;
private static readonly BitVector32.Section fRtsControl;
private static readonly int fAbortOnError;
static DCB()
{
// Create Boolean Mask
int previousMask;
fBinary = BitVector32.CreateMask();
fParity = BitVector32.CreateMask(fBinary);
fOutxCtsFlow = BitVector32.CreateMask(fParity);
fOutxDsrFlow = BitVector32.CreateMask(fOutxCtsFlow);
previousMask = BitVector32.CreateMask(fOutxDsrFlow);
previousMask = BitVector32.CreateMask(previousMask);
fDsrSensitivity = BitVector32.CreateMask(previousMask);
fTXContinueOnXoff = BitVector32.CreateMask(fDsrSensitivity);
fOutX = BitVector32.CreateMask(fTXContinueOnXoff);
fInX = BitVector32.CreateMask(fOutX);
fErrorChar = BitVector32.CreateMask(fInX);
fNull = BitVector32.CreateMask(fErrorChar);
previousMask = BitVector32.CreateMask(fNull);
previousMask = BitVector32.CreateMask(previousMask);
fAbortOnError = BitVector32.CreateMask(previousMask);
}
}
#endregion
#region COMMTIMEOUTS
struct COMMTIMEOUTS
{
public UInt32 ReadIntervalTimeout;
public UInt32 ReadTotalTimeoutMultiplier;
public UInt32 ReadTotalTimeoutConstant;
public UInt32 WriteTotalTimeoutMultiplier;
public UInt32 WriteTotalTimeoutConstant;
}
#endregion
#region METODY
public bool openCOM(int numer)
{
hCOM = CreateFile("COM" + numer, GENERIC_READ | GENERIC_WRITE, 0, 0, 3, 0, 0);
if (hCOM == INVALID_HANDLE_VALUE)
return false;
if (!GetCommState(hCOM, ref dcb))
return false;
if (!GetCommTimeouts(hCOM, ref commtime))
return false;
return true;
}
public bool closeCOM()
{
return CloseHandle(hCOM);
}
public bool setCOM(int baud, byte bsize, Parity par, StopBits sbits)
{
dcb.BaudRate = (uint)baud;
dcb.ByteSize = bsize;
dcb.Parity = par;
dcb.StopBits = sbits;
return SetCommState(hCOM, ref dcb);
}
public bool cleanCOM()
{
return PurgeComm(hCOM, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
}
public bool flushCOM()
{
return FlushFileBuffers(hCOM);
}
public bool TxByteCOM(byte dane)
{
return TransmitCommChar(hCOM, (char)dane);
}
public bool TxDataCOM(byte[] daneW)
{
int byteZap = 0;
return WriteFile(hCOM, daneW, daneW.Length, ref byteZap, 0);
}
public byte[] RxDataCOM(byte[] odebraneBytes, int ilosc, int odczytane)
{
if(ilosc == 0)
ilosc = bufferSize;
if(hCOM != INVALID_HANDLE_VALUE)
{
odczytane = 0;
bool stanOdczytu = true;
odebraneBytes = new byte[ilosc];
stanOdczytu = ReadFile (hCOM, odebraneBytes, ilosc, ref odczytane, 0);
if(stanOdczytu == false)
{
throw new ApplicationException("Błąd odczytu");
}
else
{
return odebraneBytes;
}
}
else
{
throw new ApplicationException ("Port nie został otwarty");
}
}
public int returnTime()
{
return GetTickCount();
}
private bool IsOdd(int value)
{
return value % 2 != 0;
}
public int sendByte(int X)
{
int D = 0;
int czas, temp;
byte[] B = new byte[1];
temp = X;
setCOM(115200, 8, Parity.None, StopBits.One);
cleanCOM();
czas = returnTime() + 50;
for (int n = 1; n <= 8; n++)
{
if (IsOdd(temp)) TxByteCOM(0xFF);
else TxByteCOM(0x00);
temp = temp << 1;
do
{
RxDataCOM(B, 1, D);
} while ((D > 1) | (czas < returnTime()));
if (D != 1) break;
if (IsOdd(B[0])) temp = (temp | 0x80);
}
return temp;
}
public bool sendCOM(byte[] WrByt)
{
int BytesWritten = 0;
bool writeState = false;
if (hCOM != INVALID_HANDLE_VALUE)
{
PurgeComm(hCOM, PURGE_RXCLEAR | PURGE_TXCLEAR);
writeState = WriteFile(hCOM, WrByt, WrByt.Length, ref BytesWritten, 0);
if (writeState == false)
{
throw new ApplicationException("Błąd zapisu do portu ");
}
}
else
{
throw new ApplicationException("Port nie został otwarty");
}
return writeState;
}
public byte[] receiveCOM (int NumBytes)
{
if(NumBytes == 0)
NumBytes = bufferSize;
if (hCOM != INVALID_HANDLE_VALUE)
{
int BytesRead = 0;
bool readState = true;
ReceiveBytes = new byte[NumBytes];
readState = ReadFile(hCOM, ReceiveBytes, NumBytes, ref BytesRead, 0);
if (readState == false)
{
throw new ApplicationException("Błąd odczytu");
}
else
{
return ReceiveBytes;
}
}
else
{
throw new ApplicationException("Port nie został otwarty");
}
}
public bool resetCOM()
{
int czasOd;
int D = 0;
byte[] baju = new byte[1];
byte[] lista = new byte[1];
setCOM(9600, 8, Parity.None, StopBits.One);
cleanCOM();
lista[0] = 0xF0;
sendCOM(lista);
czasOd = returnTime() + 50;
do
{
RxDataCOM(baju, 1, D);
}
while ((D == 1) | czasOd < returnTime());
if (D == 1)
return (baju[0] != 0xF0);
return true;
}
#endregion
}
}
This is code of my application:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using COMAPI;
namespace temp_3
{
class Program
{
static private COMutils util = new COMutils();
static void Main(string[] args)
{
int TH,TL;
int TEMP_READ;
double TEMP;
util.openCOM(1);
byte[] lista = new byte[1];
bool czy = util.resetCOM();
lista[0] = 0xCC;
czy = util.sendCOM(lista);
lista[0] = 0x44;
czy = util.sendCOM(lista);
Thread.Sleep(750);
czy = util.resetCOM();
lista[0] = 0xCC;
czy = util.sendCOM(lista);
lista[0] = 0xBE;
czy = util.sendCOM(lista);
TL = util.sendByte(0xFF);
Console.WriteLine(TL);
TH = util.sendByte(0xFF);
Console.WriteLine(TH);
czy = util.resetCOM();
Console.WriteLine(czy);
TEMP_READ = TL + 256 * TH;
TEMP = TEMP_READ / 16.0;
Console.WriteLine(TEMP);
Console.WriteLine(czy);
Console.ReadKey();
}
}
}
Unfortunately, based on this code, it reads the temperature of over 8000 degrees Celsius. Could someone point out where is the mistake? Any hint will be appreciated. Here are functions wrote in Delphi:
function Reset_COM:boolean;
var B:Byte;
D,time:DWord;
begin
Result := False;
//ustawienie portu RS232
Ustaw_COM(9600,8,NOPARITY,ONESTOPBIT);
//czyszczenie RS232
Result := Czysc_COM;
TransmitCommChar(hCom,Chr($F0));
time:=GetTickCount+50;
repeat
ReadFile(hCom,B,1,D,nil)
until (D=1) or (time<GetTickCount);
if D=1 then
Result:=(B<>$F0);
end;
function send_Bajt(X:Byte):Byte;
var
n:Integer;
B:Byte;
D,time:DWord;
begin
Result:=$FF;
Ustaw_Com(115200,8,NOPARITY,ONESTOPBIT);
Czysc_COM;
time:=GetTickCount+50;
for n:=1 to 8 do
begin
if Odd(X) then TransmitCommChar(hCom,Chr($FF)) else TransmitCommChar(hCom,Chr($00));
X:=X shr 1;
repeat ReadFile(hCom,B,1,D,nil)
until (D=1) or (time<GetTickCount);
if D<>1 then exit;
if Odd(B) then X:=X or $80;
if Odd(B xor CRC)
then CRC:=((CRC xor $18) shr 1) or $80
else CRC:=CRC shr 1;
end;
Result:=X;
end;
function Odczytaj_TEMP:real;
var TH,TL:Byte;
TEMP_READ: Smallint;
Temp: double;
begin
Resetuj_COM;
send_Bajt($CC); //Skip ROM
send_Bajt($44); //Start T Convert
Sleep(750);
Resetuj_COM;
send_Bajt($CC); //Skip ROM
send_Bajt($BE); //Read Scratchpad
TL:=send_Bajt($FF);
TH:=send_Bajt($FF);
Resetuj_COM;
TEMP_READ := TL + 256 * TH;
Temp := TEMP_READ / 16.0;
Result := Temp;
end;

Read environment variables from a process in C#

I want to read the environment variables of process B from C# code in process A. I have seen some solutions for this in C++ but haven't tried adapting these to C#.
Is this possible from C#, and if not, has anyone wrapped a C++ solution yet?
I've skimmed through the links provided by Isalamon and Daniel Hilgarth, and also the code in CLR Profiler's GetServicesEnvironment() method which seems to be doing the same thing, and after a bit of testing found that the most reliable solution is Oleksiy's code (pure C# with P/Invoke) which he published in this blog post. It still has the limitation, where you have to be a 64bit process to read the env vars of another 64bit process.
Windows solution (32 bit CLR, can access 32 and 64 bit processes),
slightly modified from https://stackoverflow.com/a/46006415 :
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
// Slightly modified from
// https://stackoverflow.com/questions/2633628/can-i-get-command-line-arguments-of-other-processes-from-net-c
// https://stackoverflow.com/a/46006415
public static class ProcessCommandLine
{
private static class Win32Native
{
public const uint PROCESS_BASIC_INFORMATION = 0;
[Flags]
public enum OpenProcessDesiredAccessFlags : uint
{
PROCESS_VM_READ = 0x0010,
PROCESS_QUERY_INFORMATION = 0x0400,
}
[StructLayout(LayoutKind.Sequential)]
public struct ProcessBasicInformation
{
public IntPtr Reserved1;
public IntPtr PebBaseAddress;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public IntPtr[] Reserved2;
public IntPtr UniqueProcessId;
public IntPtr ParentProcessId;
}
[StructLayout(LayoutKind.Sequential)]
public struct ProcessBasicInformation64
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public IntPtr[] Reserved1;
public UInt64 PebBaseAddress;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public IntPtr[] Reserved2;
public IntPtr UniqueProcessId;
public IntPtr Reserved3;
public IntPtr ParentProcessId;
public IntPtr Reserved4;
}
[StructLayout(LayoutKind.Sequential)]
public struct UnicodeString
{
public ushort Length;
public ushort MaximumLength;
public IntPtr Buffer;
}
[StructLayout(LayoutKind.Sequential)]
public struct UnicodeString64
{
public ushort Length;
public ushort MaximumLength;
public UInt32 __padding;
public UInt64 Buffer;
}
// This is not the real struct!
// I faked it to get ProcessParameters address.
// Actual struct definition:
// https://learn.microsoft.com/en-us/windows/win32/api/winternl/ns-winternl-peb
[StructLayout(LayoutKind.Sequential)]
public struct PEB
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public IntPtr[] Reserved;
public IntPtr ProcessParameters;
}
[StructLayout(LayoutKind.Sequential)]
public struct PEB64
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
public IntPtr[] Reserved;
public UInt64 ProcessParameters;
}
/*
* https://stackoverflow.com/a/63222041
Left: Right:
32-bit struct 64-bit struct
0
ULONG MaximumLength; 0
ULONG Length; 4
ULONG Flags; 8
ULONG DebugFlags; 0c
10
PVOID ConsoleHandle; 10
ULONG ConsoleFlags; 18 +4
PVOID StdInputHandle; 20
PVOID StdOutputHandle; 28
20
PVOID StdErrorHandle; 30
UNICODE_STRING CurrentDirectoryPath; 38
PVOID CurrentDirectoryHandle; 48
30
UNICODE_STRING DllPath; 50
UNICODE_STRING ImagePathName; 60
40
UNICODE_STRING CommandLine 70
PVOID Environment; 80
ULONG StartingPositionLeft;
50
ULONG StartingPositionTop;\
ULONG Width;\
ULONG Height;\
ULONG CharWidth;\
60
ULONG CharHeight;\
ULONG ConsoleTextAttributes;\
ULONG WindowFlags;\
ULONG ShowWindowFlags;\
70
UNICODE_STRING WindowTitle; b0
UNICODE_STRING DesktopName; c0
80
UNICODE_STRING ShellInfo; d0
UNICODE_STRING RuntimeData; e0
90
RTL_DRIVE_LETTER_CURDIR DLCurrentDirectory[32];\
ULONG EnvironmentSize;\
*/
[StructLayout(LayoutKind.Sequential)]
public struct RtlUserProcessParameters
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public byte[] Reserved1;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public IntPtr[] Reserved2;
public UnicodeString ImagePathName;
public UnicodeString CommandLine;
public IntPtr Environment;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 9)]
public IntPtr[] Reserved3; // StartingPositionLeft, -Top, Width, Height, CharWidth, -Height, ConsoleTextAttributes, WindowFlags, ShowWindowFlags
public UnicodeString WindowTitle;
public UnicodeString DesktopName;
public UnicodeString ShellInfo;
public UnicodeString RuntimeData;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32 * 4)]
public IntPtr[] Reserved4;
public uint EnvironmentSize;
}
[StructLayout(LayoutKind.Sequential)]
public struct RtlUserProcessParameters64
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public byte[] Reserved1;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public IntPtr[] Reserved2;
public UnicodeString64 CurrentDirectoryPath;
public UnicodeString64 DllPath;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public IntPtr[] Reserved2b;
public UnicodeString64 ImagePathName;
public UnicodeString64 CommandLine;
public UInt64 Environment;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 9)]
public IntPtr[] Reserved3; // StartingPositionLeft, -Top, Width, Height, CharWidth, -Height, ConsoleTextAttributes, WindowFlags, ShowWindowFlags
public UnicodeString64 WindowTitle;
public UnicodeString64 DesktopName;
public UnicodeString64 ShellInfo;
public UnicodeString64 RuntimeData;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32 * 6)]
public IntPtr[] Reserved4;
public uint EnvironmentSize;
}
[DllImport("ntdll.dll")]
public static extern uint NtQueryInformationProcess(
IntPtr ProcessHandle,
uint ProcessInformationClass,
IntPtr ProcessInformation,
uint ProcessInformationLength,
out uint ReturnLength);
[DllImport("ntdll.dll")]
public static extern uint NtWow64QueryInformationProcess64(
IntPtr ProcessHandle,
uint ProcessInformationClass,
IntPtr ProcessInformation,
uint ProcessInformationLength,
out uint ReturnLength);
[DllImport("kernel32.dll")]
public static extern IntPtr OpenProcess(
OpenProcessDesiredAccessFlags dwDesiredAccess,
[MarshalAs(UnmanagedType.Bool)] bool bInheritHandle,
uint dwProcessId);
[DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool ReadProcessMemory(
IntPtr hProcess, IntPtr lpBaseAddress, IntPtr lpBuffer,
uint nSize, out uint lpNumberOfBytesRead);
[DllImport("ntdll.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool NtWow64ReadVirtualMemory64(
IntPtr hProcess, UInt64 lpBaseAddress, IntPtr lpBuffer,
UInt64 nSize, out UInt64 lpNumberOfBytesRead);
[DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CloseHandle(IntPtr hObject);
[DllImport("shell32.dll", SetLastError = true,
CharSet = CharSet.Unicode, EntryPoint = "CommandLineToArgvW")]
public static extern IntPtr CommandLineToArgv(string lpCmdLine, out int pNumArgs);
}
private static bool ReadProcessMemory(IntPtr hProcess, UInt64 lpBaseAddress, IntPtr mem, UInt32 sz, bool force64)
{
if (!force64 && (ulong)0x100000000 > lpBaseAddress)
return Win32Native.ReadProcessMemory(hProcess, (IntPtr)lpBaseAddress, mem, sz, out UInt32 read) && read == sz;
Win32Native.NtWow64ReadVirtualMemory64(hProcess, lpBaseAddress, mem, sz, out UInt64 len64);
return len64 == sz;
}
private static bool ReadStructFromProcessMemory<TStruct>(
IntPtr hProcess, IntPtr lpBaseAddress, out TStruct val, bool bit64 = false)
{
return ReadStructFromProcessMemory<TStruct>(hProcess, (UInt64)lpBaseAddress, out val, bit64);
}
private static bool ReadStructFromProcessMemory<TStruct>(
IntPtr hProcess, UInt64 lpBaseAddress, out TStruct val, bool bit64 = false)
{
val = default;
var structSize = Marshal.SizeOf<TStruct>();
var mem = Marshal.AllocHGlobal(structSize);
try
{
bool ret = ReadProcessMemory(hProcess, lpBaseAddress, mem, (UInt32)structSize, bit64);
if (ret)
{
val = Marshal.PtrToStructure<TStruct>(mem);
return true;
}
}
finally
{
Marshal.FreeHGlobal(mem);
}
return false;
}
public static string ErrorToString(int error) =>
new string[]
{
"Success",
"Failed to open process for reading",
"Failed to query process information",
"PEB address was null",
"Failed to read PEB information",
"Failed to read process parameters",
"Failed to read command line from process",
"Failed to read environment from process",
}[Math.Abs(error)];
public static int Retrieve(Process process, out string commandLine, out List<string> environment)
{
int rc = 0;
commandLine = null;
environment = null;
var hProcess = Win32Native.OpenProcess(
Win32Native.OpenProcessDesiredAccessFlags.PROCESS_QUERY_INFORMATION |
Win32Native.OpenProcessDesiredAccessFlags.PROCESS_VM_READ, false, (uint)process.Id);
if (hProcess != IntPtr.Zero)
{
try
{
var sizePBI = Marshal.SizeOf<Win32Native.ProcessBasicInformation>();
var sizePBI64 = Marshal.SizeOf<Win32Native.ProcessBasicInformation64>();
var memPBI = Marshal.AllocHGlobal(sizePBI64);
try
{
bool bit64;
var ret = Win32Native.NtQueryInformationProcess(
hProcess, Win32Native.PROCESS_BASIC_INFORMATION, memPBI,
(uint)sizePBI, out var len);
if (0 == ret)
{
var pbiInfo64 = new Win32Native.ProcessBasicInformation64();
var pbiInfo = Marshal.PtrToStructure<Win32Native.ProcessBasicInformation>(memPBI);
if (pbiInfo.PebBaseAddress != IntPtr.Zero)
{
bit64 = false;
}
else
{
bit64 = true;
ret = Win32Native.NtWow64QueryInformationProcess64(
hProcess, Win32Native.PROCESS_BASIC_INFORMATION, memPBI,
(uint)sizePBI64, out len);
pbiInfo64 = Marshal.PtrToStructure<Win32Native.ProcessBasicInformation64>(memPBI);
}
if (pbiInfo64.PebBaseAddress != (UInt64)0)
{
Win32Native.PEB64 pebInfo64;
Win32Native.PEB pebInfo;
pebInfo.ProcessParameters = (IntPtr)0; pebInfo64.ProcessParameters = (UInt64)0;
bool ok;
if (bit64)
ok = ReadStructFromProcessMemory<Win32Native.PEB64>(hProcess, pbiInfo64.PebBaseAddress, out pebInfo64, bit64);
else
ok = ReadStructFromProcessMemory<Win32Native.PEB>(hProcess, pbiInfo.PebBaseAddress, out pebInfo, bit64);
if (ok)
{
UInt32 CommandLineSize = 0;
UInt64 CommandLineBuf = 0;
UInt32 EnvironmentSize = 0;
UInt64 EnvironmentBuf = 0;
if (bit64)
{
ok = ReadStructFromProcessMemory<Win32Native.RtlUserProcessParameters64>(hProcess, pebInfo64.ProcessParameters, out var ruppInfo64, bit64);
CommandLineSize = (UInt32)ruppInfo64.CommandLine.MaximumLength;
CommandLineBuf = ruppInfo64.CommandLine.Buffer;
EnvironmentSize = (UInt32)ruppInfo64.EnvironmentSize;
EnvironmentBuf = ruppInfo64.Environment;
}
else
{
ok = ReadStructFromProcessMemory<Win32Native.RtlUserProcessParameters>(hProcess, (UInt64)pebInfo.ProcessParameters, out var ruppInfo, bit64);
CommandLineSize = ruppInfo.CommandLine.MaximumLength;
CommandLineBuf = (UInt64)ruppInfo.CommandLine.Buffer;
EnvironmentSize = ruppInfo.EnvironmentSize;
EnvironmentBuf = (UInt64)ruppInfo.Environment;
}
if (ok)
{
var memCL = Marshal.AllocHGlobal((IntPtr)CommandLineSize);
try
{
if (ReadProcessMemory(hProcess, CommandLineBuf, memCL, CommandLineSize, bit64))
{
commandLine = Marshal.PtrToStringUni(memCL);
rc = 0;
}
else
{
// couldn't read command line buffer
rc = -6;
}
}
finally
{
Marshal.FreeHGlobal(memCL);
}
memCL = Marshal.AllocHGlobal((IntPtr)EnvironmentSize);
try
{
if (ReadProcessMemory(hProcess, EnvironmentBuf, memCL, EnvironmentSize, bit64))
{
environment = new List<string>(40);
IntPtr readPtr = memCL;
while (EnvironmentSize > 0 && Marshal.ReadInt16(readPtr) != 0)
{
string slice = Marshal.PtrToStringUni(readPtr);
environment.Add(slice);
uint size = 0;
do
{
size += 2;
readPtr += 2;
}
while (Marshal.ReadInt16(readPtr) != 0);
size += 2;
readPtr += 2;
EnvironmentSize -= size;
}
rc = 0;
}
else
{
// couldn't read command line buffer
rc = -7;
}
}
finally
{
Marshal.FreeHGlobal(memCL);
}
}
else
{
// couldn't read ProcessParameters
rc = -5;
}
}
else
{
// couldn't read PEB information
rc = -4;
}
}
else
{
// PebBaseAddress is null
rc = -3;
}
}
else
{
// NtQueryInformationProcess failed
rc = -2;
}
}
finally
{
Marshal.FreeHGlobal(memPBI);
}
}
finally
{
Win32Native.CloseHandle(hProcess);
}
}
else
{
// couldn't open process for VM read
rc = -1;
}
return rc;
}
public static IReadOnlyList<string> CommandLineToArgs(string commandLine)
{
if (string.IsNullOrEmpty(commandLine)) { return new string[] { }; }
var argv = Win32Native.CommandLineToArgv(commandLine, out var argc);
if (argv == IntPtr.Zero)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
try
{
var args = new string[argc];
for (var i = 0; i < args.Length; ++i)
{
var p = Marshal.ReadIntPtr(argv, i * IntPtr.Size);
args[i] = Marshal.PtrToStringUni(p);
}
return args.ToList().AsReadOnly();
}
finally
{
Marshal.FreeHGlobal(argv);
}
}
}
Linux solution (32 and 64 bit can be freely mixed):
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
public static class ProcessCommandLine
{
public static int Retrieve(Process process, out string commandLine, out List<string> environment)
{
int pid = process.Id;
try
{
commandLine = File.ReadAllText("/proc/" + pid + "/cmdline").Replace('\0', ' ');
environment = File.ReadAllText("/proc/" + pid + "/environ").Split('\0').ToList();
return 0;
}
catch (Exception e)
{
commandLine = null; environment = null;
Console.WriteLine("ProcessCommandLine: Cannot read file - maybe you have to 'sudo mount -t procfs none /proc'? Maybe you have insufficient rights?");
Console.WriteLine("ProcessCommandLine: Exception was: " + e.GetType() + ": " + e.Message);
}
return -11;
}
}

Categories

Resources