I'm trying to use opengl in C#. I have following code which fails with error 2000 ERROR_INVALID_PIXEL_FORMAT
First definitions:
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
public static extern IntPtr GetDC(IntPtr hWnd);
[StructLayout(LayoutKind.Sequential)]
public struct PIXELFORMATDESCRIPTOR
{
public void Init()
{
nSize = (ushort) Marshal.SizeOf(typeof (PIXELFORMATDESCRIPTOR));
nVersion = 1;
dwFlags = PFD_FLAGS.PFD_DRAW_TO_WINDOW | PFD_FLAGS.PFD_SUPPORT_OPENGL | PFD_FLAGS.PFD_DOUBLEBUFFER | PFD_FLAGS.PFD_SUPPORT_COMPOSITION;
iPixelType = PFD_PIXEL_TYPE.PFD_TYPE_RGBA;
cColorBits = 24;
cRedBits = cRedShift = cGreenBits = cGreenShift = cBlueBits = cBlueShift = 0;
cAlphaBits = cAlphaShift = 0;
cAccumBits = cAccumRedBits = cAccumGreenBits = cAccumBlueBits = cAccumAlphaBits = 0;
cDepthBits = 32;
cStencilBits = cAuxBuffers = 0;
iLayerType = PFD_LAYER_TYPES.PFD_MAIN_PLANE;
bReserved = 0;
dwLayerMask = dwVisibleMask = dwDamageMask = 0;
}
ushort nSize;
ushort nVersion;
PFD_FLAGS dwFlags;
PFD_PIXEL_TYPE iPixelType;
byte cColorBits;
byte cRedBits;
byte cRedShift;
byte cGreenBits;
byte cGreenShift;
byte cBlueBits;
byte cBlueShift;
byte cAlphaBits;
byte cAlphaShift;
byte cAccumBits;
byte cAccumRedBits;
byte cAccumGreenBits;
byte cAccumBlueBits;
byte cAccumAlphaBits;
byte cDepthBits;
byte cStencilBits;
byte cAuxBuffers;
PFD_LAYER_TYPES iLayerType;
byte bReserved;
uint dwLayerMask;
uint dwVisibleMask;
uint dwDamageMask;
}
[Flags]
public enum PFD_FLAGS : uint
{
PFD_DOUBLEBUFFER = 0x00000001,
PFD_STEREO = 0x00000002,
PFD_DRAW_TO_WINDOW = 0x00000004,
PFD_DRAW_TO_BITMAP = 0x00000008,
PFD_SUPPORT_GDI = 0x00000010,
PFD_SUPPORT_OPENGL = 0x00000020,
PFD_GENERIC_FORMAT = 0x00000040,
PFD_NEED_PALETTE = 0x00000080,
PFD_NEED_SYSTEM_PALETTE = 0x00000100,
PFD_SWAP_EXCHANGE = 0x00000200,
PFD_SWAP_COPY = 0x00000400,
PFD_SWAP_LAYER_BUFFERS = 0x00000800,
PFD_GENERIC_ACCELERATED = 0x00001000,
PFD_SUPPORT_DIRECTDRAW = 0x00002000,
PFD_DIRECT3D_ACCELERATED = 0x00004000,
PFD_SUPPORT_COMPOSITION = 0x00008000,
PFD_DEPTH_DONTCARE = 0x20000000,
PFD_DOUBLEBUFFER_DONTCARE = 0x40000000,
PFD_STEREO_DONTCARE = 0x80000000
}
public enum PFD_LAYER_TYPES : byte
{
PFD_MAIN_PLANE = 0,
PFD_OVERLAY_PLANE = 1,
PFD_UNDERLAY_PLANE = 255
}
public enum PFD_PIXEL_TYPE : byte
{
PFD_TYPE_RGBA = 0,
PFD_TYPE_COLORINDEX = 1
}
[DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
public static extern int ChoosePixelFormat(IntPtr hdc, [In] ref PIXELFORMATDESCRIPTOR ppfd);
[DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
public static extern bool SetPixelFormat(IntPtr hdc, int iPixelFormat, ref PIXELFORMATDESCRIPTOR ppfd);
[DllImport("opengl32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
public static extern IntPtr wglCreateContext(IntPtr hDC);
And now the code that fails:
IntPtr dc = Win.GetDC(hwnd);
var pixelformatdescriptor = new GL.PIXELFORMATDESCRIPTOR();
pixelformatdescriptor.Init();
var pixelFormat = GL.ChoosePixelFormat(dc, ref pixelformatdescriptor);
if(!GL.SetPixelFormat(dc, pixelFormat, ref pixelformatdescriptor))
throw new Win32Exception(Marshal.GetLastWin32Error());
IntPtr hglrc;
if((hglrc = GL.wglCreateContext(dc)) == IntPtr.Zero)
throw new Win32Exception(Marshal.GetLastWin32Error()); //<----- here I have exception
the same code in managed C++ is working
HDC dc = GetDC(hWnd);
PIXELFORMATDESCRIPTOR pf;
pf.nSize = sizeof(PIXELFORMATDESCRIPTOR);
pf.nVersion = 1;
pf.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER | PFD_SUPPORT_COMPOSITION;
pf.cColorBits = 24;
pf.cRedBits = pf.cRedShift = pf.cGreenBits = pf.cGreenShift = pf.cBlueBits = pf.cBlueShift = 0;
pf.cAlphaBits = pf.cAlphaShift = 0;
pf.cAccumBits = pf.cAccumRedBits = pf.cAccumGreenBits = pf.cAccumBlueBits = pf.cAccumAlphaBits = 0;
pf.cDepthBits = 32;
pf.cStencilBits = pf.cAuxBuffers = 0;
pf.iLayerType = PFD_MAIN_PLANE;
pf.bReserved = 0;
pf.dwLayerMask = pf.dwVisibleMask = pf.dwDamageMask = 0;
int ipf = ChoosePixelFormat(dc, &pf);
SetPixelFormat(dc, ipf, &pf);
HGLRC hglrc = wglCreateContext(dc);
I've tried it on VIsta 64-bit with ATI graphic card and on Windows XP 32-bit with Nvidia with the same result in both cases.
Also I want to mention that I don't want to use any already written framework for it.
Can anyone show me where is the bug in C# code that is causing the exception?
Found solution.
Problem is very strange ugly and really hard to find. Somwhere on the internet I found that when you are linking opengl32.lib while compiling c++ application it must be placed before gdi32.lib. The reason for this is that (supposedly) opengl32.dll is overwriting ChoosePixelFormat and SetPixelFormat functions (and probably more :-). As I found in my c++ version, accidentally it was the case.
Heh, but how to do it in C#
After few days of searching I found that in tao framework they solved it using kernel32.dll LoadLibrary() function and loading opengl32.dll before calling SetPixelFormat
public static bool SetPixelFormat(IntPtr deviceContext, int pixelFormat, ref PIXELFORMATDESCRIPTOR pixelFormatDescriptor) {
Kernel.LoadLibrary("opengl32.dll");
return _SetPixelFormat(deviceContext, pixelFormat, ref pixelFormatDescriptor);
}
So we know that opengl32.dll must be loaded before gdi32.dll, is there any other way of doing this. After while I thought that we can call some NOP function from opengl32.dll to load it. For example:
[DllImport("opengl32.dll", EntryPoint = "glGetString", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
static extern IntPtr _glGetString(StringName name);
public static string glGetString(StringName name)
{
return Marshal.PtrToStringAnsi(_glGetString(name));
}
public enum StringName : uint
{
GL_VENDOR = 0x1F00,
GL_RENDERER = 0x1F01,
GL_VERSION = 0x1F02,
GL_EXTENSIONS = 0x1F03
}
and on the start of application, before any call to gdi32.dll I use this:
GL.glGetString(0);
Both ways solves the problem.
I cannot test this right now, but my first suspicion would be the structure packing. Have you tried setting the packing to 1 in the StructLayout attribute? For example:
[StructLayout(LayoutKind.Sequential, Pack=1)]
Cheers,
Brian
Calling wglCreateContext twice helps too.
if (SetPixelFormat(DC, iPixelformat, ref pfd) == false)
throw new Win32Exception(Marshal.GetLastWin32Error());
RC = wglCreateContext(DC);
if (RC == HGLRC.Zero)
{
if (SetPixelFormat(DC, iPixelformat, ref pfd) == false)
throw new Win32Exception(Marshal.GetLastWin32Error());
RC = wglCreateContext(DC);
if (RC == HGLRC.Zero)
throw new Win32Exception(Marshal.GetLastWin32Error());
}
Related
I am trying to do login in the phone. I am developing in c# and the library is in C++. The function "lineDevSpecific" returns the value "-2147483595", but it must to be positive.
[DllImport("Tapi32.dll", SetLastError = true)]
unsafe private static extern int lineDevSpecific(IntPtr hLine, IntPtr lpParams);
[StructLayout(LayoutKind.Sequential)]
public struct UserRec
{
//[MarshalAs(UnmanagedType.I4)]
public int dwMode=8;
public int dwParam1=201;
public int dwParam2=0;
}
unsafe static void Iniciar()
{
string vline = "Ip Office Phone: 201";
IntPtr hline = System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(vline);
var sizeUserRec = Marshal.SizeOf(typeof(UserRec));
var userRec = Marshal.AllocHGlobal(sizeUserRec);
int result=lineDevSpecific(hline, userRec);
var x = (UserRec)Marshal.PtrToStructure(userRec, typeof(UserRec));
Marshal.FreeHGlobal(userRec);
}
Version 2:
I have modified the initial post adding the lineInitializeEx method and lineOpen.
These methods returns a positive value, after this lineDevSpecific continues returning the same value.
[DllImport("Tapi32.dll", CharSet = CharSet.Auto)]
unsafe private static extern long lineInitializeEx(ref uint lphLineApp, uint hInstance, uint lpfnCallback, uint lpszFriendlyAppName, ref uint lpdwNumDevs, ref uint lpdwAPIVersion, ref uint lpLineInitializeExParams);
[DllImport("Tapi32.dll", SetLastError = true)]
internal static extern long lineOpen(ref uint hLineApp, uint dwDeviceID, uint lphLine, uint dwAPIVersion, uint dwExtVersion, uint dwCallbackInstance, uint dwPrivileges, uint dwMediaModes, ref uint lpCallParams);
[DllImport("Tapi32.dll", CharSet = CharSet.Auto)]
unsafe private static extern int lineDevSpecific(uint hLine, IntPtr lpParams);
uint lineApp = 0;
uint numdevs = 0;
uint apiversion = 0;
uint exparams = 0;
uint lpcallparams = 0;
string sParams = "";
long lSize = 0;
long inicio = lineInitializeEx(ref lineApp, 0, 0, 0, ref numdevs, ref apiversion, ref exparams);
if (inicio > 0)
{
long open = lineOpen(ref lineApp, 0, 0, 0, 0, 0, 0, 0, ref lpcallparams);
//string vline = "Ip Office Phone: 201";
//IntPtr hline = System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(vline);
var sizeUserRec = Marshal.SizeOf(typeof(UserRec));
var userRec = Marshal.AllocHGlobal(sizeUserRec);
int result = lineDevSpecific(lineApp, userRec);
var x = (UserRec)Marshal.PtrToStructure(userRec, typeof(UserRec));
Marshal.FreeHGlobal(userRec);
}
Solution: I have called to Julmar Atapi.
string extension = "201";
char[] c = new char[extension.Length + 2];
c[0] = (char)0x08; //login character
int i = 1;
foreach (char s in extension)
{
c[i] = s;
i++;
}
c[i] = (char)0x00; //null terminator
CurrentAddress.DeviceSpecific(Encoding.ASCII.GetBytes(c));
That is a LINEERR_ constant, see MSDN LINEERR_ Constants page
These use a hexadecimal unsigned "0x8000 00xx" style, turning them negative if you look at them as a signed int.
So this one is 0x8000 0035 LINEERR_INVALPOINTER
Your hline is wrong, this is a handle to a line not a text in a pointer. You need to get it from a lineOpen
Update for version 2
You are mixing up hLineApp and hLine. From lineOpen MSDN:
hLineApp: Handle to the application's registration with TAPI.
lphLine: Pointer to an HLINE handle that is then loaded with the handle representing the opened line device. Use this handle to identify the device when invoking other functions on the open line device.
I am using nsis7z.dll to extract 7z files. The code works just fine on .NET 3.5, but when I compile using .NET 4.5, it does extract the 7z file, but crashes afterwards with a System.ExecutionEngineException exception. From what I've searched it seems like there's something problematic with the parameters sent using the P/Invoke, mostly the one passed as ref.
The code:
public static bool ExtractNsis7z(string i_FileName, string i_ToDirectory)
{
IntPtr ptr = GlobalAlloc((uint)GlobalMemoryFlags.GPTR, (UIntPtr)Marshal.SizeOf(typeof(stack)));
stack st = (stack)Marshal.PtrToStructure(ptr, typeof(stack));
st.next = IntPtr.Zero;
for (int i = 0; i < i_FileName.Length && (i < st.text.Length - 1); i++)
{
st.text[i] = i_FileName[i];
}
return Extract7z(IntPtr.Zero, 0, i_ToDirectory, ref st, IntPtr.Zero);
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public class stack
{
public IntPtr next;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 260)]
public char[] text;
};
[DllImport("nsis7z.dll", EntryPoint = "Extract", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
static extern bool Extract7z(IntPtr hWnd, Int32 stringSize, String outputDirectory, ref stack theStack, IntPtr extra);
enum GlobalMemoryFlags : uint
{
GMEM_FIXED = 0x0000,
GMEM_MOVEABLE = 0x0002,
GMEM_ZEROINIT = 0x0040,
GMEM_MODIFY = 0x0080,
GMEM_VALID_FLAGS = 0x7F72,
GMEM_INVALID_HANDLE = 0x8000,
GHND = (GMEM_MOVEABLE | GMEM_ZEROINIT),
GPTR = (GMEM_FIXED | GMEM_ZEROINIT),
/*The following values are obsolete, but are provided for compatibility with 16-bit Windows. They are ignored.*/
GMEM_DDESHARE = 0x2000,
GMEM_DISCARDABLE = 0x0100,
GMEM_LOWER = GMEM_NOT_BANKED,
GMEM_NOCOMPACT = 0x0010,
GMEM_NODISCARD = 0x0020,
GMEM_NOT_BANKED = 0x1000,
GMEM_NOTIFY = 0x4000,
GMEM_SHARE = 0x2000
}
[DllImport("kernel32.dll")]
static extern IntPtr GlobalAlloc(uint uFlags, UIntPtr dwBytes);
When I call logoff, it works. But shutdown and restart don't work. Everything looks OK. I looked at other examples on SO and else where and the code looks pretty uniform across most places. So I'm thinking it might be something other than the code.
I'm running as admin and I tried it without the force flag.
public void ShutdownComputer(ShutdownType type, bool force)
{
switch (type)
{
case ShutdownType.Shutdown:
ExitWindowsEx(ExitWindows.ShutDown | (force ? ExitWindows.Force : ExitWindows.ForceIfHung), ShutdownReason.MajorOther | ShutdownReason.MinorOther | ShutdownReason.FlagPlanned);
break;
case ShutdownType.Restart:
ExitWindowsEx(ExitWindows.Reboot | (force ? ExitWindows.Force : ExitWindows.ForceIfHung), ShutdownReason.MajorOther | ShutdownReason.MinorOther | ShutdownReason.FlagPlanned);
break;
case ShutdownType.Logoff:
ExitWindowsEx(ExitWindows.LogOff | (force ? ExitWindows.Force : ExitWindows.ForceIfHung), ShutdownReason.MajorOther | ShutdownReason.MinorOther | ShutdownReason.FlagPlanned);
break;
}
}
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool ExitWindowsEx(ExitWindows uFlags, ShutdownReason dwReason);
[Flags]
public enum ExitWindows : uint
{
// ONE of the following five:
LogOff = 0x00,
ShutDown = 0x01,
Reboot = 0x02,
PowerOff = 0x08,
RestartApps = 0x40,
// plus AT MOST ONE of the following two:
Force = 0x04,
ForceIfHung = 0x10,
}
[Flags]
enum ShutdownReason : uint
{
MajorApplication = 0x00040000,
MajorHardware = 0x00010000,
MajorLegacyApi = 0x00070000,
MajorOperatingSystem = 0x00020000,
MajorOther = 0x00000000,
MajorPower = 0x00060000,
MajorSoftware = 0x00030000,
MajorSystem = 0x00050000,
MinorBlueScreen = 0x0000000F,
MinorCordUnplugged = 0x0000000b,
MinorDisk = 0x00000007,
MinorEnvironment = 0x0000000c,
MinorHardwareDriver = 0x0000000d,
MinorHotfix = 0x00000011,
MinorHung = 0x00000005,
MinorInstallation = 0x00000002,
MinorMaintenance = 0x00000001,
MinorMMC = 0x00000019,
MinorNetworkConnectivity = 0x00000014,
MinorNetworkCard = 0x00000009,
MinorOther = 0x00000000,
MinorOtherDriver = 0x0000000e,
MinorPowerSupply = 0x0000000a,
MinorProcessor = 0x00000008,
MinorReconfig = 0x00000004,
MinorSecurity = 0x00000013,
MinorSecurityFix = 0x00000012,
MinorSecurityFixUninstall = 0x00000018,
MinorServicePack = 0x00000010,
MinorServicePackUninstall = 0x00000016,
MinorTermSrv = 0x00000020,
MinorUnstable = 0x00000006,
MinorUpgrade = 0x00000003,
MinorWMI = 0x00000015,
FlagUserDefined = 0x40000000,
FlagPlanned = 0x80000000
}
In order to be able to shutdown the computer you have to enable the SeShutdown privilege. Once this is done you can call ExitWindowEx
public static class PowerUtilities
{
[DllImport("user32.dll", SetLastError = true)]
private static extern int ExitWindowsEx(ExitWindows uFlags, ShutdownReason dwReason);
public static bool ExitWindows(ExitWindows exitWindows, ShutdownReason reason, bool ajustToken)
{
if (ajustToken && !TokenAdjuster.EnablePrivilege("SeShutdownPrivilege", true))
{
return false;
}
return ExitWindowsEx(exitWindows, reason) != 0;
}
}
[Flags]
public enum ExitWindows : uint
{
// ONE of the following:
LogOff = 0x00,
ShutDown = 0x01,
Reboot = 0x02,
PowerOff = 0x08,
RestartApps = 0x40,
// plus AT MOST ONE of the following two:
Force = 0x04,
ForceIfHung = 0x10,
}
[Flags]
public enum ShutdownReason : uint
{
None = 0,
MajorApplication = 0x00040000,
MajorHardware = 0x00010000,
MajorLegacyApi = 0x00070000,
MajorOperatingSystem = 0x00020000,
MajorOther = 0x00000000,
MajorPower = 0x00060000,
MajorSoftware = 0x00030000,
MajorSystem = 0x00050000,
MinorBlueScreen = 0x0000000F,
MinorCordUnplugged = 0x0000000b,
MinorDisk = 0x00000007,
MinorEnvironment = 0x0000000c,
MinorHardwareDriver = 0x0000000d,
MinorHotfix = 0x00000011,
MinorHung = 0x00000005,
MinorInstallation = 0x00000002,
MinorMaintenance = 0x00000001,
MinorMMC = 0x00000019,
MinorNetworkConnectivity = 0x00000014,
MinorNetworkCard = 0x00000009,
MinorOther = 0x00000000,
MinorOtherDriver = 0x0000000e,
MinorPowerSupply = 0x0000000a,
MinorProcessor = 0x00000008,
MinorReconfig = 0x00000004,
MinorSecurity = 0x00000013,
MinorSecurityFix = 0x00000012,
MinorSecurityFixUninstall = 0x00000018,
MinorServicePack = 0x00000010,
MinorServicePackUninstall = 0x00000016,
MinorTermSrv = 0x00000020,
MinorUnstable = 0x00000006,
MinorUpgrade = 0x00000003,
MinorWMI = 0x00000015,
FlagUserDefined = 0x40000000,
FlagPlanned = 0x80000000
}
public sealed class TokenAdjuster
{
// PInvoke stuff required to set/enable security privileges
private const int SE_PRIVILEGE_ENABLED = 0x00000002;
private const int TOKEN_ADJUST_PRIVILEGES = 0X00000020;
private const int TOKEN_QUERY = 0X00000008;
private const int TOKEN_ALL_ACCESS = 0X001f01ff;
private const int PROCESS_QUERY_INFORMATION = 0X00000400;
[DllImport("advapi32", SetLastError = true), SuppressUnmanagedCodeSecurity]
private static extern int OpenProcessToken(
IntPtr ProcessHandle, // handle to process
int DesiredAccess, // desired access to process
ref IntPtr TokenHandle // handle to open access token
);
[DllImport("kernel32", SetLastError = true),
SuppressUnmanagedCodeSecurity]
private static extern bool CloseHandle(IntPtr handle);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int AdjustTokenPrivileges(
IntPtr TokenHandle,
int DisableAllPrivileges,
IntPtr NewState,
int BufferLength,
IntPtr PreviousState,
ref int ReturnLength);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool LookupPrivilegeValue(
string lpSystemName,
string lpName,
ref LUID lpLuid);
public static bool EnablePrivilege(string lpszPrivilege, bool bEnablePrivilege)
{
bool retval = false;
int ltkpOld = 0;
IntPtr hToken = IntPtr.Zero;
TOKEN_PRIVILEGES tkp = new TOKEN_PRIVILEGES();
tkp.Privileges = new int[3];
TOKEN_PRIVILEGES tkpOld = new TOKEN_PRIVILEGES();
tkpOld.Privileges = new int[3];
LUID tLUID = new LUID();
tkp.PrivilegeCount = 1;
if (bEnablePrivilege)
tkp.Privileges[2] = SE_PRIVILEGE_ENABLED;
else
tkp.Privileges[2] = 0;
if (LookupPrivilegeValue(null, lpszPrivilege, ref tLUID))
{
Process proc = Process.GetCurrentProcess();
if (proc.Handle != IntPtr.Zero)
{
if (OpenProcessToken(proc.Handle, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
ref hToken) != 0)
{
tkp.PrivilegeCount = 1;
tkp.Privileges[2] = SE_PRIVILEGE_ENABLED;
tkp.Privileges[1] = tLUID.HighPart;
tkp.Privileges[0] = tLUID.LowPart;
const int bufLength = 256;
IntPtr tu = Marshal.AllocHGlobal(bufLength);
Marshal.StructureToPtr(tkp, tu, true);
if (AdjustTokenPrivileges(hToken, 0, tu, bufLength, IntPtr.Zero, ref ltkpOld) != 0)
{
// successful AdjustTokenPrivileges doesn't mean privilege could be changed
if (Marshal.GetLastWin32Error() == 0)
{
retval = true; // Token changed
}
}
TOKEN_PRIVILEGES tokp = (TOKEN_PRIVILEGES)Marshal.PtrToStructure(tu, typeof(TOKEN_PRIVILEGES));
Marshal.FreeHGlobal(tu);
}
}
}
if (hToken != IntPtr.Zero)
{
CloseHandle(hToken);
}
return retval;
}
[StructLayout(LayoutKind.Sequential)]
internal struct LUID
{
internal int LowPart;
internal int HighPart;
}
[StructLayout(LayoutKind.Sequential)]
private struct LUID_AND_ATTRIBUTES
{
private LUID Luid;
private int Attributes;
}
[StructLayout(LayoutKind.Sequential)]
internal struct TOKEN_PRIVILEGES
{
internal int PrivilegeCount;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
internal int[] Privileges;
}
[StructLayout(LayoutKind.Sequential)]
private struct _PRIVILEGE_SET
{
private int PrivilegeCount;
private int Control;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] // ANYSIZE_ARRAY = 1
private LUID_AND_ATTRIBUTES[] Privileges;
}
}
this question seems posted at many places over the interwebs and SO, but I could not find a satisfactory answer :(
How can I convert a RGB value to a CMYK value using an ICC profile?
The closest answer I have is there, where it explains how to convert from CMYK to RGB but not the other way around, which is what I need.
(http://stackoverflow.com/questions/4920482/cmyk-to-rgb-formula-of-photoshop/5076731#5076731)
float[] colorValues = new float[4];
colorValues[0] = c / 255f;
colorValues[1] = m / 255f;
colorValues[2] = y / 255f;
colorValues[3] = k / 255f;
System.Windows.Media.Color color = Color.FromValues(colorValues,
new Uri(#"C:\Users\me\Documents\ISOcoated_v2_300_eci.icc"));
System.Drawing.Color rgbColor = System.Drawing.Color.FromArgb(color.R, color.G, color.B);
I guess I should be using some classes/structures/methods from the System.Windows.Media namespace.
The System.Windows.Media.Color structure contains a method FromRgb, but I can't get CMYK values after, out of that System.Windows.Media.Color!
Many thanks
I don't know of any C# API or library that can achieve this. However, if you have enough C/C++ knowledge to build a wrapper for C#, I see two options:
Windows Color System (WCS) (part of Windows)
LittleCMS
The System.Windows.Media namespace is very limited. There's probably a powerful engine (WCS?) behind it, but just a small part is made available.
Update:
Here's some C# code to do the conversion using WCS. It certainly could use a wrapper that would make it easier to use:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace ICM
{
public class WindowsColorSystem
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public class ProfileFilename
{
public uint type;
[MarshalAs(UnmanagedType.LPTStr)]
public string profileData;
public uint dataSize;
public ProfileFilename(string filename)
{
type = ProfileFilenameType;
profileData = filename;
dataSize = (uint)filename.Length * 2 + 2;
}
};
public const uint ProfileFilenameType = 1;
public const uint ProfileMembufferType = 2;
public const uint ProfileRead = 1;
public const uint ProfileReadWrite = 2;
public enum FileShare : uint
{
Read = 1,
Write = 2,
Delete = 4
};
public enum CreateDisposition : uint
{
CreateNew = 1,
CreateAlways = 2,
OpenExisting = 3,
OpenAlways = 4,
TruncateExisting = 5
};
public enum LogicalColorSpace : uint
{
CalibratedRGB = 0x00000000,
sRGB = 0x73524742,
WindowsColorSpace = 0x57696E20
};
public enum ColorTransformMode : uint
{
ProofMode = 0x00000001,
NormalMode = 0x00000002,
BestMode = 0x00000003,
EnableGamutChecking = 0x00010000,
UseRelativeColorimetric = 0x00020000,
FastTranslate = 0x00040000,
PreserveBlack = 0x00100000,
WCSAlways = 0x00200000
};
enum ColorType : int
{
Gray = 1,
RGB = 2,
XYZ = 3,
Yxy = 4,
Lab = 5,
_3_Channel = 6,
CMYK = 7,
_5_Channel = 8,
_6_Channel = 9,
_7_Channel = 10,
_8_Channel = 11,
Named = 12
};
public const uint IntentPerceptual = 0;
public const uint IntentRelativeColorimetric = 1;
public const uint IntentSaturation = 2;
public const uint IntentAbsoluteColorimetric = 3;
public const uint IndexDontCare = 0;
[StructLayout(LayoutKind.Sequential)]
public struct RGBColor
{
public ushort red;
public ushort green;
public ushort blue;
public ushort pad;
};
[StructLayout(LayoutKind.Sequential)]
public struct CMYKColor
{
public ushort cyan;
public ushort magenta;
public ushort yellow;
public ushort black;
};
[DllImport("mscms.dll", SetLastError = true, EntryPoint = "OpenColorProfileW", CallingConvention = CallingConvention.Winapi)]
static extern IntPtr OpenColorProfile(
[MarshalAs(UnmanagedType.LPStruct)] ProfileFilename profile,
uint desiredAccess,
FileShare shareMode,
CreateDisposition creationMode);
[DllImport("mscms.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
static extern bool CloseColorProfile(IntPtr hProfile);
[DllImport("mscms.dll", SetLastError = true, EntryPoint = "GetStandardColorSpaceProfileW", CallingConvention = CallingConvention.Winapi)]
static extern bool GetStandardColorSpaceProfile(
uint machineName,
LogicalColorSpace profileID,
[MarshalAs(UnmanagedType.LPTStr), In, Out] StringBuilder profileName,
ref uint size);
[DllImport("mscms.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
static extern IntPtr CreateMultiProfileTransform(
[In] IntPtr[] profiles,
uint nProfiles,
[In] uint[] intents,
uint nIntents,
ColorTransformMode flags,
uint indexPreferredCMM);
[DllImport("mscms.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
static extern bool DeleteColorTransform(IntPtr hTransform);
[DllImport("mscms.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
static extern bool TranslateColors(
IntPtr hColorTransform,
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2), In] RGBColor[] inputColors,
uint nColors,
ColorType ctInput,
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2), Out] CMYKColor[] outputColors,
ColorType ctOutput);
public static void Test()
{
bool success;
StringBuilder profileName = new StringBuilder(256);
uint size = (uint)profileName.Capacity * 2;
success = GetStandardColorSpaceProfile(0, LogicalColorSpace.sRGB, profileName, ref size);
ProfileFilename sRGBFilename = new ProfileFilename(profileName.ToString());
IntPtr hSRGBProfile = OpenColorProfile(sRGBFilename, ProfileRead, FileShare.Read, CreateDisposition.OpenExisting);
ProfileFilename isoCoatedFilename = new ProfileFilename(#"C:\Users\me\Documents\ISOcoated_v2_300_eci.icc");
IntPtr hIsoCoatedProfile = OpenColorProfile(isoCoatedFilename, ProfileRead, FileShare.Read, CreateDisposition.OpenExisting);
IntPtr[] profiles = new IntPtr[] { hSRGBProfile, hIsoCoatedProfile };
uint[] intents = new uint[] { IntentPerceptual };
IntPtr transform = CreateMultiProfileTransform(profiles, 2, intents, 1, ColorTransformMode.BestMode, IndexDontCare);
RGBColor[] rgbColors = new RGBColor[1];
rgbColors[0] = new RGBColor();
CMYKColor[] cmykColors = new CMYKColor[1];
cmykColors[0] = new CMYKColor();
rgbColors[0].red = 30204;
rgbColors[0].green = 4420;
rgbColors[0].blue = 60300;
success = TranslateColors(transform, rgbColors, 1, ColorType.RGB, cmykColors, ColorType.CMYK);
success = DeleteColorTransform(transform);
success = CloseColorProfile(hSRGBProfile);
success = CloseColorProfile(hIsoCoatedProfile);
}
}
}
None of the answers here seem to satisfactorily address the need to use an ICC profile.
I found an MS Connect issue page that has some sample code using Windows Imaging Components to convert an RBG JPEG to CMYK using an ICC profile.
If you have your ICC file and a sample JPEG file, you can set-up a console app to use this code very quickly.
I have saved the ICC profile in a folder called "Profiles" and have set the "Copy to Output Directory" value to "Always".
The JPEG is saved to a folder called "Images", and I have set its "Build Action" value to "Embedded Resource".
The console app project needs references to the following modules:
PresentationCore
System.Xaml
WindowsBase
The console app in full (named CMYKConversion) :
Program.cs:
using System;
namespace CMYKConversion
{
class Program
{
static void Main(string[] args)
{
Converter c = new Converter();
c.Convert();
Console.ReadKey();
}
}
}
Converter.cs:
using System;
using System.IO;
using System.Reflection;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace CMYKConversion
{
public class Converter
{
public void Convert()
{
var rgbJpeg = BitmapFrame.Create(GetStreamFromResource("CMYKConversion.Images.Desert.jpg"));
var iccCmykJpeg = new ColorConvertedBitmap(
rgbJpeg,
new ColorContext(PixelFormats.Default),
new ColorContext(GetProfilePath("Profiles/1010_ISO_Coated_39L.icc")),
PixelFormats.Cmyk32
);
var jpegBitmapEncoder = new JpegBitmapEncoder();
jpegBitmapEncoder.Frames.Add(BitmapFrame.Create(iccCmykJpeg));
var iccCmykJpegStream = new MemoryStream();
jpegBitmapEncoder.Save(iccCmykJpegStream);
iccCmykJpegStream.Flush();
SaveMemoryStream(iccCmykJpegStream, "C:\\desertCMYK.jpg");
iccCmykJpegStream.Close();
}
private Stream GetStreamFromResource(string name)
{
return typeof(Program).Assembly.GetManifestResourceStream(name);
}
private Uri GetProfilePath(string name)
{
string folder = Path.GetDirectoryName(Assembly.GetAssembly(typeof(Program)).CodeBase);
return new Uri(Path.Combine(folder, name));
}
private void SaveMemoryStream(MemoryStream ms, string fileName)
{
FileStream outStream = File.OpenWrite(fileName);
ms.WriteTo(outStream);
outStream.Flush();
outStream.Close();
}
}
}
According to an MVP GDI+ can read CMYK but can't encode it (Source: http://www.pcreview.co.uk/forums/convert-rgb-image-cmyk-t1419911.html). They go on to say that using TIF as an intermediation format may be the way to go.
Other than that, you might try Graphics Mill imaging SDK for .NET at http://imaging.aurigma.com/ (I'm not affiliated with this company).
I know this isn't much of an answer, but hopefully it sheds some light and points you in the right direction.
You could have a look at this: Convert RGB color to CMYK?
Although this conversion is fairly subjective, hence the need for the ICC profile, it may be that you can extract that "factor" from the ICC and adjust the formula?
What is the context is which you need to convert the RGB values to CMYK?
this question seems posted at many places over the interwebs and SO, but I could not find a satisfactory answer :(
How can I convert a RGB value to a CMYK value using an ICC profile?
The closest answer I have is there, where it explains how to convert from CMYK to RGB but not the other way around, which is what I need.
(http://stackoverflow.com/questions/4920482/cmyk-to-rgb-formula-of-photoshop/5076731#5076731)
float[] colorValues = new float[4];
colorValues[0] = c / 255f;
colorValues[1] = m / 255f;
colorValues[2] = y / 255f;
colorValues[3] = k / 255f;
System.Windows.Media.Color color = Color.FromValues(colorValues,
new Uri(#"C:\Users\me\Documents\ISOcoated_v2_300_eci.icc"));
System.Drawing.Color rgbColor = System.Drawing.Color.FromArgb(color.R, color.G, color.B);
I guess I should be using some classes/structures/methods from the System.Windows.Media namespace.
The System.Windows.Media.Color structure contains a method FromRgb, but I can't get CMYK values after, out of that System.Windows.Media.Color!
Many thanks
I don't know of any C# API or library that can achieve this. However, if you have enough C/C++ knowledge to build a wrapper for C#, I see two options:
Windows Color System (WCS) (part of Windows)
LittleCMS
The System.Windows.Media namespace is very limited. There's probably a powerful engine (WCS?) behind it, but just a small part is made available.
Update:
Here's some C# code to do the conversion using WCS. It certainly could use a wrapper that would make it easier to use:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace ICM
{
public class WindowsColorSystem
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public class ProfileFilename
{
public uint type;
[MarshalAs(UnmanagedType.LPTStr)]
public string profileData;
public uint dataSize;
public ProfileFilename(string filename)
{
type = ProfileFilenameType;
profileData = filename;
dataSize = (uint)filename.Length * 2 + 2;
}
};
public const uint ProfileFilenameType = 1;
public const uint ProfileMembufferType = 2;
public const uint ProfileRead = 1;
public const uint ProfileReadWrite = 2;
public enum FileShare : uint
{
Read = 1,
Write = 2,
Delete = 4
};
public enum CreateDisposition : uint
{
CreateNew = 1,
CreateAlways = 2,
OpenExisting = 3,
OpenAlways = 4,
TruncateExisting = 5
};
public enum LogicalColorSpace : uint
{
CalibratedRGB = 0x00000000,
sRGB = 0x73524742,
WindowsColorSpace = 0x57696E20
};
public enum ColorTransformMode : uint
{
ProofMode = 0x00000001,
NormalMode = 0x00000002,
BestMode = 0x00000003,
EnableGamutChecking = 0x00010000,
UseRelativeColorimetric = 0x00020000,
FastTranslate = 0x00040000,
PreserveBlack = 0x00100000,
WCSAlways = 0x00200000
};
enum ColorType : int
{
Gray = 1,
RGB = 2,
XYZ = 3,
Yxy = 4,
Lab = 5,
_3_Channel = 6,
CMYK = 7,
_5_Channel = 8,
_6_Channel = 9,
_7_Channel = 10,
_8_Channel = 11,
Named = 12
};
public const uint IntentPerceptual = 0;
public const uint IntentRelativeColorimetric = 1;
public const uint IntentSaturation = 2;
public const uint IntentAbsoluteColorimetric = 3;
public const uint IndexDontCare = 0;
[StructLayout(LayoutKind.Sequential)]
public struct RGBColor
{
public ushort red;
public ushort green;
public ushort blue;
public ushort pad;
};
[StructLayout(LayoutKind.Sequential)]
public struct CMYKColor
{
public ushort cyan;
public ushort magenta;
public ushort yellow;
public ushort black;
};
[DllImport("mscms.dll", SetLastError = true, EntryPoint = "OpenColorProfileW", CallingConvention = CallingConvention.Winapi)]
static extern IntPtr OpenColorProfile(
[MarshalAs(UnmanagedType.LPStruct)] ProfileFilename profile,
uint desiredAccess,
FileShare shareMode,
CreateDisposition creationMode);
[DllImport("mscms.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
static extern bool CloseColorProfile(IntPtr hProfile);
[DllImport("mscms.dll", SetLastError = true, EntryPoint = "GetStandardColorSpaceProfileW", CallingConvention = CallingConvention.Winapi)]
static extern bool GetStandardColorSpaceProfile(
uint machineName,
LogicalColorSpace profileID,
[MarshalAs(UnmanagedType.LPTStr), In, Out] StringBuilder profileName,
ref uint size);
[DllImport("mscms.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
static extern IntPtr CreateMultiProfileTransform(
[In] IntPtr[] profiles,
uint nProfiles,
[In] uint[] intents,
uint nIntents,
ColorTransformMode flags,
uint indexPreferredCMM);
[DllImport("mscms.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
static extern bool DeleteColorTransform(IntPtr hTransform);
[DllImport("mscms.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
static extern bool TranslateColors(
IntPtr hColorTransform,
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2), In] RGBColor[] inputColors,
uint nColors,
ColorType ctInput,
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2), Out] CMYKColor[] outputColors,
ColorType ctOutput);
public static void Test()
{
bool success;
StringBuilder profileName = new StringBuilder(256);
uint size = (uint)profileName.Capacity * 2;
success = GetStandardColorSpaceProfile(0, LogicalColorSpace.sRGB, profileName, ref size);
ProfileFilename sRGBFilename = new ProfileFilename(profileName.ToString());
IntPtr hSRGBProfile = OpenColorProfile(sRGBFilename, ProfileRead, FileShare.Read, CreateDisposition.OpenExisting);
ProfileFilename isoCoatedFilename = new ProfileFilename(#"C:\Users\me\Documents\ISOcoated_v2_300_eci.icc");
IntPtr hIsoCoatedProfile = OpenColorProfile(isoCoatedFilename, ProfileRead, FileShare.Read, CreateDisposition.OpenExisting);
IntPtr[] profiles = new IntPtr[] { hSRGBProfile, hIsoCoatedProfile };
uint[] intents = new uint[] { IntentPerceptual };
IntPtr transform = CreateMultiProfileTransform(profiles, 2, intents, 1, ColorTransformMode.BestMode, IndexDontCare);
RGBColor[] rgbColors = new RGBColor[1];
rgbColors[0] = new RGBColor();
CMYKColor[] cmykColors = new CMYKColor[1];
cmykColors[0] = new CMYKColor();
rgbColors[0].red = 30204;
rgbColors[0].green = 4420;
rgbColors[0].blue = 60300;
success = TranslateColors(transform, rgbColors, 1, ColorType.RGB, cmykColors, ColorType.CMYK);
success = DeleteColorTransform(transform);
success = CloseColorProfile(hSRGBProfile);
success = CloseColorProfile(hIsoCoatedProfile);
}
}
}
None of the answers here seem to satisfactorily address the need to use an ICC profile.
I found an MS Connect issue page that has some sample code using Windows Imaging Components to convert an RBG JPEG to CMYK using an ICC profile.
If you have your ICC file and a sample JPEG file, you can set-up a console app to use this code very quickly.
I have saved the ICC profile in a folder called "Profiles" and have set the "Copy to Output Directory" value to "Always".
The JPEG is saved to a folder called "Images", and I have set its "Build Action" value to "Embedded Resource".
The console app project needs references to the following modules:
PresentationCore
System.Xaml
WindowsBase
The console app in full (named CMYKConversion) :
Program.cs:
using System;
namespace CMYKConversion
{
class Program
{
static void Main(string[] args)
{
Converter c = new Converter();
c.Convert();
Console.ReadKey();
}
}
}
Converter.cs:
using System;
using System.IO;
using System.Reflection;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace CMYKConversion
{
public class Converter
{
public void Convert()
{
var rgbJpeg = BitmapFrame.Create(GetStreamFromResource("CMYKConversion.Images.Desert.jpg"));
var iccCmykJpeg = new ColorConvertedBitmap(
rgbJpeg,
new ColorContext(PixelFormats.Default),
new ColorContext(GetProfilePath("Profiles/1010_ISO_Coated_39L.icc")),
PixelFormats.Cmyk32
);
var jpegBitmapEncoder = new JpegBitmapEncoder();
jpegBitmapEncoder.Frames.Add(BitmapFrame.Create(iccCmykJpeg));
var iccCmykJpegStream = new MemoryStream();
jpegBitmapEncoder.Save(iccCmykJpegStream);
iccCmykJpegStream.Flush();
SaveMemoryStream(iccCmykJpegStream, "C:\\desertCMYK.jpg");
iccCmykJpegStream.Close();
}
private Stream GetStreamFromResource(string name)
{
return typeof(Program).Assembly.GetManifestResourceStream(name);
}
private Uri GetProfilePath(string name)
{
string folder = Path.GetDirectoryName(Assembly.GetAssembly(typeof(Program)).CodeBase);
return new Uri(Path.Combine(folder, name));
}
private void SaveMemoryStream(MemoryStream ms, string fileName)
{
FileStream outStream = File.OpenWrite(fileName);
ms.WriteTo(outStream);
outStream.Flush();
outStream.Close();
}
}
}
According to an MVP GDI+ can read CMYK but can't encode it (Source: http://www.pcreview.co.uk/forums/convert-rgb-image-cmyk-t1419911.html). They go on to say that using TIF as an intermediation format may be the way to go.
Other than that, you might try Graphics Mill imaging SDK for .NET at http://imaging.aurigma.com/ (I'm not affiliated with this company).
I know this isn't much of an answer, but hopefully it sheds some light and points you in the right direction.
You could have a look at this: Convert RGB color to CMYK?
Although this conversion is fairly subjective, hence the need for the ICC profile, it may be that you can extract that "factor" from the ICC and adjust the formula?
What is the context is which you need to convert the RGB values to CMYK?