【问题标题】:Pass function pointer into a DLL from PowerShell将函数指针从 PowerShell 传递到 DLL
【发布时间】:2021-11-27 05:39:45
【问题描述】:

问题

我有一个导出以下函数的 DLL。

extern void Finalize(void (*WriteEntry)(const char* entry));

我正在使用 P-Invoke 从 PowerShell 调用 DLL。

在 PowerShell 脚本中,我定义了一个函数 WriteEntry,每当 Finalize 调用 WriteEntry 函数指针时,我都需要调用它。


到目前为止我的方法

似乎正确的方法是围绕WriteEntry PowerShell 函数创建一个C 风格的包装器,其中包装器作为函数指针传递给Finalize

经过一番调查,我发现了如何创建一个可以与非托管代码互操作的委托(参见How to pass function pointer from C# to a C++ Dll?)。我创建了以下CallbackDelegate 类型。我能够将它从 C# 代码传递到 DLL。现在,我需要一种 PowerShell 应用程序能够构造委托并将其传递给 DLL 的方法。

$delegateTypeDefinition = @'
 [System.Runtime.InteropServices.UnmanagedFunctionPointer(System.Runtime.InteropServices.CallingConvention.Cdecl)]
 public delegate void CallbackDelegate([System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPTStr)] string entry);
'@

我被困在哪里

我不确定如何将 CallbackDelegate 委托包装在 WriteEntry PowerShell 函数周围。

所以,我认为我需要回答的问题是,我们如何从 PowerShell 函数构造委托。如果结果证明是 XY 问题,我希望我已经提供了足够的细节来指出正确的方向。

【问题讨论】:

    标签: powershell dll delegates interopservices


    【解决方案1】:

    不幸的是,我不相信你可以单独使用 PowerShell 来完成你所要求的事情,至少不能跳过一系列的障碍来创建一个继承自 MulticastDelegate 的类,然后尝试注册它。 PowerShell 没有与 delegate 关键字等效的用于轻松定义的关键字。

    我们可以改为定义 C# 源来执行此操作,并最终从 PowerShell 本身执行调用。我们已经需要来自 PowerShell 的 P/Invoke,因此我们将保留您以 C# 形式提供的 delegate 定义。


    delegate 在 C# 中与 C++ 中的回调在功能上等效(没有双关语)。 Method 可以用作delegateAction 可以用作delegateFunc 可以用作delegatedelegate 的核心是某种形式的可执行代码块。那么这如何转化为 PowerShell?

    PowerShell 代码将使用ScriptBlock {} 来定义可执行代码块。它们经常被使用,从函数定义到类的方法定义,再到循环构造等等。 ScriptBlock 甚至可以使用调用运算符& 单独使用和执行。 ScriptBlock 只是 未命名函数 的粗俗名称。由于 ScriptBlock 是一个函数,我们可以将其用作委托值。

    这里的关键是ScriptBlock 需要转换为您的delegate 类型。在我们做这件事的时候,让我们用 using 语句修复那一大口合格的类型名?

    注意:如果您的 C# 代码是在源文件中定义的,您可以改为使用
    Get-Content -Raw 将其读入。如果它已经编译成 DLL,您可以跳过该部分并使用 Add-Type 提供 DLL 的路径。

    $delegateTypeDefinition = @"
    using System.Runtime.InteropServices;
    
    # Here is the callback delegate you provided
    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void CallbackDelegate([MarshalAs(UnmanagedType.LPTStr)] string entry);
    
    public static class DelegateHelper
    {
      # Use your actual DLL
      [DllImport("DllName.dll", CharSet=CharSet.Ansi, CallingConvention=CallingConvention.Cdecl)]
      public static extern void Finalize(CallbackDelegate del);
    }
    "@
    
    # Compile your C# code
    Add-Type $delegateTypeDefinition
    
    # Define your delegate function with the correct parameters
    [CallbackDelegate]$callbackDelegate = {
      Param(
        [string]$entry
      )
    
      # Your callback code here
    }
    

    通常然后您会调用 $callbackDelegate.Invoke("some string") 来运行您的
    C# 定义的委托函数。但既然您想将其传递给 Finalize,我们将改为这样做。

    注意:这就是我们将DllImport 放在类中的原因。它不必定义为静态类或成员,但导入的 P/Invoke 函数必须像方法一样在 a 类中定义。

    # Call Finalize
    [DelegateHelper]::Finalize($callbackDelegate)
    

    理论上,这应该适用于传入回调。


    也就是说,delegate 类型,尤其是在访问具有 delegate 类型的成员时,在 PowerShell 中可能表现得有些古怪。此外,我没有或不知道本机 DLL 可以用自己的方式进行测试。我只能保证$callbackDelegate.Invoke("some value") 将在设置将ScriptBlock 分配给delegate 类型并调用它时起作用。

    如果您遇到更多问题,或者我在上面在您的用例中提供的内容存在“陷阱”,您说您可以在 C# 中使用它。您可能需要考虑在 C# 中完全 实现此部分,同时在帮助程序类上创建一个公共帮助程序函数以获取 entry 字符串并在 C# 代码中运行 Finalize。使用Add-Type 从您的脚本编译源代码或加载预编译的 DLL。从那里您可以在 PowerShell 中运行您的辅助函数来执行 Finalize 函数。这有点逃避,但delegate 类型是 PowerShell 有时难以使用的领域。

    【讨论】:

      猜你喜欢
      • 2018-12-20
      • 1970-01-01
      • 2017-08-30
      • 2020-08-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多