src/programFrames/DllExport.cs

using System;
using System.Collections;
using System.Collections.Generic;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
 
// 原生 DLL 导出层:与 default.cs 的 PSRunnerEntry 组成同一个 partial 类。
// 工具(src/DllExportCompiler.ps1)会把每个 #_DllExport 声明生成一个包装方法,并注入到本文件末尾的
// 导出方法标记处;编译成类库后再由 AsmResolver 给这些方法设置 UnmanagedExportInfo,写出原生导出表。
namespace PSRunnerNS {
    static partial class PSRunnerEntry {
        private static readonly object _dllLock = new object();
        private static bool _dllInited = false;
 
        // 首次导出调用时惰性初始化:运行脚本(在全局作用域,确保其函数定义持久可用),
        // 之后各导出方法就能按名字调用这些函数。多线程调用由 _dllLock 串行化。
        private static void DllInitChecker() {
            lock (_dllLock) {
                if (_dllInited) return;
                PSRunner.BaseInit();
                me = new PSRunner();
                try {
                    me.pwsh.Streams.Error.Clear();
                    me.pwsh.Commands.Clear();
                    // 顶层运行脚本而不是调用 PSEXEMainFunction:函数定义在函数体内是局部的,
                    // 只有顶层(或点源)定义才会留在 runspace 的全局作用域里供导出调用。
                    me.pwsh.AddScript(". ([scriptblock]::Create($PSEXEscript))");
                    System.Collections.ObjectModel.Collection<PSObject> initOutput = me.pwsh.Invoke();
                    foreach (PSObject outputItem in initOutput)
                        System.Console.WriteLine(outputItem == null ? "" : outputItem.ToString());
                    foreach (ErrorRecord errorItem in me.pwsh.Streams.Error)
                        me.ui.WriteErrorRecord(errorItem);
                    me.pwsh.Streams.Error.Clear();
                    if (me.pwsh.InvocationStateInfo.State == PSInvocationState.Failed)
                        throw new InvalidOperationException("PowerShell script init failed: " + me.pwsh.InvocationStateInfo.Reason.Message);
                }
                catch (Exception ex) {
                    ReportDllExportError("<init>", ex);
                }
                _dllInited = true;
            }
        }
 
        // 以 PSEXEDLLCallIngParameters 数组为参数调用脚本里的函数,返回其输出。
        private static object InvokePSFunction(string exportName, object[] args) {
            lock (_dllLock) {
                if (!_dllInited || me == null) throw new InvalidOperationException("PSRunner is not initialized.");
                if (me.ShouldExit) throw new InvalidOperationException("PSRunner is exiting.");
                me.PSRunSpace.SessionStateProxy.SetVariable("PSEXEDLLCallIngParameters", new ArrayList(args));
                me.PSRunSpace.SessionStateProxy.SetVariable("PSEXEDLLExportName", exportName);
                me.pwsh.Streams.Error.Clear();
                me.pwsh.Commands.Clear();
                me.pwsh.AddScript("& $PSEXEDLLExportName @PSEXEDLLCallIngParameters");
                System.Collections.ObjectModel.Collection<PSObject> output = me.pwsh.Invoke();
                foreach (ErrorRecord errorItem in me.pwsh.Streams.Error)
                    me.ui.WriteErrorRecord(errorItem);
                me.pwsh.Streams.Error.Clear();
                if (me.pwsh.InvocationStateInfo.State == PSInvocationState.Failed)
                    throw new InvalidOperationException("PowerShell function '" + exportName + "' failed: " + me.pwsh.InvocationStateInfo.Reason.Message);
                if (output.Count == 1 && output[0] != null) return output[0].BaseObject;
                if (output.Count > 1) {
                    object[] results = new object[output.Count];
                    for (int i = 0; i < output.Count; i++)
                        results[i] = output[i] == null ? null : output[i].BaseObject;
                    return results;
                }
                return null;
            }
        }
 
        // 异常不能穿过 native 边界(会变成进程级崩溃),这里记录到 stderr 后由包装方法返回默认值。
        private static void ReportDllExportError(string exportName, Exception ex) {
            try { System.Console.Error.WriteLine("PS12exe DllExport '" + exportName + "': " + ex.Message); }
            catch { }
        }
        /*__PS12EXE_DLL_EXPORTS__*/
    }
}