从托管 C# 代码调用非托管 C++ 代码以生成离线域加入 blob

Calling unmanaged c++ code from managed C# code to generate offline domain join blob

我正在编写一段生成 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# 编写)中添加一个方法来为我执行此功能。

这里的一个问题是,Microsoft 提供的 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);
    }
}

当我运行应用程序时,它总是returns87(在控制台上显示),快速搜索后发现是一条错误消息:参数无效。

我在这里做错了什么?我的 PInvoke 类型是否不是与母语对应的正确类型 API?

它可能是您的文本文件的编码 - 如果您将其保存为 ANSI 并以 Unicode 格式使用,它可能无法工作。

使用与上述代码相对应的语句:

IntPtr pProvisionTextData = Marshal.StringToHGlobalAnsi(blob);

[DllImport("user32", CharSet=CharSet::Ansi)] 

希望对您有所帮助。

最后 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,但这只是一个猜测。