动态编译一个新的 NativeApi 类
要动态编译一个新的 NativeApi 类,可以按照以下步骤进行:
- 创建一个新的 NativeApi 类。
- 在 NativeApi 类中定义所需的方法和属性。
- 在 MainPage 中实例化并使用新的 NativeApi 类。
使用 Roslyn 编译器来动态编译 C# 源代码并将其加载到内存中
安装 Microsoft.CodeAnalysis.CSharp NuGet 包
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Emit;
using System.Reflection;
/// <summary>
/// 代码来动态编译和加载 C# 源代码
/// </summary>
public class DynamicCompiler
{
/// <summary>
/// 编译并加载代码
/// </summary>
/// <param name="code"></param>
/// <param name="typeName"></param>
/// <returns></returns>
public static object? CompileAndLoad(string code, string typeName)
{
var syntaxTree = CSharpSyntaxTree.ParseText(code);
var references = AppDomain.CurrentDomain.GetAssemblies()
.Where(a => !a.IsDynamic)
.Select(a => MetadataReference.CreateFromFile(a.Location))
.Cast<MetadataReference>();
var compilation = CSharpCompilation.Create("DynamicAssembly")
.WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
.AddReferences(references)
.AddSyntaxTrees(syntaxTree);
using var ms = new MemoryStream();
EmitResult result = compilation.Emit(ms);
if (!result.Success)
{
var failures = result.Diagnostics.Where(diagnostic =>
diagnostic.IsWarningAsError ||
diagnostic.Severity == DiagnosticSeverity.Error);
foreach (var diagnostic in failures)
{
Console.Error.WriteLine($"{diagnostic.Id}: {diagnostic.GetMessage()}");
}
return null;
}
else
{
ms.Seek(0, SeekOrigin.Begin);
var assembly = Assembly.Load(ms.ToArray());
var type = assembly.GetType(typeName);
return Activator.CreateInstance(type!);
}
}
}
using MauiPlus;
using System.Reflection.Emit;
using System.Reflection;
namespace MauiPlusDemo
{
public partial class MainPage : ContentPage
{
private NativeBridge? api;
public MainPage()
{
InitializeComponent();
//附加本机功能处理
WebView? wvBrowser = FindByName("webView") as WebView;
if (wvBrowser != null)
{
LoadHtmlToWebView(wvBrowser);
var nativeApiInstance = CreateNativeApiInstance();
TestCreateNativeApiInstance(nativeApiInstance);
api = new NativeBridge(wvBrowser);
//api.AddTarget("dialogs", new NativeApi());
api.AddTarget("dialogs", nativeApiInstance!);
}
#if MACCATALYST
Microsoft.Maui.Handlers.WebViewHandler.Mapper.AppendToMapping("Inspect", (handler, view) =>
{
if (OperatingSystem.IsMacCatalystVersionAtLeast(16, 6))
handler.PlatformView.Inspectable = true;
});
#endif
}
private async void LoadHtmlToWebView(WebView wvBrowser)
{
// 加载本地 HTML 文件
var htmlSource = new HtmlWebViewSource
{
BaseUrl = FileSystem.AppDataDirectory,
Html = await LoadLocalHtml("demo.html")
};
wvBrowser.Source = htmlSource;
}
private async Task<string> LoadLocalHtml(string fileName)
{
using var stream = await FileSystem.OpenAppPackageFileAsync(fileName);
using var reader = new StreamReader(stream);
var contents = await reader.ReadToEndAsync();
return contents;
}
private async void TestCreateNativeApiInstance(object? nativeApiInstance)
{
if (nativeApiInstance != null)
{
Console.WriteLine("Dynamic compilation and loading succeeded.");
// 打印出所有方法名称,确认方法确实存在
var methods = nativeApiInstance.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public);
foreach (var methodInfo in methods)
{
Console.WriteLine($"Found method: {methodInfo.Name}");
}
// 调用异步方法
var method = nativeApiInstance.GetType().GetMethod("get_config", BindingFlags.Instance | BindingFlags.Public);
if (method != null)
{
var task = (Task<string>)method.Invoke(nativeApiInstance, null);
string result = await task;
Console.WriteLine(result);
}
else
{
Console.WriteLine("Method 'get_config' not found.");
}
}
else
{
Console.WriteLine("Dynamic compilation and loading failed.");
}
}
private object? CreateNativeApiInstance()
{
string code = """
using System;
using System.Threading.Tasks;
public class NativeApi
{
public string set_config()
{
return "set_config ok";
}
public async Task<string> get_config()
{
await Task.Delay(200); // 模拟异步操作
return "get_config 123";
}
public async Task<string> open_file_dialog()
{
await Task.Delay(500); // 模拟异步操作
return "open_file_dialog ok";
}
public string save_file(string content, string filename)
{
return "save_file ok";
}
}
""";
return DynamicCompiler.CompileAndLoad(code, "NativeApi");
}
}
}
以下是Reflection.Emit方式,没那么直观
using MauiPlus;
using System.Reflection.Emit;
using System.Reflection;
namespace MauiPlusDemo
{
public partial class MainPage : ContentPage
{
private NativeBridge? api;
public MainPage()
{
InitializeComponent();
//附加本机功能处理
WebView? wvBrowser = FindByName("webView") as WebView;
if (wvBrowser != null)
{
LoadHtmlToWebView(wvBrowser);
var nativeApiInstance = CreateNativeApiInstance();
api = new NativeBridge(wvBrowser);
//api.AddTarget("dialogs", new NativeApi());
api.AddTarget("dialogs", nativeApiInstance!);
}
#if MACCATALYST
Microsoft.Maui.Handlers.WebViewHandler.Mapper.AppendToMapping("Inspect", (handler, view) =>
{
if (OperatingSystem.IsMacCatalystVersionAtLeast(16, 6))
handler.PlatformView.Inspectable = true;
});
#endif
}
private async void LoadHtmlToWebView(WebView wvBrowser)
{
// 加载本地 HTML 文件
var htmlSource = new HtmlWebViewSource
{
BaseUrl = FileSystem.AppDataDirectory,
Html = await LoadLocalHtml("demo.html")
};
wvBrowser.Source = htmlSource;
}
private async Task<string> LoadLocalHtml(string fileName)
{
using var stream = await FileSystem.OpenAppPackageFileAsync(fileName);
using var reader = new StreamReader(stream);
var contents = await reader.ReadToEndAsync();
return contents;
}
private object? CreateNativeApiInstance()
{
var assemblyName = new AssemblyName("DynamicAssembly");
var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
var moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
var typeBuilder = moduleBuilder.DefineType("NativeApi", TypeAttributes.Public);
// 定义 get_config 异步方法
var get_configMethodBuilder = typeBuilder.DefineMethod("get_config", MethodAttributes.Public, typeof(Task<string>), Type.EmptyTypes);
var get_configIlGenerator = get_configMethodBuilder.GetILGenerator();
get_configIlGenerator.Emit(OpCodes.Ldstr, "Hello from dynamically generated NativeApi!");
get_configIlGenerator.Emit(OpCodes.Ret);
// 定义 GetMessage 方法
var methodBuilder = typeBuilder.DefineMethod("GetMessage", MethodAttributes.Public, typeof(string), Type.EmptyTypes);
var ilGenerator = methodBuilder.GetILGenerator();
ilGenerator.Emit(OpCodes.Ldstr, "Hello from dynamically generated NativeApi!");
ilGenerator.Emit(OpCodes.Ret);
// 定义 Add 方法
var addMethodBuilder = typeBuilder.DefineMethod("Add", MethodAttributes.Public, typeof(int), new[] { typeof(int), typeof(int) });
var addIlGenerator = addMethodBuilder.GetILGenerator();
addIlGenerator.Emit(OpCodes.Ldarg_1);
addIlGenerator.Emit(OpCodes.Ldarg_2);
addIlGenerator.Emit(OpCodes.Add);
addIlGenerator.Emit(OpCodes.Ret);
// 定义 Subtract 方法
var subtractMethodBuilder = typeBuilder.DefineMethod("Subtract", MethodAttributes.Public, typeof(int), new[] { typeof(int), typeof(int) });
var subtractIlGenerator = subtractMethodBuilder.GetILGenerator();
subtractIlGenerator.Emit(OpCodes.Ldarg_1);
subtractIlGenerator.Emit(OpCodes.Ldarg_2);
subtractIlGenerator.Emit(OpCodes.Sub);
subtractIlGenerator.Emit(OpCodes.Ret);
// 定义 Multiply 方法
var multiplyMethodBuilder = typeBuilder.DefineMethod("Multiply", MethodAttributes.Public, typeof(int), new[] { typeof(int), typeof(int) });
var multiplyIlGenerator = multiplyMethodBuilder.GetILGenerator();
multiplyIlGenerator.Emit(OpCodes.Ldarg_1);
multiplyIlGenerator.Emit(OpCodes.Ldarg_2);
multiplyIlGenerator.Emit(OpCodes.Mul);
multiplyIlGenerator.Emit(OpCodes.Ret);
// 定义 Divide 方法
var divideMethodBuilder = typeBuilder.DefineMethod("Divide", MethodAttributes.Public, typeof(int), new[] { typeof(int), typeof(int) });
var divideIlGenerator = divideMethodBuilder.GetILGenerator();
var endLabel = divideIlGenerator.DefineLabel();
var catchLabel = divideIlGenerator.BeginExceptionBlock();
// try block
divideIlGenerator.Emit(OpCodes.Ldarg_1);
divideIlGenerator.Emit(OpCodes.Ldarg_2);
divideIlGenerator.Emit(OpCodes.Div);
divideIlGenerator.Emit(OpCodes.Stloc_0);
divideIlGenerator.Emit(OpCodes.Leave_S, endLabel);
// catch block
divideIlGenerator.BeginCatchBlock(typeof(DivideByZeroException));
divideIlGenerator.Emit(OpCodes.Ldc_I4_0);
divideIlGenerator.Emit(OpCodes.Stloc_0);
divideIlGenerator.Emit(OpCodes.Leave_S, endLabel);
divideIlGenerator.EndExceptionBlock();
divideIlGenerator.MarkLabel(endLabel);
divideIlGenerator.Emit(OpCodes.Ldloc_0);
divideIlGenerator.Emit(OpCodes.Ret);
var nativeApiType = typeBuilder.CreateType();
return Activator.CreateInstance(nativeApiType);
}
}
}
关联项目
FreeSql QQ群:4336577
BA & Blazor QQ群:795206915
Maui Blazor 中文社区 QQ群:645660665
知识共享许可协议
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名AlexChow(包含链接: https://github.com/densen2014 ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系 。
转载声明
本文来自博客园,作者:周创琳 AlexChow,转载请注明原文链接:https://www.cnblogs.com/densen2014/p/18710852
AlexChow
今日头条 | 博客园 | 知乎 | Gitee | GitHub


浙公网安备 33010602011771号