.NET Runtime Embedding
If you are developing .NET applications, you know they require the .NET Runtime to run.
If the runtime is not installed, the program fails to start, showing an error like:
“mscoree.dll could not be found.”
Let's see how to make a .NET application run even on systems without the .NET Runtime installed, using BoxedApp SDK.
1. The Goal
We have a simple .NET app — WindowsApplication1.exe — that displays a form with a progress bar and a few links.
Our goal is to make it launch on a clean system without .NET Framework installed.
BoxedApp SDK provides a virtual file system and virtual registry.
If we create virtual equivalents of the necessary runtime files and registry keys, our app will run successfully.
2. Virtual Registry Setup
The key registry hive involved is:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFrameworkThis key contains InstallRoot, which points to the directory where .NET runtimes are stored (e.g., C:\Windows\Microsoft.NET\Framework).
Typical subfolders include:
v1.0.3705
v1.1.4322
v2.0.50727
v3.0
v3.5In this example, we'll use v2.0.50727.
We'll emulate that folder structure under C:\DotNet using virtual files and keys.
// Initialize BoxedAppSDK
BoxedAppSDK_Init();
tstring strDotNetRoot = _T("C:\\DotNet");
DWORD dwDisposition;
HKEY hKey__DotNet;
LONG lRes = BoxedAppSDK_CreateVirtualRegKey(
HKEY_LOCAL_MACHINE,
_T("SOFTWARE\\Microsoft\\.NETFramework"),
0,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL,
&hKey__DotNet,
&dwDisposition);
LPCTSTR szValue = _T("C:\\DotNet\\");
RegSetValueEx(hKey__DotNet, _T("InstallRoot"), 0, REG_SZ, (CONST BYTE*)szValue, (lstrlen(szValue) + 1) * sizeof(TCHAR));3. Preparing the Runtime Files
We need paths to Windows directories:
TCHAR szWinDir[MAX_PATH] = { 0 };
GetWindowsDirectory(szWinDir, MAX_PATH);
tstring strWinDir = szWinDir;
TCHAR szSystemDir[MAX_PATH] = { 0 };
GetSystemDirectory(szSystemDir, MAX_PATH);
tstring strSystemDir = szSystemDir;We embed required runtime DLLs and assemblies as binary resources, for example:
WindowsApplication1.exe BIN "WindowsApplication1.exe"
AppLauncher.dll BIN "AppLauncher.dll"
mscoree.dll BIN "system32\mscoree.dll"
mscorjit.dll BIN "v2.0.50727\mscorjit.dll"
mscorwks.dll BIN "v2.0.50727\mscorwks.dll"
System.Windows.Forms.dll BIN "assembly\GAC_MSIL\System.Windows.Forms\2.0.0.0__b77a5c561934e089\System.Windows.Forms.dll"
mscorlib.dll BIN "assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll"
System.dll BIN "v2.0.50727\System.dll"
System.Drawing.dll BIN "v2.0.50727\System.Drawing.dll"
GdiPlus.dll BIN "system32\GdiPlus.dll"...and so on.
4. Helper Functions
To load and use embedded resources:
void LoadResourceHelper(LPCTSTR lpszName, LPCTSTR lpszType, LPVOID& lpData, DWORD& dwSize)
{
HMODULE hModule = GetModuleHandle(NULL);
HRSRC hResInfo = FindResource(hModule, lpszName, lpszType);
HGLOBAL hResData = LoadResource(hModule, hResInfo);
lpData = LockResource(hResData);
dwSize = SizeofResource(hModule, hResInfo);
}
void CreateVirtualFileFromResource(LPCTSTR szVirtualPath, LPCTSTR szResName, LPCTSTR szResType)
{
DWORD temp;
LPVOID pData;
DWORD dwSize;
LoadResourceHelper(szResName, szResType, pData, dwSize);
HANDLE hFile = BoxedAppSDK_CreateVirtualFile(szVirtualPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, 0, NULL);
WriteFile(hFile, pData, dwSize, &temp, NULL);
CloseHandle(hFile);
}Now you can create the virtual files required for runtime execution:
CreateVirtualFileFromResource(_T("C:\\DotNet\\v2.0.50727\\mscorjit.dll"), _T("mscorjit.dll"), _T("BIN"));
CreateVirtualFileFromResource(_T("C:\\DotNet\\v2.0.50727\\mscorwks.dll"), _T("mscorwks.dll"), _T("BIN"));
CreateVirtualFileFromResource(_T("C:\\AppLauncher.dll"), _T("AppLauncher.dll"), _T("BIN"));
CreateVirtualFileFromResource(_T("C:\\WindowsApplication1.exe"), _T("WindowsApplication1.exe"), _T("BIN"));5. Launching the Application via mscoree.dll
mscoree.dll (also embedded) exports CorBindToRuntimeEx, which returns an ICLRRuntimeHost.
That interface allows us to execute managed code directly.
HMODULE hMSCoree = LoadLibrary((strSystemDir + _T("\\mscoree.dll")).c_str());
typedef HRESULT (__stdcall *P_CorBindToRuntimeEx)(LPCWSTR, LPCWSTR, DWORD, REFCLSID, REFIID, LPVOID FAR*);
P_CorBindToRuntimeEx pCorBindToRuntimeEx =
(P_CorBindToRuntimeEx)GetProcAddress(hMSCoree, "CorBindToRuntimeEx");
ICLRRuntimeHost* pCLRRuntimeHost = NULL;
pCorBindToRuntimeEx(L"v2.0.50727", NULL, 0, CLSID_CLRRuntimeHost, IID_ICLRRuntimeHost, (void**)&pCLRRuntimeHost);Only methods with this signature can be called using ExecuteInDefaultAppDomain:
int SomeMethod(string argument);Our launcher assembly bridges this gap:
using System;
using System.Reflection;
namespace AppLauncher
{
public class Launcher
{
public static int Launch(string path)
{
Assembly assembly = Assembly.LoadFile(path);
assembly.EntryPoint.Invoke(null, null);
return 0;
}
}
}Finally, execute the .NET app:
pCLRRuntimeHost->Start();
DWORD nRetValue;
pCLRRuntimeHost->ExecuteInDefaultAppDomain(
L"C:\\AppLauncher.dll",
L"AppLauncher.Launcher",
L"Launch",
L"C:\\WindowsApplication1.exe",
&nRetValue);
pCLRRuntimeHost->Stop();
pCLRRuntimeHost->Release();
FreeLibrary(hMSCoree);Summary
By using BoxedApp SDK, you can embed and emulate the .NET Runtime completely in memory.
Your application will launch even on systems without .NET installed, making it fully portable and self-contained.
