Note: I only have one computer in my home network, so only one computer is shown in the screenshot above. It does work with many computers.
Introduction
This submission was actually written as part of a University project for my BSc., where I was using .NET Remoting and I had a server application and a client application, and I needed the client application to be able to connect to a given server application.
The problem was, I did not know what machine name the server was running on, in fact I wanted this to be flexible, and selectable by the user. So after I searched the .NET documentation on MSDN and couldn't find any managed classes which dealt with network computers or anything like that, I consulted a C++ programming friend of mine (Nick Cross, you know who you are), who mentioned the NetApi32.dll and the NetServerEnum
method. After I looked at the NetApi32.dll documentation, it looked as though it seemed to provide a list network computer names. Lovely.
Eureka I thought, but then I had to trawl the MSDN documentation for the relevant structures and methods I need to consume the NetApi32.dll unmanaged methods within my C# (managed) code.
So this article is really a culmination of research and pain, that I had, trying to get my head around how to consume the NetApi32 C++ DLL within C#. It was not fun, but I got there in the end.
I hope this article helps someone out, as it helped me.
Using the code
The demo project provided actually contains all the source code needed. The demo is based on a simple Windows Forms application written in Visual Studio 2005. The code works the same in .NET v1.1.
So now onto the code. The main class is called NetworkBrowser
which really does all the work. The demo form is a throw away, which is simply used to demo the code submission.
using System;
using System.Runtime.InteropServices;
using System.Security;
using System.Collections;
using System.Windows.Forms;
namespace ListNetworkComputers
{
#region NetworkBrowser CLASS
public sealed class NetworkBrowser
{
#region Dll Imports
[DllImport("Netapi32", CharSet = CharSet.Auto,
SetLastError = true),
SuppressUnmanagedCodeSecurityAttribute]
public static extern int NetServerEnum(
string ServerNane,
int dwLevel,
ref IntPtr pBuf,
int dwPrefMaxLen,
out int dwEntriesRead,
out int dwTotalEntries,
int dwServerType,
string domain,
out int dwResumeHandle
);
[DllImport("Netapi32", SetLastError = true),
SuppressUnmanagedCodeSecurityAttribute]
public static extern int NetApiBufferFree(
IntPtr pBuf);
[StructLayout(LayoutKind.Sequential)]
public struct _SERVER_INFO_100
{
internal int sv100_platform_id;
[MarshalAs(UnmanagedType.LPWStr)]
internal string sv100_name;
}
#endregion
#region Public Constructor
public NetworkBrowser()
{
}
#endregion
#region Public Methods
public ArrayList getNetworkComputers()
{
ArrayList networkComputers = new ArrayList();
const int MAX_PREFERRED_LENGTH = -1;
int SV_TYPE_WORKSTATION = 1;
int SV_TYPE_SERVER = 2;
IntPtr buffer = IntPtr.Zero;
IntPtr tmpBuffer = IntPtr.Zero;
int entriesRead = 0;
int totalEntries = 0;
int resHandle = 0;
int sizeofINFO = Marshal.SizeOf(typeof(_SERVER_INFO_100));
try
{
int ret = NetServerEnum(null, 100, ref buffer,
MAX_PREFERRED_LENGTH,
out entriesRead,
out totalEntries, SV_TYPE_WORKSTATION |
SV_TYPE_SERVER, null, out
resHandle);
if (ret == 0)
{
for (int i = 0; i < totalEntries; i++)
{
tmpBuffer = new IntPtr((int)buffer +
(i * sizeofINFO));
_SERVER_INFO_100 svrInfo = (_SERVER_INFO_100)
Marshal.PtrToStructure(tmpBuffer,
typeof(_SERVER_INFO_100));
networkComputers.Add(svrInfo.sv100_name);
}
}
}
catch (Exception ex)
{
MessageBox.Show("Problem with acessing " +
"network computers in NetworkBrowser " +
"\r\n\r\n\r\n" + ex.Message,
"Error", MessageBoxButtons.OK,
MessageBoxIcon.Error);
return null;
}
finally
{
NetApiBufferFree(buffer);
}
return networkComputers;
}
#endregion
}
#endregion
}
I have tried to comment the code as much as possible, so I'll just let the code do the talking. You've heard enough about me.
Points of Interest
On a personal level, I have found this to be quite a rewarding exercise, and I found that consuming DLLs within C# is quite a black art. But in the end, it's all worth it.
History