4

我正在尝试以编程方式在 Windows 7 上创建和销毁网桥。从
技术上讲,我希望留在 .Net 4 领域(PInvokes 很好,ofc),但使用 C++ 是一种选择。

到目前为止,我的研究表明,对于配置,netsh-commands 是要走的路线。然而,似乎没有选择真正与他们建立一座新的桥梁。
我目前正在调查这个使用INetCfgAPI 的程序,但似乎该程序或更具体地说,API 无法(再次)构建新的桥梁。

如果有人可以为解决问题做出贡献,我们将不胜感激任何形式的帮助。

[更新:] 似乎 newtork 网桥是使用驱动程序实现的,然后绑定到两个设备。我还不能充分利用这些信息,所以仍然感谢任何帮助。

4

5 回答 5

3

在互联网上多次搜索不成功后,我编写并成功使用以下 Windows 脚本主机脚本“BridgeConnections.vbs”在 Windows XP 上创建网桥(此方法也适用于 Windows 7 和 Windows 8,稍作修改)。它可以从命令提示符或批处理文件中运行,如下所示:

C:\Temp> cscript BridgeConnections.vbs

文件BridgeConnections.vbs:

' This VBScript opens the "Network Connections" control panel window,
' sends Ctrl+A ("Select All") and Alt+N ("Advanced" menu) and
' C ("Bridge Connections" menu command) keystrokes to it and then waits
' until the splash window "Please wait while Windows bridges the connections..."
' disappears from the screen

Dim WshShell, Count
Set WshShell = WScript.CreateObject("WScript.Shell")

WshShell.Exec("rundll32.exe shell32.dll,Control_RunDLL ncpa.cpl")

Count = 0
Do While Not WshShell.AppActivate("Network Connections") And Count < 10
    Count = Count + 1
    WScript.Sleep 1000
    WScript.Echo "Waiting for the 'Network Connections' window... " & CStr(Count) & "s"
Loop

WshShell.SendKeys "^(a)"
WshShell.SendKeys "%(n)"
WshShell.SendKeys "c"

Count = 0
Do While Not WshShell.AppActivate("Network Bridge") And Count < 10
    Count = Count + 1
    WScript.Sleep 1000
    WScript.Echo "Waiting for the 'Network Bridge' splash window... " & CStr(Count) & "s"
Loop

Count = 0
Do While WshShell.AppActivate("Network Bridge") And Count < 120
  Count = Count + 1
  WScript.Sleep 1000
  WScript.Echo "Waiting for the 'Network Bridge' splash window to disappear... " & CStr(Count) & "s"
Loop 

同样,如果需要,可以修改脚本以“删除”桥接器(使用 Shift 和导航键进行单个选择并发送不同的击键命令)。就我而言,我只需要从批处理文件中桥接所有可用的以太网适配器,因此上述方法可以正常工作。

根据我的经验,

devcon.exe install "C:\Windows\inf\netbrdgm.inf" ms_bridgemp

之前在此处发布的方法是,它将创建一个空的、“半支持”的桥,其中没有适配器。因此,您仍然必须转到 Windows GUI 并手动“添加”它,然后它才能真正可用。

唯一真正适合我的全自动解决方案是上面的脚本。

要在没有脚本的情况下从 C++ 或 C# 代码执行相同的操作,您需要知道并调用未记录的 Shell 网络接口 (NETSHELL.DLL) 函数,当用户通过列表视图启动操作时,Explorer Shell 又会调用这些函数Windows GUI 中的项目选择和上下文菜单命令。可以在此处查看调用 Shell 网络接口以以编程方式禁用/启用网络适配器的 C++ 示例。不幸的是,还没有创建/删除网桥适配器的示例。所以在它可用之前,我会坚持使用脚本。

于 2013-09-24T14:51:34.420 回答
3

我找到了适用于桥接服务桥接适配器驱动程序的解决方案。我UpdateDriverForPlugAndPlayDevices不像devcon那样使用,但我正在使用DiInstallDevice

但是,第一次以非交互模式(没有用户交互)安装驱动程序是不可能的。这是因为内置网桥.inf文件没有对应的.cat文件。既不也不也不适合手动安装驱动程序,其中.inf文件已包含在%SystemRoot%\inf但尚未包含在%SystemRoot%\System32\DriverStore中。UpdateDriverForPlugAndPlayDevicesDiInstallDeviceDiInstallDriver

文件应位于分发媒体或供应商创建的目录中,而不是位于系统位置,例如 %SystemRoot%\inf

所有提到的安装方法都将创建.inf文件的 OEM 副本并将其安装在驱动程序存储中。由于此 OEM 副本最初不是驱动程序存储的一部分,因此Windows 将显示一个提示对话框并要求用户交互,要么强制安装驱动程序,要么取消。顺便说一下,无需任何用户交互即可安装后续驱动程序。预装的驱动程序(参见 pnputil -a)也可以在非交互模式下安装。

所以这是我的解决方案:

  1. 首先在HKLM\System\CurrentControlSet\Enum\Root中创建一个设备条目,使用给定的硬件 id 作为设备名称 (ms_bridge, ms_bridgemp)SetupDiCreateDeviceInfo
  2. 硬件 id 分配有SetupDiSetDeviceRegistryProperty
  3. 驱动程序列表是由给定的单个.inf文件在SetupDiSetDeviceInstallParams
  4. 枚举和预选驱动程序SetupDiSetSelectedDriver
  5. 注册设备SetupDiCallClassInstaller(DIF_REGISTERDEVICE...)
  6. 安装DiInstallDevice

这是完整的代码:

HRESULT InstallDriver(const wchar_t* DriverInfFile, const wchar_t* HardwareId) {
    HRESULT Hr = S_OK;

    GUID ClassGUID;
    wchar_t ClassName[MAX_CLASS_NAME_LEN] = {0};

    if (SetupDiGetINFClass(DriverInfFile, &ClassGUID, ClassName, sizeof(ClassName) / sizeof(wchar_t), nullptr) == FALSE) {
        Hr = HRESULT_FROM_SETUPAPI(GetLastError());
        return Hr;
    }

    HDEVINFO DeviceInfoSet = SetupDiCreateDeviceInfoList(&ClassGUID, nullptr);

    if (DeviceInfoSet == INVALID_HANDLE_VALUE) {
        Hr = HRESULT_FROM_SETUPAPI(GetLastError());
        return Hr;
    }

    SP_DEVINFO_DATA DeviceInfoData = {
        sizeof(SP_DEVINFO_DATA), 0
    };

    if (SetupDiCreateDeviceInfo(DeviceInfoSet, HardwareId, &ClassGUID, nullptr, nullptr, DICD_GENERATE_ID, &DeviceInfoData) == FALSE) {
        Hr = HRESULT_FROM_SETUPAPI(GetLastError());
        SetupDiDestroyDeviceInfoList(DeviceInfoSet);
        return Hr;
    }

    if (SetupDiSetDeviceRegistryProperty(DeviceInfoSet, &DeviceInfoData, SPDRP_HARDWAREID, (LPBYTE) HardwareId, (DWORD) (wcslen(HardwareId) + 1) * sizeof(wchar_t)) == FALSE) {
        Hr = HRESULT_FROM_SETUPAPI(GetLastError());
        SetupDiDestroyDeviceInfoList(DeviceInfoSet);
        return Hr;
    }

    SP_DEVINSTALL_PARAMS InstallParams = {sizeof(SP_DEVINSTALL_PARAMS), 0};

    InstallParams.FlagsEx = DI_FLAGSEX_ALLOWEXCLUDEDDRVS | DI_FLAGSEX_ALWAYSWRITEIDS;
    InstallParams.Flags = DI_QUIETINSTALL | DI_ENUMSINGLEINF;
    wcscpy_s(InstallParams.DriverPath, DriverInfFile);

    if (SetupDiSetDeviceInstallParams(DeviceInfoSet, &DeviceInfoData, &InstallParams) == FALSE) {
        Hr = HRESULT_FROM_SETUPAPI(GetLastError());
        SetupDiDestroyDeviceInfoList(DeviceInfoSet);
        return Hr;
    }

    SP_DRVINFO_DATA DriverInfoData = {sizeof(SP_DRVINFO_DATA), 0};

    if (SetupDiBuildDriverInfoList(DeviceInfoSet, &DeviceInfoData, SPDIT_COMPATDRIVER) == FALSE) {
        Hr = HRESULT_FROM_SETUPAPI(GetLastError());
        SetupDiDestroyDriverInfoList(DeviceInfoSet, &DeviceInfoData, SPDIT_COMPATDRIVER);
    }

    // Use first best driver (since specified by inf file)

    if (SetupDiEnumDriverInfo(DeviceInfoSet, &DeviceInfoData, SPDIT_COMPATDRIVER, 0, &DriverInfoData)) {
        SetupDiSetSelectedDriver(DeviceInfoSet, &DeviceInfoData, &DriverInfoData);
    }

    if (SetupDiCallClassInstaller(DIF_REGISTERDEVICE, DeviceInfoSet, &DeviceInfoData) == FALSE) {
        Hr = HRESULT_FROM_SETUPAPI(GetLastError());
    }

    // TODO: Allow non interactive mode for drivers already contained in %SystemRoot%\inf directory

    //BOOL PreviousMode = SetupSetNonInteractiveMode(TRUE);

    if (Hr == S_OK) {
        if (DiInstallDevice(nullptr, DeviceInfoSet, &DeviceInfoData, &DriverInfoData, 0, nullptr) == FALSE) {
            Hr = HRESULT_FROM_SETUPAPI(GetLastError());
            // Ensure that the device entry in \ROOT\ENUM\ will be removed...
            SetupDiRemoveDevice(DeviceInfoSet, &DeviceInfoData);
        }
    }

    //SetupSetNonInteractiveMode(PreviousMode);

    SetupDiDestroyDeviceInfoList(DeviceInfoSet);

    return Hr;
}

Todo's : 找到一种在%SystemRoot%\inf内安装此桥驱动程序的方法,无需创建 OEM 副本,也无需任何用户交互。

您可以在Sourceforge获得对 subversion 存储库的读/写访问权限

感谢您提供任何其他信息或改进建议!请大家随时检查/修改代码。

基本命令

  • bridgeutil.exe /安装
  • bridgeutil.exe /卸载
  • bridgeutil.exe /附加
  • bridgeutil.exe /分离

例子

bridgeutil.exe /attach "PCI\VEN_10EC&DEV_8169" /attach {5d624f94-8850-40c3-a3fa-a4fd2080baf3}\vwifimp

将每个 Realtek 8169 网络接口卡和 Microsoft 虚拟 Wifi 适配器连接到桥接。如果尚未安装桥接器,则将首先安装它。

bridgeutil.exe /detach 1

从网桥中分离 id 为 1 的适配器。

要查看可桥接适配器的列表,只需调用不带任何参数的 bridgeutil.exe。

于 2014-03-05T14:46:27.323 回答
3

基于 bindview 示例,我提出了一个名为 bindbridge 的实用程序,其工作原理如下:

Usage: bindbridge <deviceId> <bind|unbind>

源代码可以在https://github.com/OurGrid/OurVirt/tree/master/tools/win32/bindbridge找到,它假设桥接设备已经存在——可以根据之前的答案使用 devcon 创建——并且它的名字是ms_bridge,可以在源代码中轻松更改。

我正在使用它以编程方式将 Tap 接口添加到网桥,因此我的命令行如下所示:

bindbridge ROOT\NET\0001 bind

于 2013-12-01T15:56:57.287 回答
3

实际上可以通过 SetupAPI 创建和网桥。
使用DevCon工具,摧毁它们就这么简单......

devcon.exe remove ms_bridgemp

...虽然可以使用以下命令来构建桥梁:

devcon.exe install "C:\Windows\inf\netbrdgm.inf" ms_bridgemp


DevCon 是开源的,因此您可以深入研究源代码以了解它如何实现这些命令(DevCon 工具本质上是 SetupAPI 的 CLI)。

请注意:这些命令与 Windows 7 相关。据说该方法适用于 XP,我想它也适用于其他 Windows 版本,但 .INF 文件可能具有不同的名称或设备 ID 可能不同。

于 2013-07-22T19:20:38.120 回答
1

事实证明,不幸的是,没有记录在案的方法来设置网桥。

执行此操作的代码位于hnetcfg.dll中,并且仅由 Windows 资源管理器调用。它安装桥接驱动程序,并配置桥接接口。

可能可以自己调用它(使用 COM),但这需要逆向工程,并且可能会在任何系统更新时中断,所以我建议不要这样做。

于 2013-07-11T21:50:31.170 回答