29 Comments

I tried to find some mechanism to get the current machine's NetBIOS domain name (the machine domain, not user domain), but couldn’t find anything in the usual places (e.g. System.Environment). If you want the fancy-schmancy Active Directory DNS domain then you can use Domain.GetComputerDomain().Name from System.DirectoryServices.ActiveDirectory, or another one that I stumbled across in Reflector was IPGlobalProperties.GetIPGlobalProperties().DomainName that lives in System.Net.NetworkInformation. But a simple way of getting the old-skool NetBIOS/LanManager-style machine domain name proved elusive.

Some googling suggested that WMI would provide the answer but I find WMI a little heavyweight, and not always reliable. The information is also probably in the registry somewhere, although I couldn’t find it after a cursory scan. The proper, supported way it would appear is to use the Network Management API. So my solution entailed P/Invoking to netapi32.dll.

If you’re after the same information I hope you find the code below useful. Once you've incorporated this in your project, just call the GetMachineNetBiosDomain method. It will return the machine's Workgroup name if the machine is not domain-joined.

UPDATE: Now works on 64-bit thanks to update sent by Rp Brongers.

using System;
using System.Runtime.InteropServices;
using System.ComponentModel;

class NetUtil
{
    [DllImport("netapi32.dll", CharSet = CharSet.Auto)]
    static extern int NetWkstaGetInfo(string server,
        int level,
        out IntPtr info);

    [DllImport("netapi32.dll")]
    static extern int NetApiBufferFree(IntPtr pBuf);

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    class WKSTA_INFO_100
    {
        public int wki100_platform_id;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string wki100_computername;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string wki100_langroup;
        public int wki100_ver_major;
        public int wki100_ver_minor;
    }

    public static string GetMachineNetBiosDomain()
    {
        IntPtr pBuffer = IntPtr.Zero;

        WKSTA_INFO_100 info;
        int retval = NetWkstaGetInfo(null, 100, out pBuffer);
        if (retval != 0)
            throw new Win32Exception(retval);

        info = (WKSTA_INFO_100)Marshal.PtrToStructure(pBuffer, typeof(WKSTA_INFO_100));
        string domainName = info.wki100_langroup;
        NetApiBufferFree(pBuffer);
        return domainName;
    }
}

Comments

Comment by Raj

The given method worked for me. Thanks for the help

Raj
Comment by JayThree

Was just looking for this.. thanks broham.

JayThree
Comment by Duncan Smart

OK, I guess there must be something missing from LocalSystem's access token that these API calls specifically check for. I would think that if you ran the service as a specific user it would then work OK. Or maybe one of the other built-in accounts: msdn.microsoft.com/.../ms686005(VS.85).aspx

Comment by nomex

Thanks, I was looking for this!

Comment by oleg_m

>Is this code running on a Domain Controller?
No. Windows Server 2003 Standard Edition, but no domain controller.

>There’s some info about this in the docs
> that might be relevant
I read it, before write to you

I think the reason is localsystem-user: he has no right to work in a network.

oleg_m
Comment by Duncan Smart

Done it, thanks! Just removed (what I thought was) an unnecessary conversion to and from Int32.

Comment by Rp Brongers

Thank you for this example, it works like a charm on 32 bits platforms. However, on Windows 7 64bits I ran into a problem, which I also fixed. Maybe you would be so kind to update the post with the fix to make it work on 64 bits as well (see below).
On a 64bit platform, structures should not be passes to the netapi call directly (or any native dll for that matter). Instead, use an IntPtr and map the result to your structure.
(Note: this was tested on a machine running Windows 7 64bit)

[DllImport("netapi32.dll", CharSet = CharSet.Auto)]
static extern int NetWkstaGetInfo([MarshalAs(UnmanagedType.LPWStr)]string server,
int level,
out IntPtr info);


[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
class WKSTA_INFO_100
{
public int wki100_platform_id;
[MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)]
public string wki100_computername;
[MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)]
public string wki100_langroup;
public int wki100_ver_major;
public int wki100_ver_minor;
}


public static string GetMachineNetBiosDomain()
{
IntPtr buffer = new IntPtr();

WKSTA_INFO_100 info;
int retval = NetWkstaGetInfo(null, 100, out buffer);
if (retval != 0)
throw new Win32Exception(retval);

Int32 pointer = buffer.ToInt32();
info = (WKSTA_INFO_100)Marshal.PtrToStructure(
new IntPtr(pointer), typeof(WKSTA_INFO_100));
string domainName = info.wki100_langroup;
NetApiBufferFree(buffer);
return domainName;
}

Rp Brongers
Comment by Edward Wilde

Hey great post, thanks. Just wanted to let you know that I found a way of looking up the netbios name using DirectoryServices or System.Environment

Thanks Ed.

Comment by Duncan Smart

Edward - that's interesting, although I note it requires a query to Active Directory, so won't work if run on say, a laptop that's not currently connected to the corporate network.

Comment by Bob Vila

this code is orgasmic exactly what i need

Bob Vila
Comment by Geert

I'm currently testing this snippet converted to vb.net
I couldn't understand from the sample and comments:

Is "Int32 pointer = buffer.ToInt32();" needed on 64 bit or not? (which is the correct function)

Thanks for the code.

Geert
Comment by HELLO

Output the FQDN of a specified server NetBIOS name. Then you can query the substring from the FQDN.

IPHostEntry he = Dns.GetHostEntry(serverNetBIOSName);
IPHostEntry he2 = Dns.GetHostEntry(he.AddressList[0]);
Console.WriteLine(he2.HostName);

HELLO
Comment by Geert

"Output the FQDN of a specified server NetBIOS name. Then you can query the substring from the FQDN." -> don't do this, it is not always correct.
The netbios name can be different from the dns name

Geert
Comment by Duncan Smart

@Geert - I incorporated Rp Brongers' changes already - but also got rid of the ToInt32 bit. Should work on x64 - pretty sure I tested it there.

Comment by GL

Thank you for code. I tried all "official" .Net approaches but seems to be Win(32)API will live long life.

GL
Comment by oleg_m

Thank you for code. But this code not working, when running as a service (by LocalSystem). Any ideas?

oleg_m
Comment by Duncan Smart

Odd - is any exception thrown?

Comment by oleg_m

Win32Exception: System.ComponentModel.Win32Exception: Access is denied

oleg_m
Comment by oleg_m

NetWkstaGetInfo Function...
If the function fails, the return value can be one of the following error codes.

ERROR_ACCESS_DENIED The user does not have access to the requested information.


32 int retval = NetWkstaGetInfo(null, 100, out pBuffer);
33 if (retval != 0)
34 throw new Win32Exception(retval);

oleg_m
Comment by Wiebren

Is there any service on the target computer needed to run in order to let this work? i couldn't find that info on Msdn.

Wiebren
Comment by Duncan Smart

Not as far as I know. Is the code not working for you?

Comment by Wiebren

Just curious :) For WMI for example you need to have the WMI service running, why actually use that if this always works. And WMI seems the newer tech.

Wiebren
Comment by Duncan Smart

WMI does a bunch of things, many of which are just wrappers around existing mechanisms such as this one. I always prefer the simplest possible thing that will work.

Comment by mutanic

Hi Duncan, is there any way we can Disable the NetBios programmatically? I'm thinking to expend your code & see if I can get it work.

mutanic