Skip to content

Commit 48601d6

Browse files
authored
Implement loader for .NET Framework
1 parent 09f258d commit 48601d6

File tree

7 files changed

+164
-8
lines changed

7 files changed

+164
-8
lines changed

.gitignore

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
# .NET Core build folders
2-
example/bin
3-
example/obj
4-
example/out
2+
bin/
3+
out/
4+
obj/
5+
.vs/
56

67
### Python ###
78
# Byte-compiled / optimized / DLL files

clr_loader/ffi/__init__.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@
33

44
import cffi
55

6-
from . import coreclr, hostfxr, mono
6+
from . import coreclr, hostfxr, mono, framework
77

8-
__all__ = ["ffi", "load_coreclr", "load_hostfxr", "load_mono"]
8+
__all__ = ["ffi", "load_coreclr", "load_hostfxr", "load_mono", "load_framework"]
99

1010
ffi = cffi.FFI()
1111

12-
for cdef in coreclr.cdef + hostfxr.cdef + mono.cdef:
12+
for cdef in coreclr.cdef + hostfxr.cdef + mono.cdef + framework.cdef:
1313
ffi.cdef(cdef)
1414

1515

@@ -40,6 +40,12 @@ def load_mono(path=None, gc=None):
4040
return ffi.dlopen(path)
4141

4242

43+
def load_framework():
44+
path = "netframework_loader/bin/x64/Debug/net472/ClrLoader.dll"
45+
46+
return ffi.dlopen(path)
47+
48+
4349
def _get_dll_name(name):
4450
import sys
4551

clr_loader/ffi/framework.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
cdef = [
2+
"""
3+
typedef void* pyclr_domain;
4+
typedef int (*entry_point)(void* buffer, int size);
5+
6+
void* pyclr_create_appdomain(const char* name, const char* config_file);
7+
entry_point pyclr_get_function(pyclr_domain domain, const char* assembly_path, const char* class_name, const char* function);
8+
void pyclr_close_appdomain(pyclr_domain domain);
9+
"""
10+
]

clr_loader/framework.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
from .ffi import ffi, load_framework
2+
3+
4+
_FW = None
5+
6+
7+
class Framework:
8+
def __init__(self, name=None, config_file=None):
9+
global _FW
10+
if _FW is None:
11+
_FW = load_framework()
12+
13+
self._domain = _FW.pyclr_create_appdomain(
14+
name or ffi.NULL, config_file or ffi.NULL
15+
)
16+
17+
def get_callable(self, assembly_path, typename, function):
18+
func = _FW.pyclr_get_function(
19+
self._domain,
20+
assembly_path.encode("utf8"),
21+
typename.encode("utf8"),
22+
function.encode("utf8"),
23+
)
24+
25+
return func
26+
27+
def __del__(self):
28+
if self._domain and _FW:
29+
_FW.pyclr_close_appdomain(self._domain)

example/example.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
3-
<TargetFrameworks>netcoreapp3.0</TargetFrameworks>
3+
<TargetFrameworks>netcoreapp30;netstandard20</TargetFrameworks>
44
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
55
</PropertyGroup>
66
</Project>

netframework_loader/ClrLoader.cs

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
using System;
2+
using System.Diagnostics;
3+
using System.Globalization;
4+
using System.IO;
5+
using System.Reflection;
6+
using System.Runtime.InteropServices;
7+
using RGiesecke.DllExport;
8+
9+
namespace ClrLoader
10+
{
11+
public static class ClrLoader
12+
{
13+
delegate int EntryPoint(IntPtr buffer, int size);
14+
15+
[DllExport("pyclr_create_appdomain", CallingConvention.Cdecl)]
16+
public static IntPtr CreateAppDomain(
17+
[MarshalAs(UnmanagedType.LPUTF8Str)] string name,
18+
[MarshalAs(UnmanagedType.LPUTF8Str)] string configFile
19+
)
20+
{
21+
Print($"Creating AppDomain {name} with {configFile}");
22+
if (!string.IsNullOrEmpty(name))
23+
{
24+
var setup = new AppDomainSetup
25+
{
26+
ConfigurationFile = configFile
27+
};
28+
var domain = AppDomain.CreateDomain(name, null, setup);
29+
30+
Print($"Located domain {domain}");
31+
32+
var handle = GCHandle.Alloc(domain, GCHandleType.Pinned);
33+
34+
Print($"Created handle {handle}");
35+
36+
return handle.AddrOfPinnedObject();
37+
}
38+
else
39+
{
40+
return IntPtr.Zero;
41+
}
42+
}
43+
44+
[DllExport("pyclr_get_function", CallingConvention.Cdecl)]
45+
public static IntPtr GetFunction(
46+
IntPtr domain,
47+
[MarshalAs(UnmanagedType.LPUTF8Str)] string assemblyPath,
48+
[MarshalAs(UnmanagedType.LPUTF8Str)] string typeName,
49+
[MarshalAs(UnmanagedType.LPUTF8Str)] string function
50+
)
51+
{
52+
try
53+
{
54+
var domainObj = AppDomain.CurrentDomain;
55+
if (domain != IntPtr.Zero)
56+
{
57+
var handle = GCHandle.FromIntPtr(domain);
58+
domainObj = (AppDomain)handle.Target;
59+
}
60+
61+
var assembly = domainObj.Load(AssemblyName.GetAssemblyName(assemblyPath));
62+
var type = assembly.GetType(typeName, throwOnError: true);
63+
Print($"Loaded type {type}");
64+
var deleg = Delegate.CreateDelegate(typeof(EntryPoint), type, function);
65+
66+
return Marshal.GetFunctionPointerForDelegate(deleg);
67+
}
68+
catch (Exception exc)
69+
{
70+
Print($"Exception in {nameof(GetFunction)}: {exc.GetType().Name} {exc.Message}\n{exc.StackTrace}");
71+
return IntPtr.Zero;
72+
}
73+
}
74+
75+
[DllExport("pyclr_close_appdomain", CallingConvention.Cdecl)]
76+
public static void CloseAppDomain(IntPtr domain)
77+
{
78+
if (domain != IntPtr.Zero)
79+
{
80+
var handle = GCHandle.FromIntPtr(domain);
81+
var domainObj = (AppDomain)handle.Target;
82+
AppDomain.Unload(domainObj);
83+
handle.Free();
84+
}
85+
}
86+
87+
static void Print(string s)
88+
{
89+
Console.WriteLine(s);
90+
}
91+
}
92+
93+
}

netframework_loader/ClrLoader.csproj

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<TargetFrameworks>net472</TargetFrameworks>
4+
<Platforms>x64;x86</Platforms>
5+
</PropertyGroup>
6+
7+
<ItemGroup>
8+
<PackageReference Include="UnmanagedExports.Repack.Upgrade" Version="1.2.1" />
9+
</ItemGroup>
10+
11+
<PropertyGroup Condition=" '$(Platform)' == 'x86'">
12+
<PlatformTarget>x86</PlatformTarget>
13+
</PropertyGroup>
14+
<PropertyGroup Condition=" '$(Platform)' == 'x64'">
15+
<PlatformTarget>x64</PlatformTarget>
16+
</PropertyGroup>
17+
</Project>

0 commit comments

Comments
 (0)