動態編譯一個新的 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,轉載請注明原文鏈接:http://www.rzrgm.cn/densen2014/p/18710852
AlexChow
今日頭條 | 博客園 | 知乎 | Gitee | GitHub


浙公網安備 33010602011771號