Cree / destruya programáticamente los puentes de red con .NET en Windows 7

Estoy intentando crear y destruir programáticamente un puente de red en Windows 7.
Tecnológicamente, me encantaría permanecer en el reino .Net 4 (los inventarios están bien, por supuesto), pero utilizar C ++ es una opción.

Mi investigación hasta el momento reveló que para la configuración, los comandos de netsh son la ruta a seguir . Sin embargo, no parece haber ninguna opción para girar realmente un nuevo puente con ellos.
Actualmente estoy investigando este progtwig que usa las API de INetCfg , pero parece que el progtwig o, más específicamente, las API, no pueden (nuevamente) construir un nuevo puente.

Si alguien puede contribuir a resolver el problema, cualquier tipo de ayuda es muy apreciada.

[Actualización:] Parece que los puentes de newtork se implementan utilizando un controlador que luego se enlaza a ambos dispositivos. Todavía no puedo aprovechar gran parte de esa información, por lo que aún se agradece cualquier ayuda.

He encontrado una solución que funciona tanto para el servicio de puente como para el controlador del adaptador de puente . No uso UpdateDriverForPlugAndPlayDevices como devcon pero en DiInstallDevice lugar estoy usando DiInstallDevice .

Sin embargo, no es posible instalar los controladores por primera vez en modo no interactivo (sin interacción del usuario). Esto se debe a que no hay archivos .cat correspondientes para los archivos .inf del puente integrado . Ni UpdateDriverForPlugAndPlayDevices ni DiInstallDevice ni DiInstallDriver está diseñado para la instalación manual de controladores donde el archivo .inf ya está contenido en % SystemRoot% \ inf pero aún no está en % SystemRoot% \ System32 \ DriverStore .

Los archivos deben estar en el medio de distribución o en un directorio creado por el proveedor, no en una ubicación del sistema como% SystemRoot% \ inf

Todos los métodos de instalación mencionados crearán una copia OEM del archivo .inf y lo instalarán en el almacén de controladores . Debido a que esta copia OEM inicialmente no forma parte del almacén de controladores , Windows mostrará un diálogo de solicitud y solicitará la interacción del usuario, ya sea forzando la instalación del controlador o la cancelación. La instalación posterior de controladores es posible sin la intervención del usuario, por cierto. También los controladores preinstalados (ver pnputil -a) se pueden instalar en modo no interactivo.

Así que esta es mi solución:

  1. Primero se crea una entrada de dispositivo en HKLM \ System \ CurrentControlSet \ Enum \ Root con el ID de hardware dado como nombre de dispositivo (ms_bridge, ms_bridgemp) con la ayuda de SetupDiCreateDeviceInfo
  2. El ID de hardware se asigna con SetupDiSetDeviceRegistryProperty
  3. La lista de controladores está construida exactamente por el único archivo .inf dado con la ayuda de SetupDiSetDeviceInstallParams
  4. SetupDiSetSelectedDriver y preseleccionar el controlador con SetupDiSetSelectedDriver
  5. Registro de dispositivo con SetupDiCallClassInstaller(DIF_REGISTERDEVICE...)
  6. Instalación con DiInstallDevice

Este es el código completo:

 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 : Encuentre una manera de instalar los controladores de este puente desde dentro de % SystemRoot% \ inf sin crear copias OEM y sin la interacción del usuario.

Puede obtener acceso de lectura / escritura al repository de subversion en Sourceforge

Cualquier información adicional o sugerencia de mejora es apreciada! Todos, por favor, siéntase libre de revisar / modificar el código.

Comandos básicos :

  • bridgeutil.exe / instalar
  • bridgeutil.exe / desinstalar
  • bridgeutil.exe / attach
  • bridgeutil.exe / detach

Ejemplos :

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

Conecta cada una de las Tarjetas de Interfaz de Red Realtek 8169 y el Adaptador Wifi Virtual de Microsoft para puentear. Si el puente aún no está instalado, se instalará primero.

 bridgeutil.exe /detach 1 

Desconecta el adaptador con id 1 del puente.

Para ver una lista de adaptadores puenteables, simplemente llame a bridgeutil.exe sin ningún argumento.

En realidad, es posible crear y conectar puentes a través de SetupAPI.
Usar la herramienta DevCon , destruirlos es tan fácil como esto …

 devcon.exe remove ms_bridgemp 

… mientras que la construcción de puentes se puede hacer con este comando:

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

DevCon es de código abierto , por lo que puede profundizar en las fonts para ver cómo implementa esos comandos (la Herramienta DevCon es esencialmente una CLI para el SetupAPI).

Tenga en cuenta: los comandos se relacionan con Windows 7. Se dice que el enfoque funciona en XP y supongo que también funciona en otras versiones de Windows, pero el archivo .INF puede tener un nombre diferente o la ID del dispositivo puede diferir.

Después de muchas búsquedas fallidas en Internet, escribí y he utilizado con éxito la siguiente secuencia de comandos de Windows Script Host “BridgeConnections.vbs” para crear un puente de red en Windows XP (este método también funciona en Windows 7 y Windows 8 con pequeñas modificaciones). Se puede ejecutar desde el símbolo del sistema o desde un archivo por lotes de la siguiente manera:

 C:\Temp> cscript BridgeConnections.vbs 

Archivo 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 

Del mismo modo, se podría modificar el script para "Eliminar" el puente si fuera necesario (haga una sola selección con las teclas Mayús y navegar y envíe un comando de pulsación de tecla diferente). En mi caso, solo necesito unir todos los adaptadores Ethernet disponibles de un archivo por lotes para que el método anterior funcione bien.

En mi experiencia, el "leve" problema con el

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

El enfoque publicado aquí anteriormente es que crearía un puente vacío, de "medio respaldo", sin adaptadores en él. Así que aún tendrá que ir a la GUI de Windows y "Agregar" los adaptadores uno a uno manualmente antes de que sea realmente utilizable.

La única solución totalmente automatizada que realmente funciona para mí es el script anterior.

Para realizar las mismas acciones desde un código C ++ o C # sin la secuencia de comandos, debe conocer y llamar a las funciones de Interfaces de red de Shell no documentadas (NETSHELL.DLL) que, a su vez, llama el Shell de Explorer cuando el usuario inicia las acciones a través de la vista de lista Selección de elementos y comando del menú contextual en la GUI de Windows. Una muestra de C ++ de llamadas a la interfaz de red de Shell para deshabilitar / habilitar programáticamente un adaptador de red puede verse aquí . Lamentablemente, aún no hay una muestra para crear / eliminar el adaptador de puente de red. Así que hasta que esté disponible me quedo con el guión.

Basándome en el ejemplo de bindview, instalé un utilitario llamado bindbridge, que funciona de la siguiente manera:

 Usage: bindbridge   

La fuente se puede encontrar en https://github.com/OurGrid/OurVirt/tree/master/tools/win32/bindbridge , y se supone que el dispositivo puente ya existe, que se puede crear con devcon, según las respuestas anteriores, y su nombre será ms_bridge , lo que se puede cambiar fácilmente en las fonts.

Lo estoy usando para agregar programáticamente interfaces de tap al puente, por lo que mi línea de comandos es algo así como:

bindbridge ROOT\NET\0001 bind

Resulta que, desafortunadamente, no hay una forma documentada de configurar un puente de red.

El código que hace eso se encuentra dentro de hnetcfg.dll y solo lo invoca el Explorador de Windows. Instala el controlador del puente y configura la interfaz del puente.

Podría ser posible llamarlo usted mismo (usando COM), pero eso requeriría ingeniería inversa y podría interrumpir cualquier actualización del sistema, por lo que recomiendo no hacerlo.