我正在将 this 实现(以桌面用户身份启动进程)从
[DllImport]
移植到 [LibraryImport]
(.NET 8)。 Visual Studio Roslyn 已将方法“AdjustTokenPrivileges”更改为
[LibraryImport("advapi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static partial bool AdjustTokenPrivileges(IntPtr htok, [MarshalAs(UnmanagedType.Bool)] bool disall, ref TOKEN_PRIVILEGES newst, int len, IntPtr prev, IntPtr relen);
我收到此错误:
SYSLIB1051: The type 'MyMethods.TOKEN_PRIVILEGES' is not supported by source-generated P/Invokes. The generated source will not handle marshalling of parameter 'newst'.
下一个方法'CreateProcessWithTokenW'更改为
[LibraryImport("advapi32", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)]
[return: MarshalAs(UnmanagedType.Bool)]
private static partial bool CreateProcessWithTokenW(IntPtr hToken, int dwLogonFlags, string lpApplicationName, string lpCommandLine, int dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, [In] ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);
错误所在:
SYSLIB1051: The type 'MyMethods.STARTUPINFO' is not supported by source-generated P/Invokes. The generated source will not handle marshalling of parameter 'lpStartupInfo'.
我尝试将 Int32 更改为 int,将 UInt32 更改为 uint,并将字符串更改为不安全的 char*,如建议的here。我也尝试实现CaiB的答案,但我无法完成它。
好吧,我不知道p/invoke的新一代酸味是什么疯狂的想法,但这远非简单。它比前一个功能强大得多,可以让您进行微调,但有些明显且简单的事情是它无法单独完成的,而且它通常不是 100% 兼容,必须做一些工作。您可以添加远未完成的文档。
好吧,所以就有了 blittable types 的概念(无论哪段代码访问它,在二进制级别上都是相同的类型)。这根本不是什么新鲜事,但它的 blittable 列表似乎受到了限制。显然,大小恒定的 blittable 类型数组本身不被视为可 blittable...
因此,当类型不可 blittable 时,您必须自己封送类型,如下所述:https://github.com/dotnet/runtime/issues/75376
这是新系统的一个已知缺陷。我们没有 今天的源生成器用于生成编组信息 用户定义的类型。您需要使用手动定义编组器 如果您想使用新的自定义类型编组器模型 使用 LibraryImport 的非 blittable 用户定义类型。
废话已经够多了,这里是 TOKEN_PRIVILEGES 编组器的示例:
[CustomMarshaller(typeof(TOKEN_PRIVILEGES), MarshalMode.ManagedToUnmanagedIn, typeof(TOKEN_PRIVILEGESMarshaller))]
private unsafe static class TOKEN_PRIVILEGESMarshaller
{
public struct Unmanaged
{
public uint PrivilegeCount;
public LUID_AND_ATTRIBUTES* Privileges;
}
public static Unmanaged ConvertToUnmanaged(TOKEN_PRIVILEGES managed)
{
var unmanaged = new Unmanaged
{
PrivilegeCount = managed.PrivilegeCount,
Privileges = ArrayMarshaller<LUID_AND_ATTRIBUTES, LUID_AND_ATTRIBUTES>.AllocateContainerForUnmanagedElements(managed.Privileges, out var count)
};
ArrayMarshaller<LUID_AND_ATTRIBUTES, LUID_AND_ATTRIBUTES>.GetManagedValuesSource(managed.Privileges)
.CopyTo(ArrayMarshaller<LUID_AND_ATTRIBUTES, LUID_AND_ATTRIBUTES>
.GetUnmanagedValuesDestination(unmanaged.Privileges, count));
return unmanaged;
}
public static void Free(Unmanaged unmanaged) => ArrayMarshaller<LUID_AND_ATTRIBUTES, LUID_AND_ATTRIBUTES>.Free(unmanaged.Privileges);
}
[NativeMarshalling(typeof(TOKEN_PRIVILEGESMarshaller))]
private struct TOKEN_PRIVILEGES
{
public uint PrivilegeCount;
//[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] // you can keep that for compat reasons
public LUID_AND_ATTRIBUTES[] Privileges;
}
一些有用的文档:用于源生成的互操作的用户定义类型编组和教程:在源生成的 P/Invoke 中使用自定义编组器
你可以这样称呼它:
[LibraryImport("advapi32", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static partial bool AdjustTokenPrivileges(IntPtr htok, [MarshalAs(UnmanagedType.Bool)] bool disall, TOKEN_PRIVILEGES newst, int len, IntPtr prev, IntPtr relen);
第二个,只要去掉
[In]
,由于某些原因,发电机就卡住了:
[LibraryImport("advapi32", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)]
[return: MarshalAs(UnmanagedType.Bool)]
private static partial bool CreateProcessWithTokenW(IntPtr hToken, int dwLogonFlags, string lpApplicationName, string lpCommandLine, int dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);
我在这里放置了完整的源代码副本:https://gist.github.com/smourier/70cc9208b47534ba475279a4554180ce