Check if unmanaged DLL is 32-bit or 64-bit? - c#

How can I programmatically tell in C# if an unmanaged DLL file is x86 or x64?

Refer to the specifications. Here's a basic implementation:
public static MachineType GetDllMachineType (string dllPath)
{
// See http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx
// Offset to PE header is always at 0x3C.
// The PE header starts with "PE\0\0" = 0x50 0x45 0x00 0x00,
// followed by a 2-byte machine type field (see the document above for the enum).
//
using (var fs = new FileStream (dllPath, FileMode.Open, FileAccess.Read))
using (var br = new BinaryReader (fs))
{
fs.Seek (0x3c, SeekOrigin.Begin);
Int32 peOffset = br.ReadInt32();
fs.Seek (peOffset, SeekOrigin.Begin);
UInt32 peHead = br.ReadUInt32();
if (peHead != 0x00004550) // "PE\0\0", little-endian
throw new Exception ("Can't find PE header");
return (MachineType)br.ReadUInt16();
}
}
The MachineType enum is defined as:
public enum MachineType : ushort
{
IMAGE_FILE_MACHINE_UNKNOWN = 0x0,
IMAGE_FILE_MACHINE_AM33 = 0x1d3,
IMAGE_FILE_MACHINE_AMD64 = 0x8664,
IMAGE_FILE_MACHINE_ARM = 0x1c0,
IMAGE_FILE_MACHINE_EBC = 0xebc,
IMAGE_FILE_MACHINE_I386 = 0x14c,
IMAGE_FILE_MACHINE_IA64 = 0x200,
IMAGE_FILE_MACHINE_M32R = 0x9041,
IMAGE_FILE_MACHINE_MIPS16 = 0x266,
IMAGE_FILE_MACHINE_MIPSFPU = 0x366,
IMAGE_FILE_MACHINE_MIPSFPU16 = 0x466,
IMAGE_FILE_MACHINE_POWERPC = 0x1f0,
IMAGE_FILE_MACHINE_POWERPCFP = 0x1f1,
IMAGE_FILE_MACHINE_R4000 = 0x166,
IMAGE_FILE_MACHINE_SH3 = 0x1a2,
IMAGE_FILE_MACHINE_SH3DSP = 0x1a3,
IMAGE_FILE_MACHINE_SH4 = 0x1a6,
IMAGE_FILE_MACHINE_SH5 = 0x1a8,
IMAGE_FILE_MACHINE_THUMB = 0x1c2,
IMAGE_FILE_MACHINE_WCEMIPSV2 = 0x169,
IMAGE_FILE_MACHINE_ARM64 = 0xaa64
}
I only needed three of these, but I included them all for completeness. Final 64-bit check:
// Returns true if the dll is 64-bit, false if 32-bit, and null if unknown
public static bool? UnmanagedDllIs64Bit(string dllPath)
{
switch (GetDllMachineType(dllPath))
{
case MachineType.IMAGE_FILE_MACHINE_AMD64:
case MachineType.IMAGE_FILE_MACHINE_IA64:
return true;
case MachineType.IMAGE_FILE_MACHINE_I386:
return false;
default:
return null;
}
}

Using a Visual Studio command prompt, dumpbin /headers dllname.dll works too. On my machine the beginning of the output stated:
FILE HEADER VALUES
8664 machine (x64)
5 number of sections
47591774 time date stamp Fri Dec 07 03:50:44 2007

Even easier: check out the System.Reflection.Module class. It includes the GetPEKind method, which returns 2 enums that describe the type of code and the CPU target. No more hex!
(the rest of this very informative post was copied shamelessly from http://www.developersdex.com/vb/message.asp?p=2924&r=6413567)
Sample code:
Assembly assembly = Assembly.ReflectionOnlyLoadFrom(#"<assembly Path>");
PortableExecutableKinds kinds;
ImageFileMachine imgFileMachine;
assembly.ManifestModule.GetPEKind(out kinds, out imgFileMachine);
PortableExecutableKinds can be used to check what kind of the assembly. It
has 5 values:
ILOnly: The executable contains only Microsoft intermediate language
(MSIL), and is therefore neutral with respect to 32-bit or 64-bit
platforms.
NotAPortableExecutableImage: The file is not in portable executable (PE)
file format.
PE32Plus: The executable requires a 64-bit platform.
Required32Bit: The executable can be run on a 32-bit platform, or in the
32-bit Windows on Windows (WOW) environment on a 64-bit platform.
Unmanaged32Bit: The executable contains pure unmanaged code.
Following are the links:
Module.GetPEKind Method:
http://msdn.microsoft.com/en-us/library/system.reflection.module.getpekind.aspx
PortableExecutableKinds Enumeration:
http://msdn.microsoft.com/en-us/library/system.reflection.portableexecutablekinds(VS.80).aspx
ImageFileMachine Enumeration:
http://msdn.microsoft.com/en-us/library/system.reflection.imagefilemachine.aspx

Instead of Assembly.LoadFile, use Assembly.ReflectionOnlyLoadFrom. This will let you work around the "Bad Image Format" exceptions.

I know it has been a while since this was updated. I was able to get away with the "Bad Image Format" exceptions by loading the file into it's own AppDomain.
private static (string pkName, string imName) FindPEKind(string filename)
{
// some files, especially if loaded into memory
// can cause errors. Thus, load into their own appdomain
AppDomain tempDomain = AppDomain.CreateDomain(Guid.NewGuid().ToString());
PEWorkerClass remoteWorker =
(PEWorkerClass)tempDomain.CreateInstanceAndUnwrap(
typeof(PEWorkerClass).Assembly.FullName,
typeof(PEWorkerClass).FullName);
(string pkName, string imName) = remoteWorker.TryReflectionOnlyLoadFrom_GetManagedType(filename);
AppDomain.Unload(tempDomain);
return (pkName, imName);
}
At this point, I do the following:
public (string pkName, string imName) TryReflectionOnlyLoadFrom_GetManagedType(string fileName)
{
string pkName;
string imName;
try
{
Assembly assembly = Assembly.ReflectionOnlyLoadFrom(assemblyFile: fileName);
assembly.ManifestModule.GetPEKind(
peKind: out PortableExecutableKinds peKind,
machine: out ImageFileMachine imageFileMachine);
// Any CPU builds are reported as 32bit.
// 32bit builds will have more value for PortableExecutableKinds
if (peKind == PortableExecutableKinds.ILOnly && imageFileMachine == ImageFileMachine.I386)
{
pkName = "AnyCPU";
imName = "";
}
else
{
PortableExecutableKindsNames.TryGetValue(
key: peKind,
value: out pkName);
if (string.IsNullOrEmpty(value: pkName))
{
pkName = "*** ERROR ***";
}
ImageFileMachineNames.TryGetValue(
key: imageFileMachine,
value: out imName);
if (string.IsNullOrEmpty(value: pkName))
{
imName = "*** ERROR ***";
}
}
return (pkName, imName);
}
catch (Exception ex)
{
return (ExceptionHelper(ex), "");
}
}
Running this against my Widows\Assembly directory gives me zero errors with over 3600 files processed.
note: I use a dictionary to load the values being returned.
I hope it helps. YMMV

Related

Getting Windows OS version programmatically

I am trying to fetch Windows version with C# on my Windows 10 machine.
I always get those values (with C#\C++):
Major: 6
Minor: 2
Which is Windows 8 OS, accordingly to MSDN
C# code:
var major = OperatingSystem.Version.Major
var minor = OperatingSystem.Version.Minor
C++ code
void print_os_info()
{
//http://stackoverflow.com/questions/1963992/check-windows-version
OSVERSIONINFOW info;
ZeroMemory(&info, sizeof(OSVERSIONINFOW));
info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW);
LPOSVERSIONINFOW lp_info = &info;
GetVersionEx(lp_info);
printf("Windows version: %u.%u\n", info.dwMajorVersion, info.dwMinorVersion);
}
Windows 10 suppose to be with those:
Major: 10
Minor: 0*
(When I am taking a dump file from running process I can see that the OS version of that file is set to 10.0)
built by: 10.0.10586.0 (th2_release.151029-1700)
What am I missing here?
In my scenario I needed my application to capture computer info for possible bug-reports and statistics.
I did not find the solutions where an application manifest had to be added satisfactory. Most of the suggestions I found while googling this suggested just that, unfortunately.
Thing is, when using a manifest, each OS version has to be added manually to it in order for that particular OS version to be able to report itself at runtime.
In other words, this becomes a race condition: A user of my app may very well be using a version of my app that pre-dates the OS in use. I would have to upgrade the app immediately when a new OS version was launched by Microsoft. I would also have to force the users to upgrade the app at the same time as they updated the OS.
In other words, not very feasible.
After browsing through the options I found some references (surprisingly few compared to the app manifest) that instead suggested using registry lookups.
My (chopped down) ComputerInfo class with only WinMajorVersion, WinMinorVersion and IsServer properties looks like this:
using Microsoft.Win32;
namespace Inspection
{
/// <summary>
/// Static class that adds convenient methods for getting information on the running computers basic hardware and os setup.
/// </summary>
public static class ComputerInfo
{
/// <summary>
/// Returns the Windows major version number for this computer.
/// </summary>
public static uint WinMajorVersion
{
get
{
dynamic major;
// The 'CurrentMajorVersionNumber' string value in the CurrentVersion key is new for Windows 10,
// and will most likely (hopefully) be there for some time before MS decides to change this - again...
if (TryGetRegistryKey(#"SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentMajorVersionNumber", out major))
{
return (uint) major;
}
// When the 'CurrentMajorVersionNumber' value is not present we fallback to reading the previous key used for this: 'CurrentVersion'
dynamic version;
if (!TryGetRegistryKey(#"SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentVersion", out version))
return 0;
var versionParts = ((string) version).Split('.');
if (versionParts.Length != 2) return 0;
uint majorAsUInt;
return uint.TryParse(versionParts[0], out majorAsUInt) ? majorAsUInt : 0;
}
}
/// <summary>
/// Returns the Windows minor version number for this computer.
/// </summary>
public static uint WinMinorVersion
{
get
{
dynamic minor;
// The 'CurrentMinorVersionNumber' string value in the CurrentVersion key is new for Windows 10,
// and will most likely (hopefully) be there for some time before MS decides to change this - again...
if (TryGetRegistryKey(#"SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentMinorVersionNumber",
out minor))
{
return (uint) minor;
}
// When the 'CurrentMinorVersionNumber' value is not present we fallback to reading the previous key used for this: 'CurrentVersion'
dynamic version;
if (!TryGetRegistryKey(#"SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentVersion", out version))
return 0;
var versionParts = ((string) version).Split('.');
if (versionParts.Length != 2) return 0;
uint minorAsUInt;
return uint.TryParse(versionParts[1], out minorAsUInt) ? minorAsUInt : 0;
}
}
/// <summary>
/// Returns whether or not the current computer is a server or not.
/// </summary>
public static uint IsServer
{
get
{
dynamic installationType;
if (TryGetRegistryKey(#"SOFTWARE\Microsoft\Windows NT\CurrentVersion", "InstallationType",
out installationType))
{
return (uint) (installationType.Equals("Client") ? 0 : 1);
}
return 0;
}
}
private static bool TryGetRegistryKey(string path, string key, out dynamic value)
{
value = null;
try
{
using(var rk = Registry.LocalMachine.OpenSubKey(path))
{
if (rk == null) return false;
value = rk.GetValue(key);
return value != null;
}
}
catch
{
return false;
}
}
}
}
You'll need to add an app.manifest to your application:
then uncomment the following line:
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
Registry.GetValue(#"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentBuildNumber", string.Empty).ToString()
same code for all OSes from XP till current 10.16299,
over scenarios not properly work from windows 8
The OSVersion property reports the same version number (6.2.0.0) for both Windows 8 and Windows 8.1 and the same major and minor version number for Windows 10.
https://msdn.microsoft.com/library/system.environment.osversion.aspx
As the accepted answer is only for C#, here is a solution for C++.
It uses the RtlGetVersion in the ntdll.dll that uses the same structure as GetVersionEx (name is different, but the elements are the same) and gives you the correct version.
As this function is normally used for driver development, the function is declared in the DDK and not in the SDK. So I used a dynamic solution to call the function.
Please be aware that the ntdll.dll is loaded and released in every call. So if you need the function more often, keep the library loaded.
The structure pOSversion is pointing to must be initialized like for GetVersionEx.
BOOL GetTrueWindowsVersion(OSVERSIONINFOEX* pOSversion)
{
// Function pointer to driver function
NTSTATUS (WINAPI *pRtlGetVersion)(
PRTL_OSVERSIONINFOW lpVersionInformation) = NULL;
// load the System-DLL
HINSTANCE hNTdllDll = LoadLibrary("ntdll.dll");
// successfully loaded?
if (hNTdllDll != NULL)
{
// get the function pointer to RtlGetVersion
pRtlGetVersion = (NTSTATUS (WINAPI *)(PRTL_OSVERSIONINFOW))
GetProcAddress (hNTdllDll, "RtlGetVersion");
// if successfull then read the function
if (pRtlGetVersion != NULL)
pRtlGetVersion((PRTL_OSVERSIONINFOW)pOSversion);
// free the library
FreeLibrary(hNTdllDll);
} // if (hNTdllDll != NULL)
// if function failed, use fallback to old version
if (pRtlGetVersion == NULL)
GetVersionEx((OSVERSIONINFO*)pOSversion);
// always true ...
return (TRUE);
} // GetTrueWindowsVersion
You can do this in C# the same way C++ answer has it
[SecurityCritical]
[DllImport("ntdll.dll", SetLastError = true)]
internal static extern bool RtlGetVersion(ref OSVERSIONINFOEX versionInfo);
[StructLayout(LayoutKind.Sequential)]
internal struct OSVERSIONINFOEX
{
// The OSVersionInfoSize field must be set to Marshal.SizeOf(typeof(OSVERSIONINFOEX))
internal int OSVersionInfoSize;
internal int MajorVersion;
internal int MinorVersion;
internal int BuildNumber;
internal int PlatformId;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
internal string CSDVersion;
internal ushort ServicePackMajor;
internal ushort ServicePackMinor;
internal short SuiteMask;
internal byte ProductType;
internal byte Reserved;
}
...
var osVersionInfo = new OSVERSIONINFOEX { OSVersionInfoSize = Marshal.SizeOf(typeof(OSVERSIONINFOEX)) };
if (!RtlGetVersion(ref osVersionInfo))
{
// TODO: Error handling, call GetVersionEx, etc.
}
You can read from registry through code and do specific action what you intended.
Say for example:
Registry key is located at HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion and then look for "ProductName".
You can open registry information by giving regedit.exe in run (Windows+r)
var reg =Registry.LocalMachine.OpenSubKey(#"SOFTWARE\Microsoft\WindowsNT\CurrentVersion");
string productName = (string)reg.GetValue("ProductName");
if (productName.StartsWith("Windows 10"))
{
}
else
{
}

ghostscript.net rasterizer 'gsapi_init_with_args' is made: -15 error

When I run this code:
var stream = File.OpenRead(#"C:\tmp\PdfToTest.PDF");
var latestVersion = GhostscriptVersionInfo.GetLastInstalledVersion();
rasterizer = new GhostscriptRasterizer();
rasterizer.Open(stream, latestVersion, false);
I am getting this error
An exception of type 'Ghostscript.NET.GhostscriptAPICallException' occurred in Ghostscript.NET.dll but was not handled in user code
Additional information: An error occured when call to 'gsapi_init_with_args' is made: -15
The error is in this line:
rasterizer.Open(stream, latestVersion, false);
Anyone could point me what it is causing this to happen?
I am running this in local machine. Installed the Ghostscript on Package manager console. Everything seems to be right, but it simple doesn't work.
-15 is a 'rangecheck' error. There should be considerable extra backchannel information which might give some useful details. However since you are not using Ghostscript directly I can't tell you where it might be going.
You should put the PDF file you are using as input somewhere public at least so we can look at it.
Ideally you should reproduce the problem with Ghostscript itself, from the command line, but in any event you must supply the configuration information (ie what settings you have used). The version of Ghostscript (and whether its 32 or 64 bit) would also be useful information.
I'm afraid there's nothing much anyone can do with what you've given us to go on.
This is my working example.
So I call the method ResizePDF(string filePath) and give the file path including extension (eg. C:\tmp\file.pdf) as parameter.
The method returns the memoryStream with the resized file that I can use to do whatever.
There are some work to do around it, however it is working so far.
internal MemoryStream ResizePDF(string filePath)
{
string inputFilePath = String.Format(#"{0}", filePath);
GhostscriptPipedOutput gsPipedOutput = new GhostscriptPipedOutput();
string outputPipeHandle = "%handle%" + int.Parse(gsPipedOutput.ClientHandle).ToString("X2");
MemoryStream memStream = null;
using (GhostscriptProcessor processor = new GhostscriptProcessor())
{
try
{
processor.Process(GetGsArgs(inputFile, outputPipeHandle));
byte[] rawDocumentData = gsPipedOutput.Data;
memStream = new MemoryStream(rawDocumentData);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
gsPipedOutput.Dispose();
gsPipedOutput = null;
}
}
return memStream;
}
private string[] GetGsArgs(string inputFilePath, string outputFilePath)
{
List<string> switches = new List<string>();
switches.Add("-empty");
switches.Add("-dQUIET");
switches.Add("-dSAFER");
switches.Add("-dBATCH");
switches.Add("-dNOPAUSE");
switches.Add("-dNOPROMPT");
switches.Add("-dPDFSETTINGS=/ebook");
switches.Add("-sDEVICE=pdfwrite");
switches.Add("-sPAPERSIZE=a4");
switches.Add("-sOutputFile=" + outputPipeHandle);
switches.Add("-f");
switches.Add(inputFilePath);
return switches.ToArray();
}
Thanks you all.

Finding a pointer of a field in C# using reflection

This question is for educational purposes only.
I am aware of how a native program is working. The compiler takes each primitive and gives it an address, then uses that address in the program. For structures, it simply stacks the address together (with some padding) - but basically, a structure doesn't really "exist".
The native program doesn't tell me which fields and variables it has. It only accesses different addresses - and if I look at the assembly, I can name each address if I want to, but the program won't give me that information. So assuming I am looking for a specific variable, I cannot do it without either examining the executing of the program, or it's assembly.
The .NET environment does tell me which variables it has and which fields it has. Using the Assembly class and Reflection namespace, I can load up a file and see which fields and classes it has.
Then, using a program which searches memory (whether its native or not) I can find the physical location of the field (by using it value, filtering out etc) - like Cheat Engine does. It will give me the actual address of the field in the memory, which is accessed by the assembly made by the JIT.
I know that the MSIL does not contain information about the desired location of a specific field. I am also almost certain that the JIT will never optimize the code by removing any class.
I know that the .NET debugger is an actual class in the program which allows Visual Studio to interact with the internal information of an application. When the debugger is missing, Visual Studio cannot read or write to fields, nor can it inspect the application in any way.
Is there any way, without the use of Cheat Engine or similar tools to find the physical location of a field in a static (or of a specific instance) class in a running .NET process? Will the address be the same after each executing (such as in native program) ? May it differ only on different platforms or machines? How does the JIT decide where to place a field?
If I was unclear, I wish to do it without access to the code of the program, i.e externally by another process (like a debugger, but for programs compiled under release).
Next code inject Injector method in Paint.net and get MainForm fields.
NInject.exe
public static int Injector(string parameter)
{
try
{
var mainForm = Application.OpenForms.OfType<Form>().FirstOrDefault(form => form.GetType().FullName.EndsWith("MainForm"));
var builder = new StringBuilder();
builder.AppendFormat("process: {0}\r\n\r\n", Application.ExecutablePath);
builder.AppendFormat("type: {0}\r\n", mainForm.GetType().FullName);
foreach (var field in mainForm.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
{
builder.AppendFormat("field {0}: {1}\r\n", field.Name, field.GetValue(mainForm));
}
new Form()
{
Controls =
{
new TextBox
{
Text = builder.ToString(),
Multiline = true,
Dock = DockStyle.Fill
}
}
}
.ShowDialog();
}
catch (Exception exc)
{
MessageBox.Show(exc.ToString());
}
return 0;
}
static void Main(string[] args)
{
var process = System.Diagnostics.Process.GetProcessesByName("PaintDotNet").FirstOrDefault();
var processHandle = OpenProcess(ProcessAccessFlags.All, false, process.Id);
var proxyPath = System.IO.Path.Combine(System.Windows.Forms.Application.StartupPath, "NInjector.dll");
var pathBytes = System.Text.Encoding.ASCII.GetBytes(proxyPath);
var remoteBuffer = VirtualAllocEx(processHandle, IntPtr.Zero, (uint)pathBytes.Length, AllocationType.Commit, MemoryProtection.ReadWrite);
WriteProcessMemory(process.Handle, remoteBuffer, pathBytes, (uint)pathBytes.Length, IntPtr.Zero);
var remoteThread = CreateRemoteThread(processHandle, IntPtr.Zero, 0, GetProcAddress(GetModuleHandle("kernel32"), "LoadLibraryA") , remoteBuffer, 0, IntPtr.Zero);
WaitForSingleObject(remoteThread, unchecked((uint)-1));
CloseHandle(remoteThread);
}
NInjector.dll (native)
#include "MSCorEE.h"
#pragma comment (lib, "MSCorEE")
void StartTheDotNetRuntime()
{
MessageBox(0, L"Started", L"proxy", 0);
ICLRRuntimeHost *pClrHost = NULL;
HRESULT hr = CorBindToRuntimeEx(
NULL, L"wks", 0, CLSID_CLRRuntimeHost,
IID_ICLRRuntimeHost, (PVOID*)&pClrHost);
hr = pClrHost->Start();
DWORD dwRet = 0;
hr = pClrHost->ExecuteInDefaultAppDomain(
L"bla-bla\\NInject.exe",
L"NInject.NInject_Program", L"Injector", L"MyParameter", &dwRet);
hr = pClrHost->Stop();
pClrHost->Release();
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
StartTheDotNetRuntime();
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
Output:
process: C:\Program Files\Paint.NET\PaintDotNet.exe
type: PaintDotNet.Dialogs.MainForm
field appWorkspace: PaintDotNet.Controls.AppWorkspace
field defaultButton: System.Windows.Forms.Button, Text:
field floaters: PaintDotNet.Dialogs.FloatingToolForm[]
field floaterOpacityTimer: [System.Windows.Forms.Timer], Interval: 25
field deferredInitializationTimer:
field components: System.ComponentModel.Container
field killAfterInit: False
field singleInstanceManager: PaintDotNet.SystemLayer.SingleInstanceManager
field queuedInstanceMessages: System.Collections.Generic.List`1[System.String]
field processingOpen: False
field scrollPosition: {X=0,Y=0}

ExecuteInDefaultAppDomain returns 8013101B

I am trying to host CLR in my native Win32 C++ application.
CLR loading works okay, but when i try to execute a method in the assembly, then ExecuteInDefaultAppDomain returns 0x8013101B, and bails out.
Here is the code snippet:
// Managed Code
namespace ManagedLibrary
{
public class LibraryBootstrapper
{
static LibraryBootstrapper()
{
MessageBox.Show("Static LibraryBootsrapper");
}
public LibraryBootstrapper()
{
}
public static int Initialize(String str)
{
MessageBox.Show("Hi " + str + ", Library Bootsrapped");
return 0;
}
}
// Native Code
int tmain()
{
// Bind to the runtime.
ICLRRuntimeHost *pClrHost = NULL;
HRESULT hrCorBind = CorBindToRuntimeEx(
NULL, // Load the latest CLR version available
L"wks", // Workstation GC ("wks" or "svr" overrides)
0, // No flags needed
CLSID_CLRRuntimeHost,
IID_ICLRRuntimeHost,
(PVOID*)&pClrHost);
// Now, start the CLR.
HRESULT hrStart = pClrHost->Start();
DWORD result = 0;
// Load an assembly and execute a method in it.
HRESULT hrExecute = pClrHost->ExecuteInDefaultAppDomain(L"C:\\KIRAN\\Workspaces\\VS 2010\\HostCLR\\ManagedLibrary\\bin\\Debug\\ManagedLibrary.dll", L"ManagedLibrary.LibraryBootstrapper", L"Initialize", L"Kiran", &result);
//HRESULT hrStop = pClrHost->Stop();
return;
}
I figured it out!
The problem was that the versions of .NET frame that was being referenced by native and managed projects were different. Syncing that up worked.
And, btw, the error code 0x8013101B, corresponds to COR_E_NEWER_RUNTIME (see corerror.h), which helped me figure out the problem.
The error codes are explained here: http://blogs.msdn.com/b/yizhang/archive/2010/12/17/interpreting-hresults-returned-from-net-clr-0x8013xxxx.aspx

RegLoadAppKey working fine on 32-bit OS, failing on 64-bit OS, even if both processes are 32-bit

I'm using .NET 4 and the new RegistryKey.FromHandle call so I can take the hKey I get from opening a software registry file with RegLoadAppKey and operate on it with the existing managed API.
I thought at first it was just a matter of a busted DllImport and my call had an invalid type in the params or a missing MarshalAs or whatever, but looking at other registry functions and their DllImport declarations (for instance, on pinvoke.net), I don't see what else to try (I've had hKey returned as both int and IntPtr, both worked on 32-bit OS and fail on 64-bit OS)
I've got it down to as simple a repro case as I can - it just tries to create a 'random' subkey then write a value to it. It works fine on my Win7 x86 box and fails on Win7 x64 and 2008 R2 x64, even when it's still a 32-bit process, even run from a 32-bit cmd prompt. EDIT: It also fails in the same way if it's a 64-bit process.
EDIT: it works fine if the file passed in is empty - the problem case is for the existing software registry hive. I extracted 'bare' software registry hive files from 2008 r2 (x64) and WHS v1 (x86) iso's and both have the same problem.
on Win7 x86:
INFO: Running as Admin in 32-bit process on 32-bit OS
Was able to create Microsoft\Windows\CurrentVersion\RunOnceEx\a95b1bbf-7a04-4707-bcca-6aee6afbfab7 and write a value under it
on Win7 x64, as 32-bit:
INFO: Running as Admin in 32-bit process on 64-bit OS
Unhandled Exception: System.UnauthorizedAccessException: Access to the registry key '\Microsoft\Windows\CurrentVersion\RunOnceEx\ce6d5ff6-c3af-47f7-b3dc-c5a1b9a3cd22' is denied.
at Microsoft.Win32.RegistryKey.Win32Error(Int32 errorCode, String str)
at Microsoft.Win32.RegistryKey.CreateSubKeyInternal(String subkey, RegistryKeyPermissionCheck permissionCheck, Object registrySecurityObj, RegistryOptions registryOptions)
at Microsoft.Win32.RegistryKey.CreateSubKey(String subkey)
at LoadAppKeyAndModify.Program.Main(String[] args)
on Win7 x64, as 64-bit:
INFO: Running as Admin in 64-bit process on 64-bit OS
Unhandled Exception: System.UnauthorizedAccessException: Access to the registry key '\Microsoft\Windows\CurrentVersion\RunOnceEx\43bc857d-7d07-499c-8070-574d6732c130' is denied.
at Microsoft.Win32.RegistryKey.Win32Error(Int32 errorCode, String str)
at Microsoft.Win32.RegistryKey.CreateSubKeyInternal(String subkey, RegistryKeyPermissionCheck permissionCheck, Object registrySecurityObj, RegistryOptions registryOptions)
at Microsoft.Win32.RegistryKey.CreateSubKey(String subkey, RegistryKeyPermissionCheck permissionCheck)
at LoadAppKeyAndModify.Program.Main(String[] args)
source:
class Program
{
static void Main(string[] args)
{
Console.WriteLine("INFO: Running as {0} in {1}-bit process on {2}-bit OS",
new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator) ? "Admin" : "Normal User",
Environment.Is64BitProcess ? 64 : 32,
Environment.Is64BitOperatingSystem ? 64 : 32);
if (args.Length != 1)
{
throw new ApplicationException("Need 1 argument - path to the software hive file on disk");
}
string softwareHiveFile = Path.GetFullPath(args[0]);
if (File.Exists(softwareHiveFile) == false)
{
throw new ApplicationException("Specified file does not exist: " + softwareHiveFile);
}
// pick a random subkey so it doesn't already exist
var existingKeyPath = #"Microsoft\Windows\CurrentVersion";
var keyPathToCreate = #"RunOnceEx\" + Guid.NewGuid();
var completeKeyPath = Path.Combine(existingKeyPath, keyPathToCreate);
var hKey = RegistryNativeMethods.RegLoadAppKey(softwareHiveFile);
using (var safeRegistryHandle = new SafeRegistryHandle(new IntPtr(hKey), true))
using (var appKey = RegistryKey.FromHandle(safeRegistryHandle))
using (var currentVersionKey = appKey.OpenSubKey(existingKeyPath, true))
{
if (currentVersionKey == null)
{
throw new ApplicationException("Specified file is not a well-formed software registry hive: " + softwareHiveFile);
}
using (var randomSubKey = currentVersionKey.CreateSubKey(keyPathToCreate))
{
randomSubKey.SetValue("foo", "bar");
Console.WriteLine("Was able to create {0} and write a value under it", completeKeyPath);
}
}
}
}
internal static class RegistryNativeMethods
{
[Flags]
public enum RegSAM
{
AllAccess = 0x000f003f
}
private const int REG_PROCESS_APPKEY = 0x00000001;
// approximated from pinvoke.net's RegLoadKey and RegOpenKey
// NOTE: changed return from long to int so we could do Win32Exception on it
[DllImport("advapi32.dll", SetLastError = true)]
private static extern int RegLoadAppKey(String hiveFile, out int hKey, RegSAM samDesired, int options, int reserved);
public static int RegLoadAppKey(String hiveFile)
{
int hKey;
int rc = RegLoadAppKey(hiveFile, out hKey, RegSAM.AllAccess, REG_PROCESS_APPKEY, 0);
if (rc != 0)
{
throw new Win32Exception(rc, "Failed during RegLoadAppKey of file " + hiveFile);
}
return hKey;
}
}
Ended up with opening a support case with Microsoft Support - the problem is specific to 1) the hives that ship on the install media for recent versions of Windows and 2) RegLoadAppKey as an API. Switching over to RegLoadKey/RegUnLoadKey instead worked fine for the exact same files (in the same process, even), and since the bug in RegLoadAppKey is unlikely to get fixed (let alone soon) to deal with those specific files, I just switched to RegLoadKey/RegUnLoadKey instead.
Following link should be helpful in this scenario:
http://msdn.microsoft.com/en-us/library/ms973190.aspx#64mig_topic5

Categories

Resources