5

问题

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

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

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

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


到目前为止我的方法

似乎正确的方法是围绕 PowerShell 函数创建一个 C 样式的包装器,将包装器作为函数指针WriteEntry传递给该包装器。Finalize

在做了一些调查之后,我发现了如何创建一个可以与非托管代码互操作的委托(请参阅如何将函数指针从 C# 传递到 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 委托包装在WriteEntryPowerShell 函数周围。

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

4

1 回答 1

2

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

相反,我们可以为此定义 C# 源代码,并最终从 PowerShell 本身执行调用。我们已经需要P/Invoke从 PowerShell 中进行,因此我们将保留delegate以 C# 形式提供的定义。


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

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

这里的关键是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函数必须像方法一样在类中定义。

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

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


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

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

于 2021-10-15T05:02:50.177 回答