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

Windows 9x/2000 跨版本建立共享資料夾:動態載入 NetShareAdd 完整實作

     編輯:關於C++

建立網路共享資料夾需呼叫 NetShareAdd,但該函數在 **Windows 9x 位於 `svrapi.dll`**,**Windows NT/2000/XP 位於 `netapi32.dll`**,參數結構截然不同。本文採用 **`LoadLibrary` + `GetProcAddress` 動態載入** 策略,單一執行檔同時支援 9x 與 NT 系平台,並提供完整錯誤碼處理與權限提升代碼。

核心策略:執行期偵測平台 → 動態載入對應 DLL → 填充專用結構 → 呼叫函數指標

關鍵資料結構差異

平台DLL層級結構關鍵欄位
Win9xsvrapi.dll50share_info_50ansi 字串、密碼欄位分讀寫/唯讀
WinNT/2k+netapi32.dll2SHARE_INFO_2Unicode (LPWST)、權限位元組、最大連線數

完整實作代碼 (C++ Builder)


// 型別定義
typedef DWORD (WINAPI *PFN_NETSHAREADD_9X)(LPCSTR, short, LPCSTR, unsigned short);
typedef DWORD (WINAPI *PFN_NETSHAREADD_NT)(LPWSTR, DWORD, LPBYTE, LPDWORD);

PFN_NETSHAREADD_9X g_pNetShareAdd9x = nullptr;
PFN_NETSHAREADD_NT g_pNetShareAddNt = nullptr;
HMODULE g_hShareDll = nullptr;
bool g_bIsNT = false;

// 初始化:偵測平台並載入 DLL
bool InitShareApi() {
    OSVERSIONINFO vi = {sizeof(vi)};
    GetVersionEx(&vi);
    g_bIsNT = (vi.dwPlatformId == VER_PLATFORM_WIN32_NT);

    g_hShareDll = LoadLibrary(g_bIsNT ? L"netapi32.dll" : L"svrapi.dll");
    if (!g_hShareDll) return false;

    if (g_bIsNT) {
        g_pNetShareAddNt = (PFN_NETSHAREADD_NT)GetProcAddress(g_hShareDll, "NetShareAdd");
        return g_pNetShareAddNt != nullptr;
    } else {
        g_pNetShareAdd9x = (PFN_NETSHAREADD_9X)GetProcAddress(g_hShareDll, "NetShareAdd");
        return g_pNetShareAdd9x != nullptr;
    }
}

// 統一介面
AnsiString CreateShare(const AnsiString& Path, const AnsiString& ShareName,
                       const AnsiString& Password, const AnsiString& Remark)
{
    if (!InitShareApi()) return "載入 DLL 失敗";

    if (!g_bIsNT) { // -------- Win9x --------
        share_info_50 si50 = {0};
        strncpy(si50.shi50_netname, ShareName.c_str(), LM20_NNLEN);
        si50.shi50_type = STYPE_DISKTREE;
        si50.shi50_flags = SHI50F_FULL;
        si50.shi50_remark = Remark.c_str();
        si50.shi50_path = Path.c_str();
        strncpy(si50.shi50_rw_password, Password.c_str(), SHPWLEN);
        strncpy(si50.shi50_ro_password, Password.c_str(), SHPWLEN);

        DWORD err = g_pNetShareAdd9x(nullptr, 50, (LPCSTR)&si50, sizeof(si50));
        FreeLibrary(g_hShareDll);
        return (err == NERR_Success) ? "共建立成功" : "錯誤碼: " + IntToStr(err);
    }
    else { // -------- WinNT/2k/XP --------
        // 提升權限:SeSecurityPrivilege / SeCreatePermanentPrivilege
        HANDLE hToken; TOKEN_PRIVILEGES tp;
        OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken);
        LookupPrivilegeValue(nullptr, SE_SECURITY_NAME, &tp.Privileges[0].Luid);
        tp.PrivilegeCount = 1; tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
        AdjustTokenPrivileges(hToken, FALSE, &tp, 0, nullptr, 0);

        SHARE_INFO_2 si2 = {0};
        si2.shi2_netname = WideString(ShareName).c_bstr();
        si2.shi2_remark = WideString(Remark).c_bstr();
        si2.shi2_path = WideString(Path).c_bstr();
        si2.shi2_passwd = WideString(Password).c_bstr(); // 空字串 = 無密碼
        si2.shi2_type = STYPE_DISKTREE;
        si2.shi2_permissions = ACCESS_ALL; // 0 = 全部權限
        si2.shi2_max_uses = (DWORD)-1;     // 無限制
        si2.shi2_current_uses = 0;

        DWORD paramErr = 0;
        DWORD err = g_pNetShareAddNt(nullptr, 2, (LPBYTE)&si2, ¶mErr);
        FreeLibrary(g_hShareDll);

        switch (err) {
            case NERR_Success: return "建立成功";
            case ERROR_ACCESS_DENIED: return "拒絕存取 (需管理員)";
            case NERR_DuplicateShare: return "共享名稱已存在";
            case NERR_UnknownDevDir: return "路徑不存在";
            default: return "錯誤碼: " + IntToStr(err);
        }
    }
}

常見錯誤碼與對策

錯誤碼常見原因解決方式
ERROR_ACCESS_DENIED (5)非管理員 / 無 SeSecurityPrivilege以管理員身分執行、啟用 UAC 虛擬化、加入 manifest requireAdministrator
NERR_DuplicateShare (2118)同名共享已存在先呼叫 NetShareDel 刪除、或改用新名稱
NERR_UnknownDevDir (2116)路徑不存在或非本機磁碟確認路徑存在、為 NTFS、非映射磁碟機
ERROR_INVALID_PARAMETER (87)結構填寫錯誤 (Unicode/ANSI 混用)嚴格區分 9x 用 ANSI、NT 用 Unicode (WideString/BSTR)

進階補充

  • 刪除共享:同樣動態載入 NetShareDel,9x 層級 0,NT 層級 1。
  • 列舉共享:NetShareEnum 可取得現有共享清單,避免重複建立。
  • 防火牆:建立共享後需開啟 TCP 445/139 連入規則 (Windows Firewall API)。
  • 現代替代:Vista+ 建議使用 NetShareAdd 層級 502/1005 (支援 SMB 簽名、加密、CA 憑證),或透過 WMI `Win32_Share.Create`。

結語

透過 `LoadLibrary`/`GetProcAddress` 動態綁定 `NetShareAdd`,配合平台專用結構體與權限提升,即可在單一執行檔中實現 Windows 9x 至現代 Windows 10/11 的跨版本共享資料夾建立功能。此技術對維護舊系統相容性、撰寫系統管理工具極具實務價值。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved