从 PowerShell 将函数指针传递到 DLL
Pass function pointer into a DLL from PowerShell
问题
我有一个导出了以下函数的 DLL。
extern void Finalize(void (*WriteEntry)(const char* entry));
我正在使用 P-Invoke 从 PowerShell 调用 DLL。
在 PowerShell 脚本中,我定义了一个函数 WriteEntry
,每当 Finalize
调用 WriteEntry
函数指针时我都需要调用它。
到目前为止我的方法
似乎正确的方法是围绕 WriteEntry
PowerShell 函数创建一个 C 风格的包装器,其中包装器作为函数指针传递给 Finalize
。
经过一番调查后,我发现了有关创建可与非托管代码互操作的委托的信息(请参阅 )。我创建了以下 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);
'@
我被困在哪里
我不确定如何围绕 WriteEntry
PowerShell 函数包装 CallbackDelegate 委托。
因此,我认为我需要回答的问题是,我们如何从 PowerShell 函数构造委托。如果这证明是 XY 问题,我希望我提供了足够的细节以指出正确的方向。
不幸的是,我不相信你可以单独使用 PowerShell 完成你所要求的,至少不能不跳过一系列的箍来创建一个 class 从 MulticastDelegate
继承,然后试图注册它。 PowerShell 没有等效的 delegate
关键字来轻松定义。
我们可以为此定义 C# 源代码,并最终从 PowerShell 本身执行调用。我们已经需要从 PowerShell P/Invoke
,所以我们将保留您以 C# 形式提供的 delegate
定义。
A delegate
在 C# 中与 C++ 中的回调在功能上等效(没有双关语意)。 Method
可以用作 delegate
。 Action
可以用作 delegate
。 Func
可以用作 delegate
。 delegate
的核心是某种形式的可执行代码块。那么这如何转化为 PowerShell?
PowerShell 代码将使用 ScriptBlock
{}
来定义可执行代码块。这些经常被使用,从函数定义到 class 上的方法定义,再到循环构造等等。 ScriptBlock
甚至可以使用调用运算符 &
单独使用和执行。 ScriptBlock
只是 未命名函数 的一个 schmancy 名称。由于 ScriptBlock
是一个函数,我们可以将其用作委托值。
这里的关键是 ScriptBlock
需要转换为您的 delegate
类型。当我们这样做的时候,让我们用一条 using 语句
来修复那满口的限定类型名称
Note: If your C# code is defined in a source file, you can instead read it in with
Get-Content -Raw
. If it is already compiled into a DLL, you can skip that part and use Add-Type
providing the path to the 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
,我们就这样做。
Note: This is why we put the DllImport
inside of a class. It does not have to be defined as a static class or member but imported P/Invoke
functions have to be defined in a class like a method.
# Call Finalize
[DelegateHelper]::Finalize($callbackDelegate)
理论上,这应该可以传递回调。
也就是说,delegate
类型,特别是在访问具有 delegate
类型的成员时,在 PowerShell 中可能表现得有些古怪。此外,我没有或不知道有什么本机 DLL 可以自己测试。我只能保证 $callbackDelegate.Invoke("some value")
会在设置将 ScriptBlock
分配给 delegate
类型并调用它时起作用。
如果您 运行 遇到更多问题,或者我上面在您的用例中提供的内容有一个“问题”,您说您在 C# 中可以正常工作。您可能需要考虑在 C# 中 完全 实现此部分,同时在您的助手 class 上另外创建一个 public 助手函数以获取 entry
字符串和运行 Finalize
在 C# 代码中。使用 Add-Type
从脚本编译源代码或加载预编译的 DLL。您可以从那里 运行 PowerShell 中的辅助函数来执行 Finalize
函数。这有点逃避,但 delegate
类型是 PowerShell 有时难以处理的一个领域。
问题
我有一个导出了以下函数的 DLL。
extern void Finalize(void (*WriteEntry)(const char* entry));
我正在使用 P-Invoke 从 PowerShell 调用 DLL。
在 PowerShell 脚本中,我定义了一个函数 WriteEntry
,每当 Finalize
调用 WriteEntry
函数指针时我都需要调用它。
到目前为止我的方法
似乎正确的方法是围绕 WriteEntry
PowerShell 函数创建一个 C 风格的包装器,其中包装器作为函数指针传递给 Finalize
。
经过一番调查后,我发现了有关创建可与非托管代码互操作的委托的信息(请参阅 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);
'@
我被困在哪里
我不确定如何围绕 WriteEntry
PowerShell 函数包装 CallbackDelegate 委托。
因此,我认为我需要回答的问题是,我们如何从 PowerShell 函数构造委托。如果这证明是 XY 问题,我希望我提供了足够的细节以指出正确的方向。
不幸的是,我不相信你可以单独使用 PowerShell 完成你所要求的,至少不能不跳过一系列的箍来创建一个 class 从 MulticastDelegate
继承,然后试图注册它。 PowerShell 没有等效的 delegate
关键字来轻松定义。
我们可以为此定义 C# 源代码,并最终从 PowerShell 本身执行调用。我们已经需要从 PowerShell P/Invoke
,所以我们将保留您以 C# 形式提供的 delegate
定义。
A delegate
在 C# 中与 C++ 中的回调在功能上等效(没有双关语意)。 Method
可以用作 delegate
。 Action
可以用作 delegate
。 Func
可以用作 delegate
。 delegate
的核心是某种形式的可执行代码块。那么这如何转化为 PowerShell?
PowerShell 代码将使用 ScriptBlock
{}
来定义可执行代码块。这些经常被使用,从函数定义到 class 上的方法定义,再到循环构造等等。 ScriptBlock
甚至可以使用调用运算符 &
单独使用和执行。 ScriptBlock
只是 未命名函数 的一个 schmancy 名称。由于 ScriptBlock
是一个函数,我们可以将其用作委托值。
这里的关键是 ScriptBlock
需要转换为您的 delegate
类型。当我们这样做的时候,让我们用一条 using 语句
Note: If your C# code is defined in a source file, you can instead read it in with
Get-Content -Raw
. If it is already compiled into a DLL, you can skip that part and useAdd-Type
providing the path to the 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
,我们就这样做。
Note: This is why we put the
DllImport
inside of a class. It does not have to be defined as a static class or member but importedP/Invoke
functions have to be defined in a class like a method.
# Call Finalize
[DelegateHelper]::Finalize($callbackDelegate)
理论上,这应该可以传递回调。
也就是说,delegate
类型,特别是在访问具有 delegate
类型的成员时,在 PowerShell 中可能表现得有些古怪。此外,我没有或不知道有什么本机 DLL 可以自己测试。我只能保证 $callbackDelegate.Invoke("some value")
会在设置将 ScriptBlock
分配给 delegate
类型并调用它时起作用。
如果您 运行 遇到更多问题,或者我上面在您的用例中提供的内容有一个“问题”,您说您在 C# 中可以正常工作。您可能需要考虑在 C# 中 完全 实现此部分,同时在您的助手 class 上另外创建一个 public 助手函数以获取 entry
字符串和运行 Finalize
在 C# 代码中。使用 Add-Type
从脚本编译源代码或加载预编译的 DLL。您可以从那里 运行 PowerShell 中的辅助函数来执行 Finalize
函数。这有点逃避,但 delegate
类型是 PowerShell 有时难以处理的一个领域。