从托管 C# 代码调用非托管 C++ 代码以生成脱机域加入 blob
Posted
技术标签:
【中文标题】从托管 C# 代码调用非托管 C++ 代码以生成脱机域加入 blob【英文标题】:Calling unmanaged c++ code from managed C# code to generate offline domain join blob 【发布时间】:2018-01-09 01:33:25 【问题描述】:我正在编写一个程序来生成Offline Domain Join
blob 并将其保存以备将来使用。可以使用命令提示符完成此操作。下面是一个示例命令,它将生成上述文件并将其保存在 D 盘上:
D:\djoin.exe /REUSE /PROVISION /DOMAIN MyDomain.MyCompany.com /MACHINE "user1-pc" /SAVEFILE blob.txt
更多信息:Offline Domain Join (Djoin.exe) Step-by-Step Guide
现在,我想向我的程序(用 C# 编写)添加一个方法来为我执行此功能。
这里的一个问题是,微软提供的 API 是 C++ API。我尝试使用PInvoke
在托管代码中使用 API。下面是我写的代码。
using System;
using System.Runtime.InteropServices;
namespace TestBlob
public class Program
static void Main(string[] args)
String domain = "MyDomain.MyCompany.com";
String machineName = "user1-pc";
String machineAccoutOU = null;
String dcName = "MyDomain";
uint options = 1;
IntPtr provisionBinData = IntPtr.Zero;
IntPtr provisionBinDataSize = IntPtr.Zero;
string blob = string.Empty;
IntPtr pProvisionTextData = Marshal.StringToHGlobalUni(blob);
uint status = ODJNativeMethods.NetProvisionComputerAccount(domain, machineName, machineAccoutOU, dcName, options, ref provisionBinData, ref provisionBinDataSize, ref pProvisionTextData);
Console.WriteLine(status);
Console.WriteLine(Marshal.PtrToStringUni(pProvisionTextData));
Console.Read();
public static class NativeMethods
[DllImport("Netapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern uint NetProvisionComputerAccount([In] String lpDomain,
[In] String lpMachineName,
[In] String lpMachineAccountOU,
[In] String lpDcName,
[In] uint dwOptions,
[In] [Out] ref IntPtr pProvisionBinData,
[In] [Out] ref IntPtr pdwProvisionBinDataSize,
[In] [Out] ref IntPtr pProvisionTextData);
当我运行应用程序时,它总是返回 87(显示在控制台上),快速搜索后发现是错误消息:参数无效。
我在这里做错了什么?我的 PInvoke 类型与本机语言 API 对应的类型不正确吗?
【问题讨论】:
您的最后三个选项似乎不正确 - 我认为这些应该是ref byte[]
、ref uint
和 ref string
,或者只有 3 个 IntPtr
s..
其实他们都是 IntPtr first,同样的错误信息。编组呢?我做对了吗?它甚至需要吗?
@yaakov 我只需要使用我的 C# 应用程序获取 blob 文件。你知道我如何利用这个原生 DLL 并在 C# 程序中使用吗?在线参考实际上是零示例代码!我一直在尝试和寻找几个小时!
目前您无法判断错误是在您传递给函数的数据中,还是在您的 p/invoke 处理中。下一步是通过编写针对官方 SDK 编译的 C++ 程序来缩小范围,该程序成功。此时,您将拥有一个已知有效的数据模板。如果您的 C# 变体使用相同的数据失败,那么您知道问题在您的 pinvoke 中。
【参考方案1】:
最后 3 个参数被声明为 out,这意味着你不能初始化它们,而是传递正确的指针,以便函数可以为你分配东西。
另外,根据我阅读函数文档的理解,二进制文件和字符串文件是互斥的,所以假设你想取回二进制文件,那么你可以像这样定义 API([in] 是通常是隐含的):
[DllImport("netapi32.dll", CharSet = CharSet.Unicode)]
public static extern int NetProvisionComputerAccount(
string lpDomain,
string lpMachineName,
string lpMachineAccountOU,
string lpDcName,
int dwOptions,
out IntPtr pProvisionBinData,
out int pdwProvisionBinDataSize,
IntPtr pProvisionTextData);
注意该函数不使用SetLastError,所以不要在声明中声明。
这是如何称呼它的:
string domain = "MyDomain.MyCompany.com";
string machineName = "user1-pc";
string machineAccoutOU = null;
string dcName = "MyDomain";
// I suggest you don't use hardcoded values to be nice with readers
const int NETSETUP_PROVISION_DOWNLEVEL_PRIV_SUPPORT = 1;
int status = NetProvisionComputerAccount(
domain,
machineName,
machineAccoutOU,
dcName,
NETSETUP_PROVISION_DOWNLEVEL_PRIV_SUPPORT,
out IntPtr binData, // let the system allocate the binary thing
out int binDataSize, // this will be the size of the binary thing
IntPtr.Zero); // we don't use this one here, pass null
我无法进一步测试(我收到错误 1354,我认为这在我的上下文中是正常的)。
请注意,该文档没有说明释放函数分配的内容(如果它分配了一些东西?有一些罕见的 Windows API 使用他们拥有的静态缓冲区)。我认为您应该在完成所有工作后在 binData 上调用 NetApiBufferFree,但这只是一个猜测。
【讨论】:
非常感谢!这实际上有效:) 并感谢您提到解除分配!我会看看我应该如何处理。【参考方案2】:这可能是您的文本文件的编码 - 如果您将其保存为 ANSI 并将其作为 Unicode 使用,它可能无法正常工作。
使用与您上面的代码相对应的这些语句:
IntPtr pProvisionTextData = Marshal.StringToHGlobalAnsi(blob);
和
[DllImport("user32", CharSet=CharSet::Ansi)]
希望对你有帮助。
【讨论】:
Win32函数定义为LPCWSTR
,即unicode。
是的。我仍然尝试过,但没有通过。感谢您的评论。以上是关于从托管 C# 代码调用非托管 C++ 代码以生成脱机域加入 blob的主要内容,如果未能解决你的问题,请参考以下文章
让非托管 c++ 代码调用调用 c# 代码的托管 c++ 代码