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
Related
I want to call a C function with an empty void* parameter and retrieve it in C# to print the struct values.
In this example:
The C code will randomly assign one of the two structures to the void parameter.
The C code return must be an int.
The C code structures can be modified
How to retrieve and print the C struct content in C#?
C
#include <time.h>
#include <stdlib.h>
void PrintKdesc(interTICKeyDescription* kdescr)
{
kdescr->nItems++;
printf("nItems = %d\n", kdescr->nItems);
}
void PrintKevent(interTICEvent* kevent)
{
kevent->nbr++;
printf("nbr = %d\n", kevent->nbr);
}
EXPORT int VoidPtrTest(void* test)
{
interTICKeyDescription kdesc = { 1, 0x250021, 0, 42 };
interTICEvent kevent = { 2, 8 };
srand(time(NULL));
int r = rand() % 2;
if (r == 1)
{
test = &kdesc;
PrintKdesc(test);
}
else
{
test = &kevent;
PrintKevent(test);
}
return (0);
}
typedef struct {
int id;
unsigned int networkId;
unsigned int implVersion;
int nItems;
} interTICKeyDescription;
typedef struct {
int id;
int nbr;
} interTICEvent;
C#
[StructLayout(LayoutKind.Sequential)]
public struct InterTICKeyDescription
{
public int id;
public UInt32 networkId;
public UInt32 implVersion;
public int nItems;
}
[StructLayout(LayoutKind.Sequential)]
public struct InterTICEvent
{
public int id;
public int nbr;
}
[DllImport("Sandbox.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
extern static int VoidPtrTest(IntPtr test);
My attempt
static void Main()
{
InterTICKeyDescription kdesc = new InterTICKeyDescription();
InterTICEvent kevent = new InterTICEvent();
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(kdesc));
VoidPtrTest(ptr);
int id = Marshal.ReadInt32(ptr, 0);
if (id == 1)
{
kdesc = (InterTICKeyDescription)Marshal.PtrToStructure(ptr, typeof(InterTICKeyDescription));
Console.WriteLine("Id 1: " + kdesc.nItems);
}
else
{
kevent = (InterTICEvent)Marshal.PtrToStructure(ptr, typeof(InterTICEvent));
Console.WriteLine("Id 2: " + kevent.nbr);
}
Marshal.FreeHGlobal(ptr);
}
I have Unmanaged C# DLL:
[DllExport(ExportName = "GetStudentsList", CallingConvention = CallingConvention.StdCall)]
static public List<StudentsStruct>GetStudentsList() { return List<StudentsStruct>; }
[DllExport(ExportName = "maxElement", CallingConvention = CallingConvention.StdCall)]
static public int maxElement(int a, int b) { return c; }
I want to return List<StudentsStruct> from the function.
And I want to run above function in a C++ Application:
using GetStudentsListFn = List<StudentsStruct> (__stdcall *) (void);
GetStudentsListFn GetStudentsList = reinterpret_cast<GetStudentsListFn> (GetProcAddress(mod, "GetStudentsList"));
List<StudentsStruct> myList = GetStudentsList();
using MaxElementFn = int(__stdcall *) (int a, int b);
MaxElementFn maxElement = reinterpret_cast<MaxElementFn> (GetProcAddress(mod, "maxElement"));
std::printf("max: %d\n", maxElement(1, 2));
MaxElement( ) function is working perfectly because it return an int. But i want to return List/Array of "StudentsStruct", from C# to C++.
I would do this with out array parameter and return the size, like this:
using ExportDllAttribute.DllExport;
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct StudentsStruct
{
public string Name;
public int SomeInt;
public double SomeDouble;
[DllExport]
public static int GetStudentsList([Out] out StudentsStruct[] students)
{
students = new StudentsStruct[] { new StudentsStruct { Name = "Satan", SomeInt = 666, SomeDouble = 666 },
new StudentsStruct { Name = "Techno", SomeInt = 777, SomeDouble = 777 } };
return students.Length;
}
}
and the C++ code:
#include<Windows.h>
struct StudentsStruct
{
public:
LPWSTR Name;
int SomeInt;
double SomeDouble;
};
using GetStudentsListFn = int (__stdcall*) (StudentsStruct **);
int main()
{
HMODULE hModule = LoadLibraryA("DllExportArrayTest.dll");
if (hModule)
{
GetStudentsListFn GetStudentsList = reinterpret_cast<GetStudentsListFn>(GetProcAddress(hModule, "GetStudentsList"));
StudentsStruct* students = NULL;
auto size = GetStudentsList(&students);
for (int i = 0; i < size; i++)
auto student = students[i];
FreeLibrary(hModule);
}
}
I'm currently writing a small utility library to help improve performance when writing to the console, and I'm facing an issue where it fails to actually output any text. Below is my code:
public static class QuickDraw
{
private static short Width => (short)Console.WindowWidth;
private static short Height => (short)Console.WindowHeight;
private static SafeFileHandle Handle =>
Kernel32.CreateFile("$CONOUT", 0x40000000, 2, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero);
private static Kernel32.Coord Cursor =>
new Kernel32.Coord((short)Console.CursorLeft, (short)Console.CursorTop);
private static Kernel32.SmallRect WriteRegion =>
new Kernel32.SmallRect() { Left = 0, Top = 0, Right = Width, Bottom = Height };
private static Kernel32.Coord BufferSize =>
new Kernel32.Coord(Width, Height);
private static Kernel32.Coord BufferCoord =>
new Kernel32.Coord(0, 0);
public static void Write(char[] text, ConsoleColor fg, ConsoleColor bg)
{
Kernel32.CharInfo[] buffer = new Kernel32.CharInfo[Width * Height];
Kernel32.Coord cursor = Cursor;
for (int i = 0; i < text.Length; i++)
{
if (text[i] == '\n')
{
cursor.X = 0;
cursor.Y++;
}
else
{
int index = (cursor.Y * Width) + cursor.X;
// Set character
buffer[index].Char.AsciiChar = (byte)text[i];
// Set color
// (Crazy heckin bitwise crap, don't touch.)
buffer[index].Attributes = (short)((int)fg | ((int)bg | (2 << 4)));
// Increment cursor
cursor.X++;
}
// Make sure that cursor does not exceed bounds of window
if (cursor.X >= Width)
{
cursor.X = 0;
cursor.Y++;
}
if (cursor.Y >= Height)
{
cursor.Y = 0;
}
}
var writeRegion = WriteRegion;
Kernel32.WriteConsoleOutput(Handle, buffer, BufferSize, BufferCoord, ref writeRegion);
Console.SetCursorPosition(cursor.X, cursor.Y);
}
}
// Taken from https://stackoverflow.com/a/2754674/7937949
internal static class Kernel32
{
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern SafeFileHandle CreateFile(
string fileName,
[MarshalAs(UnmanagedType.U4)] uint fileAccess,
[MarshalAs(UnmanagedType.U4)] uint fileShare,
IntPtr securityAttributes,
[MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
[MarshalAs(UnmanagedType.U4)] int flags,
IntPtr template);
[DllImport("Kernel32.dll", SetLastError = true)]
public static extern bool WriteConsoleOutput(
SafeFileHandle hConsoleOutput,
CharInfo[] lpBuffer,
Coord dwBufferSize,
Coord dwBufferCoord,
ref SmallRect lpWriteRegion);
[StructLayout(LayoutKind.Sequential)]
public struct Coord
{
public short X;
public short Y;
public Coord(short x, short y)
{
X = x;
Y = y;
}
}
[StructLayout(LayoutKind.Explicit)]
public struct CharUnion
{
[FieldOffset(0)] public char UnicodeChar;
[FieldOffset(0)] public byte AsciiChar;
}
[StructLayout(LayoutKind.Explicit)]
public struct CharInfo
{
[FieldOffset(2)] public CharUnion Char;
[FieldOffset(2)] public short Attributes;
}
[StructLayout(LayoutKind.Sequential)]
public struct SmallRect
{
public short Left;
public short Top;
public short Right;
public short Bottom;
}
}
I've mostly taken the implementation from this post, adding a new class to simplify its use. I'm not entirely sure where my issue is, as my debugger is not working right, but I'm pretty sure it's in my implementation in QuickDraw.
When I try to use QuickDraw.Write(), the cursor moves to the end of whatever string it was trying to print, but nothing actually shows up. What am I doing wrong?
First of all, you didn't copy the code right. There are some mistakes.
in CharInfo change
[FieldOffset(2)] public CharUnion Char;
to
[FieldOffset(0)] public CharUnion Char;
Change $CONOUT to this CONOUT$
First set the Attribute and then the AsciiChar
And of course, if you want the correct foreground-/background color representation then you have to delete the 2 << 4. I don't even know why you put it there.
I am uploading a cab file from a web form, and want to unpack it in memory. I've tried tackling the issue with a CabInfo file, but without success. I do know how to unpack a cab file to my local disk, but do not know how to apply this in memory.
Any assistance would be appreciated.
If using another library is possible, take a look at this. The description clearly states the library will allow you to extract into memoty.
WIX is an open source project. You could always ask for this feature, ask for a better solution on their forum or simply modify the code for your need.
Vadim
Your initial question seems to indicate that you are allowed to use the Microsoft.Deployment.Compression DLL. If true, the code below should get you close to what you want:
CabEngine engine = new CabEngine();
foreach (ArchiveFileInfo archiveFileInfo in engine.GetFileInfo(fileStream))
{
Stream stream = engine.Unpack(fileStream, archiveFileInfo.Name);
byte[] buffer = new byte[stream.Length];
//The (int) below is a dirty trick for demonstration purposes only;
//re-work for production code
stream.Read(buffer, 0, (int)stream.Length);
}
Later answers from you seem to indicate that you are not allowed to use 3rd party DLLs in which case you will want to use the FDI* API. This link has some code that you will be able to modify from using hard-coded paths to (memory) streams: Extract .cab file in C#
There is a ready to use project that you can download: Cabinet File (*.CAB) Compression and Extraction
According to version history since Sep 2008 "..you can extract directly to memory."
Here is a utility class that should work in memory (it supports x86 or x64 compilation). Here is out you would use it, if we suppose a .CAB file has been uploaded into ASP.NET using the standard upload protocol:
using (CabFile file = new CabFile(HttpContext.Current.Request.Files[0].InputStream))
{
file.EntryExtract += CabEntryExtract;
file.ExtractEntries();
}
static void CabEntryExtract(object sender, CabEntryExtractEventArgs e)
{
// e.Entry.Name contains the entry name
// e.Entry.Data contains a byte[] with the entry data
// e.Entry.LastWriteTime contains the entry last write time
// e.Entry.Size contains the entry uncompressed size
}
And here is the utility and associated classes:
public sealed class CabFile : IDisposable
{
private IntPtr _hfdi;
private ERF _erf;
private GCHandle _erfHandle;
private byte[] _data;
private Dictionary<IntPtr, object> _handles = new Dictionary<IntPtr, object>();
private MemoryStream _currentEntryData;
private FNALLOC _alloc;
private FNCLOSE _close;
private FNFREE _free;
private FNOPEN _open;
private FNREAD _read;
private FNWRITE _write;
private FNSEEK _seek;
private FNFDINOTIFY _extract;
public event EventHandler<CabEntryExtractEventArgs> EntryExtract;
public CabFile(string filePath)
: this(GetStream(filePath))
{
}
private static Stream GetStream(string filePath)
{
if (filePath == null)
throw new ArgumentNullException("filePath");
return new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);
}
public CabFile(Stream stream)
{
if (stream == null)
throw new ArgumentNullException("stream");
using (MemoryStream data = new MemoryStream())
{
stream.CopyTo(data);
_data = data.ToArray();
}
_erf = new ERF();
_alloc = new FNALLOC(FnAlloc);
_free = new FNFREE(FnFree);
_close = new FNCLOSE(FnClose);
_open = new FNOPEN(FnOpen);
_read = new FNREAD(FnRead);
_write = new FNWRITE(FnWrite);
_seek = new FNSEEK(FnSeek);
_extract = new FNFDINOTIFY(FnNotifyExtract);
_erfHandle = GCHandle.Alloc(_erf, GCHandleType.Pinned);
_hfdi = FDICreate(
Marshal.GetFunctionPointerForDelegate(_alloc),
Marshal.GetFunctionPointerForDelegate(_free),
Marshal.GetFunctionPointerForDelegate(_open),
Marshal.GetFunctionPointerForDelegate(_read),
Marshal.GetFunctionPointerForDelegate(_write),
Marshal.GetFunctionPointerForDelegate(_close),
Marshal.GetFunctionPointerForDelegate(_seek)
, -1, _erfHandle.AddrOfPinnedObject());
}
public void ExtractEntries()
{
FDICopy(_hfdi, string.Empty, string.Empty, 0, Marshal.GetFunctionPointerForDelegate(_extract), IntPtr.Zero, IntPtr.Zero);
}
public void Dispose()
{
if (_hfdi != IntPtr.Zero)
{
FDIDestroy(_hfdi);
_hfdi = IntPtr.Zero;
}
_erfHandle.Free();
}
private void OnEntryExtract(CabEntry entry)
{
EventHandler<CabEntryExtractEventArgs> handler = EntryExtract;
if (handler != null)
{
handler(this, new CabEntryExtractEventArgs(entry));
}
}
private IntPtr FnAlloc(int cb)
{
return Marshal.AllocHGlobal(cb);
}
private void FnFree(IntPtr pv)
{
Marshal.FreeHGlobal(pv);
}
private IntPtr FnOpen(string pszFile, int oflag, int pmode)
{
// only used for reading archive
IntPtr h = new IntPtr(_handles.Count + 1);
_handles.Add(h, 0);
return h;
}
private int FnRead(IntPtr hf, byte[] pv, int cb)
{
// only used for reading archive
int pos = (int)_handles[hf];
int left = _data.Length - pos;
int read = Math.Min(left, cb);
if (read > 0)
{
Array.Copy(_data, pos, pv, 0, read);
_handles[hf] = pos + read;
}
return read;
}
private int FnWrite(IntPtr hf, byte[] pv, int cb)
{
// only used for writing entries
_currentEntryData.Write(pv, 0, cb);
return cb;
}
private int FnClose(IntPtr hf)
{
object o = _handles[hf];
CabEntry entry = o as CabEntry;
if (entry != null)
{
entry.Data = _currentEntryData.ToArray();
_currentEntryData.Dispose();
}
_handles.Remove(hf);
return 0;
}
private int FnSeek(IntPtr hf, int dist, SeekOrigin seektype)
{
// only used for seeking archive
int pos;
switch (seektype)
{
case SeekOrigin.Begin:
pos = dist;
break;
case SeekOrigin.Current:
pos = (int)_handles[hf] + dist;
break;
//case SeekOrigin.End:
default:
pos = _data.Length + dist;
break;
}
_handles[hf] = pos;
return pos;
}
private IntPtr FnNotifyExtract(FDINOTIFICATIONTYPE fdint, FDINOTIFICATION fdin)
{
CabEntry entry;
switch (fdint)
{
case FDINOTIFICATIONTYPE.COPY_FILE:
entry = new CabEntry(fdin);
entry._handle = new IntPtr(_handles.Count + 1);
_handles.Add(entry._handle, entry);
_currentEntryData = new MemoryStream();
return entry._handle;
case FDINOTIFICATIONTYPE.CLOSE_FILE_INFO:
entry = (CabEntry)_handles[fdin.hf];
FnClose(fdin.hf);
OnEntryExtract(entry);
return new IntPtr(1);
default:
return IntPtr.Zero;
}
}
private enum FDINOTIFICATIONTYPE
{
CABINET_INFO = 0,
PARTIAL_FILE = 1,
COPY_FILE = 2,
CLOSE_FILE_INFO = 3,
NEXT_CABINET = 4,
ENUMERATE = 5,
}
[StructLayout(LayoutKind.Sequential)]
private struct ERF
{
public int erfOper;
public int erfType;
public int fError;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
internal class FDINOTIFICATION
{
public int cb;
public IntPtr psz1;
public IntPtr psz2;
public IntPtr psz3;
public IntPtr pv;
public IntPtr hf;
public ushort date;
public ushort time;
public ushort attribs;
public ushort setID;
public ushort iCabinet;
public ushort iFolder;
public int fdie;
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate IntPtr FNALLOC(int cb);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void FNFREE(IntPtr pv);
[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private delegate IntPtr FNOPEN(string pszFile, int oflag, int pmode);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate int FNREAD(IntPtr hf, [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] byte[] pv, int cb);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate int FNWRITE(IntPtr hf, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] byte[] pv, int cb);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate int FNCLOSE(IntPtr hf);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate int FNSEEK(IntPtr hf, int dist, SeekOrigin seektype);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate IntPtr FNFDINOTIFY(FDINOTIFICATIONTYPE fdint, FDINOTIFICATION pfdin);
[DllImport("cabinet.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private static extern IntPtr FDICreate(IntPtr pfnalloc, IntPtr pfnfree, IntPtr pfnopen, IntPtr pfnread, IntPtr pfnwriter, IntPtr pfnclose, IntPtr pfnseek, int cpuType, IntPtr perf);
[DllImport("cabinet.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr FDIDestroy(IntPtr hdfi);
[DllImport("cabinet.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private static extern IntPtr FDICopy(IntPtr hdfi, string pszCabinet, string pszCabPath, int flags, IntPtr fnNotify, IntPtr fnDecrypt, IntPtr userData);
}
public sealed class CabEntry
{
internal IntPtr _handle;
internal CabEntry(CabFile.FDINOTIFICATION fdin)
{
Name = Marshal.PtrToStringAnsi(fdin.psz1);
Size = fdin.cb;
LastWriteTime = new DateTime(1980 + GetMask(fdin.date, 9, 15), GetMask(fdin.date, 5, 8), GetMask(fdin.date, 0, 4),
GetMask(fdin.time, 11, 15), GetMask(fdin.time, 5, 10), 2 * GetMask(fdin.time, 0, 4));
}
private static int GetMask(int value, byte startByte, byte endByte)
{
int final = 0;
int v = 1;
for (byte b = startByte; b <= endByte; b++)
{
if ((value & (1 << b)) != 0)
{
final += v;
}
v = v * 2;
}
return final;
}
public string Name { get; private set; }
public int Size { get; private set; }
public DateTime LastWriteTime { get; private set; }
public byte[] Data { get; internal set; }
}
public sealed class CabEntryExtractEventArgs : EventArgs
{
public CabEntryExtractEventArgs(CabEntry entry)
{
if (entry == null)
throw new ArgumentNullException("entry");
Entry = entry;
}
public CabEntry Entry { get; private set; }
}
NOTE: this code allocates big byte[] chunks so it could be optimized to use things like ChunkedMemoryStream (available for example in this library: CodeFluent Runtime Client) instead of byte[] to avoid impacting the LOH (Large Object Heap) too much.
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