Unfortunately I don't believe you can do what you're asking with PowerShell alone, at least not without jumping through a series of hoops to create a class inheriting from MulticastDelegate and then attempting to register it. PowerShell doesn't have an equivalent of the delegate keyword for defining one easily.
We can instead define the C# source for doing so, and ultimately perform the invocation from PowerShell itself. We already need to P/Invoke from PowerShell, so we'll just keep your delegate definition provided in C# form.
A delegate is functionally equivalent (no pun intended) in C# to a callback in C++. A Method can be used as a delegate. An Action can be used as a delegate. A Func can be used as a delegate. At its core, a delegate is an executable block of code, in some form. So how does this translate to PowerShell?
PowerShell code will use a ScriptBlock {} in order to define executable blocks of code. These are used quite often, from function definitions, to method definitions on a class, to looping constructs, and more. A ScriptBlock can even be used and executed on its own using the call operator &. A ScriptBlock is just a schmancy name for an unnamed function. Since a ScriptBlock is a function, we can use that as the delegate value.
The kicker here is that ScriptBlock needs to be converted to your delegate type. And while we're at it, let's fix that mouthful of qualified type-names with a using statement
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
}
Normally you would then call $callbackDelegate.Invoke("some string") to run your
C#-defined delegate function. But since you want to pass it to Finalize, we'll do that instead.
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)
In theory, this should work for passing in the callback.
That said, delegate types, in particular when accessing members which have a delegate type, can behave somewhat funky in PowerShell. In addition, I don't have or know offhand of a native DLL to test this with myself. I can only guarantee $callbackDelegate.Invoke("some value") will work as far as setting assigning a ScriptBlock to a delegate type and invoking it.
If you run into more issues, or there's a "gotcha" with what I provided above in your use case, you said you got this working in C#. You may want to consider implementing this piece entirely in C# while additionally creating a public helper function on your helper class to take the entry string and run Finalize within the C# code. Use Add-Type to either compile the source from your script or load a pre-compiled DLL. From there you can run your helper function within PowerShell to execute the Finalize function. It's a bit of a cop-out, but delegate types are an area where PowerShell can be difficult to work with at times.