首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C# 呼叫 Windows API 列舉區域網路主機 (WNetEnumResource 實作)

     編輯:關於C語言

前言

在 Windows 環境下列舉網路上的電腦、共用資源與網域,可透過 mpr.dll 提供的 WNetOpenEnum、WNetEnumResource、WNetCloseEnum API 達成。本文提供完整的 C# P/Invoke 宣告、結構體定義、遞歸列舉邏輯與 TreeView 顯示範例,適用於需掃描區域網路主機的設定工具或管理程式。

P/Invoke 宣告與常數定義

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) 相關埠開放

現代替代方案

  • System.DirectoryServices / DirectorySearcher:查詢 Active Directory (LDAP),適用於網域環境,功能更強大。
  • WMI (System.Management):查詢 Win32_ComputerSystem、Win32_NetworkAdapter 等類別。
  • SSDP / WS-Discovery:現代裝置發現協定,適用於 IoT/UPnP 裝置。

完整錯誤碼對照表


// 參考 NERR 列舉,常見:
// 234 (ERROR_MORE_DATA) - 緩衝區不足,需增加 buffer 或分頁讀取
// 5  (ERROR_ACCESS_DENIED) - 權限不足
// 53 (ERROR_BAD_NETPATH) - 網路路徑不存在
// 6118 (ERROR_NO_BROWSER_SERVERS_FOUND) - 找不到瀏覽器服務(主瀏覽器未就緒)
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved