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
Related
Is there a way to call this dialog from c#?
I traced the apis, but non of the calls seems to call the dialog. Dsuiext.dll sounds very promissing, but there I foud just a LDAP browser.
This Microsoft sample provides the expected result. You pass an ADS path as parameter and it calls the property window.
PropSheetHost.exe "LDAP://CN=user,DC=MyDomain,DC=MyTldDomain"
It is important that it is case sensitive, so "ldap://.." doesn't work. The code is definitely not designed to get called multiple times before terminating, so it is probably the best way to use the exe without changes like that:
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = #"PropSheetHost.exe";
startInfo.Arguments = #"LDAP://CN=user,DC=MyDomain,DC=MyTldDomain";
Process.Start(startInfo);
I wrote a wrapper to call it directly from C# and corrected the error what I found. Since I haven't programmed C for nearly 30 years, I am grateful for any hint if the implementation is incorrect. All changes are explained and marked with //MW: .... This works in my code, but you can open only one windows at a time and need to close it before opening another window.
The entry point:
__declspec(dllexport) HRESULT __stdcall CallPropSheetHost(const char* ldapPath)
{
TCHAR szTemp[MAX_ADSPATH_CHARS];
LPWSTR pwszADsPath = NULL;
HRESULT hr = E_FAIL; // MW: move before "if" and preset
CoInitialize(NULL);
{
//MW: copy the parameter
_tcsncpy_s(szTemp, ARRAYSIZE(szTemp), ldapPath, MAX_ADSPATH_CHARS - 1);
}
DWORD dwChars = lstrlen(szTemp) + 1;
pwszADsPath = new WCHAR[dwChars];
if (pwszADsPath)
{
HINSTANCE hInstance = NULL;
HWND hwndConsole = GetConsoleWindow();
if (hwndConsole)
{
hInstance = (HINSTANCE)(LONG_PTR)GetWindowLongPtr(hwndConsole, GWLP_HINSTANCE);
}
CPropSheetHost* pHost = new CPropSheetHost(hInstance);
LocalToWideChar(pwszADsPath, dwChars, szTemp, dwChars);
// Hold a reference count for the CPropSheetHost object.
pHost->AddRef();
hr = pHost->SetObject(pwszADsPath);
if (FAILED(hr))
{
goto ExitMain;
}
//MW: My implmentation returns E_Fail when the registration fails
hr = pHost->Run();
if (FAILED(hr))
{
pHost->Release();
goto ExitMain;
}
//Release the CPropSheetHost object. Other components may still hold a
//reference to the object, so this cannot just be deleted here. Let
//the object delete itself when all references are released.
pHost->Release();
}
ExitMain:
if (pwszADsPath)
{
delete pwszADsPath;
return hr; //MW: return th HRESULT
}
CoUninitialize();
return hr; //MW: return th HRESULT
}
The original implementation doesn't unregister a class. Therefore it fails when it's used multiple times. These are my changes in PropSheetHost.cpp to fix that.
//MW: new method
void CPropSheetHost::_UnregisterWndClass()
{
UnregisterClass(m_szHiddenWindowClass, m_hInst);
}
//MW: added a HRESULT and calling of "_UnregisterWndClass"
HRESULT CPropSheetHost::Run()
{
if (!m_spADObject.p)
{
return E_FAIL; //MW: added a return value
}
// Create the hidden window.
m_hwndHidden = _CreateHiddenWindow();
if (!m_hwndHidden)
{
return E_FAIL; //MW: added a return value
}
/*
Display the proeprty sheet. This is a modal call and will not return
until the property sheet is dimissed.
*/
_CreatePropertySheet();
// Destroy the hidden window.
DestroyWindow(m_hwndHidden);
//WM: Unregister the class; this call was missing
_UnregisterWndClass();
return ERROR_SUCCESS; //MW: added a return value
}
... and the call from C#:
using System;
using System.Runtime.InteropServices;
using System.Windows;
const int MAX_ADSPATH_CHARS = 2047;
[DllImport("PropSheetHost.dll", EntryPoint = "CallPropSheetHost", CallingConvention = CallingConvention.Cdecl)]
private static extern int CallPropSheetHost(string ldapPath);
///CAUTION:
/// * This call is modal and won't return until the called window is closed
/// * You can open only one window at a time. Trying opening a second window before closing the the first one fails
public static int Win32PropSheetHost(string distinguishedName, string serverOrDomain = null)
{
if (string.IsNullOrEmpty(distinguishedName)) throw new ArgumentNullException("EXC262: the distinguished name must not be null nor empty");
//<----------
/// Caution: "LDAP" must be uppercase!
string ldapPath = string.IsNullOrEmpty(serverOrDomain)
? $"LDAP://{ distinguishedName }"
: $"LDAP://{ serverOrDomain }/{ distinguishedName }";
if (ldapPath.Length > MAX_ADSPATH_CHARS) throw new ArgumentException($"EXC263: the complete lds path must not be longer than { MAX_ADSPATH_CHARS } characters (current path: \"{ ldapPath }\")");
//<----------
try
{
return CallPropSheetHost(ldapPath);
}
catch (DllNotFoundException ex)
{
/// Could't find a dll, mos likely our propsheethost.dll
return ResultWin32.ERROR_DLL_NOT_FOUND;
}
}
For the translation of the Windows Error Codes I use this class.
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
{
}
I had intended to implement the Manzana.dll library in order to detect iPhone connection events and interact with the device. The problem is that it only seems to work if the client machine has the iTunes dlls and resources installed, which I cannot rely on. Therefore I am trying to use a custom implementation of the Manzana source code to point it's references to the necessary iTunes files that I am including with the project.
Although everything looks ok the compiled library throws a NullReferenceException when used from my application. The application load and initializes ok, but when an iPhone is connected the connectedevent throws an exception.
The actual error is:
System.TypeInitializationException: The type initializer for 'istreamwrapper.MobileDevice' threw an exception. ---> System.NullReferenceException: Object reference not set to an instance of an object.
at istreamwrapper.MobileDevice..cctor()
--- End of inner exception stack trace ---
at istreamwrapper.MobileDevice.AMDeviceNotificationSubscribe(DeviceNotificationCallback callback, UInt32 unused1, UInt32 unused2, UInt32 unused3, Void*& am_device_notification_ptr)
at istreamwrapper.iPhone.doConstruction()
I was able to use that to narrow down the problem to to this method from my iPhone class
private unsafe void doConstruction()
{
void* voidPtr;
this.dnc = new DeviceNotificationCallback(this.NotifyCallback);
this.drn1 = new DeviceRestoreNotificationCallback(this.DfuConnectCallback);
this.drn2 = new DeviceRestoreNotificationCallback(this.RecoveryConnectCallback);
this.drn3 = new DeviceRestoreNotificationCallback(this.DfuDisconnectCallback);
this.drn4 = new DeviceRestoreNotificationCallback(this.RecoveryDisconnectCallback);
int num = MobileDevice.AMDeviceNotificationSubscribe(this.dnc, 0, 0, 0, out voidPtr);
if (num != 0)
{
throw new Exception("AMDeviceNotificationSubscribe failed with error " + num);
}
num = MobileDevice.AMRestoreRegisterForDeviceNotifications(this.drn1, this.drn2, this.drn3, this.drn4, 0, null);
if (num != 0)
{
throw new Exception("AMRestoreRegisterForDeviceNotifications failed with error " + num);
}
this.current_directory = "/";
}
}
The issue comes from
num = MobileDevice.AMDeviceNotificationSubscribe(this.dnc, 0, 0, 0, out voidPtr);
which points to this code which is located in my MobileDevice class
[DllImport("iTunesMobileDevice.dll", CallingConvention = CallingConvention.Cdecl)]
public static unsafe extern int AMDeviceNotificationSubscribe(DeviceNotificationCallback callback, uint unused1, uint unused2, uint unused3, out void* am_device_notification_ptr);
That in turn seems to reference this in it's own class
namespace istreamwrapper
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate void DeviceNotificationCallback(ref AMDeviceNotificationCallbackInfo callback_info);
}
which then points to another class with:
namespace istreamwrapper
{
[StructLayout(LayoutKind.Sequential, Pack = 1)]
internal struct AMDeviceNotificationCallbackInfo
{
internal unsafe void* dev_ptr;
public NotificationMessage msg;
public unsafe void* dev
{
get
{
return this.dev_ptr;
}
}
}
}
The vast majority of this code was copied straight from the Manzana.dll, the only thing I changed was where the itunesmobiledevice files are located (which is now a set path, rather detected at run time)
Old code:
namespace Manzana
{
internal class MobileDevice
{
private static readonly FileInfo iTunesMobileDeviceFile = new FileInfo(Registry.GetValue("HKEY_LOCAL_MACHINE\\SOFTWARE\\Apple Inc.\\Apple Mobile Device Support\\Shared", "iTunesMobileDeviceDLL", (object) "iTunesMobileDevice.dll").ToString());
private static readonly DirectoryInfo ApplicationSupportDirectory = new DirectoryInfo(Registry.GetValue("HKEY_LOCAL_MACHINE\\SOFTWARE\\Apple Inc.\\Apple Application Support", "InstallDir", (object) Environment.CurrentDirectory).ToString());
private const string DLLName = "iTunesMobileDevice.dll";
static MobileDevice()
{
string str = MobileDevice.iTunesMobileDeviceFile.DirectoryName;
if (!MobileDevice.iTunesMobileDeviceFile.Exists)
{
str = Environment.GetFolderPath(Environment.SpecialFolder.CommonProgramFiles) + "\\Apple\\Mobile Device Support\\bin";
if (!File.Exists(str + "\\iTunesMobileDevice.dll"))
str = "C:\\Program Files\\Common Files\\Apple\\Mobile Device Support";
}
Environment.SetEnvironmentVariable("Path", string.Join(";", new string[3]
{
Environment.GetEnvironmentVariable("Path"),
str,
MobileDevice.ApplicationSupportDirectory.FullName
}));
}
New code:
namespace istreamwrapper
{
class MobileDevice
{
static MobileDevice()
{
string str = "[XX_MYPATHHERE_XX]\\Apple\\Mobile Device Support";
string AppSuppDirectory = #"[XX_MYPATHHERE_XX]\Apple\Apple Application Support";
Environment.SetEnvironmentVariable("Path", string.Join(";", new string[3] { Environment.GetEnvironmentVariable("Path"), str, AppSuppDirectory }));
}
Is there something I'm missing that is causing that call to return null? I'll admit I don't fully understand everything that is happening in the above code so it's entirely possible it's something simple.
Yes, I believe the answer was really that the path was incorrect and thus could not find the file. I just didn't realize this because the error it was throwing was too generic.
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}
I am using the MySQL Embedded Library and using P/Invoke to call the necessary functions to start the server. We resolved some issues regarding it in this topic, however another issue has presented itself.
The mysql_server_init() function returns 0 if success, 1 if error. Unfortunately, in my code when it returns 1, and I use Marshal.GetLastWin32Error() the error code is 0. I am assuming that it is not picking up on the error being generated by mysql_server_init(), but I am at a loss as to how to find out where the problem is.
Here is the relevant code block...
[DllImportAttribute("libmysqld.dll", SetLastError = true)]
static extern int mysql_server_init(int argc, string[] argv, string[] groups);
static string[] server_options = new string[2];
static string[] server_groups = new string[3];
public static bool Start()
{
server_options[0] = "mysql_test"; // not used?
server_options[1] = "--defaults-file=./my.ini";
server_groups[0] = "client";
server_groups[1] = "server";
server_groups[2] = "\0";
if (mysql_server_init(2, server_options, server_groups) != 0)
{
int lastError = Marshal.GetLastWin32Error();
Console.WriteLine("MySQL Library Init Failed with error code: " + lastError);
return false;
}
Console.WriteLine("MySQL Library Started Successfully!");
return true;
}
The mysql_server_init function does not report errors via the Win32 error reporting mechanism SetLastError() and GetLastError(). So, you can"t use Marshal.GetLastWin32Error() to obtain the last error. The embedded mysql database reports errors via the functions mysql_error() and mysql_errno(). However, those functions seem to only report errors AFTER a successful call to mysql_server_init().
I think the problem of your code lies in the way you terminate your server_groups array.
You should use "null" instead of "\0" to "terminate" your array:
public static bool Start()
{
server_options[0] = "mysql_test"; // not used?
server_options[1] = "--defaults-file=./my.ini";
server_groups[0] = "client";
server_groups[1] = "server";
server_groups[2] = null;
if (mysql_server_init(2, server_options, server_groups) != 0)
{
int lastError = Marshal.GetLastWin32Error();
Console.WriteLine("MySQL Library Init Failed with error code: " + lastError);
return false;
}
}
Errors regarding your configuration should be printed to the console window by the mysql_server_init() function.
Hope, this helps.