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

本文关键字:创建 方式 网桥 编程 Windows NET | 更新日期: 2023-09-27 18:21:52

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

到目前为止,我的研究表明,对于配置,netsh-命令是一条可行的途径。然而,似乎没有选择与他们建立一座新的桥梁
我目前正在研究这个使用INetCfg API的程序,但该程序,或者更具体地说,API,似乎无法(再次)构建新的桥梁

如果有人能为解决问题做出贡献,我们将不胜感激。

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

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

我找到了一个同时适用于网桥服务桥接适配器驱动程序的解决方案。我不像devcon那样使用UpdateDriverForPlugAndPlayDevices,而是使用DiInstallDevice

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

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

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

这就是我的解决方案:

  1. 首先,在SetupDiCreateDeviceInfo的帮助下,在HKLM''System''CurrentControlSet''Enum''Root中创建一个设备条目,该条目使用给定的硬件id作为设备名称(ms_bridge,ms_bridgemp)
  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/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即可。

实际上可以通过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可能不同。

在互联网上搜索多次失败后,我编写并成功使用以下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)函数,当用户通过Windows GUI中的列表视图项选择和上下文菜单命令启动操作时,这些函数又由Explorer Shell调用。在这里可以看到一个调用Shell网络接口以编程方式禁用/启用网络适配器的C++示例。遗憾的是,目前还没有创建/删除网桥适配器的示例。因此,在它可用之前,我将坚持使用脚本。

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

Usage: bindbridge <deviceId> <bind|unbind>

来源可在https://github.com/OurGrid/OurVirt/tree/master/tools/win32/bindbridge,并且它假设桥接设备已经存在-根据之前的回答,可以使用devcon创建-并且其名称为ms_bridge,可以在源中轻松更改。

我用它来编程地向网桥添加抽头接口,所以我的命令行是

bindbridge ROOT'NET'0001 bind

不幸的是,没有记录在案的建立网桥的方法。

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

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