在 Windows 環境下列舉網路上的電腦、共用資源與網域,可透過 mpr.dll 提供的 WNetOpenEnum、WNetEnumResource、WNetCloseEnum API 達成。本文提供完整的 C# P/Invoke 宣告、結構體定義、遞歸列舉邏輯與 TreeView 顯示範例,適用於需掃描區域網路主機的設定工具或管理程式。
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
public class NetworkBrowser
{
#region DLL 匯入
[DllImport("mpr.dll", CharSet = CharSet.Auto)]
private static extern int WNetOpenEnum(
RESOURCE_SCOPE scope, RESOURCE_TYPE type, RESOURCE_USAGE usage,
[MarshalAs(UnmanagedType.AsAny)] object netResource, out IntPtr enumHandle);
[DllImport("mpr.dll", CharSet = CharSet.Auto)]
private static extern int WNetEnumResource(
IntPtr enumHandle, ref int count, IntPtr buffer, ref int bufferSize);
[DllImport("mpr.dll", CharSet = CharSet.Auto)]
private static extern int WNetCloseEnum(IntPtr enumHandle);
#endregion
#region 列舉與結構體
public enum RESOURCE_SCOPE
{
CONNECTED = 1, GLOBALNET = 2, REMEMBERED = 3, RECENT = 4, CONTEXT = 5
}
public enum RESOURCE_TYPE
{
ANY = 0, DISK = 1, PRINT = 2, RESERVED = 8
}
public enum RESOURCE_USAGE
{
CONNECTABLE = 1, CONTAINER = 2, NOLOCALDEVICE = 4,
SIBLING = 8, ATTACHED = 16,
ALL = CONNECTABLE | CONTAINER | ATTACHED
}
public enum RESOURCE_DISPLAYTYPE
{
GENERIC = 0, DOMAIN = 1, SERVER = 2, SHARE = 3, FILE = 4,
GROUP = 5, NETWORK = 6, ROOT = 7, SHAREADMIN = 8,
DIRECTORY = 9, TREE = 10, NDSCONTAINER = 11
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct NETRESOURCE
{
public RESOURCE_SCOPE dwScope;
public RESOURCE_TYPE dwType;
public RESOURCE_DISPLAYTYPE dwDisplayType;
public RESOURCE_USAGE dwUsage;
[MarshalAs(UnmanagedType.LPTStr)] public string lpLocalName;
[MarshalAs(UnmanagedType.LPTStr)] public string lpRemoteName;
[MarshalAs(UnmanagedType.LPTStr)] public string lpComment;
[MarshalAs(UnmanagedType.LPTStr)] public string lpProvider;
}
private enum NERR
{
Success = 0, MORE_DATA = 234, NO_BROWSER_SERVERS = 6118,
INVALID_LEVEL = 124, ACCESS_DENIED = 5, INVALID_PARAM = 87,
NOT_ENOUGH_MEMORY = 8, NETWORK_BUSY = 54, BAD_NETPATH = 53,
NO_NETWORK = 1222, INVALID_HANDLE_STATE = 1609, EXTENDED_ERROR = 1208
}
#endregion
private TreeView _treeView;
private TreeNode _networkNode, _domainNode, _serverNode;
public void EnumerateNetwork(TreeView treeView)
{
_treeView = treeView;
_treeView.Nodes.Clear();
EnumResources(null); // 從頂層開始
}
private void EnumResources(object netResource)
{
int result = WNetOpenEnum(
RESOURCE_SCOPE.GLOBALNET, RESOURCE_TYPE.ANY, RESOURCE_USAGE.ALL,
netResource, out IntPtr enumHandle);
if (result != 0) return; // 失敗靜默返回
const int BUFFER_SIZE = 16384;
IntPtr buffer = Marshal.AllocHGlobal(BUFFER_SIZE);
int entries = -1;
int size = BUFFER_SIZE;
try
{
while (true)
{
entries = -1;
size = BUFFER_SIZE;
result = WNetEnumResource(enumHandle, ref entries, buffer, ref size);
if (result != 0 || entries < 1) break;
int ptr = buffer.ToInt32();
for (int i = 0; i < entries; i++)
{
NETRESOURCE nr = (NETRESOURCE)Marshal.PtrToStructure(
new IntPtr(ptr), typeof(NETRESOURCE));
ptr += Marshal.SizeOf(nr);
// 只處理容器類型(網域、伺服器、共用資料夾)
if ((nr.dwUsage & RESOURCE_USAGE.CONTAINER) == RESOURCE_USAGE.CONTAINER)
{
switch (nr.dwDisplayType)
{
case RESOURCE_DISPLAYTYPE.NETWORK:
_networkNode = _treeView.Nodes.Add(nr.lpRemoteName);
_networkNode.ImageIndex = _networkNode.SelectedImageIndex = 0;
break;
case RESOURCE_DISPLAYTYPE.DOMAIN:
_domainNode = _networkNode.Nodes.Add(nr.lpRemoteName);
_domainNode.ImageIndex = _domainNode.SelectedImageIndex = 1;
break;
case RESOURCE_DISPLAYTYPE.SERVER:
string name = nr.lpRemoteName;
if (name.StartsWith(@"\\")) name = name.Substring(2);
_serverNode = _domainNode.Nodes.Add(name);
_serverNode.ImageIndex = _serverNode.SelectedImageIndex = 2;
break;
}
// 遞歸列舉子項目
EnumResources(nr);
}
}
}
}
catch (Exception ex)
{
MessageBox.Show($"列舉網路錯誤: {ex.Message}", "錯誤",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
finally
{
Marshal.FreeHGlobal(buffer);
WNetCloseEnum(enumHandle);
}
}
private void Form_Load(object sender, EventArgs e)
{
EnumerateNetwork(treeView1);
}
| 問題 | 說明 |
|---|---|
| 需要管理員權限 | 列舉某些網路資源需提升權限,建議在 manifest 設定 requireAdministrator |
| 工作群組環境無網域節點 | WORKGROUP 環境下 RESOURCE_DISPLAYTYPE.DOMAIN 可能不出現,直接掛在 NETWORK 下 |
| 伺服器名稱前雙反斜線 | lpRemoteName 為 \\SERVERNAME 格式,顯示時需移除 |
| 列舉速度較慢 | 網路廣播機制導致,大型網域可能需數十秒,建議放入 BackgroundWorker |
| 防火牆封鎖 | 確保檔案與印表機共用 (SMB/NetBIOS) 相關埠開放 |
Win32_ComputerSystem、Win32_NetworkAdapter 等類別。
// 參考 NERR 列舉,常見:
// 234 (ERROR_MORE_DATA) - 緩衝區不足,需增加 buffer 或分頁讀取
// 5 (ERROR_ACCESS_DENIED) - 權限不足
// 53 (ERROR_BAD_NETPATH) - 網路路徑不存在
// 6118 (ERROR_NO_BROWSER_SERVERS_FOUND) - 找不到瀏覽器服務(主瀏覽器未就緒)