Maui Blazor 中文社区 QQ群:645660665

动态编译一个新的 NativeApi 类

要动态编译一个新的 NativeApi 类,可以按照以下步骤进行:

  1. 创建一个新的 NativeApi 类。
  2. 在 NativeApi 类中定义所需的方法和属性。
  3. 在 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);
        }
    }

}
posted @ 2025-02-12 07:16  AlexChow  阅读(278)  评论(0)    收藏  举报