【问题标题】:Using new PC name when joining the domain with "JoinDomainOrWorkgroup"通过“JoinDomainOrWorkgroup”加入域时使用新的 PC 名称
【发布时间】:2019-07-12 14:34:39
【问题描述】:

我一直在重写我公司的域加入脚本,我正在使用 C# 中的“JoinDomainOrWorkgroup”方法将计算机加入域:

void Join(string newPCName, string destinationOU)
    {
        // Define constants used in the method.
        int JOIN_DOMAIN = 1;
        int ACCT_CREATE = 2;
        int ACCT_DELETE = 4;
        int WIN9X_UPGRADE = 16;
        int DOMAIN_JOIN_IF_JOINED = 32;
        int JOIN_UNSECURE = 64;
        int MACHINE_PASSWORD_PASSED = 128;
        int DEFERRED_SPN_SET = 256;
        int INSTALL_INVOCATION = 262144;

        string domain = "MyDomain.com";
        string password = passwordBox.Text;
        string username = usernameBox.Text;

        // Here we will set the parameters that we like using the logical OR operator.
        // If you want to create the account if it doesn't exist you should add " | ACCT_CREATE "
        int parameters = JOIN_DOMAIN | ACCT_CREATE;

        // The arguments are passed as an array of string objects in a specific order
        object[] methodArgs = { domain, password, username, destinationOU, parameters };

        // Here we construct the ManagementObject and set Options
        ManagementObject computerSystem = new ManagementObject("Win32_ComputerSystem.Name='" + Environment.MachineName + "'");
        computerSystem.Scope.Options.Authentication = System.Management.AuthenticationLevel.PacketPrivacy;
        computerSystem.Scope.Options.Impersonation = ImpersonationLevel.Impersonate;
        computerSystem.Scope.Options.EnablePrivileges = true;

        // Here we invoke the method JoinDomainOrWorkgroup passing the parameters as the second parameter
        object Oresult = computerSystem.InvokeMethod("JoinDomainOrWorkgroup", methodArgs);

        // The result is returned as an object of type int, so we need to cast.
        int result = (int)Convert.ToInt32(Oresult);

        // If the result is 0 then the computer is joined.
        if (result == 0)
        {
            MessageBox.Show("Joined Successfully!");
            this.Close();
            return;
        }
        else
        {
            // Here are the list of possible errors
            string strErrorDescription = " ";
            switch (result)
            {
                case 5:
                    strErrorDescription = "Access is denied";
                    break;
                case 87:
                    strErrorDescription = "The parameter is incorrect";
                    break;
                case 110:
                    strErrorDescription = "The system cannot open the specified object";
                    break;
                case 1323:
                    strErrorDescription = "Unable to update the password";
                    break;
                case 1326:
                    strErrorDescription = "Logon failure: unknown username or bad password";
                    break;
                case 1355:
                    strErrorDescription = "The specified domain either does not exist or could not be contacted";
                    break;
                case 2224:
                    strErrorDescription = "The account already exists";
                    break;
                case 2691:
                    strErrorDescription = "The machine is already joined to the domain";
                    break;
                case 2692:
                    strErrorDescription = "The machine is not currently joined to a domain";
                    break;
            }
            MessageBox.Show(strErrorDescription);
            return;
        }
    }

效果很好!唯一的问题是我需要它在加入域时使用新名称,而不是当前机器名称,并且无法弄清楚如何在以编程方式更改 PC 名称和运行此代码之间不重新启动的情况下执行此操作。

我们一直在使用以下 PowerShell 命令加入域,这允许我们使用新名称加入域,然后重新启动并在一次重启中设置新名称:

Add-Computer -NewName $ComputerName.ToUpper() -DomainName "MyDomain.com" -Credential $cred -OUPath $Target -ErrorAction Continue

有没有办法在 C# 中实现这一点?我试过换行:

ManagementObject computerSystem = new ManagementObject("Win32_ComputerSystem.Name='" + Environment.MachineName + "'");

到:

ManagementObject computerSystem = new ManagementObject("Win32_ComputerSystem.Name='" + newPCName + "'");

但如果“newPCName”与当前 PC 名称不匹配,它只会引发错误。

是否有人对如何引用“待定”PC 名称或在不引用当前机器名称的情况下加入域有任何想法?

非常感谢!

【问题讨论】:

标签: c#


【解决方案1】:

我终于设法让它工作了;我需要重组我的代码以首先将计算机添加到域中,然后更改计算机的名称。

在此处粘贴我的代码以帮助可能遇到相同问题的其他人:

public static bool JoinAndSetName(string newName, string target, string username, string password)
    {
        // Get WMI object for this machine
        using (ManagementObject computerSystem = new ManagementObject("Win32_ComputerSystem.Name='" + Environment.MachineName + "'"))
        {
            try
            {
                object[] methodArgs = { "MyDomain.com", password, username, target, 3 };
                computerSystem.Scope.Options.Authentication = System.Management.AuthenticationLevel.PacketPrivacy;
                computerSystem.Scope.Options.Impersonation = ImpersonationLevel.Impersonate;
                computerSystem.Scope.Options.EnablePrivileges = true;
                object joinParams = computerSystem.InvokeMethod("JoinDomainOrWorkgroup", methodArgs);                 
            }
            catch (ManagementException e)
            {
                MessageBox.Show("Join to domain didn't work");
                return false;
            }

            // Join to domain worked - now change name
            ManagementBaseObject inputArgs = computerSystem.GetMethodParameters("Rename");
            inputArgs["Name"] = newName;
            inputArgs["Password"] = password;
            inputArgs["UserName"] = username;

            // Set the name
            ManagementBaseObject nameParams = computerSystem.InvokeMethod("Rename", inputArgs, null);

            if ((uint)(nameParams.Properties["ReturnValue"].Value) != 0)
            {
                MessageBox.Show("Name change didn't work");
                return false;
            }

            // All ok
            return true;
        }
    }

一定要感谢This post;我对其进行了修改以包括指定 OU 路径和添加身份验证隐私。

【讨论】:

    【解决方案2】:

    我知道这是一个有点老的问题,但在这里我最终做了什么来重命名计算机并使用新的计算机名加入域:

    1. 在静态类或您想要的地方添加以下代码
    public enum COMPUTER_NAME_FORMAT
    {
            ComputerNameNetBIOS,
            ComputerNameDnsHostname,
            ComputerNameDnsDomain,
            ComputerNameDnsFullyQualified,
            ComputerNamePhysicalNetBIOS,
            ComputerNamePhysicalDnsHostname,
            ComputerNamePhysicalDnsDomain,
            ComputerNamePhysicalDnsFullyQualified,
    }
    [Flags]
    public enum DomainJoinOptions
    {
            NETSETUP_JOIN_DOMAIN = 0x00000001,
            NETSETUP_ACCT_CREATE = 0x00000002,
            NETSETUP_ACCT_DELETE = 0x00000004,
            NETSETUP_WIN9X_UPGRADE = 0x00000010,
            NETSETUP_DOMAIN_JOIN_IF_JOINED = 0x00000020,
            NETSETUP_JOIN_UNSECURE = 0x00000040,
            NETSETUP_MACHINE_PWD_PASSED = 0x00000080,
            NETSETUP_DEFER_SPN_SET = 0x00000100,
            NETSETUP_JOIN_WITH_NEW_NAME = 0x00000400
    }
    
    [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
    public static extern bool SetComputerNameEx(COMPUTER_NAME_FORMAT NameType, string lpBuffer);
    
    [DllImport("netapi32.dll", CharSet = CharSet.Unicode)]
    public static extern int NetJoinDomain(
              string lpServer,
              string lpDomain,
              string lpAccountOU,
              string lpAccount,
              string lpPassword,
              DomainJoinOptions NameType
    );
    

    在上面的代码中重要的是CharSet = CharSet.Unicode for NetJoinDomain 方法。

    1. 第一次调用 SetComputerNameEx 为:
    SetComputerNameEx(COMPUTER_NAME_FORMAT.ComputerNamePhysicalDnsHostname,"Name of the computer");
    
    1. 现在无需重新启动即可将域加入调用为:
    var joinOptions = DomainJoinOptions.NETSETUP_JOIN_DOMAIN | DomainJoinOptions.NETSETUP_DOMAIN_JOIN_IF_JOINED | DomainJoinOptions.NETSETUP_ACCT_CREATE | DomainJoinOptions.NETSETUP_JOIN_WITH_NEW_NAME;
    
    var domain = "mydomain.internal";
    var result = WinApi.NetJoinDomain(
        lpServer: null,
        lpDomain: domain,
        lpAccountOU: "OU of the computer or null",
        lpAccount: $@"{domain}\{AdminUsername}",
        lpPassword: AdminPassword,
        NameType: joinOptions
    );
    
    if (result == 0)
    {
        Log.Information("Domain successfully joined");
        return;
    }
    
    Log.Error(messageTemplate: "Domain join failed with error {@code}", propertyValue: result);
    var strErrorDescription = string.Empty;
    switch (result)
    {
         case 5:
              strErrorDescription = "Access is denied";
              break;
         case 87:
              strErrorDescription = "The parameter is incorrect";
              break;
         case 110:
              strErrorDescription = "The system cannot open the specified object";
              break;
         case 1323:
              strErrorDescription = "Unable to update the password";
              break;
         case 1326:
              strErrorDescription = "Logon failure: unknown username or bad password";
              break;
         case 1355:
              strErrorDescription = "The specified domain either does not exist or could not be contacted";
              break;
         case 2224:
              strErrorDescription = "The account already exists";
              break;
         case 2691:
              strErrorDescription = "The machine is already joined to the domain";
              break;
         case 2692:
              strErrorDescription = "The machine is not currently joined to a domain";
              break;
         default:
              Log.Error($"Unhandled result received: {result}");
              break;
    }
    throw new DomainJoinException(code: result.ToString(), message: strErrorDescription);
    

    在上面的代码中重要的是DomainJoinOptions.NETSETUP_JOIN_WITH_NEW_NAME 作为选项和null 作为lpServer

    1. 最后在电脑上调用reboot

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-02-05
      • 1970-01-01
      • 1970-01-01
      • 2020-05-03
      • 2017-01-29
      • 1970-01-01
      • 2015-04-04
      相关资源
      最近更新 更多