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

Windows 9x/2000 跨平台創建共享目錄:動態加載 NetShareAdd 實現

     編輯:VC 教學

背景:API 依賴差異導致的兼容性難題

創建共享目錄需調用 NetShareAdd,但其所在 DLL 不同:

  • Windows 2000/NT:Netapi32.dll
  • Windows 9x:Svrapi.dll

靜態鏈接對應 .lib 會導致在另一平台啟動失敗(缺少 DLL/導出函數)。解決方案:運行時動態加載 DLL,獲取函數指針調用。

完整跨平台實現代碼

1. 類型定義與常量

// 函數指針類型
typedef DWORD (WINAPI *NETSHAREADD2K)(LPWSTR, DWORD, LPBYTE, LPDWORD); // NT/2K: Unicode, Level 2
typedef DWORD (WINAPI *NETSHAREADD9X)(const char FAR*, short, const char FAR*, unsigned short); // 9x: ANSI, Level 50

NETSHAREADD2K pNetShareAdd2k = NULL;
NETSHAREADD9X pNetShareAdd9x = NULL;
bool bWinNT = false;
HINSTANCE hDll = NULL;

// 常量
#define NNLEN 80
#define LM20_NNLEN 12
#define SHPWLEN 8
#define STYPE_DISKTREE 0
#define SHI50F_FULL 0x0002
#define NERR_Success 0
#define NERR_BASE 2100
#define NERR_DuplicateShare (NERR_BASE + 18)
#define NERR_RedirectedPath (NERR_BASE + 17)
#define NERR_UnknownDevDir (NERR_BASE + 16)

// 9x 結構 (Level 50)
struct share_info_50 {
    char shi50_netname[LM20_NNLEN + 1];
    unsigned char shi50_type;
    unsigned short shi50_flags;
    char FAR *shi50_remark;
    char FAR *shi50_path;
    char shi50_rw_password[SHPWLEN + 1];
    char shi50_ro_password[SHPWLEN + 1];
};

// 2K 結構 (Level 2)
struct SHARE_INFO_2 {
    LPWSTR shi2_netname;
    DWORD shi2_type;
    LPWSTR shi2_remark;
    DWORD shi2_permissions;
    DWORD shi2_max_uses;
    DWORD shi2_current_uses;
    LPWSTR shi2_path;
    LPWSTR shi2_passwd;
};

2. 初始化:檢測系統並加載對應 DLL

bool __fastcall InitApi() {
    OSVERSIONINFO info = { sizeof(info) };
    GetVersionEx(&info);
    bWinNT = (info.dwPlatformId == VER_PLATFORM_WIN32_NT);

    if (bWinNT) {
        hDll = LoadLibrary("Netapi32.dll");
        if (hDll) pNetShareAdd2k = (NETSHAREADD2K)GetProcAddress(hDll, "NetShareAdd");
    } else {
        hDll = LoadLibrary("Svrapi.dll");
        if (hDll) pNetShareAdd9x = (NETSHAREADD9X)GetProcAddress(hDll, "NetShareAdd");
    }
    return (bWinNT ? pNetShareAdd2k : pNetShareAdd9x) != NULL;
}

3. 統一調用接口:MyPathToNetShare

String __fastcall MyPathToNetShare(String strPath, String strNetName,
    String strPassword, String strRemark) {

    if (!InitApi()) return "函數獲取錯誤!";

    if (!bWinNT) { // Windows 9x
        share_info_50 *pBuf = (share_info_50*)malloc(sizeof(share_info_50));
        if (!pBuf) return "內存分配失敗!";

        strcpy(pBuf->shi50_netname, strNetName.c_str());
        pBuf->shi50_type = STYPE_DISKTREE;
        pBuf->shi50_flags = SHI50F_FULL;
        pBuf->shi50_remark = strRemark.c_str();
        pBuf->shi50_path = strPath.c_str();
        strcpy(pBuf->shi50_rw_password, strPassword.c_str());
        strcpy(pBuf->shi50_ro_password, strPassword.c_str());

        DWORD nStatus = pNetShareAdd9x(NULL, 50, (char FAR*)pBuf, sizeof(share_info_50));
        free(pBuf);
        FreeLibrary(hDll);

        return (nStatus == NERR_Success) ? "添加成功!" : "錯誤碼: " + String(nStatus);
    }
    else { // Windows 2000/NT
        // 提升權限(需要 SE_SECURITY_PRIVILEGE 或管理員)
        HANDLE hToken;
        TOKEN_PRIVILEGES tk;
        OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken);
        LookupPrivilegeValue(NULL, SE_SECURITY_NAME, &tk.Privileges[0].Luid); // 注意:原代碼用 SE_SHUTDOWN_NAME 可能不夠
        tk.PrivilegeCount = 1;
        tk.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
        AdjustTokenPrivileges(hToken, FALSE, &tk, 0, NULL, 0);

        SHARE_INFO_2 si2 = {0};
        si2.shi2_path = StringToOleStr(strPath);
        si2.shi2_remark = StringToOleStr(strRemark);
        si2.shi2_netname = StringToOleStr(strNetName);
        si2.shi2_passwd = StringToOleStr(strPassword);
        si2.shi2_type = STYPE_DISKTREE;
        si2.shi2_permissions = 0; // ACCESS_READ | ACCESS_WRITE...
        si2.shi2_max_uses = -1;
        si2.shi2_current_uses = 0;

        DWORD dwParamErr;
        DWORD nas = pNetShareAdd2k(NULL, 2, (LPBYTE)&si2, &dwParamErr);
        FreeLibrary(hDll);

        switch (nas) {
            case NERR_Success: return "設置成功!";
            case ERROR_ACCESS_DENIED: return "拒絕訪問! (需管理員權限)";
            case NERR_DuplicateShare: return "共享名已存在!";
            case NERR_UnknownDevDir: return "路徑不存在!";
            default: return "未知錯誤: " + String(nas);
        }
    }
}

4. 調用示例

void __fastcall TForm1::Button1Click(TObject *Sender) {
    ShowMessage(MyPathToNetShare("C:\\ShareDir", "MyShare", "", "共享說明"));
}

關鍵技術點總結

  1. 動態加載:LoadLibrary + GetProcAddress 解決平台差異,核心技巧。
  2. 字符集處理:9x 用 ANSI (char*),2K 用 Unicode (LPWSTR),StringToOleStr (SysAllocString) 轉換 BSTR,用完需 SysFreeString(此處簡化未釋放,生產代碼必須釋放)。
  3. 結構體對齊:#pragma pack(push, 1) 確保與系統定義一致,防止參數錯位。
  4. 權限提升:NT 系列創建共享需管理員權限,代碼演示了 Token 權限調整(建議使用清單 requireAdministrator 或檢查權限)。
  5. 錯誤碼映射:將系統錯誤碼轉為友好提示,提升用戶體驗。

現代化建議 (Vista+)

  • 優先使用 NetShareAdd (Netapi32.dll 統一),但需處理 UAC、共享權限 ACL (SID/ACE)。
  • 考慮 WMI (Win32_Share.Create) 或 PowerShell (New-SmbShare) 更高層抽象。
  • C++ 可封裝為 RAII 類,自動管理 DLL 生命週期、字符串轉換、錯誤處理。

總結

動態加載 NetShareAdd 是解決 Win9x/2K 跨平台創建共享的經典方案。理解平台差異、字符集、結構體、權限模型,舉一反三可應用於其他跨版本系統 API 調用(如 NetUserAdd、NetServerEnum 等)。

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