在Windows 7上以编程方式使用.NET创建/销毁网桥

时间:2013-07-11 08:47:03

标签: c# windows winapi networking

我正在尝试以编程方式在Windows 7上创建和销毁网桥。从技术上来说,我希望保持在.Net 4领域内(PInvokes很好,ofc),但是使用C ++是一种选择。登记/>
到目前为止,我的研究发现,对于配置,netsh - 命令是the route to go。然而,似乎没有选择用它们实际开辟一座新的桥梁 我目前正在调查使用INetCfg API的this程序,但似乎该程序或更具体地说,API无法(再次)构建新桥。
如果有人可以为解决问题做出贡献,我们非常感谢任何帮助。

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

5 个答案:

答案 0 :(得分:3)

实际上可以通过SetupAPI创建和建立网桥 使用DevCon工具,销毁它们就像这样简单......

devcon.exe remove ms_bridgemp

...虽然可以使用此命令构建桥:

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


DevCon is open source,因此您可以深入了解源代码以了解它是如何实现这些命令的(DevCon Tool本质上是SetupAPI的CLI)。

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

答案 1 :(得分: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 ++示例here。遗憾的是,还没有用于创建/删除网桥适配器的示例。所以在它可用之前我会坚持使用脚本。

答案 2 :(得分:3)

我找到了适用于网桥服务网桥适配器驱动程序的解决方案。我不像{strong> devcon 那样使用UpdateDriverForPlugAndPlayDevices,而是使用DiInstallDevice代替。

但是,无法在非交互模式下首次安装驱动程序(无需用户交互)。这是因为内置网桥 .inf 文件没有相应的 .cat 文件。对于 .inf 文件已包含在%SystemRoot%\ inf .inf 文件,UpdateDriverForPlugAndPlayDevicesDiInstallDeviceDiInstallDriver都不适用于手动驱动程序安装>但尚未在%SystemRoot%\ System32 \ DriverStore

  

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

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

所以这是我的解决方案:

  1. 首先在SetupDiCreateDeviceInfo的帮助下,使用给定的硬件ID作为设备名称(ms_bridge,ms_bridgemp)创建 HKLM \ System \ CurrentControlSet \ Enum \ Root 中的设备条目
  2. 硬件ID已分配SetupDiSetDeviceRegistryProperty
  3. SetupDiSetDeviceInstallParams
  4. 的帮助下,驱动程序列表完全由给定的单个 .inf 文件构建
  5. 使用SetupDiSetSelectedDriver
  6. 枚举和预选驱动程序
  7. 使用SetupDiCallClassInstaller(DIF_REGISTERDEVICE...)
  8. 注册设备
  9. 使用DiInstallDevice
  10. 进行安装

    这是完整的代码:

    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 :找到一种方法从%SystemRoot%\ inf 中安装此桥驱动程序,而无需创建OEM副本且无需任何用户交互。

    您可以在Sourceforge

    获得对subversion存储库的读/写访问权限

    任何有关改进的其他信息或建议表示赞赏!每个人都可以随时查看/修改代码。

    基本命令

    • bridgeutil.exe / install
    • bridgeutil.exe / uninstall
    • bridgeutil.exe / attach
    • bridgeutil.exe / detach

    <强>实施例

    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。

答案 3 :(得分:2)

基于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

答案 4 :(得分:1)

事实证明,遗憾的是,没有记录的建立网桥的方法。

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

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