/* $Id: VBoxNetCfg.cpp $ */
/** @file
 * VBoxNetCfg.cpp - Network Configuration API.
 */

/*
 * Copyright (C) 2011-2023 Oracle and/or its affiliates.
 *
 * This file is part of VirtualBox base platform packages, as
 * available from https://www.virtualbox.org.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation, in version 3 of the
 * License.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see <https://www.gnu.org/licenses>.
 *
 * The contents of this file may alternatively be used under the terms
 * of the Common Development and Distribution License Version 1.0
 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
 * in the VirtualBox distribution, in which case the provisions of the
 * CDDL are applicable instead of those of the GPL.
 *
 * You may elect to license modified versions of this file under the
 * terms and conditions of either the GPL or the CDDL or both.
 *
 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
 */


/*********************************************************************************************************************************
*   Header Files                                                                                                                 *
*********************************************************************************************************************************/
#define _WIN32_DCOM

#include "VBox/VBoxNetCfg-win.h"
#include "VBox/VBoxDrvCfg-win.h"

#include <devguid.h>
#include <regstr.h>
#include <iprt/win/shlobj.h>
#include <cfgmgr32.h>
#include <iprt/win/objbase.h>

#include <Wbemidl.h>

#include <iprt/win/winsock2.h>
#include <iprt/win/ws2tcpip.h>
#include <ws2ipdef.h>
#include <iprt/win/netioapi.h>
#include <iprt/win/iphlpapi.h>

#include <iprt/asm.h>
#include <iprt/assertcompile.h>
#include <iprt/mem.h>
#include <iprt/list.h>
#include <iprt/rand.h>
#include <iprt/string.h>
#include <iprt/utf16.h>
#include <VBox/com/string.h>


/*********************************************************************************************************************************
*   Defined Constants And Macros                                                                                                 *
*********************************************************************************************************************************/
#ifndef Assert   /** @todo r=bird: where would this be defined? */
//# ifdef DEBUG
//#  define Assert(_expr) assert(_expr)
//# else
//#  define Assert(_expr) do{ }while (0)
//# endif
# define Assert _ASSERT
# define AssertMsg(expr, msg) do{}while (0)
#endif

#define NonStandardLog              DoLogging
#define NonStandardLogFlow(x)       DoLogging x

#define SetErrBreak(strAndArgs) \
    if (1) { \
        hrc = E_FAIL; \
        NonStandardLog strAndArgs; \
        bstrError.printfNoThrow strAndArgs; \
        break; \
    } else do {} while (0)


#define VBOXNETCFGWIN_NETADP_ID_SZ  "sun_VBoxNetAdp"
#define VBOXNETCFGWIN_NETADP_ID_WSZ RT_CONCAT(L,VBOXNETCFGWIN_NETADP_ID_SZ)
#define DRIVERHWID                  VBOXNETCFGWIN_NETADP_ID_WSZ

/* We assume the following name matches the device description in vboxnetadp6.inf */
#define HOSTONLY_ADAPTER_NAME_SZ    "VirtualBox Host-Only Ethernet Adapter"
#define HOSTONLY_ADAPTER_NAME_WSZ   RT_CONCAT(L,HOSTONLY_ADAPTER_NAME_SZ)

#define VBOX_CONNECTION_NAME_SZ     "VirtualBox Host-Only Network"
#define VBOX_CONNECTION_NAME_WSZ    RT_CONCAT(L,VBOX_CONNECTION_NAME_SZ)

#define VBOXNETCFGWIN_NETLWF_ID     L"oracle_VBoxNetLwf"



/*********************************************************************************************************************************
*   Global Variables                                                                                                             *
*********************************************************************************************************************************/
static PFNVBOXNETCFGLOGGER volatile g_pfnLogger = NULL;

/*
 * Wrappers for HelpAPI functions
 */
typedef void FNINITIALIZEIPINTERFACEENTRY( _Inout_ PMIB_IPINTERFACE_ROW row);
typedef FNINITIALIZEIPINTERFACEENTRY *PFNINITIALIZEIPINTERFACEENTRY;

typedef NETIOAPI_API FNGETIPINTERFACEENTRY( _Inout_ PMIB_IPINTERFACE_ROW row);
typedef FNGETIPINTERFACEENTRY *PFNGETIPINTERFACEENTRY;

typedef NETIOAPI_API FNSETIPINTERFACEENTRY( _Inout_ PMIB_IPINTERFACE_ROW row);
typedef FNSETIPINTERFACEENTRY *PFNSETIPINTERFACEENTRY;


/*********************************************************************************************************************************
*   Internal Functions                                                                                                           *
*********************************************************************************************************************************/
static PFNINITIALIZEIPINTERFACEENTRY    g_pfnInitializeIpInterfaceEntry = NULL;
static PFNGETIPINTERFACEENTRY           g_pfnGetIpInterfaceEntry        = NULL;
static PFNSETIPINTERFACEENTRY           g_pfnSetIpInterfaceEntry        = NULL;

static void DoLogging(const char *pszString, ...);

/*
 * Forward declaration for using vboxNetCfgWinSetupMetric()
 */
static HRESULT vboxNetCfgWinSetupMetric(IN NET_LUID *pLuid);
static HRESULT vboxNetCfgWinGetInterfaceLUID(IN HKEY hKey, OUT NET_LUID *pLUID);



static HRESULT vboxNetCfgWinINetCfgLock(IN INetCfg *pNetCfg,
                                        IN LPCWSTR pszwClientDescription,
                                        IN DWORD cmsTimeout,
                                        OUT LPWSTR *ppszwClientDescription)
{
    INetCfgLock *pLock;
    HRESULT hr = pNetCfg->QueryInterface(IID_INetCfgLock, (PVOID *)&pLock);
    if (FAILED(hr))
    {
        NonStandardLogFlow(("QueryInterface failed: %Rhrc\n", hr));
        return hr;
    }

    hr = pLock->AcquireWriteLock(cmsTimeout, pszwClientDescription, ppszwClientDescription);
    if (hr == S_FALSE)
        NonStandardLogFlow(("Write lock busy\n"));
    else if (FAILED(hr))
        NonStandardLogFlow(("AcquireWriteLock failed: %Rhrc\n", hr));

    pLock->Release();
    return hr;
}

static HRESULT vboxNetCfgWinINetCfgUnlock(IN INetCfg *pNetCfg)
{
    INetCfgLock *pLock;
    HRESULT hr = pNetCfg->QueryInterface(IID_INetCfgLock, (PVOID *)&pLock);
    if (FAILED(hr))
    {
        NonStandardLogFlow(("QueryInterface failed: %Rhrc\n", hr));
        return hr;
    }

    hr = pLock->ReleaseWriteLock();
    if (FAILED(hr))
        NonStandardLogFlow(("ReleaseWriteLock failed: %Rhrc\n", hr));

    pLock->Release();
    return hr;
}

VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinQueryINetCfg(OUT INetCfg **ppNetCfg,
                                                      IN BOOL fGetWriteLock,
                                                      IN LPCWSTR pszwClientDescription,
                                                      IN DWORD cmsTimeout,
                                                      OUT LPWSTR *ppszwClientDescription)
{
    INetCfg *pNetCfg = NULL;
    HRESULT hr = CoCreateInstance(CLSID_CNetCfg, NULL, CLSCTX_INPROC_SERVER, IID_INetCfg, (PVOID *)&pNetCfg);
    if (FAILED(hr))
    {
        NonStandardLogFlow(("CoCreateInstance failed: %Rhrc\n", hr));
        return hr;
    }

    if (fGetWriteLock)
    {
        hr = vboxNetCfgWinINetCfgLock(pNetCfg, pszwClientDescription, cmsTimeout, ppszwClientDescription);
        if (hr == S_FALSE)
        {
            NonStandardLogFlow(("Write lock is busy\n", hr));
            hr = NETCFG_E_NO_WRITE_LOCK;
        }
    }

    if (SUCCEEDED(hr))
    {
        hr = pNetCfg->Initialize(NULL);
        if (SUCCEEDED(hr))
        {
            *ppNetCfg = pNetCfg;
            return S_OK;
        }
        NonStandardLogFlow(("Initialize failed: %Rhrc\n", hr));
    }

    pNetCfg->Release();
    return hr;
}

VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinReleaseINetCfg(IN INetCfg *pNetCfg, IN BOOL fHasWriteLock)
{
    if (!pNetCfg) /* If network config has been released already, just bail out. */
    {
        NonStandardLogFlow(("Warning: No network config given but write lock is set to TRUE\n"));
        return S_OK;
    }

    HRESULT hr = pNetCfg->Uninitialize();
    if (FAILED(hr))
    {
        NonStandardLogFlow(("Uninitialize failed: %Rhrc\n", hr));
        /* Try to release the write lock below. */
    }

    if (fHasWriteLock)
    {
        HRESULT hr2 = vboxNetCfgWinINetCfgUnlock(pNetCfg);
        if (FAILED(hr2))
            NonStandardLogFlow(("vboxNetCfgWinINetCfgUnlock failed: %Rhrc\n", hr2));
        if (SUCCEEDED(hr))
            hr = hr2;
    }

    pNetCfg->Release();
    return hr;
}

static HRESULT vboxNetCfgWinGetComponentByGuidEnum(IEnumNetCfgComponent *pEnumNcc,
                                                   IN const GUID *pGuid,
                                                   OUT INetCfgComponent **ppNcc)
{
    HRESULT hr = pEnumNcc->Reset();
    if (FAILED(hr))
    {
        NonStandardLogFlow(("Reset failed: %Rhrc\n", hr));
        return hr;
    }

    INetCfgComponent *pNcc = NULL;
    while ((hr = pEnumNcc->Next(1, &pNcc, NULL)) == S_OK)
    {
        ULONG uComponentStatus = 0;
        hr = pNcc->GetDeviceStatus(&uComponentStatus);
        if (SUCCEEDED(hr))
        {
            if (uComponentStatus == 0)
            {
                GUID NccGuid;
                hr = pNcc->GetInstanceGuid(&NccGuid);

                if (SUCCEEDED(hr))
                {
                    if (NccGuid == *pGuid)
                    {
                        /* found the needed device */
                        *ppNcc = pNcc;
                        break;
                    }
                }
                else
                    NonStandardLogFlow(("GetInstanceGuid failed: %Rhrc\n", hr));
            }
        }

        pNcc->Release();
    }
    return hr;
}

VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinGetComponentByGuid(IN INetCfg *pNc,
                                                            IN const GUID *pguidClass,
                                                            IN const GUID * pComponentGuid,
                                                            OUT INetCfgComponent **ppncc)
{
    IEnumNetCfgComponent *pEnumNcc = NULL;
    HRESULT hr = pNc->EnumComponents(pguidClass, &pEnumNcc);
    if (SUCCEEDED(hr))
    {
        hr = vboxNetCfgWinGetComponentByGuidEnum(pEnumNcc, pComponentGuid, ppncc);
        if (hr == S_FALSE)
            NonStandardLogFlow(("Component not found\n"));
        else if (FAILED(hr))
            NonStandardLogFlow(("vboxNetCfgWinGetComponentByGuidEnum failed: %Rhrc\n", hr));
        pEnumNcc->Release();
    }
    else
        NonStandardLogFlow(("EnumComponents failed: %Rhrc\n", hr));
    return hr;
}

static HRESULT vboxNetCfgWinQueryInstaller(IN INetCfg *pNetCfg, IN const GUID *pguidClass, INetCfgClassSetup **ppSetup)
{
    HRESULT hr = pNetCfg->QueryNetCfgClass(pguidClass, IID_INetCfgClassSetup, (void **)ppSetup);
    if (FAILED(hr))
        NonStandardLogFlow(("QueryNetCfgClass failed: %Rhrc\n", hr));
    return hr;
}

VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinInstallComponent(IN INetCfg *pNetCfg, IN LPCWSTR pszwComponentId,
                                                          IN const GUID *pguidClass, OUT INetCfgComponent **ppComponent)
{
    INetCfgClassSetup *pSetup;
    HRESULT hr = vboxNetCfgWinQueryInstaller(pNetCfg, pguidClass, &pSetup);
    if (FAILED(hr))
    {
        NonStandardLogFlow(("vboxNetCfgWinQueryInstaller failed: %Rhrc\n", hr));
        return hr;
    }

    OBO_TOKEN Token;
    RT_ZERO(Token);
    Token.Type = OBO_USER;

    INetCfgComponent *pTempComponent = NULL;
    hr = pSetup->Install(pszwComponentId, &Token,
                         0,    /* IN DWORD dwSetupFlags */
                         0,    /* IN DWORD dwUpgradeFromBuildNo */
                         NULL, /* IN LPCWSTR pszwAnswerFile */
                         NULL, /* IN LPCWSTR pszwAnswerSections */
                         &pTempComponent);
    if (SUCCEEDED(hr))
    {
        if (pTempComponent != NULL)
        {
            /*
             *   Set default metric value of interface to fix multicast issue
             *   See @bugref{6379} for details.
             */
            HKEY    hKey = (HKEY)INVALID_HANDLE_VALUE;
            HRESULT hrc2 = pTempComponent->OpenParamKey(&hKey);

            /* Set default metric value for host-only interface only */
            if (   SUCCEEDED(hrc2)
                && hKey != (HKEY)INVALID_HANDLE_VALUE
                /* Original was weird: && wcsnicmp(pszwComponentId, VBOXNETCFGWIN_NETADP_ID_WSZ, 256) == 0) */
                && RTUtf16ICmpAscii(pszwComponentId, VBOXNETCFGWIN_NETADP_ID_SZ) == 0)
            {
                NET_LUID luid;
                hrc2 = vboxNetCfgWinGetInterfaceLUID(hKey, &luid);

                /* Close the key as soon as possible. See @bugref{7973}. */
                RegCloseKey(hKey);
                hKey = (HKEY)INVALID_HANDLE_VALUE;

                if (FAILED(hrc2))
                {
                    /*
                     *   The setting of Metric is not very important functionality,
                     *   So we will not break installation process due to this error.
                     */
                    NonStandardLogFlow(("VBoxNetCfgWinInstallComponent Warning! vboxNetCfgWinGetInterfaceLUID failed, default metric for new interface will not be set: %Rhrc\n", hrc2));
                }
                else
                {
                    hrc2 = vboxNetCfgWinSetupMetric(&luid);
                    if (FAILED(hrc2))
                    {
                        /*
                         *   The setting of Metric is not very important functionality,
                         *   So we will not break installation process due to this error.
                         */
                        NonStandardLogFlow(("VBoxNetCfgWinInstallComponent Warning! vboxNetCfgWinSetupMetric failed, default metric for new interface will not be set: %Rhrc\n", hrc2));
                    }
                }
            }
            if (hKey != (HKEY)INVALID_HANDLE_VALUE)
                RegCloseKey(hKey);
            if (ppComponent != NULL)
                *ppComponent = pTempComponent;
            else
                pTempComponent->Release();
        }

        /* ignore the apply failure */
        HRESULT hrc3 = pNetCfg->Apply();
        Assert(hrc3 == S_OK);
        if (hrc3 != S_OK)
            NonStandardLogFlow(("Apply failed: %Rhrc\n", hrc3));
    }
    else
        NonStandardLogFlow(("Install failed: %Rhrc\n", hr));

    pSetup->Release();
    return hr;
}

static HRESULT vboxNetCfgWinInstallInfAndComponent(IN INetCfg *pNetCfg, IN LPCWSTR pszwComponentId, IN const GUID *pguidClass,
                                                   IN LPCWSTR const *apwszInfPaths, IN UINT cInfPaths,
                                                   OUT INetCfgComponent **ppComponent)
{
    NonStandardLogFlow(("Installing %u INF files ...\n", cInfPaths));

    HRESULT hr              = S_OK;
    UINT    cFilesProcessed = 0;
    for (; cFilesProcessed < cInfPaths; cFilesProcessed++)
    {
        NonStandardLogFlow(("Installing INF file \"%ls\" ...\n", apwszInfPaths[cFilesProcessed]));
        hr = VBoxDrvCfgInfInstall(apwszInfPaths[cFilesProcessed]);
        if (FAILED(hr))
        {
            NonStandardLogFlow(("VBoxNetCfgWinInfInstall failed: %Rhrc\n", hr));
            break;
        }
    }

    if (SUCCEEDED(hr))
    {
        hr = VBoxNetCfgWinInstallComponent(pNetCfg, pszwComponentId, pguidClass, ppComponent);
        if (FAILED(hr))
            NonStandardLogFlow(("VBoxNetCfgWinInstallComponent failed: %Rhrc\n", hr));
    }

    if (FAILED(hr))
    {
        NonStandardLogFlow(("Installation failed, rolling back installation set ...\n"));

        do
        {
            HRESULT hr2 = VBoxDrvCfgInfUninstall(apwszInfPaths[cFilesProcessed], 0);
            if (FAILED(hr2))
                NonStandardLogFlow(("VBoxDrvCfgInfUninstall failed: %Rhrc\n", hr2));
                /* Keep going. */
            if (!cFilesProcessed)
                break;
        } while (cFilesProcessed--);

        NonStandardLogFlow(("Rollback complete\n"));
    }

    return hr;
}

VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinUninstallComponent(IN INetCfg *pNetCfg, IN INetCfgComponent *pComponent)
{
    GUID GuidClass;
    HRESULT hr = pComponent->GetClassGuid(&GuidClass);
    if (FAILED(hr))
    {
        NonStandardLogFlow(("GetClassGuid failed: %Rhrc\n", hr));
        return hr;
    }

    INetCfgClassSetup *pSetup = NULL;
    hr = vboxNetCfgWinQueryInstaller(pNetCfg, &GuidClass, &pSetup);
    if (FAILED(hr))
    {
        NonStandardLogFlow(("vboxNetCfgWinQueryInstaller failed: %Rhrc\n", hr));
        return hr;
    }

    OBO_TOKEN Token;
    RT_ZERO(Token);
    Token.Type = OBO_USER;

    hr = pSetup->DeInstall(pComponent, &Token, NULL /* OUT LPWSTR *pmszwRefs */);
    if (SUCCEEDED(hr))
    {
        hr = pNetCfg->Apply();
        if (FAILED(hr))
            NonStandardLogFlow(("Apply failed: %Rhrc\n", hr));
    }
    else
        NonStandardLogFlow(("DeInstall failed: %Rhrc\n", hr));

    if (pSetup)
        pSetup->Release();
    return hr;
}

typedef BOOL (*PFN_VBOXNETCFGWIN_NETCFGENUM_CALLBACK_T)(IN INetCfg *pNetCfg, IN INetCfgComponent *pNetCfgComponent,
                                                        PVOID pvContext);

static HRESULT vboxNetCfgWinEnumNetCfgComponents(IN INetCfg *pNetCfg,
                                                 IN const GUID *pguidClass,
                                                 PFN_VBOXNETCFGWIN_NETCFGENUM_CALLBACK_T pfnCallback,
                                                 PVOID pContext)
{
    IEnumNetCfgComponent *pEnumComponent = NULL;
    HRESULT hr = pNetCfg->EnumComponents(pguidClass, &pEnumComponent);
    if (SUCCEEDED(hr))
    {
        INetCfgComponent *pNetCfgComponent;
        hr = pEnumComponent->Reset();
        for (;;)
        {
            hr = pEnumComponent->Next(1, &pNetCfgComponent, NULL);
            if (hr == S_OK)
            {
//                ULONG uComponentStatus;
//                hr = pNcc->GetDeviceStatus(&uComponentStatus);
//                if (SUCCEEDED(hr))
                BOOL fResult = FALSE;
                if (pNetCfgComponent)
                {
                    fResult = pfnCallback(pNetCfg, pNetCfgComponent, pContext);
                    pNetCfgComponent->Release();
                }

                if (!fResult)
                    break;
            }
            else
            {
                if (hr == S_FALSE)
                    hr = S_OK; /* no more components */
                else
                    NonStandardLogFlow(("Next failed: %Rhrc\n", hr));
                break;
            }
        }
        pEnumComponent->Release();
    }
    return hr;
}

/** PFNVBOXNETCFGWINNETENUMCALLBACK */
static BOOL vboxNetCfgWinRemoveAllNetDevicesOfIdCallback(HDEVINFO hDevInfo, PSP_DEVINFO_DATA pDev, PVOID pvContext)
{
    RT_NOREF1(pvContext);

    SP_REMOVEDEVICE_PARAMS rmdParams;
    RT_ZERO(rmdParams);
    rmdParams.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
    rmdParams.ClassInstallHeader.InstallFunction = DIF_REMOVE;
    rmdParams.Scope = DI_REMOVEDEVICE_GLOBAL;

    if (SetupDiSetClassInstallParams(hDevInfo,pDev,
                                     &rmdParams.ClassInstallHeader, sizeof(rmdParams)))
    {
        if (SetupDiSetSelectedDevice(hDevInfo, pDev))
        {
#ifndef VBOXNETCFG_DELAYEDRENAME
            /* Figure out NetCfgInstanceId. */
            HKEY hKey = SetupDiOpenDevRegKey(hDevInfo,
                                             pDev,
                                             DICS_FLAG_GLOBAL,
                                             0,
                                             DIREG_DRV,
                                             KEY_READ);
            if (hKey == INVALID_HANDLE_VALUE)
                NonStandardLogFlow(("vboxNetCfgWinRemoveAllNetDevicesOfIdCallback: SetupDiOpenDevRegKey failed with error %u\n",
                                    GetLastError()));
            else
            {
                WCHAR wszCfgGuidString[50] = { L'' };
                DWORD cbSize = sizeof(wszCfgGuidString) - sizeof(WCHAR); /* make sure we get a terminated string back */
                DWORD dwValueType = 0;
                LSTATUS lrc = RegQueryValueExW(hKey, L"NetCfgInstanceId", NULL, &dwValueType, (LPBYTE)wszCfgGuidString, &cbSize);
                if (lrc == ERROR_SUCCESS)
                {
                    /** @todo r=bird: original didn't check the type here, just assumed it was a
                     * valid zero terminated string. (zero term handled by -sizeof(WHCAR) above now). */
                    if (dwValueType == REG_SZ || dwValueType == REG_EXPAND_SZ || dwValueType == REG_EXPAND_SZ)
                    {
                        NonStandardLogFlow(("vboxNetCfgWinRemoveAllNetDevicesOfIdCallback: Processing device ID \"%ls\"\n",
                                            wszCfgGuidString));

                        /* Figure out device name. */
                        WCHAR wszDevName[256 + 1] = {0};
                        if (SetupDiGetDeviceRegistryPropertyW(hDevInfo, pDev, SPDRP_FRIENDLYNAME, NULL, (PBYTE)wszDevName,
                                                              sizeof(wszDevName) - sizeof(WCHAR) /* yes, in bytes */, NULL))
                        {
                            /*
                             * Rename the connection before removing the device. This will
                             * hopefully prevent an error when we will be attempting
                             * to rename a newly created connection (see @bugref{6740}).
                             */
                            WCHAR wszNewName[RT_ELEMENTS(wszDevName) + 128 /* ensure sufficient buffer */];
                            HRESULT hr = VBoxNetCfgWinGenHostonlyConnectionName(wszDevName, wszNewName,
                                                                                RT_ELEMENTS(wszNewName) - 10 /*removed++*/,
                                                                                NULL);
                            RTUtf16CatAscii(wszNewName, sizeof(wszNewName), " removed");
                            if (SUCCEEDED(hr))
                                hr = VBoxNetCfgWinRenameConnection(wszCfgGuidString, wszNewName);
                            //NonStandardLogFlow(("VBoxNetCfgWinRenameConnection(%S,%S) => 0x%x\n", wszCfgGuidString, TempName, hr_tmp));
                        }
                        else
                            NonStandardLogFlow(("vboxNetCfgWinRemoveAllNetDevicesOfIdCallback: Failed to get friendly name for device \"%ls\"\n",
                                                wszCfgGuidString));
                    }
                    else
                        NonStandardLogFlow(("vboxNetCfgWinRemoveAllNetDevicesOfIdCallback: Friendly name for \"%S\" isn't a string: %d\n",
                                            wszCfgGuidString, dwValueType
                }
                else
                    NonStandardLogFlow(("vboxNetCfgWinRemoveAllNetDevicesOfIdCallback: Querying instance ID failed with %u (%#x)\n",
                                        lrc, lrc));

                RegCloseKey(hKey);
            }
#endif /* VBOXNETCFG_DELAYEDRENAME */

            if (SetupDiCallClassInstaller(DIF_REMOVE, hDevInfo, pDev))
            {
                SP_DEVINSTALL_PARAMS_W DevParams = { sizeof(DevParams) };
                if (SetupDiGetDeviceInstallParams(hDevInfo, pDev, &DevParams))
                {
                    if (   (DevParams.Flags & DI_NEEDRESTART)
                        || (DevParams.Flags & DI_NEEDREBOOT))
                        NonStandardLog(("vboxNetCfgWinRemoveAllNetDevicesOfIdCallback: A reboot is required\n"));
                }
                else
                    NonStandardLogFlow(("vboxNetCfgWinRemoveAllNetDevicesOfIdCallback: SetupDiGetDeviceInstallParams failed with %u\n",
                                        GetLastError()));
            }
            else
                NonStandardLogFlow(("vboxNetCfgWinRemoveAllNetDevicesOfIdCallback: SetupDiCallClassInstaller failed with %u\n",
                                    GetLastError()));
        }
        else
            NonStandardLogFlow(("vboxNetCfgWinRemoveAllNetDevicesOfIdCallback: SetupDiSetSelectedDevice failed with %u\n",
                                GetLastError()));
    }
    else
        NonStandardLogFlow(("vboxNetCfgWinRemoveAllNetDevicesOfIdCallback: SetupDiSetClassInstallParams failed with %u\n",
                            GetLastError()));

    /* Continue enumeration. */
    return TRUE;
}

typedef struct VBOXNECTFGWINPROPCHANGE
{
    VBOXNECTFGWINPROPCHANGE_TYPE_T enmPcType;
    HRESULT hr;
} VBOXNECTFGWINPROPCHANGE, *PVBOXNECTFGWINPROPCHANGE;

static BOOL vboxNetCfgWinPropChangeAllNetDevicesOfIdCallback(HDEVINFO hDevInfo, PSP_DEVINFO_DATA pDev, PVOID pContext)
{
    PVBOXNECTFGWINPROPCHANGE pPc = (PVBOXNECTFGWINPROPCHANGE)pContext;

    SP_PROPCHANGE_PARAMS PcParams;
    RT_ZERO(PcParams);
    PcParams.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
    PcParams.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE;
    PcParams.Scope = DICS_FLAG_GLOBAL;

    switch (pPc->enmPcType)
    {
        case VBOXNECTFGWINPROPCHANGE_TYPE_DISABLE:
            PcParams.StateChange = DICS_DISABLE;
            NonStandardLogFlow(("vboxNetCfgWinPropChangeAllNetDevicesOfIdCallback: Change type (DICS_DISABLE): %d\n", pPc->enmPcType));
            break;
        case VBOXNECTFGWINPROPCHANGE_TYPE_ENABLE:
            PcParams.StateChange = DICS_ENABLE;
            NonStandardLogFlow(("vboxNetCfgWinPropChangeAllNetDevicesOfIdCallback: Change type (DICS_ENABLE): %d\n", pPc->enmPcType));
            break;
        default:
            NonStandardLogFlow(("vboxNetCfgWinPropChangeAllNetDevicesOfIdCallback: Unexpected prop change type: %d\n", pPc->enmPcType));
            pPc->hr = E_INVALIDARG;
            return FALSE;
    }

    if (SetupDiSetClassInstallParamsW(hDevInfo, pDev, &PcParams.ClassInstallHeader, sizeof(PcParams)))
    {
        if (SetupDiSetSelectedDevice(hDevInfo, pDev))
        {
            if (SetupDiCallClassInstaller(DIF_PROPERTYCHANGE, hDevInfo, pDev))
            {
                SP_DEVINSTALL_PARAMS_W DevParams = { sizeof(DevParams) };
                if (SetupDiGetDeviceInstallParamsW(hDevInfo, pDev, &DevParams))
                {
                    if (   (DevParams.Flags & DI_NEEDRESTART)
                        || (DevParams.Flags & DI_NEEDREBOOT))
                        NonStandardLog(("vboxNetCfgWinPropChangeAllNetDevicesOfIdCallback: A reboot is required\n"));
                }
                else
                    NonStandardLogFlow(("vboxNetCfgWinPropChangeAllNetDevicesOfIdCallback: SetupDiGetDeviceInstallParams failed with %u\n",
                                        GetLastError()));
            }
            else
                NonStandardLogFlow(("vboxNetCfgWinPropChangeAllNetDevicesOfIdCallback: SetupDiCallClassInstaller failed with %u\n",
                                    GetLastError()));
        }
        else
            NonStandardLogFlow(("SetupDiSetSelectedDevice failed with %u\n", GetLastError()));
    }
    else
        NonStandardLogFlow(("SetupDiSetClassInstallParams failed with %u\n", GetLastError()));

    /* Continue enumeration. */
    return TRUE;
}

typedef BOOL (*PFNVBOXNETCFGWINNETENUMCALLBACK)(HDEVINFO hDevInfo, PSP_DEVINFO_DATA pDev, PVOID pContext);

static HRESULT vboxNetCfgWinEnumNetDevices(LPCWSTR pwszPnPId, PFNVBOXNETCFGWINNETENUMCALLBACK pfnCallback, PVOID pvContext)
{
    NonStandardLogFlow(("VBoxNetCfgWinEnumNetDevices: Searching for: %ls\n", pwszPnPId));

    HRESULT hr;
    HDEVINFO hDevInfo = SetupDiGetClassDevsExW(&GUID_DEVCLASS_NET,
                                               NULL,            /* IN PCTSTR Enumerator, OPTIONAL */
                                               NULL,            /* IN HWND hwndParent, OPTIONAL */
                                               DIGCF_PRESENT,   /* IN DWORD Flags,*/
                                               NULL,            /* IN HDEVINFO DeviceInfoSet, OPTIONAL */
                                               NULL,            /* IN PCTSTR MachineName, OPTIONAL */
                                               NULL             /* IN PVOID Reserved */);
    if (hDevInfo != INVALID_HANDLE_VALUE)
    {
        size_t const cwcPnPId = RTUtf16Len(pwszPnPId);
        DWORD        winEr    = NO_ERROR;
        DWORD        dwDevId  = 0;
        DWORD        cbBuffer = 0;
        PBYTE        pbBuffer = NULL;
        for (;;)
        {
            SP_DEVINFO_DATA Dev;
            memset(&Dev, 0, sizeof(SP_DEVINFO_DATA));
            Dev.cbSize = sizeof(SP_DEVINFO_DATA);

            if (!SetupDiEnumDeviceInfo(hDevInfo, dwDevId, &Dev))
            {
                winEr = GetLastError();
                if (winEr == ERROR_NO_MORE_ITEMS)
                    winEr = NO_ERROR;
                break;
            }

            NonStandardLogFlow(("VBoxNetCfgWinEnumNetDevices: Enumerating device %u ... \n", dwDevId));
            dwDevId++;

            DWORD cbRequired = 0;
            SetLastError(0);
            if (!SetupDiGetDeviceRegistryPropertyW(hDevInfo, &Dev,
                                                   SPDRP_HARDWAREID, /* IN DWORD Property */
                                                   NULL,             /* OUT PDWORD PropertyRegDataType OPTIONAL */
                                                   pbBuffer,         /* OUT PBYTE PropertyBuffer */
                                                   cbBuffer,         /* IN DWORD PropertyBufferSize */
                                                   &cbRequired       /* OUT PDWORD RequiredSize OPTIONAL */))
            {
                winEr = GetLastError();
                if (winEr != ERROR_INSUFFICIENT_BUFFER)
                {
                    if (winEr == ERROR_INVALID_DATA)
                    {
                        NonStandardLogFlow(("VBoxNetCfgWinEnumNetDevices: SetupDiGetDeviceRegistryPropertyW (1) failed with ERROR_INVALID_DATA - ignoring, skipping to next device\n"));
                        continue;
                    }
                    NonStandardLogFlow(("VBoxNetCfgWinEnumNetDevices: SetupDiGetDeviceRegistryPropertyW (1) failed with %u\n", winEr));
                    break;
                }
                winEr = NO_ERROR;

                cbBuffer = RT_ALIGN_32(cbRequired, 64);
                void *pvNew = RTMemRealloc(pbBuffer, cbBuffer);
                if (pvNew)
                    pbBuffer = (PBYTE)pvNew;
                else
                {
                    NonStandardLogFlow(("VBoxNetCfgWinEnumNetDevices: Out of memory allocating %u bytes\n", cbBuffer));
                    winEr = ERROR_OUTOFMEMORY;
                    break;
                }

                if (!SetupDiGetDeviceRegistryPropertyW(hDevInfo, &Dev,
                                                       SPDRP_HARDWAREID, /* IN DWORD Property */
                                                       NULL,             /* OUT PDWORD PropertyRegDataType, OPTIONAL */
                                                       pbBuffer,         /* OUT PBYTE PropertyBuffer */
                                                       cbBuffer,         /* IN DWORD PropertyBufferSize */
                                                       &cbRequired       /* OUT PDWORD RequiredSize OPTIONAL */))
                {
                    winEr = GetLastError();
                    NonStandardLogFlow(("VBoxNetCfgWinEnumNetDevices: SetupDiGetDeviceRegistryPropertyW (2) failed with %u\n",
                                        winEr));
                    break;
                }
            }

            PWSTR  pwszCurId = (PWSTR)pbBuffer;
            size_t cwcCurId  = RTUtf16Len(pwszCurId);

            NonStandardLogFlow(("VBoxNetCfgWinEnumNetDevices: Device %u: %ls\n", dwDevId, pwszCurId));

            if (cwcCurId >= cwcPnPId)
            {
                NonStandardLogFlow(("!RTUtf16NICmp(pwszCurId = (%ls), pwszPnPId = (%ls), cwcPnPId = (%d))\n", pwszCurId, pwszPnPId, cwcPnPId));

                pwszCurId += cwcCurId - cwcPnPId;
                if (!RTUtf16NICmp(pwszCurId, pwszPnPId, cwcPnPId))
                {
                    if (!pfnCallback(hDevInfo, &Dev, pvContext))
                        break;
                }
            }
        }

        NonStandardLogFlow(("VBoxNetCfgWinEnumNetDevices: Found %u devices total\n", dwDevId));

        if (pbBuffer)
            RTMemFree(pbBuffer);

        hr = HRESULT_FROM_WIN32(winEr);

        SetupDiDestroyDeviceInfoList(hDevInfo);
    }
    else
    {
        DWORD winEr = GetLastError();
        NonStandardLogFlow(("VBoxNetCfgWinEnumNetDevices: SetupDiGetClassDevsExW failed with %u\n", winEr));
        hr = HRESULT_FROM_WIN32(winEr);
    }

    NonStandardLogFlow(("VBoxNetCfgWinEnumNetDevices: Ended with hr (0x%x)\n", hr));
    return hr;
}

VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinRemoveAllNetDevicesOfId(IN LPCWSTR pwszPnPId)
{
    return vboxNetCfgWinEnumNetDevices(pwszPnPId, vboxNetCfgWinRemoveAllNetDevicesOfIdCallback, NULL);
}

VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinPropChangeAllNetDevicesOfId(IN LPCWSTR pwszPnPId, VBOXNECTFGWINPROPCHANGE_TYPE_T enmPcType)
{
    VBOXNECTFGWINPROPCHANGE Pc;
    Pc.enmPcType = enmPcType;
    Pc.hr = S_OK;
    NonStandardLogFlow(("Calling VBoxNetCfgWinEnumNetDevices with pwszPnPId (= %ls) and vboxNetCfgWinPropChangeAllNetDevicesOfIdCallback\n", pwszPnPId));

    HRESULT hr = vboxNetCfgWinEnumNetDevices(pwszPnPId, vboxNetCfgWinPropChangeAllNetDevicesOfIdCallback, &Pc);
    if (!SUCCEEDED(hr))
    {
        NonStandardLogFlow(("VBoxNetCfgWinEnumNetDevices failed 0x%x\n", hr));
        return hr;
    }

    if (!SUCCEEDED(Pc.hr))
    {
        NonStandardLogFlow(("vboxNetCfgWinPropChangeAllNetDevicesOfIdCallback failed 0x%x\n", Pc.hr));
        return Pc.hr;
    }

    return S_OK;
}



/*********************************************************************************************************************************
*   Logging                                                                                                                      *
*********************************************************************************************************************************/

static void DoLogging(const char *pszString, ...)
{
    PFNVBOXNETCFGLOGGER pfnLogger = g_pfnLogger;
    if (pfnLogger)
    {
        char szBuffer[4096];
        va_list va;
        va_start(va, pszString);
        RTStrPrintfV(szBuffer, RT_ELEMENTS(szBuffer), pszString, va);
        va_end(va);

        pfnLogger(szBuffer);
    }
}

VBOXNETCFGWIN_DECL(void) VBoxNetCfgWinSetLogging(IN PFNVBOXNETCFGLOGGER pfnLogger)
{
    g_pfnLogger = pfnLogger;
}



/*********************************************************************************************************************************
*   IP configuration API                                                                                                         *
*********************************************************************************************************************************/
/* network settings config */
#if 1 /** @todo r=bird: Can't we replace this with VBox/com/ptr.h? */
/**
 *  Strong referencing operators. Used as a second argument to ComPtr<>/ComObjPtr<>.
 */
template<class C>
class ComStrongRef
{
protected:

    static void addref(C *p)  { p->AddRef(); }
    static void release(C *p) { p->Release(); }
};


/**
 *  Base template for smart COM pointers. Not intended to be used directly.
 */
template<class C, template<class> class RefOps = ComStrongRef>
class ComPtrBase : protected RefOps<C>
{
public:

    /* special template to disable AddRef()/Release() */
    template<class I>
    class NoAddRefRelease : public I
    {
    public:
        virtual ~NoAddRefRelease() { /* Make VC++ 19.2 happy. */ }
    private:
#ifndef VBOX_WITH_XPCOM
        STDMETHOD_(ULONG, AddRef)() = 0;
        STDMETHOD_(ULONG, Release)() = 0;
#else
        NS_IMETHOD_(nsrefcnt) AddRef(void) = 0;
        NS_IMETHOD_(nsrefcnt) Release(void) = 0;
#endif
    };

protected:

    ComPtrBase() : p(NULL) {}
    ComPtrBase(const ComPtrBase &that) : p(that.p) { addref(); }
    ComPtrBase(C *that_p) : p(that_p)              { addref(); }

    ~ComPtrBase() { release(); }

    ComPtrBase &operator=(const ComPtrBase &that)
    {
        safe_assign(that.p);
        return *this;
    }

    ComPtrBase &operator=(C *that_p)
    {
        safe_assign(that_p);
        return *this;
    }

public:

    void setNull()
    {
        release();
        p = NULL;
    }

    bool isNull() const
    {
        return (p == NULL);
    }

    bool operator!() const              { return isNull(); }

    bool operator<(C* that_p) const     { return p < that_p; }
    bool operator==(C* that_p) const    { return p == that_p; }

    template<class I>
    bool equalsTo(I *aThat) const
    {
        return ComPtrEquals(p, aThat);
    }

    template<class OC>
    bool equalsTo(const ComPtrBase<OC> &oc) const
    {
        return equalsTo((OC *) oc);
    }

    /** Intended to pass instances as in parameters to interface methods */
    operator C *() const { return p; }

    /**
     *  Dereferences the instance (redirects the -> operator to the managed
     *  pointer).
     */
    NoAddRefRelease<C> *operator->() const
    {
        AssertMsg (p, ("Managed pointer must not be null\n"));
        return (NoAddRefRelease <C> *) p;
    }

    template<class I>
    HRESULT queryInterfaceTo(I **pp) const
    {
        if (pp)
        {
            if (p)
                return p->QueryInterface(COM_IIDOF(I), (void **)pp);
            *pp = NULL;
            return S_OK;
        }
        return E_INVALIDARG;
    }

    /** Intended to pass instances as out parameters to interface methods */
    C **asOutParam()
    {
        setNull();
        return &p;
    }

private:

    void addref()
    {
        if (p)
            RefOps<C>::addref(p);
    }

    void release()
    {
        if (p)
            RefOps<C>::release(p);
    }

    void safe_assign(C *that_p)
    {
        /* be aware of self-assignment */
        if (that_p)
            RefOps<C>::addref(that_p);
        release();
        p = that_p;
    }

    C *p;
};

/**
 *  Smart COM pointer wrapper that automatically manages refcounting of
 *  interface pointers.
 *
 *  @param I    COM interface class
 */
template<class I, template<class> class RefOps = ComStrongRef>
class ComPtr : public ComPtrBase<I, RefOps>
{
    typedef ComPtrBase<I, RefOps> Base;

public:

    ComPtr() : Base() {}
    ComPtr(const ComPtr &that) : Base(that) {}
    ComPtr&operator= (const ComPtr &that)
    {
        Base::operator=(that);
        return *this;
    }

    template<class OI>
    ComPtr(OI *that_p) : Base () { operator=(that_p); }

    /* specialization for I */
    ComPtr(I *that_p) : Base (that_p) {}

    template <class OC>
    ComPtr(const ComPtr<OC, RefOps> &oc) : Base() { operator=((OC *) oc); }

    template<class OI>
    ComPtr &operator=(OI *that_p)
    {
        if (that_p)
            that_p->QueryInterface(COM_IIDOF(I), (void **)Base::asOutParam());
        else
            Base::setNull();
        return *this;
    }

    /* specialization for I */
    ComPtr &operator=(I *that_p)
    {
        Base::operator=(that_p);
        return *this;
    }

    template<class OC>
    ComPtr &operator=(const ComPtr<OC, RefOps> &oc)
    {
        return operator=((OC *) oc);
    }
};
#endif

static HRESULT netIfWinFindAdapterClassById(IWbemServices *pSvc, const GUID *pGuid, IWbemClassObject **pAdapterConfig)
{
    HRESULT hr;

    WCHAR wszGuid[50];
    int cwcGuid = StringFromGUID2(*pGuid, wszGuid, RT_ELEMENTS(wszGuid));
    if (cwcGuid)
    {
        com::BstrFmt bstrQuery("SELECT * FROM Win32_NetworkAdapterConfiguration WHERE SettingID = \"%ls\"", wszGuid);
        IEnumWbemClassObject* pEnumerator = NULL;
        hr = pSvc->ExecQuery(com::Bstr("WQL").raw(), bstrQuery.raw(), WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
                             NULL, &pEnumerator);
        if (SUCCEEDED(hr))
        {
            if (pEnumerator)
            {
                IWbemClassObject *pclsObj = NULL;
                ULONG uReturn = 0;
                hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);
                NonStandardLogFlow(("netIfWinFindAdapterClassById: IEnumWbemClassObject::Next -> hr=0x%x pclsObj=%p uReturn=%u 42=%u\n",
                                    hr, (void *)pclsObj, uReturn, 42));
                if (SUCCEEDED(hr))
                {
                    if (uReturn && pclsObj != NULL)
                    {
                        *pAdapterConfig = pclsObj;
                        pEnumerator->Release();
                        NonStandardLogFlow(("netIfWinFindAdapterClassById: S_OK and %p\n", *pAdapterConfig));
                        return S_OK;
                    }
                    hr = E_FAIL;
                }
                pEnumerator->Release();
            }
            else
            {
                NonStandardLogFlow(("ExecQuery returned no enumerator\n"));
                hr = E_FAIL;
            }
        }
        else
            NonStandardLogFlow(("ExecQuery failed (0x%x)\n", hr));
    }
    else
    {
        DWORD winEr = GetLastError();
        hr = HRESULT_FROM_WIN32(winEr);
        if (SUCCEEDED(hr))
            hr = E_FAIL;
        NonStandardLogFlow(("StringFromGUID2 failed winEr=%u, hr=0x%x\n", winEr, hr));
    }

    NonStandardLogFlow(("netIfWinFindAdapterClassById: 0x%x and %p\n", hr, *pAdapterConfig));
    return hr;
}

static HRESULT netIfWinIsHostOnly(IWbemClassObject *pAdapterConfig, BOOL *pfIsHostOnly)
{
    VARIANT vtServiceName;
    VariantInit(&vtServiceName);

    HRESULT hr = pAdapterConfig->Get(L"ServiceName", 0 /*lFlags*/, &vtServiceName, NULL /*pvtType*/, NULL /*plFlavor*/);
    if (SUCCEEDED(hr))
    {
        *pfIsHostOnly = RTUtf16CmpAscii(vtServiceName.bstrVal, "VBoxNetAdp") == 0;

        VariantClear(&vtServiceName);
    }

    return hr;
}

static HRESULT netIfWinGetIpSettings(IWbemClassObject * pAdapterConfig, ULONG *pIpv4, ULONG *pMaskv4)
{
    *pIpv4   = 0;
    *pMaskv4 = 0;

    VARIANT vtIp;
    VariantInit(&vtIp);
    HRESULT hr = pAdapterConfig->Get(L"IPAddress", 0, &vtIp, 0, 0);
    if (SUCCEEDED(hr))
    {
        if (vtIp.vt == (VT_ARRAY | VT_BSTR))
        {
            VARIANT vtMask;
            VariantInit(&vtMask);
            hr = pAdapterConfig->Get(L"IPSubnet", 0, &vtMask, 0, 0);
            if (SUCCEEDED(hr))
            {
                if (vtMask.vt == (VT_ARRAY | VT_BSTR))
                {
                    SAFEARRAY *pIpArray   = vtIp.parray;
                    SAFEARRAY *pMaskArray = vtMask.parray;
                    if (pIpArray && pMaskArray)
                    {
                        BSTR pBstrCurIp;
                        BSTR pBstrCurMask;
                        for (LONG i = 0;
                                SafeArrayGetElement(pIpArray,  &i,  (PVOID)&pBstrCurIp)   == S_OK
                             && SafeArrayGetElement(pMaskArray, &i, (PVOID)&pBstrCurMask) == S_OK;
                             i++)
                        {
                            com::Utf8Str strIp(pBstrCurIp);
                            ULONG Ipv4 = inet_addr(strIp.c_str());
                            if (Ipv4 != INADDR_NONE)
                            {
                                *pIpv4 = Ipv4;

                                com::Utf8Str strMask(pBstrCurMask);
                                *pMaskv4 = inet_addr(strMask.c_str());
                                break;
                            }
                        }
                    }
                }
                VariantClear(&vtMask);
            }
        }
        VariantClear(&vtIp);
    }

    return hr;
}

#if 0 /* unused */

static HRESULT netIfWinHasIpSettings(IWbemClassObject * pAdapterConfig, SAFEARRAY * pCheckIp, SAFEARRAY * pCheckMask, bool *pFound)
{
    VARIANT vtIp;
    HRESULT hr;
    VariantInit(&vtIp);

    *pFound = false;

    hr = pAdapterConfig->Get(L"IPAddress", 0, &vtIp, 0, 0);
    if (SUCCEEDED(hr))
    {
        VARIANT vtMask;
        VariantInit(&vtMask);
        hr = pAdapterConfig->Get(L"IPSubnet", 0, &vtMask, 0, 0);
        if (SUCCEEDED(hr))
        {
            SAFEARRAY * pIpArray = vtIp.parray;
            SAFEARRAY * pMaskArray = vtMask.parray;
            if (pIpArray && pMaskArray)
            {
                BSTR pIp, pMask;
                for (LONG k = 0;
                    SafeArrayGetElement(pCheckIp, &k, (PVOID)&pIp) == S_OK
                    && SafeArrayGetElement(pCheckMask, &k, (PVOID)&pMask) == S_OK;
                    k++)
                {
                    BSTR pCurIp;
                    BSTR pCurMask;
                    for (LONG i = 0;
                        SafeArrayGetElement(pIpArray, &i, (PVOID)&pCurIp) == S_OK
                        && SafeArrayGetElement(pMaskArray, &i, (PVOID)&pCurMask) == S_OK;
                        i++)
                    {
                        if (!wcsicmp(pCurIp, pIp))
                        {
                            if (!wcsicmp(pCurMask, pMask))
                                *pFound = true;
                            break;
                        }
                    }
                }
            }


            VariantClear(&vtMask);
        }

        VariantClear(&vtIp);
    }

    return hr;
}

static HRESULT netIfWinWaitIpSettings(IWbemServices *pSvc, const GUID * pGuid, SAFEARRAY * pCheckIp, SAFEARRAY * pCheckMask, ULONG sec2Wait, bool *pFound)
{
    /* on Vista we need to wait for the address to get applied */
    /* wait for the address to appear in the list */
    HRESULT hr = S_OK;
    ULONG i;
    *pFound = false;
    ComPtr<IWbemClassObject> pAdapterConfig;
    for (i = 0;
            (hr = netIfWinFindAdapterClassById(pSvc, pGuid, pAdapterConfig.asOutParam())) == S_OK
         && (hr = netIfWinHasIpSettings(pAdapterConfig, pCheckIp, pCheckMask, pFound)) == S_OK
         && !(*pFound)
         && i < sec2Wait/6;
         i++)
    {
        Sleep(6000);
    }

    return hr;
}

#endif /* unused */

static HRESULT netIfWinCreateIWbemServices(IWbemServices **ppSvc)
{
    IWbemLocator *pLoc = NULL;
    HRESULT hr = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *)&pLoc);
    if (SUCCEEDED(hr))
    {
        IWbemServices *pSvc = NULL;
        hr = pLoc->ConnectServer(com::Bstr(L"ROOT\\CIMV2").raw(), /* [in] const BSTR strNetworkResource */
                                 NULL, /* [in] const BSTR strUser */
                                 NULL, /* [in] const BSTR strPassword */
                                 0,    /* [in] const BSTR strLocale */
                                 NULL, /* [in] LONG lSecurityFlags */
                                 0,    /* [in] const BSTR strAuthority */
                                 0,    /* [in] IWbemContext* pCtx */
                                 &pSvc /* [out] IWbemServices** ppNamespace */);
        if (SUCCEEDED(hr))
        {
            hr = CoSetProxyBlanket(pSvc, /* IUnknown * pProxy */
                                   RPC_C_AUTHN_WINNT, /* DWORD dwAuthnSvc */
                                   RPC_C_AUTHZ_NONE, /* DWORD dwAuthzSvc */
                                   NULL, /* WCHAR * pServerPrincName */
                                   RPC_C_AUTHN_LEVEL_CALL, /* DWORD dwAuthnLevel */
                                   RPC_C_IMP_LEVEL_IMPERSONATE, /* DWORD dwImpLevel */
                                   NULL, /* RPC_AUTH_IDENTITY_HANDLE pAuthInfo */
                                   EOAC_NONE /* DWORD dwCapabilities */
                                   );
            if (SUCCEEDED(hr))
            {
                *ppSvc = pSvc;
                /* do not need it any more */
                pLoc->Release();
                return hr;
            }

            NonStandardLogFlow(("CoSetProxyBlanket failed: %Rhrc\n", hr));
            pSvc->Release();
        }
        else
            NonStandardLogFlow(("ConnectServer failed: %Rhrc\n", hr));
        pLoc->Release();
    }
    else
        NonStandardLogFlow(("CoCreateInstance failed: %Rhrc\n", hr));
    return hr;
}

static HRESULT netIfWinAdapterConfigPath(IWbemClassObject *pObj, com::Bstr *pRet)
{
    VARIANT index;
    VariantInit(&index);
    HRESULT hr = pObj->Get(L"Index", 0, &index, 0, 0);
    if (SUCCEEDED(hr))
        hr = pRet->printfNoThrow("Win32_NetworkAdapterConfiguration.Index='%u'", index.uintVal);
    else
    {
        pRet->setNull();
        NonStandardLogFlow(("Get failed: %Rhrc\n", hr));
    }
    return hr;
}

static HRESULT netIfExecMethod(IWbemServices * pSvc, IWbemClassObject *pClass, com::Bstr const &rObjPath,
                               const char *pszMethodName, LPWSTR *papwszArgNames, LPVARIANT *pArgs, UINT cArgs,
                               IWbemClassObject **ppOutParams)
{
    *ppOutParams = NULL;
    com::Bstr bstrMethodName;
    HRESULT hr = bstrMethodName.assignEx(pszMethodName);
    if (SUCCEEDED(hr))
    {
        ComPtr<IWbemClassObject> pInParamsDefinition;
        ComPtr<IWbemClassObject> pClassInstance;
        if (cArgs)
        {
            hr = pClass->GetMethod(bstrMethodName.raw(), 0, pInParamsDefinition.asOutParam(), NULL);
            if (SUCCEEDED(hr))
            {
                hr = pInParamsDefinition->SpawnInstance(0, pClassInstance.asOutParam());
                if (SUCCEEDED(hr))
                {
                    for (UINT i = 0; i < cArgs; i++)
                    {
                        hr = pClassInstance->Put(papwszArgNames[i], 0, pArgs[i], 0);
                        if (FAILED(hr))
                            break;
                    }
                }
            }
        }

        if (SUCCEEDED(hr))
        {
            IWbemClassObject *pOutParams = NULL;
            hr = pSvc->ExecMethod(rObjPath.raw(), bstrMethodName.raw(), 0, NULL, pClassInstance, &pOutParams, NULL);
            if (SUCCEEDED(hr))
                *ppOutParams = pOutParams;
        }
    }

    return hr;
}

static HRESULT netIfWinCreateIpArray(SAFEARRAY **ppArray, in_addr const *paIps, UINT cIps)
{
    HRESULT    hr = S_OK;
    SAFEARRAY *pIpArray = SafeArrayCreateVector(VT_BSTR, 0, cIps);
    if (pIpArray)
    {
        for (UINT i = 0; i < cIps; i++)
        {
            com::Bstr bstrVal;
            hr = bstrVal.printfNoThrow("%RTnaipv4", paIps[i].s_addr);
            if (SUCCEEDED(hr))
            {
                Assert(bstrVal.equals(inet_ntoa(paIps[i])));

                BSTR pRawVal;
                hr = bstrVal.detachToEx(&pRawVal);
                if (SUCCEEDED(hr))
                {
                    LONG aIndex[1] = { (LONG)i };
                    hr = SafeArrayPutElement(pIpArray, aIndex, pRawVal);
                    if (SUCCEEDED(hr))
                        continue;
                    SysFreeString(pRawVal);
                }
            }
            break;
        }

        if (SUCCEEDED(hr))
            *ppArray = pIpArray;
        else
            SafeArrayDestroy(pIpArray);
    }
    else
        hr = HRESULT_FROM_WIN32(GetLastError());
    return hr;
}

#if 0 /* unused */
static HRESULT netIfWinCreateIpArrayV4V6(SAFEARRAY **ppArray, BSTR Ip)
{
    HRESULT hr;
    SAFEARRAY *pIpArray = SafeArrayCreateVector(VT_BSTR, 0, 1);
    if (pIpArray)
    {
        BSTR val = com::Bstr(Ip, false).copy();
        long aIndex[1];
        aIndex[0] = 0;
        hr = SafeArrayPutElement(pIpArray, aIndex, val);
        if (FAILED(hr))
        {
            SysFreeString(val);
            SafeArrayDestroy(pIpArray);
        }

        if (SUCCEEDED(hr))
        {
            *ppArray = pIpArray;
        }
    }
    else
        hr = HRESULT_FROM_WIN32(GetLastError());

    return hr;
}
#endif


static HRESULT netIfWinCreateIpArrayVariantV4(VARIANT *pIpAddresses, in_addr const *paIps, UINT cIps)
{
    VariantInit(pIpAddresses);
    pIpAddresses->vt = VT_ARRAY | VT_BSTR;

    SAFEARRAY *pIpArray;
    HRESULT hr = netIfWinCreateIpArray(&pIpArray, paIps, cIps);
    if (SUCCEEDED(hr))
        pIpAddresses->parray = pIpArray;
    return hr;
}

#if 0 /* unused */
static HRESULT netIfWinCreateIpArrayVariantV4V6(VARIANT * pIpAddresses, BSTR Ip)
{
    HRESULT hr;
    VariantInit(pIpAddresses);
    pIpAddresses->vt = VT_ARRAY | VT_BSTR;
    SAFEARRAY *pIpArray;
    hr = netIfWinCreateIpArrayV4V6(&pIpArray, Ip);
    if (SUCCEEDED(hr))
    {
        pIpAddresses->parray = pIpArray;
    }
    return hr;
}
#endif

static HRESULT netIfWinEnableStatic(IWbemServices *pSvc, const GUID *pGuid, com::Bstr &rObjPath, VARIANT *pIp, VARIANT *pMask)
{
    com::Bstr bstrClassName;
    HRESULT hr = bstrClassName.assignEx("Win32_NetworkAdapterConfiguration");
    if (SUCCEEDED(hr))
    {
        ComPtr<IWbemClassObject> pClass;
        hr = pSvc->GetObject(bstrClassName.raw(), 0, NULL, pClass.asOutParam(), NULL);
        if (SUCCEEDED(hr))
        {
            LPWSTR argNames[] = {L"IPAddress", L"SubnetMask"};
            LPVARIANT  args[] = {         pIp,        pMask };

            ComPtr<IWbemClassObject> pOutParams;
            hr = netIfExecMethod(pSvc, pClass, rObjPath.raw(), "EnableStatic", argNames, args,
                                 2, pOutParams.asOutParam());
            if (SUCCEEDED(hr))
            {
                com::Bstr bstrReturnValue;
                hr = bstrReturnValue.assignEx("ReturnValue");
                if (SUCCEEDED(hr))
                {
                    VARIANT varReturnValue;
                    VariantInit(&varReturnValue);
                    hr = pOutParams->Get(bstrReturnValue.raw(), 0, &varReturnValue, NULL, 0);
                    Assert(SUCCEEDED(hr));
                    if (SUCCEEDED(hr))
                    {
                        //Assert(varReturnValue.vt == VT_UINT);
                        int winEr = varReturnValue.uintVal;
                        switch (winEr)
                        {
                            case 0:
                            {
                                hr = S_OK;
                                //bool bFound;
                                //HRESULT tmpHr = netIfWinWaitIpSettings(pSvc, pGuid, pIp->parray, pMask->parray, 180, &bFound);
                                NOREF(pGuid);
                                break;
                            }
                            default:
                                hr = HRESULT_FROM_WIN32( winEr );
                                break;
                        }
                    }
                }
            }
        }
    }
    return hr;
}


static HRESULT netIfWinEnableStaticV4(IWbemServices *pSvc, const GUID *pGuid, com::Bstr &rObjPath,
                                      in_addr const *paIps, in_addr const *paMasks, UINT cIpAndMasks)
{
    VARIANT ipAddresses;
    HRESULT hr = netIfWinCreateIpArrayVariantV4(&ipAddresses, paIps, cIpAndMasks);
    if (SUCCEEDED(hr))
    {
        VARIANT ipMasks;
        hr = netIfWinCreateIpArrayVariantV4(&ipMasks, paMasks, cIpAndMasks);
        if (SUCCEEDED(hr))
        {
            hr = netIfWinEnableStatic(pSvc, pGuid, rObjPath, &ipAddresses, &ipMasks);
            VariantClear(&ipMasks);
        }
        VariantClear(&ipAddresses);
    }
    return hr;
}

#if 0 /* unused */

static HRESULT netIfWinEnableStaticV4V6(IWbemServices * pSvc, const GUID * pGuid, BSTR ObjPath, BSTR Ip, BSTR Mask)
{
    VARIANT ipAddresses;
    HRESULT hr = netIfWinCreateIpArrayVariantV4V6(&ipAddresses, Ip);
    if (SUCCEEDED(hr))
    {
        VARIANT ipMasks;
        hr = netIfWinCreateIpArrayVariantV4V6(&ipMasks, Mask);
        if (SUCCEEDED(hr))
        {
            hr = netIfWinEnableStatic(pSvc, pGuid, ObjPath, &ipAddresses, &ipMasks);
            VariantClear(&ipMasks);
        }
        VariantClear(&ipAddresses);
    }
    return hr;
}

/* win API allows to set gw metrics as well, we are not setting them */
static HRESULT netIfWinSetGateways(IWbemServices * pSvc, BSTR ObjPath, VARIANT * pGw)
{
    ComPtr<IWbemClassObject> pClass;
    BSTR ClassName = SysAllocString(L"Win32_NetworkAdapterConfiguration");
    HRESULT hr;
    if (ClassName)
    {
        hr = pSvc->GetObject(ClassName, 0, NULL, pClass.asOutParam(), NULL);
        if (SUCCEEDED(hr))
        {
            LPWSTR argNames[] = {L"DefaultIPGateway"};
            LPVARIANT args[] = {pGw};
            ComPtr<IWbemClassObject> pOutParams;

            hr = netIfExecMethod(pSvc, pClass, ObjPath, com::Bstr(L"SetGateways"), argNames, args, 1, pOutParams.asOutParam());
            if (SUCCEEDED(hr))
            {
                VARIANT varReturnValue;
                hr = pOutParams->Get(com::Bstr(L"ReturnValue"), 0, &varReturnValue, NULL, 0);
                Assert(SUCCEEDED(hr));
                if (SUCCEEDED(hr))
                {
//                    Assert(varReturnValue.vt == VT_UINT);
                    int winEr = varReturnValue.uintVal;
                    switch (winEr)
                    {
                    case 0:
                        hr = S_OK;
                        break;
                    default:
                        hr = HRESULT_FROM_WIN32( winEr );
                        break;
                    }
                }
            }
        }
        SysFreeString(ClassName);
    }
    else
        hr = HRESULT_FROM_WIN32(GetLastError());

    return hr;
}

/* win API allows to set gw metrics as well, we are not setting them */
static HRESULT netIfWinSetGatewaysV4(IWbemServices * pSvc, BSTR ObjPath, in_addr* aGw, UINT cGw)
{
    VARIANT gwais;
    HRESULT hr = netIfWinCreateIpArrayVariantV4(&gwais, aGw, cGw);
    if (SUCCEEDED(hr))
    {
        netIfWinSetGateways(pSvc, ObjPath, &gwais);
        VariantClear(&gwais);
    }
    return hr;
}

/* win API allows to set gw metrics as well, we are not setting them */
static HRESULT netIfWinSetGatewaysV4V6(IWbemServices * pSvc, BSTR ObjPath, BSTR Gw)
{
    VARIANT vGw;
    HRESULT hr = netIfWinCreateIpArrayVariantV4V6(&vGw, Gw);
    if (SUCCEEDED(hr))
    {
        netIfWinSetGateways(pSvc, ObjPath, &vGw);
        VariantClear(&vGw);
    }
    return hr;
}

#endif /* unused */

static HRESULT netIfWinEnableDHCP(IWbemServices * pSvc, const com::Bstr &rObjPath)
{
    com::Bstr bstrClassName;
    HRESULT hr = bstrClassName.assignEx("Win32_NetworkAdapterConfiguration");
    if (SUCCEEDED(hr))
    {
        ComPtr<IWbemClassObject> pClass;
        hr = pSvc->GetObject(bstrClassName.raw(), 0, NULL, pClass.asOutParam(), NULL);
        if (SUCCEEDED(hr))
        {
            ComPtr<IWbemClassObject> pOutParams;
            hr = netIfExecMethod(pSvc, pClass, rObjPath, "EnableDHCP", NULL, NULL, 0, pOutParams.asOutParam());
            if (SUCCEEDED(hr))
            {
                com::Bstr bstrReturnValue;
                hr = bstrReturnValue.assignEx("ReturnValue");
                if (SUCCEEDED(hr))
                {
                    VARIANT varReturnValue;
                    VariantInit(&varReturnValue);
                    hr = pOutParams->Get(bstrReturnValue.raw(), 0, &varReturnValue, NULL, 0);
                    Assert(SUCCEEDED(hr));
                    if (SUCCEEDED(hr))
                    {
                        //Assert(varReturnValue.vt == VT_UINT);
                        int winEr = varReturnValue.uintVal;
                        switch (winEr)
                        {
                            case 0:
                                hr = S_OK;
                                break;
                            default:
                                hr = HRESULT_FROM_WIN32( winEr );
                                break;
                        }
                    }
                }
            }
        }
    }
    return hr;
}

static HRESULT netIfWinDhcpRediscover(IWbemServices * pSvc, const com::Bstr &rObjPath)
{
    com::Bstr bstrClassName;
    HRESULT hr = bstrClassName.assignEx("Win32_NetworkAdapterConfiguration");
    if (SUCCEEDED(hr))
    {
        ComPtr<IWbemClassObject> pClass;
        hr = pSvc->GetObject(bstrClassName.raw(), 0, NULL, pClass.asOutParam(), NULL);
        if (SUCCEEDED(hr))
        {
            ComPtr<IWbemClassObject> pOutParams;
            hr = netIfExecMethod(pSvc, pClass, rObjPath, "ReleaseDHCPLease", NULL, NULL, 0, pOutParams.asOutParam());
            if (SUCCEEDED(hr))
            {
                com::Bstr bstrReturnValue;
                hr = bstrReturnValue.assignEx("ReturnValue");
                if (SUCCEEDED(hr))
                {
                    VARIANT varReturnValue;
                    VariantInit(&varReturnValue);
                    hr = pOutParams->Get(bstrReturnValue.raw(), 0, &varReturnValue, NULL, 0);
                    Assert(SUCCEEDED(hr));
                    if (SUCCEEDED(hr))
                    {
                        //Assert(varReturnValue.vt == VT_UINT);
                        int winEr = varReturnValue.uintVal;
                        if (winEr == 0)
                        {
                            hr = netIfExecMethod(pSvc, pClass, rObjPath, "RenewDHCPLease", NULL, NULL, 0, pOutParams.asOutParam());
                            if (SUCCEEDED(hr))
                            {
                                hr = pOutParams->Get(bstrReturnValue.raw(), 0, &varReturnValue, NULL, 0);
                                Assert(SUCCEEDED(hr));
                                if (SUCCEEDED(hr))
                                {
                                    //Assert(varReturnValue.vt == VT_UINT);
                                    winEr = varReturnValue.uintVal;
                                    if (winEr == 0)
                                        hr = S_OK;
                                    else
                                        hr = HRESULT_FROM_WIN32( winEr );
                                }
                            }
                        }
                        else
                            hr = HRESULT_FROM_WIN32( winEr );
                    }
                }
            }
        }
    }

    return hr;
}

static HRESULT vboxNetCfgWinIsDhcpEnabled(IWbemClassObject *pAdapterConfig, BOOL *pfEnabled)
{
    VARIANT vtEnabled;
    VariantInit(&vtEnabled);
    HRESULT hr = pAdapterConfig->Get(L"DHCPEnabled", 0, &vtEnabled, 0, 0);
    if (SUCCEEDED(hr))
        *pfEnabled = vtEnabled.boolVal;
    else
        *pfEnabled = FALSE;
    return hr;
}

VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinGetAdapterSettings(IN const GUID *pGuid, OUT PADAPTER_SETTINGS pSettings)
{
    ComPtr<IWbemServices> pSvc;
    HRESULT hr = netIfWinCreateIWbemServices(pSvc.asOutParam());
    if (SUCCEEDED(hr))
    {
        ComPtr<IWbemClassObject> pAdapterConfig;
        hr = netIfWinFindAdapterClassById(pSvc, pGuid, pAdapterConfig.asOutParam());
        if (SUCCEEDED(hr))
        {
            hr = vboxNetCfgWinIsDhcpEnabled(pAdapterConfig, &pSettings->bDhcp);
            if (SUCCEEDED(hr))
                hr = netIfWinGetIpSettings(pAdapterConfig, &pSettings->ip, &pSettings->mask);
        }
    }

    return hr;
}

VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinIsDhcpEnabled(const GUID * pGuid, BOOL *pEnabled)
{
    ComPtr<IWbemServices> pSvc;
    HRESULT hr = netIfWinCreateIWbemServices(pSvc.asOutParam());
    if (SUCCEEDED(hr))
    {
        ComPtr<IWbemClassObject> pAdapterConfig;
        hr = netIfWinFindAdapterClassById(pSvc, pGuid, pAdapterConfig.asOutParam());
        if (SUCCEEDED(hr))
        {
            VARIANT vtEnabled;
            hr = pAdapterConfig->Get(L"DHCPEnabled", 0, &vtEnabled, 0, 0);
            if (SUCCEEDED(hr))
                *pEnabled = vtEnabled.boolVal;
        }
    }

    return hr;
}

VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinEnableStaticIpConfig(IN const GUID *pGuid, IN ULONG ip, IN ULONG mask)
{
    NonStandardLogFlow(("VBoxNetCfgWinEnableStaticIpConfig: ip=0x%x mask=0x%x\n", ip, mask));
    ComPtr<IWbemServices> pSvc;
    HRESULT hr = netIfWinCreateIWbemServices(pSvc.asOutParam());
    if (SUCCEEDED(hr))
    {
        ComPtr<IWbemClassObject> pAdapterConfig;
        hr = netIfWinFindAdapterClassById(pSvc, pGuid, pAdapterConfig.asOutParam());
        if (SUCCEEDED(hr))
        {
            BOOL fIsHostOnly;
            hr = netIfWinIsHostOnly(pAdapterConfig, &fIsHostOnly);
            if (SUCCEEDED(hr))
            {
                if (fIsHostOnly)
                {
                    in_addr aIp[1];
                    in_addr aMask[1];
                    aIp[0].S_un.S_addr   = ip;
                    aMask[0].S_un.S_addr = mask;

                    com::Bstr bstrObjPath;
                    hr = netIfWinAdapterConfigPath(pAdapterConfig, &bstrObjPath);
                    if (SUCCEEDED(hr))
                    {
                        hr = netIfWinEnableStaticV4(pSvc, pGuid, bstrObjPath, aIp, aMask, ip != 0 ? 1 : 0);
                        if (SUCCEEDED(hr))
                        {
#if 0
                            in_addr aGw[1];
                            aGw[0].S_un.S_addr = gw;
                            hr = netIfWinSetGatewaysV4(pSvc, bstrObjPath, aGw, 1);
                            if (SUCCEEDED(hr))
#endif
                            {
                            }
                        }
                    }
                }
                else
                {
                    hr = E_FAIL;
                }
            }
        }
    }

    NonStandardLogFlow(("VBoxNetCfgWinEnableStaticIpConfig: returns %Rhrc\n", hr));
    return hr;
}

#if 0
static HRESULT netIfEnableStaticIpConfigV6(const GUID *pGuid, IN_BSTR aIPV6Address, IN_BSTR aIPV6Mask, IN_BSTR aIPV6DefaultGateway)
{
    HRESULT hr;
        ComPtr<IWbemServices> pSvc;
        hr = netIfWinCreateIWbemServices(pSvc.asOutParam());
        if (SUCCEEDED(hr))
        {
            ComPtr<IWbemClassObject> pAdapterConfig;
            hr = netIfWinFindAdapterClassById(pSvc, pGuid, pAdapterConfig.asOutParam());
            if (SUCCEEDED(hr))
            {
                BSTR ObjPath;
                hr = netIfWinAdapterConfigPath(pAdapterConfig, &ObjPath);
                if (SUCCEEDED(hr))
                {
                    hr = netIfWinEnableStaticV4V6(pSvc, pAdapterConfig, ObjPath, aIPV6Address, aIPV6Mask);
                    if (SUCCEEDED(hr))
                    {
                        if (aIPV6DefaultGateway)
                        {
                            hr = netIfWinSetGatewaysV4V6(pSvc, ObjPath, aIPV6DefaultGateway);
                        }
                        if (SUCCEEDED(hr))
                        {
//                            hr = netIfWinUpdateConfig(pIf);
                        }
                    }
                    SysFreeString(ObjPath);
                }
            }
        }

    return SUCCEEDED(hr) ? VINF_SUCCESS : VERR_GENERAL_FAILURE;
}

static HRESULT netIfEnableStaticIpConfigV6(const GUID *pGuid, IN_BSTR aIPV6Address, ULONG aIPV6MaskPrefixLength)
{
    RTNETADDRIPV6 Mask;
    int rc = RTNetPrefixToMaskIPv6(aIPV6MaskPrefixLength, &Mask);
    if (RT_SUCCESS(rc))
    {
        Bstr maskStr = composeIPv6Address(&Mask);
        rc = netIfEnableStaticIpConfigV6(pGuid, aIPV6Address, maskStr, NULL);
    }
    return rc;
}
#endif

VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinEnableDynamicIpConfig(IN const GUID *pGuid)
{
    ComPtr<IWbemServices> pSvc;
    HRESULT hr = netIfWinCreateIWbemServices(pSvc.asOutParam());
    if (SUCCEEDED(hr))
    {
        ComPtr<IWbemClassObject> pAdapterConfig;
        hr = netIfWinFindAdapterClassById(pSvc, pGuid, pAdapterConfig.asOutParam());
        if (SUCCEEDED(hr))
        {
            BOOL fIsHostOnly;
            hr = netIfWinIsHostOnly(pAdapterConfig, &fIsHostOnly);
            if (SUCCEEDED(hr))
            {
                if (fIsHostOnly)
                {
                    com::Bstr bstrObjPath;
                    hr = netIfWinAdapterConfigPath(pAdapterConfig, &bstrObjPath);
                    if (SUCCEEDED(hr))
                    {
                        hr = netIfWinEnableDHCP(pSvc, bstrObjPath);
                        if (SUCCEEDED(hr))
                        {
                            //hr = netIfWinUpdateConfig(pIf);
                        }
                    }
                }
                else
                    hr = E_FAIL;
            }
        }
    }
    return hr;
}

VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinDhcpRediscover(IN const GUID *pGuid)
{
    ComPtr<IWbemServices> pSvc;
    HRESULT hr = netIfWinCreateIWbemServices(pSvc.asOutParam());
    if (SUCCEEDED(hr))
    {
        ComPtr<IWbemClassObject> pAdapterConfig;
        hr = netIfWinFindAdapterClassById(pSvc, pGuid, pAdapterConfig.asOutParam());
        if (SUCCEEDED(hr))
        {
            BOOL fIsHostOnly;
            hr = netIfWinIsHostOnly(pAdapterConfig, &fIsHostOnly);
            if (SUCCEEDED(hr))
            {
                if (fIsHostOnly)
                {
                    com::Bstr bstrObjPath;
                    hr = netIfWinAdapterConfigPath(pAdapterConfig, &bstrObjPath);
                    if (SUCCEEDED(hr))
                    {
                        hr = netIfWinDhcpRediscover(pSvc, bstrObjPath);
                        if (SUCCEEDED(hr))
                        {
                            //hr = netIfWinUpdateConfig(pIf);
                        }
                    }
                }
                else
                    hr = E_FAIL;
            }
        }
    }


    return hr;
}

static const char *vboxNetCfgWinAddrToStr(char *pszBuf, size_t cbBuf, LPSOCKADDR pAddr)
{
    switch (pAddr->sa_family)
    {
        case AF_INET:
            RTStrPrintf(pszBuf, cbBuf, "%d.%d.%d.%d",
                        ((PSOCKADDR_IN)pAddr)->sin_addr.S_un.S_un_b.s_b1,
                        ((PSOCKADDR_IN)pAddr)->sin_addr.S_un.S_un_b.s_b2,
                        ((PSOCKADDR_IN)pAddr)->sin_addr.S_un.S_un_b.s_b3,
                        ((PSOCKADDR_IN)pAddr)->sin_addr.S_un.S_un_b.s_b4);
            break;
        case AF_INET6:
            RTStrPrintf(pszBuf, cbBuf, "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
                        ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[0], ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[1],
                        ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[2], ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[3],
                        ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[4], ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[5],
                        ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[6], ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[7],
                        ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[8], ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[9],
                        ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[10], ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[11],
                        ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[12], ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[13],
                        ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[14], ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[15]);
            break;
        default:
            RTStrCopy(pszBuf, cbBuf, "unknown");
            break;
    }
    return pszBuf;
}

typedef bool (*PFNVBOXNETCFG_IPSETTINGS_CALLBACK) (ULONG ip, ULONG mask, PVOID pContext);

static void vboxNetCfgWinEnumIpConfig(PIP_ADAPTER_ADDRESSES pAddresses, PFNVBOXNETCFG_IPSETTINGS_CALLBACK pfnCallback, PVOID pContext)
{
    PIP_ADAPTER_ADDRESSES pAdapter;
    for (pAdapter = pAddresses; pAdapter; pAdapter = pAdapter->Next)
    {
        NonStandardLogFlow(("+- Enumerating adapter '%ls' %s\n", pAdapter->FriendlyName, pAdapter->AdapterName));
        for (PIP_ADAPTER_PREFIX pPrefix = pAdapter->FirstPrefix; pPrefix; pPrefix = pPrefix->Next)
        {
            char szBuf[80];
            const char *pcszAddress = vboxNetCfgWinAddrToStr(szBuf, sizeof(szBuf), pPrefix->Address.lpSockaddr);

            /* We are concerned with IPv4 only, ignore the rest. */
            if (pPrefix->Address.lpSockaddr->sa_family != AF_INET)
            {
                NonStandardLogFlow(("| +- %s %d: not IPv4, ignoring\n", pcszAddress, pPrefix->PrefixLength));
                continue;
            }

            /* Ignore invalid prefixes as well as host addresses. */
            if (pPrefix->PrefixLength < 1 || pPrefix->PrefixLength > 31)
            {
                NonStandardLogFlow(("| +- %s %d: host or broadcast, ignoring\n", pcszAddress, pPrefix->PrefixLength));
                continue;
            }

            /* Ignore multicast and beyond. */
            ULONG ip = ((struct sockaddr_in *)pPrefix->Address.lpSockaddr)->sin_addr.s_addr;
            if ((ip & 0xF0) > 224)
            {
                NonStandardLogFlow(("| +- %s %d: multicast, ignoring\n", pcszAddress, pPrefix->PrefixLength));
                continue;
            }

            ULONG mask = htonl((~(((ULONG)~0) >> pPrefix->PrefixLength)));
            bool fContinue = pfnCallback(ip, mask, pContext);
            if (!fContinue)
            {
                NonStandardLogFlow(("| +- %s %d: CONFLICT!\n", pcszAddress, pPrefix->PrefixLength));
                return;
            }

            NonStandardLogFlow(("| +- %s %d: no conflict, moving on\n", pcszAddress, pPrefix->PrefixLength));
        }
    }
}

typedef struct _IPPROBE_CONTEXT
{
    ULONG Prefix;
    bool fConflict;
}IPPROBE_CONTEXT, *PIPPROBE_CONTEXT;

#define IPPROBE_INIT(a_pContext, a_addr) \
    do { (a_pContext)->fConflict = false; (a_pContext)->Prefix = (a_addr); } while (0)

#define IPPROBE_INIT_STR(a_pContext, a_straddr) \
    IPROBE_INIT(a_pContext, inet_addr(_straddr))

static bool vboxNetCfgWinIpProbeCallback (ULONG ip, ULONG mask, PVOID pContext)
{
    PIPPROBE_CONTEXT pProbe = (PIPPROBE_CONTEXT)pContext;

    if ((ip & mask) == (pProbe->Prefix & mask))
    {
        pProbe->fConflict = true;
        return false;
    }

    return true;
}

VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinGenHostOnlyNetworkNetworkIp(OUT PULONG pNetIp, OUT PULONG pNetMask)
{
    HRESULT hr = S_OK;

    *pNetIp   = 0;
    *pNetMask = 0;

    /*
     * MSDN recommends to pre-allocate a 15KB buffer.
     */
    ULONG                 cbBuf       = 15 * _1K;
    PIP_ADAPTER_ADDRESSES paAddresses = (PIP_ADAPTER_ADDRESSES)RTMemAllocZ(cbBuf);
    if (!paAddresses)
        return E_OUTOFMEMORY;
    DWORD dwRc = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, NULL, paAddresses, &cbBuf);
    if (dwRc == ERROR_BUFFER_OVERFLOW)
    {
        /* Impressive! More than 10 adapters! Get more memory and try again. */
        RTMemFree(paAddresses);
        paAddresses = (PIP_ADAPTER_ADDRESSES)RTMemAllocZ(cbBuf);
        if (!paAddresses)
            return E_OUTOFMEMORY;
        dwRc = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, NULL, paAddresses, &cbBuf);
    }
    if (dwRc == NO_ERROR)
    {
        const ULONG ip192168 = inet_addr("192.168.0.0");
        for (int i = 0; i < 384; i++)
        {
#if 0
            ULONG ipProbe = rand()*255 / RAND_MAX;
#else
            uint32_t ipProbe = RTRandU32Ex(0, 255);
#endif
            ipProbe = ip192168 | (ipProbe << 16);
            NonStandardLogFlow(("probing %RTnaipv4\n", ipProbe));

            IPPROBE_CONTEXT Context;
            IPPROBE_INIT(&Context, ipProbe);
            vboxNetCfgWinEnumIpConfig(paAddresses, vboxNetCfgWinIpProbeCallback, &Context);
            if (!Context.fConflict)
            {
                NonStandardLogFlow(("found unused net %RTnaipv4\n", ipProbe));
                *pNetIp = ipProbe;
                *pNetMask = inet_addr("255.255.255.0");
                break;
            }
        }
        if (*pNetIp == 0)
            dwRc = ERROR_DHCP_ADDRESS_CONFLICT;
    }
    else
        NonStandardLogFlow(("GetAdaptersAddresses err (%u)\n", dwRc));

    RTMemFree(paAddresses);

    if (dwRc != NO_ERROR)
        hr = HRESULT_FROM_WIN32(dwRc);
    return hr;
}

/*
 * convenience functions to perform netflt/adp manipulations
 */
#define VBOXNETCFGWIN_NETFLT_ID    L"sun_VBoxNetFlt"
#define VBOXNETCFGWIN_NETFLT_MP_ID L"sun_VBoxNetFltmp"

static HRESULT vboxNetCfgWinNetFltUninstall(IN INetCfg *pNc, DWORD InfRmFlags)
{
    INetCfgComponent *pNcc = NULL;
    HRESULT hr = pNc->FindComponent(VBOXNETCFGWIN_NETFLT_ID, &pNcc);
    if (hr == S_OK)
    {
        NonStandardLog("NetFlt is installed currently, uninstalling ...\n");

        hr = VBoxNetCfgWinUninstallComponent(pNc, pNcc);
        NonStandardLogFlow(("NetFlt component uninstallation ended with hr (%Rhrc)\n", hr));

        pNcc->Release();
    }
    else if (hr == S_FALSE)
        NonStandardLog("NetFlt is not installed currently\n");
    else
        NonStandardLogFlow(("FindComponent failed: %Rhrc\n", hr));

    VBoxDrvCfgInfUninstallAllF(L"NetService", VBOXNETCFGWIN_NETFLT_ID, InfRmFlags);
    VBoxDrvCfgInfUninstallAllF(L"Net", VBOXNETCFGWIN_NETFLT_MP_ID, InfRmFlags);

    return hr;
}

VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinNetFltUninstall(IN INetCfg *pNc)
{
    return vboxNetCfgWinNetFltUninstall(pNc, 0);
}

VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinNetFltInstall(IN INetCfg *pNc, IN LPCWSTR const *pwszInfFullPath, IN UINT cInfFullPaths)
{
    HRESULT hr = vboxNetCfgWinNetFltUninstall(pNc, SUOI_FORCEDELETE);
    if (SUCCEEDED(hr))
    {
        NonStandardLog("NetFlt will be installed ...\n");
        hr = vboxNetCfgWinInstallInfAndComponent(pNc, VBOXNETCFGWIN_NETFLT_ID,
                                                 &GUID_DEVCLASS_NETSERVICE,
                                                 pwszInfFullPath,
                                                 cInfFullPaths,
                                                 NULL);
    }
    return hr;
}

static HRESULT vboxNetCfgWinNetAdpUninstall(IN INetCfg *pNc, LPCWSTR pwszId, DWORD InfRmFlags)
{
    NOREF(pNc);
    NonStandardLog("Finding NetAdp driver package and trying to uninstall it ...\n");

    VBoxDrvCfgInfUninstallAllF(L"Net", pwszId, InfRmFlags);
    NonStandardLog("NetAdp is not installed currently\n");
    return S_OK;
}

VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinNetAdpUninstall(IN INetCfg *pNc, IN LPCWSTR pwszId)
{
    return vboxNetCfgWinNetAdpUninstall(pNc, pwszId, SUOI_FORCEDELETE);
}

VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinNetAdpInstall(IN INetCfg *pNc,
                                                       IN LPCWSTR const pwszInfFullPath)
{
    NonStandardLog("NetAdp will be installed ...\n");
    HRESULT hr = vboxNetCfgWinInstallInfAndComponent(pNc, VBOXNETCFGWIN_NETADP_ID_WSZ,
                                                     &GUID_DEVCLASS_NET,
                                                     &pwszInfFullPath,
                                                     1,
                                                     NULL);
    return hr;
}


static HRESULT vboxNetCfgWinNetLwfUninstall(IN INetCfg *pNc, DWORD InfRmFlags)
{
    INetCfgComponent * pNcc = NULL;
    HRESULT hr = pNc->FindComponent(VBOXNETCFGWIN_NETLWF_ID, &pNcc);
    if (hr == S_OK)
    {
        NonStandardLog("NetLwf is installed currently, uninstalling ...\n");

        hr = VBoxNetCfgWinUninstallComponent(pNc, pNcc);

        pNcc->Release();
    }
    else if (hr == S_FALSE)
    {
        NonStandardLog("NetLwf is not installed currently\n");
        hr = S_OK;
    }
    else
    {
        NonStandardLogFlow(("FindComponent failed: %Rhrc\n", hr));
        hr = S_OK;
    }

    VBoxDrvCfgInfUninstallAllF(L"NetService", VBOXNETCFGWIN_NETLWF_ID, InfRmFlags);

    return hr;
}

VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinNetLwfUninstall(IN INetCfg *pNc)
{
    return vboxNetCfgWinNetLwfUninstall(pNc, 0);
}

static void VBoxNetCfgWinFilterLimitWorkaround(void)
{
    /*
     * Need to check if the system has a limit of installed filter drivers. If it
     * has, bump the limit to 14, which the maximum value supported by Windows 7.
     * Note that we only touch the limit if it is set to the default value (8).
     * See @bugref{7899}.
     */
    /** @todo r=bird: This code was mixing HRESULT and LSTATUS, checking the return
     * codes using SUCCEEDED(lrc) instead of lrc == ERROR_SUCCESS.  So, it might
     * have misbehaved on bogus registry content, but worked fine on sane values. */
    HKEY hKeyNet = NULL;
    LSTATUS lrc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Network", 0,
                                KEY_QUERY_VALUE | KEY_SET_VALUE, &hKeyNet);
    if (lrc == ERROR_SUCCESS)
    {
        DWORD dwMaxNumFilters = 0;
        DWORD cbMaxNumFilters = sizeof(dwMaxNumFilters);
        lrc = RegQueryValueExW(hKeyNet, L"MaxNumFilters", NULL, NULL, (LPBYTE)&dwMaxNumFilters, &cbMaxNumFilters);
        if (lrc == ERROR_SUCCESS && cbMaxNumFilters == sizeof(dwMaxNumFilters) && dwMaxNumFilters == 8)
        {
            dwMaxNumFilters = 14;
            lrc = RegSetValueExW(hKeyNet, L"MaxNumFilters", 0, REG_DWORD, (LPBYTE)&dwMaxNumFilters, sizeof(dwMaxNumFilters));
            if (lrc == ERROR_SUCCESS)
                NonStandardLog("Adjusted the installed filter limit to 14...\n");
            else
                NonStandardLog("Failed to set MaxNumFilters, error code %d\n", lrc);
        }
        RegCloseKey(hKeyNet);
    }
    else
        NonStandardLog("Failed to open network key, error code %d\n", lrc);

}

VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinNetLwfInstall(IN INetCfg *pNc, IN LPCWSTR const pwszInfFullPath)
{
    HRESULT hr = vboxNetCfgWinNetLwfUninstall(pNc, SUOI_FORCEDELETE);
    if (SUCCEEDED(hr))
    {
        VBoxNetCfgWinFilterLimitWorkaround();
        NonStandardLog("NetLwf will be installed ...\n");
        hr = vboxNetCfgWinInstallInfAndComponent(pNc, VBOXNETCFGWIN_NETLWF_ID,
                                                 &GUID_DEVCLASS_NETSERVICE,
                                                 &pwszInfFullPath,
                                                 1,
                                                 NULL);
    }
    return hr;
}

VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinGenHostonlyConnectionName(IN PCWSTR pwszDevName, OUT WCHAR *pwszBuf,
                                                                   IN ULONG cwcBuf, OUT PULONG pcwcNeeded)
{
    /* Look for a suffix that we need to preserve. */
    size_t const cwcDevName = RTUtf16Len(pwszDevName);
    size_t       offSuffix  = cwcDevName;
    while (offSuffix > 0 && pwszDevName[offSuffix - 1] != '#')
        offSuffix--;
    size_t const cwcSuffix  = pwszDevName[offSuffix] != '#' ? 0 : cwcDevName - offSuffix;

    /* Calculate required buffer size: */
    size_t cwcNeeded = sizeof(VBOX_CONNECTION_NAME_WSZ) / sizeof(wchar_t) /* includes terminator */
                     + !!cwcSuffix /*space*/ + cwcSuffix;
    if (pcwcNeeded)
        *pcwcNeeded = (ULONG)cwcNeeded;

    if (cwcNeeded <= cwcBuf)
    {
        memcpy(pwszBuf, VBOX_CONNECTION_NAME_WSZ, sizeof(VBOX_CONNECTION_NAME_WSZ));
        if (cwcSuffix > 0)
        {
            size_t offDst = sizeof(VBOX_CONNECTION_NAME_WSZ) / sizeof(wchar_t) - 1;
            pwszBuf[offDst++] = ' ';
            memcpy(&pwszBuf[offDst], &pwszDevName[offSuffix], cwcSuffix * sizeof(wchar_t));
            pwszBuf[offDst + cwcSuffix] = '\0';
        }
        return S_OK;
    }
    return E_FAIL;
}

static BOOL vboxNetCfgWinAdjustHostOnlyNetworkInterfacePriority(IN INetCfg *pNc, IN INetCfgComponent *pNcc, PVOID pContext)
{
    GUID * const pGuid = (GUID*)pContext;
    RT_NOREF1(pNc);

    /* Get component's binding. */
    INetCfgComponentBindings *pNetCfgBindings = NULL;
    HRESULT hr = pNcc->QueryInterface(IID_INetCfgComponentBindings, (PVOID *)&pNetCfgBindings);
    if (SUCCEEDED(hr))
    {
        /* Get binding path enumerator reference. */
        IEnumNetCfgBindingPath *pEnumNetCfgBindPath = NULL;
        hr = pNetCfgBindings->EnumBindingPaths(EBP_BELOW, &pEnumNetCfgBindPath);
        if (SUCCEEDED(hr))
        {
            bool fFoundIface = false;
            hr = pEnumNetCfgBindPath->Reset();
            do
            {
                INetCfgBindingPath *pNetCfgBindPath = NULL;
                hr = pEnumNetCfgBindPath->Next(1, &pNetCfgBindPath, NULL);
                if (hr == S_OK)
                {
                    IEnumNetCfgBindingInterface *pEnumNetCfgBindIface;
                    hr = pNetCfgBindPath->EnumBindingInterfaces(&pEnumNetCfgBindIface);
                    if (hr == S_OK)
                    {
                        pEnumNetCfgBindIface->Reset();
                        do
                        {
                            INetCfgBindingInterface *pNetCfgBindIfce;
                            hr = pEnumNetCfgBindIface->Next(1, &pNetCfgBindIfce, NULL);
                            if (hr == S_OK)
                            {
                                INetCfgComponent *pNetCfgCompo;
                                hr = pNetCfgBindIfce->GetLowerComponent(&pNetCfgCompo);
                                if (hr == S_OK)
                                {
                                    ULONG uComponentStatus;
                                    hr = pNetCfgCompo->GetDeviceStatus(&uComponentStatus);
                                    if (hr == S_OK)
                                    {
                                        GUID guid;
                                        hr = pNetCfgCompo->GetInstanceGuid(&guid);
                                        if (   hr == S_OK
                                            && guid == *pGuid)
                                        {
                                            hr = pNetCfgBindings->MoveAfter(pNetCfgBindPath, NULL);
                                            if (FAILED(hr))
                                                NonStandardLogFlow(("Unable to move interface: %Rhrc\n", hr));
                                            fFoundIface = true;

                                            /*
                                             * Enable binding paths for host-only adapters bound to bridged filter
                                             * (see @bugref{8140}).
                                             */
                                            HRESULT hr2;
                                            LPWSTR  pwszHwId = NULL;
                                            if ((hr2 = pNcc->GetId(&pwszHwId)) != S_OK)
                                                NonStandardLogFlow(("Failed to get HW ID: %Rhrc\n", hr2));
                                            else
                                            {
                                                /** @todo r=bird: Original was:
                                                 *    _wcsnicmp(pwszHwId, VBOXNETCFGWIN_NETLWF_ID, sizeof(VBOXNETCFGWIN_NETLWF_ID)/2)
                                                 * which is the same as _wcsicmp. Not sure if this was accidental, but it's not the
                                                 * only one in this code area (VBoxNetFltNobj.cpp had some too IIRC). */
                                                if (RTUtf16ICmp(pwszHwId, VBOXNETCFGWIN_NETLWF_ID) != 0)
                                                    NonStandardLogFlow(("Ignoring component %ls\n", pwszHwId));
                                                else if ((hr2 = pNetCfgBindPath->IsEnabled()) != S_FALSE)
                                                    NonStandardLogFlow(("Already enabled binding path: %Rhrc\n", hr2));
                                                else if ((hr2 = pNetCfgBindPath->Enable(TRUE)) != S_OK)
                                                    NonStandardLogFlow(("Failed to enable binding path: %Rhrc\n", hr2));
                                                else
                                                    NonStandardLogFlow(("Enabled binding path\n"));
                                                CoTaskMemFree(pwszHwId);
                                            }
                                        }
                                    }
                                    pNetCfgCompo->Release();
                                }
                                else
                                    NonStandardLogFlow(("GetLowerComponent failed: %Rhrc\n", hr));
                                pNetCfgBindIfce->Release();
                            }
                            else
                            {
                                if (hr == S_FALSE) /* No more binding interfaces? */
                                    hr = S_OK;
                                else
                                    NonStandardLogFlow(("Next binding interface failed: %Rhrc\n", hr));
                                break;
                            }
                        } while (!fFoundIface);
                        pEnumNetCfgBindIface->Release();
                    }
                    else
                        NonStandardLogFlow(("EnumBindingInterfaces failed: %Rhrc\n", hr));
                    pNetCfgBindPath->Release();
                }
                else
                {
                    if (hr == S_FALSE) /* No more binding paths? */
                        hr = S_OK;
                    else
                        NonStandardLogFlow(("Next bind path failed: %Rhrc\n", hr));
                    break;
                }
            } while (!fFoundIface);
            pEnumNetCfgBindPath->Release();
        }
        else
            NonStandardLogFlow(("EnumBindingPaths failed: %Rhrc\n", hr));
        pNetCfgBindings->Release();
    }
    else
        NonStandardLogFlow(("QueryInterface for IID_INetCfgComponentBindings failed: %Rhrc\n", hr));
    return TRUE;
}

/** Callback for SetupDiSetDeviceInstallParams used  by
 *  vboxNetCfgWinCreateHostOnlyNetworkInterface */
static UINT WINAPI vboxNetCfgWinPspFileCallback(PVOID Context, UINT Notification, UINT_PTR Param1, UINT_PTR Param2)
{
    switch (Notification)
    {
        case SPFILENOTIFY_TARGETNEWER:
        case SPFILENOTIFY_TARGETEXISTS:
            return TRUE;
    }
    return SetupDefaultQueueCallbackW(Context, Notification, Param1, Param2);
}




/* The original source of the VBoxNetAdp adapter creation/destruction code has the following copyright: */
/*
   Copyright 2004 by the Massachusetts Institute of Technology

   All rights reserved.

   Permission to use, copy, modify, and distribute this software and its
   documentation for any purpose and without fee is hereby granted,
   provided that the above copyright notice appear in all copies and that
   both that copyright notice and this permission notice appear in
   supporting documentation, and that the name of the Massachusetts
   Institute of Technology (M.I.T.) not be used in advertising or publicity
   pertaining to distribution of the software without specific, written
   prior permission.

   M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
   ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
   M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
   ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
   WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
   ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
   SOFTWARE.
*/


/**
 *  Use the IShellFolder API to rename the connection.
 */
static HRESULT rename_shellfolder(PCWSTR pwszGuid, PCWSTR pwszNewName)
{
    /* Build the display name in the form "::{GUID}". Do this first in case it overflows. */
    WCHAR wszAdapterGuid[MAX_PATH + 2] = {0};
    ssize_t cwc = RTUtf16Printf(wszAdapterGuid, RT_ELEMENTS(wszAdapterGuid), "::%ls", pwszGuid);
    if (cwc < 0)
        return E_INVALIDARG;

    /* This is the GUID for the network connections folder. It is constant.
     * {7007ACC7-3202-11D1-AAD2-00805FC1270E} */
    const GUID MY_CLSID_NetworkConnections = {
        0x7007ACC7, 0x3202, 0x11D1, {
            0xAA, 0xD2, 0x00, 0x80, 0x5F, 0xC1, 0x27, 0x0E
        }
    };

    /* Create an instance of the network connections folder. */
    IShellFolder *pShellFolder = NULL;
    HRESULT hr = CoCreateInstance(MY_CLSID_NetworkConnections, NULL, CLSCTX_INPROC_SERVER, IID_IShellFolder,
                                  reinterpret_cast<LPVOID *>(&pShellFolder));
    if (SUCCEEDED(hr))
    {
        /* Parse the display name. */
        LPITEMIDLIST pidl = NULL;
        hr = pShellFolder->ParseDisplayName(NULL, NULL, wszAdapterGuid, NULL, &pidl, NULL);
        if (SUCCEEDED(hr))
            hr = pShellFolder->SetNameOf(NULL, pidl, pwszNewName, SHGDN_NORMAL, &pidl);
        CoTaskMemFree(pidl);
        pShellFolder->Release();
    }
    return hr;
}

/**
 * Loads a system DLL.
 *
 * @returns Module handle or NULL
 * @param   pwszName            The DLL name.
 */
static HMODULE loadSystemDll(const wchar_t *pwszName)
{
    WCHAR  wszPath[MAX_PATH];
    UINT   cwcPath = GetSystemDirectoryW(wszPath, RT_ELEMENTS(wszPath));
    size_t cwcName = RTUtf16Len(pwszName) + 1;
    if (cwcPath + 1 + cwcName > RT_ELEMENTS(wszPath))
        return NULL;

    wszPath[cwcPath++] = '\\';
    memcpy(&wszPath[cwcPath], pwszName, cwcName * sizeof(wszPath[0]));
    return LoadLibraryW(wszPath);
}

static bool vboxNetCfgWinDetectStaleConnection(PCWSTR pwszName)
{
    HKEY    hKeyAdapters = NULL;
    LSTATUS lrc = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
                               L"SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}",
                               0 /*ulOptions*/, KEY_ALL_ACCESS, &hKeyAdapters);
    if (lrc != ERROR_SUCCESS)
        return false;

    bool fFailureImminent = false;
    for (DWORD i = 0; !fFailureImminent; ++i)
    {
        WCHAR wszAdapterSubKeyName[MAX_PATH];
        DWORD cwcAdapterSubKeyName = MAX_PATH;
        lrc = RegEnumKeyEx(hKeyAdapters, i, wszAdapterSubKeyName, &cwcAdapterSubKeyName, NULL, NULL, NULL, NULL);
        if (lrc != ERROR_SUCCESS)
            break;

        HKEY hKeyAdapter = NULL;
        lrc = RegOpenKeyEx(hKeyAdapters, wszAdapterSubKeyName, 0, KEY_ALL_ACCESS, &hKeyAdapter);
        if (lrc == ERROR_SUCCESS)
        {
            HKEY hKeyConnection = NULL;
            lrc = RegOpenKeyEx(hKeyAdapter, L"Connection", 0, KEY_ALL_ACCESS, &hKeyConnection);
            if (lrc == ERROR_SUCCESS)
            {
                WCHAR wszCurName[MAX_PATH + 1];
                DWORD cbCurName = sizeof(wszCurName) - sizeof(WCHAR);
                DWORD dwType    = REG_SZ;
                lrc = RegQueryValueEx(hKeyConnection, L"Name", NULL, NULL, (LPBYTE)wszCurName, &cbCurName);
                if (   lrc == ERROR_SUCCESS
                    /** @todo r=bird: The original code didn't do any value type checks, thus allowing all SZ types. */
                    && (dwType == REG_SZ || dwType == REG_EXPAND_SZ || dwType == REG_MULTI_SZ))
                {
                    wszCurName[MAX_PATH] = '\0'; /* returned values doesn't necessarily need to be terminated */

                    if (RTUtf16ICmp(pwszName, pwszName) == 0)
                        fFailureImminent = true;
                }
                RegCloseKey(hKeyConnection);
            }
            RegCloseKey(hKeyAdapter);
        }
    }
    RegCloseKey(hKeyAdapters);

    return fFailureImminent;
}

VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinRenameConnection(LPWSTR pwszGuid, PCWSTR NewName)
{
    /*
     * Before attempting to rename the connection, check if there is a stale
     * connection with the same name. We must return ok, so the rest of
     * configuration process proceeds normally.
     */
    if (vboxNetCfgWinDetectStaleConnection(NewName))
        return S_OK;

    /* First try the IShellFolder interface, which was unimplemented
     * for the network connections folder before XP. */
    HRESULT hrc = rename_shellfolder(pwszGuid, NewName);
    if (hrc == E_NOTIMPL)
    {
/** @todo that code doesn't seem to work! */
        /* The IShellFolder interface is not implemented on this platform.
         * Try the (undocumented) HrRenameConnection API in the netshell
         * library. */
        CLSID     clsid;
        hrc = CLSIDFromString((LPOLESTR)pwszGuid, &clsid);
        if (FAILED(hrc))
            return E_FAIL;

        HINSTANCE hNetShell = loadSystemDll(L"netshell.dll");
        if (hNetShell == NULL)
            return E_FAIL;

        typedef HRESULT (WINAPI *PFNHRRENAMECONNECTION)(const GUID *, PCWSTR);
        PFNHRRENAMECONNECTION pfnRenameConnection = (PFNHRRENAMECONNECTION)GetProcAddress(hNetShell, "HrRenameConnection");
        if (pfnRenameConnection != NULL)
            hrc = pfnRenameConnection(&clsid, NewName);
        else
            hrc = E_FAIL;

        FreeLibrary(hNetShell);
    }
    if (FAILED(hrc))
        return hrc;
    return S_OK;
}


VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinRemoveHostOnlyNetworkInterface(IN const GUID *pGUID, OUT BSTR *pBstrErrMsg)
{
    HRESULT hrc = S_OK;
    com::Bstr bstrError;

    do /* break non-loop */
    {
        WCHAR wszPnPInstanceId[512] = {0};

        /*
         * We have to find the device instance ID through a registry search
         */
        HKEY hkeyNetwork    = NULL;
        HKEY hkeyConnection = NULL;
        do /* another non-loop for breaking out of */
        {
            WCHAR wszGuid[50];
            int cwcGuid = StringFromGUID2(*pGUID, wszGuid, RT_ELEMENTS(wszGuid));
            if (!cwcGuid)
                SetErrBreak(("Failed to create a Guid string"));

            WCHAR wszRegLocation[128 + RT_ELEMENTS(wszGuid)];
            RTUtf16Printf(wszRegLocation, RT_ELEMENTS(wszRegLocation),
                          "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}\\%ls", wszGuid);

            LSTATUS lrc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszRegLocation, 0, KEY_READ, &hkeyNetwork);
            if (lrc != ERROR_SUCCESS || !hkeyNetwork)
                SetErrBreak(("Host interface network is not found in registry (%S): lrc=%u [1]", wszRegLocation, lrc));

            lrc = RegOpenKeyExW(hkeyNetwork, L"Connection", 0, KEY_READ, &hkeyConnection);
            if (lrc != ERROR_SUCCESS || !hkeyConnection)
                SetErrBreak(("Host interface network is not found in registry (%S): lrc=%u [2]", wszRegLocation, lrc));

            DWORD cbValue = sizeof(wszPnPInstanceId) - sizeof(WCHAR);
            DWORD dwType  = ~0U;
            lrc = RegQueryValueExW(hkeyConnection, L"PnPInstanceID", NULL, &dwType, (LPBYTE)wszPnPInstanceId, &cbValue);
            if (lrc != ERROR_SUCCESS || dwType != REG_SZ)
                SetErrBreak(("Host interface network is not found in registry (%S): lrc=%u, dwType=%u [3]",
                             wszRegLocation, lrc, dwType));
        } while (0);

        if (hkeyConnection)
            RegCloseKey(hkeyConnection);
        if (hkeyNetwork)
            RegCloseKey(hkeyNetwork);
        if (FAILED(hrc))
            break;

        /*
         * Now we are going to enumerate all network devices and
         * wait until we encounter the right device instance ID
         */
        HDEVINFO hDeviceInfo = INVALID_HANDLE_VALUE;
        do /* break-only, not-a-loop */
        {
            BOOL  ok;

            /* initialize the structure size */
            SP_DEVINFO_DATA DeviceInfoData = { sizeof(DeviceInfoData) };

            /* copy the net class GUID */
            GUID netGuid;
            memcpy(&netGuid, &GUID_DEVCLASS_NET, sizeof(GUID_DEVCLASS_NET));

            /* return a device info set contains all installed devices of the Net class */
            hDeviceInfo = SetupDiGetClassDevs(&netGuid, NULL, NULL, DIGCF_PRESENT);
            if (hDeviceInfo == INVALID_HANDLE_VALUE)
                SetErrBreak(("SetupDiGetClassDevs failed (0x%08X)", GetLastError()));

            /* Enumerate the driver info list. */
            bool fFound = false;
            for (DWORD index = 0; !fFound; index++)
            {
                if (!SetupDiEnumDeviceInfo(hDeviceInfo, index, &DeviceInfoData))
                {
                    if (GetLastError() == ERROR_NO_MORE_ITEMS)
                        break;
                    continue;
                }

                /* try to get the hardware ID registry property */
                DWORD cbValue = 0;
                if (SetupDiGetDeviceRegistryPropertyW(hDeviceInfo,
                                                      &DeviceInfoData,
                                                      SPDRP_HARDWAREID,
                                                      NULL,
                                                      NULL,
                                                      0,
                                                      &cbValue))
                    continue; /* Something is wrong.  This shouldn't have worked with a NULL buffer! */
                if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
                    continue;

                WCHAR *pwszzDeviceHwId = (WCHAR *)RTMemAllocZ(cbValue + sizeof(WCHAR) * 2);
                if (!pwszzDeviceHwId)
                    break;
                if (SetupDiGetDeviceRegistryPropertyW(hDeviceInfo,
                                                      &DeviceInfoData,
                                                      SPDRP_HARDWAREID,
                                                      NULL,
                                                      (PBYTE)pwszzDeviceHwId,
                                                      cbValue,
                                                      &cbValue))
                {
                    /* search the string list. */
                    for (WCHAR *pwszCurHwId = pwszzDeviceHwId;
                         (uintptr_t)pwszCurHwId - (uintptr_t)pwszzDeviceHwId < cbValue && *pwszCurHwId != L'\0';
                         pwszCurHwId += RTUtf16Len(pwszCurHwId) + 1)
                        if (RTUtf16ICmp(DRIVERHWID, pwszCurHwId) == 0)
                        {
                            /* get the device instance ID */
                            WCHAR wszDevId[MAX_DEVICE_ID_LEN];
                            if (CM_Get_Device_IDW(DeviceInfoData.DevInst, wszDevId, MAX_DEVICE_ID_LEN, 0) == CR_SUCCESS)
                            {
                                /* compare to what we determined before */
                                if (RTUtf16Cmp(wszDevId, wszPnPInstanceId) == 0)
                                {
                                    fFound = true;
                                    break;
                                }
                            }
                        }
                }
                RTMemFree(pwszzDeviceHwId);
            }

            if (!fFound)
                SetErrBreak(("Host Interface Network driver not found (0x%08X)", GetLastError()));

            ok = SetupDiSetSelectedDevice(hDeviceInfo, &DeviceInfoData);
            if (!ok)
                SetErrBreak(("SetupDiSetSelectedDevice failed (0x%08X)", GetLastError()));

            ok = SetupDiCallClassInstaller(DIF_REMOVE, hDeviceInfo, &DeviceInfoData);
            if (!ok)
                SetErrBreak(("SetupDiCallClassInstaller (DIF_REMOVE) failed (0x%08X)", GetLastError()));
        } while (0);

        /* clean up the device info set */
        if (hDeviceInfo != INVALID_HANDLE_VALUE)
            SetupDiDestroyDeviceInfoList (hDeviceInfo);
    } while (0);

    if (pBstrErrMsg)
    {
        *pBstrErrMsg = NULL;
        if (bstrError.isNotEmpty())
            bstrError.detachToEx(pBstrErrMsg);
    }
    return hrc;
}

VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinUpdateHostOnlyNetworkInterface(LPCWSTR pcsxwInf, BOOL *pfRebootRequired, LPCWSTR pcsxwId)
{
    return VBoxDrvCfgDrvUpdate(pcsxwId, pcsxwInf, pfRebootRequired);
}

static const char *vboxNetCfgWinGetStateText(DWORD dwState)
{
    switch (dwState)
    {
        case SERVICE_STOPPED:           return "is not running";
        case SERVICE_STOP_PENDING:      return "is stopping";
        case SERVICE_CONTINUE_PENDING:  return "continue is pending";
        case SERVICE_PAUSE_PENDING:     return "pause is pending";
        case SERVICE_PAUSED:            return "is paused";
        case SERVICE_RUNNING:           return "is running";
        case SERVICE_START_PENDING:     return "is starting";
    }
    return "state is invalid";
}

static DWORD vboxNetCfgWinGetNetSetupState(SC_HANDLE hService)
{
    SERVICE_STATUS status;
    status.dwCurrentState = SERVICE_RUNNING;
    if (hService) {
        if (QueryServiceStatus(hService, &status))
            NonStandardLogFlow(("NetSetupSvc %s\n", vboxNetCfgWinGetStateText(status.dwCurrentState)));
        else
            NonStandardLogFlow(("QueryServiceStatus failed (0x%x)\n", GetLastError()));
    }
    return status.dwCurrentState;
}

DECLINLINE(bool) vboxNetCfgWinIsNetSetupRunning(SC_HANDLE hService)
{
    return vboxNetCfgWinGetNetSetupState(hService) == SERVICE_RUNNING;
}

DECLINLINE(bool) vboxNetCfgWinIsNetSetupStopped(SC_HANDLE hService)
{
    return vboxNetCfgWinGetNetSetupState(hService) == SERVICE_STOPPED;
}

typedef struct
{
    BSTR bstrName;
    GUID *pGuid;
    HRESULT hr;
} RENAMING_CONTEXT;

static BOOL vboxNetCfgWinRenameHostOnlyNetworkInterface(IN INetCfg *pNc, IN INetCfgComponent *pNcc, PVOID pContext)
{
    RT_NOREF1(pNc);
    RENAMING_CONTEXT *pParams = (RENAMING_CONTEXT *)pContext;

    GUID guid;
    pParams->hr = pNcc->GetInstanceGuid(&guid);
    if ( pParams->hr == S_OK && guid == *pParams->pGuid)
    {
        /* Located our component, rename it */
        pParams->hr = pNcc->SetDisplayName(pParams->bstrName);
        return FALSE;
    }
    return TRUE;
}

/**
 * Enumerate all host-only adapters collecting their names into a set, then
 * come up with the next available name by taking the first unoccupied index.
 */
static HRESULT vboxNetCfgWinNextAvailableDevName(com::Bstr *pbstrName)
{
    SP_DEVINFO_DATA DeviceInfoData = { sizeof(SP_DEVINFO_DATA) };
    HDEVINFO hDeviceInfoSet = SetupDiGetClassDevsW(&GUID_DEVCLASS_NET, NULL, NULL, DIGCF_PRESENT);
    if (hDeviceInfoSet == INVALID_HANDLE_VALUE)
        return HRESULT_FROM_WIN32(GetLastError());

    typedef struct VBOXDEVNAMEENTRY
    {
        RTLISTNODE  ListEntry;
        WCHAR       wszDevName[64];
        WCHAR       wcZeroParanoia;
    } VBOXDEVNAMEENTRY;

#if 0
    /*
     * Build a list of names starting with HOSTONLY_ADAPTER_NAME_WSZ belonging to our device.
     */
    RTLISTANCHOR Head; /* VBOXDEVNAMEENTRY */
    RTListInit(&Head);
    HRESULT      hrc = S_OK;
#else
    /*
     * Build a bitmap of in-use index values of devices starting with HOSTONLY_ADAPTER_NAME_WSZ.
     * Reserving 0 for one w/o a suffix, and marking 1 as unusable.
     */
    uint64_t bmIndexes[_32K / 64]; /* 4KB - 32767 device should be sufficient. */
    RT_ZERO(bmIndexes);
    ASMBitSet(&bmIndexes, 1);
#endif
    for (DWORD i = 0; SetupDiEnumDeviceInfo(hDeviceInfoSet, i, &DeviceInfoData); ++i)
    {
        /* Should be more than enough for both our device id and our device name, we do not care about the rest */
        VBOXDEVNAMEENTRY Entry = { { 0, 0 }, L"", 0 }; /* (initialize it to avoid the wrath of asan) */
        if (!SetupDiGetDeviceRegistryPropertyW(hDeviceInfoSet, &DeviceInfoData, SPDRP_HARDWAREID,
                                               NULL, (PBYTE)Entry.wszDevName, sizeof(Entry.wszDevName), NULL))
            continue;

        /* Ignore everything except our host-only adapters */
        if (RTUtf16ICmp(Entry.wszDevName, DRIVERHWID) == 0)
        {
            if (   SetupDiGetDeviceRegistryPropertyW(hDeviceInfoSet, &DeviceInfoData, SPDRP_FRIENDLYNAME,
                                                     NULL, (PBYTE)Entry.wszDevName, sizeof(Entry.wszDevName), NULL)
                || SetupDiGetDeviceRegistryPropertyW(hDeviceInfoSet, &DeviceInfoData, SPDRP_DEVICEDESC,
                                                     NULL, (PBYTE)Entry.wszDevName, sizeof(Entry.wszDevName), NULL))
            {
                /* We can ignore any host-only adapter with a non-standard name. */
                if (RTUtf16NICmp(Entry.wszDevName, HOSTONLY_ADAPTER_NAME_WSZ,
                                 RT_ELEMENTS(HOSTONLY_ADAPTER_NAME_WSZ) - 1) == 0)
                {
#if 0
                    VBOXDEVNAMEENTRY *pEntry = (VBOXDEVNAMEENTRY *)RTMemDup(&Entry, sizeof(Entry));
                    if (pEntry)
                        RTListAppend(&Head, &pEntry->ListEntry);
                    else
                    {
                        hrc = E_OUTOFMEMORY;
                        break;
                    }
#else
                    WCHAR const *pwc = &Entry.wszDevName[RT_ELEMENTS(HOSTONLY_ADAPTER_NAME_WSZ) - 1];

                    /* skip leading space */
                    WCHAR wc = *pwc;
                    while (wc == L' ' || wc == L'\t' || wc == L'\n' || wc == L'\r')
                        wc = *++pwc;

                    /* If end of string, use index 0. */
                    if (wc == L'\0')
                        ASMBitSet(bmIndexes, 0);

                    /* Hash and digit? */
                    else if (wc == L'#')
                    {
                        wc = *++pwc;
                        while (wc == L' ' || wc == L'\t' || wc == L'\n' || wc == L'\r') /* just in case */
                            wc = *++pwc;
                        if (wc >= L'0' && wc <= L'9')
                        {
                            /* Convert what we can to a number and mark it as allocated in the bitmap. */
                            uint64_t uIndex = wc - L'0';
                            while ((wc = *++pwc) >= L'0' && wc <= L'9')
                                uIndex = uIndex * 10 + wc - L'0';
                            if (uIndex < sizeof(bmIndexes) * 8 && uIndex > 0)
                                ASMBitSet(bmIndexes, (int32_t)uIndex);
                        }
                    }
#endif
                }
            }
        }
    }
#if 0
    if (SUCCEEDED(hrc))
    {
        /*
         * First try a name w/o an index, then try index #2 and up.
         *
         * Note! We have to use ASCII/UTF-8 strings here as Bstr will confuse WCHAR
         *       with BSTR/OLECHAR strings and use SysAllocString to duplicate it .
         */
        char         szName[sizeof(HOSTONLY_ADAPTER_NAME_SZ " #4294967296") + 32] = HOSTONLY_ADAPTER_NAME_SZ;
        size_t const cchBase = sizeof(HOSTONLY_ADAPTER_NAME_SZ) - 1;
        for (DWORD idx = 2;; idx++)
        {
            bool              fFound = false;
            VBOXDEVNAMEENTRY *pCur;
            RTListForEach(&Head, pCur, VBOXDEVNAMEENTRY, ListEntry)
            {
                fFound = RTUtf16ICmpAscii(pCur->wszDevName, szName) == 0;
                if (fFound)
                {
                    hrc = pbstrName->assignEx(szName);
                    break;
                }
            }
            if (fFound)
                break;
            RTStrPrintf(&szName[cchBase], sizeof(szName) - cchBase, " #%u", idx);
        }
    }

    VBOXDEVNAMEENTRY *pFirst;
    while ((pFirst = RTListRemoveFirst(&Head, VBOXDEVNAMEENTRY, ListEntry)) != NULL)
        RTMemFree(pFirst);

#else
    /*
     * Find an unused index value and format the corresponding name.
     */
    HRESULT hrc;
    int32_t iBit = ASMBitFirstClear(bmIndexes, sizeof(bmIndexes) * 8);
    if (iBit >= 0)
    {
        if (iBit == 0)
            hrc = pbstrName->assignEx(HOSTONLY_ADAPTER_NAME_SZ); /* Not _WSZ! */
        else
            hrc = pbstrName->printfNoThrow(HOSTONLY_ADAPTER_NAME_SZ " #%u", iBit);
    }
    else
    {
        NonStandardLogFlow(("vboxNetCfgWinNextAvailableDevName: no unused index in the first 32K!\n"));
        hrc = E_FAIL;
    }
#endif

    if (hDeviceInfoSet)
        SetupDiDestroyDeviceInfoList(hDeviceInfoSet);
    return hrc;
}

static HRESULT vboxNetCfgWinCreateHostOnlyNetworkInterface(IN LPCWSTR pwszInfPath, IN bool fIsInfPathFile,
                                                           IN BSTR pBstrDesiredName,
                                                           OUT GUID *pGuid, OUT BSTR *pBstrName, OUT BSTR *pBstrErrMsg)
{
    com::Bstr bstrError;

    /* Determine the interface name. We make a copy of the input here for
       renaming reasons, see futher down. */
    com::Bstr bstrNewInterfaceName;
    HRESULT   hrc;
    if (SysStringLen(pBstrDesiredName) != 0)
        hrc = bstrNewInterfaceName.assignEx(pBstrDesiredName);
    else
    {
        hrc = vboxNetCfgWinNextAvailableDevName(&bstrNewInterfaceName);
        if (FAILED(hrc))
            NonStandardLogFlow(("vboxNetCfgWinNextAvailableDevName failed with 0x%x\n", hrc));
    }
    if (FAILED(hrc))
        return hrc;

    WCHAR           wszCfgGuidString[50]  = {0};
    WCHAR           wszDevName[256 + 1]   = {0};
    SP_DEVINFO_DATA DeviceInfoData        = { sizeof(DeviceInfoData) };
    HDEVINFO        hDeviceInfo           = INVALID_HANDLE_VALUE;
    PVOID           pQueueCallbackContext = NULL;
    BOOL            fRegistered           = FALSE;
    BOOL            destroyList           = FALSE;
    HKEY            hkey                  = (HKEY)INVALID_HANDLE_VALUE;
    LSTATUS         lrcRet                = ERROR_SUCCESS; /* the */

    do /* non-loop, for breaking. */
    {
        /* copy the net class GUID */
        GUID netGuid;
        memcpy(&netGuid, &GUID_DEVCLASS_NET, sizeof(GUID_DEVCLASS_NET));

        /* Create an empty device info set associated with the net class GUID: */
        hDeviceInfo = SetupDiCreateDeviceInfoList(&netGuid, NULL);
        if (hDeviceInfo == INVALID_HANDLE_VALUE)
            SetErrBreak(("SetupDiCreateDeviceInfoList failed (%Rwc)", GetLastError()));

        /* Translate the GUID to a class name: */
        WCHAR wszClassName[MAX_PATH];
        if (!SetupDiClassNameFromGuid(&netGuid, wszClassName, MAX_PATH, NULL))
            SetErrBreak(("SetupDiClassNameFromGuid failed (%Rwc)", GetLastError()));

        /* Create a device info element and add the new device instance key to registry: */
        if (!SetupDiCreateDeviceInfo(hDeviceInfo, wszClassName, &netGuid, NULL, NULL, DICD_GENERATE_ID, &DeviceInfoData))
            SetErrBreak(("SetupDiCreateDeviceInfo failed (%Rwc)", GetLastError()));

        /* Select the newly created device info to be the currently selected member: */
        if (!SetupDiSetSelectedDevice(hDeviceInfo, &DeviceInfoData))
            SetErrBreak(("SetupDiSetSelectedDevice failed (%Rwc)", GetLastError()));

        SP_DEVINSTALL_PARAMS DeviceInstallParams;
        if (pwszInfPath)
        {
            /* get the device install parameters and disable filecopy */
            DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
            if (SetupDiGetDeviceInstallParams(hDeviceInfo, &DeviceInfoData, &DeviceInstallParams))
            {
                memset(DeviceInstallParams.DriverPath, 0, sizeof(DeviceInstallParams.DriverPath));
                size_t pathLenght = wcslen(pwszInfPath) + 1/* null terminator */;
                if (pathLenght < sizeof(DeviceInstallParams.DriverPath)/sizeof(DeviceInstallParams.DriverPath[0]))
                {
                    memcpy(DeviceInstallParams.DriverPath, pwszInfPath, pathLenght*sizeof(DeviceInstallParams.DriverPath[0]));

                    if (fIsInfPathFile)
                        DeviceInstallParams.Flags |= DI_ENUMSINGLEINF;

                    if (!SetupDiSetDeviceInstallParams(hDeviceInfo, &DeviceInfoData, &DeviceInstallParams))
                    {
                        NonStandardLogFlow(("SetupDiSetDeviceInstallParams failed (%Rwc)\n", GetLastError()));
                        break;
                    }
                }
                else
                {
                    NonStandardLogFlow(("SetupDiSetDeviceInstallParams faileed: INF path is too long\n"));
                    break;
                }
            }
            else
                NonStandardLogFlow(("SetupDiGetDeviceInstallParams failed (%Rwc)\n", GetLastError()));
        }

        /* build a list of class drivers */
        if (!SetupDiBuildDriverInfoList(hDeviceInfo, &DeviceInfoData, SPDIT_CLASSDRIVER))
            SetErrBreak(("SetupDiBuildDriverInfoList failed (%Rwc)", GetLastError()));

        destroyList = TRUE;

        /*
         * Enumerate the driver info list.
         */
        /* For our purposes, 2k buffer is more than enough to obtain the
           hardware ID of the VBoxNetAdp driver. */ /** @todo r=bird: The buffer isn't 2KB, it's 8KB, but whatever. */
        DWORD           detailBuf[2048];
        SP_DRVINFO_DATA DriverInfoData = { sizeof(DriverInfoData) };
        bool            fFound         = false;
        for (DWORD index = 0; !fFound; index++)
        {
            /* If the function fails with last error set to ERROR_NO_MORE_ITEMS,
               then we have reached the end of the list.  Otherwise there was
               something wrong with this particular driver. */
            if (!SetupDiEnumDriverInfo(hDeviceInfo, &DeviceInfoData, SPDIT_CLASSDRIVER, index, &DriverInfoData))
            {
                if (GetLastError() == ERROR_NO_MORE_ITEMS)
                    break;
                continue;
            }

            /* if we successfully find the hardware ID and it turns out to
             * be the one for the loopback driver, then we are done. */
            PSP_DRVINFO_DETAIL_DATA_W pDriverInfoDetail = (PSP_DRVINFO_DETAIL_DATA_W)detailBuf;
            pDriverInfoDetail->cbSize = sizeof(SP_DRVINFO_DETAIL_DATA);
            DWORD cbValue = 0;
            if (SetupDiGetDriverInfoDetailW(hDeviceInfo,
                                            &DeviceInfoData,
                                            &DriverInfoData,
                                            pDriverInfoDetail,
                                            sizeof(detailBuf) - sizeof(detailBuf[0]),
                                            &cbValue))
            {
                /* Sure that the HardwareID string list is properly zero terminated (paranoia). */
                detailBuf[RT_ELEMENTS(detailBuf) - 1] = 0; AssertCompile(sizeof(detailBuf[0]) == sizeof(WCHAR) * 2);

                /* pDriverInfoDetail->HardwareID is a MULTISZ string.  Go through the whole
                   list and see if there is a match somewhere: */
                for (WCHAR *pwszCurHwId = pDriverInfoDetail->HardwareID;
                     (uintptr_t)pwszCurHwId - (uintptr_t)pDriverInfoDetail < cbValue && *pwszCurHwId != L'\0';
                     pwszCurHwId += RTUtf16Len(pwszCurHwId) + 1)
                    if (RTUtf16ICmp(DRIVERHWID, pwszCurHwId) == 0)
                    {
                        fFound = true;
                        break;
                    }
            }
        }

        if (!fFound)
            SetErrBreak(("Could not find Host Interface Networking driver! Please reinstall"));

        /* set the loopback driver to be the currently selected */
        if (!SetupDiSetSelectedDriver(hDeviceInfo, &DeviceInfoData, &DriverInfoData))
            SetErrBreak(("SetupDiSetSelectedDriver failed (%u)", GetLastError()));

        /* register the phantom device to prepare for install */
        if (!SetupDiCallClassInstaller(DIF_REGISTERDEVICE, hDeviceInfo, &DeviceInfoData))
            SetErrBreak(("SetupDiCallClassInstaller failed (%u)", GetLastError()));

        /* registered, but remove if errors occur in the following code */
        fRegistered = TRUE;

        /* ask the installer if we can install the device */
        if (!SetupDiCallClassInstaller(DIF_ALLOW_INSTALL, hDeviceInfo, &DeviceInfoData))
        {
            if (GetLastError() != ERROR_DI_DO_DEFAULT)
                SetErrBreak(("SetupDiCallClassInstaller (DIF_ALLOW_INSTALL) failed (%u)", GetLastError()));
            /* that's fine */
        }

        /* get the device install parameters and disable filecopy */
        DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
        if (SetupDiGetDeviceInstallParams(hDeviceInfo, &DeviceInfoData, &DeviceInstallParams))
        {
            pQueueCallbackContext = SetupInitDefaultQueueCallback(NULL);
            if (pQueueCallbackContext)
            {
                DeviceInstallParams.InstallMsgHandlerContext = pQueueCallbackContext;
                DeviceInstallParams.InstallMsgHandler = (PSP_FILE_CALLBACK)vboxNetCfgWinPspFileCallback;
                if (!SetupDiSetDeviceInstallParamsW(hDeviceInfo, &DeviceInfoData, &DeviceInstallParams))
                {
                    DWORD winEr = GetLastError();
                    NonStandardLogFlow(("SetupDiSetDeviceInstallParamsW failed, winEr (%d)\n", winEr));
                    Assert(0);
                }
            }
            else
            {
                DWORD winEr = GetLastError();
                NonStandardLogFlow(("SetupInitDefaultQueueCallback failed, winEr (%d)\n", winEr));
            }
        }
        else
        {
            DWORD winEr = GetLastError();
            NonStandardLogFlow(("SetupDiGetDeviceInstallParams failed, winEr (%d)\n", winEr));
        }

        /* install the files first */
        if (!SetupDiCallClassInstaller(DIF_INSTALLDEVICEFILES, hDeviceInfo, &DeviceInfoData))
            SetErrBreak(("SetupDiCallClassInstaller (DIF_INSTALLDEVICEFILES) failed (%Rwc)", GetLastError()));

        /* get the device install parameters and disable filecopy */
        DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
        if (SetupDiGetDeviceInstallParams(hDeviceInfo, &DeviceInfoData, &DeviceInstallParams))
        {
            DeviceInstallParams.Flags |= DI_NOFILECOPY;
            if (!SetupDiSetDeviceInstallParamsW(hDeviceInfo, &DeviceInfoData, &DeviceInstallParams))
                SetErrBreak(("SetupDiSetDeviceInstallParamsW failed (%Rwc)", GetLastError()));
        }
        /** @todo r=bird: Why isn't SetupDiGetDeviceInstallParams failure fatal here? */

        /*
         * Register any device-specific co-installers for this device,
         */
        if (!SetupDiCallClassInstaller(DIF_REGISTER_COINSTALLERS, hDeviceInfo, &DeviceInfoData))
            SetErrBreak(("SetupDiCallClassInstaller (DIF_REGISTER_COINSTALLERS) failed (%Rwc)", GetLastError()));

        /*
         * install any installer-specified interfaces.
         * and then do the real install
         */
        if (!SetupDiCallClassInstaller(DIF_INSTALLINTERFACES, hDeviceInfo, &DeviceInfoData))
            SetErrBreak(("SetupDiCallClassInstaller (DIF_INSTALLINTERFACES) failed (%Rwc)", GetLastError()));

        if (!SetupDiCallClassInstaller(DIF_INSTALLDEVICE, hDeviceInfo, &DeviceInfoData))
            SetErrBreak(("SetupDiCallClassInstaller (DIF_INSTALLDEVICE) failed (%Rwc)", GetLastError()));

        /*
         * Query the instance ID; on Windows 10, the registry key may take a short
         * while to appear. Microsoft recommends waiting for up to 5 seconds, but
         * we want to be on the safe side, so let's wait for 20 seconds. Waiting
         * longer is harmful as network setup service will shut down after a period
         * of inactivity.
         */
        for (int retries = 0; retries < 2 * 20; ++retries)
        {
            Sleep(500); /* half second */

            /* Figure out NetCfgInstanceId */
            hkey = SetupDiOpenDevRegKey(hDeviceInfo, &DeviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_READ);
            if (hkey == INVALID_HANDLE_VALUE)
                break;

            DWORD cbSize       = sizeof(wszCfgGuidString);
            DWORD dwValueType = 0;
            lrcRet = RegQueryValueExW(hkey, L"NetCfgInstanceId", NULL, &dwValueType, (LPBYTE)wszCfgGuidString, &cbSize);
            /* As long as the return code is FILE_NOT_FOUND, sleep and retry. */
            if (lrcRet != ERROR_FILE_NOT_FOUND)
                break;

            RegCloseKey(hkey);
            hkey = (HKEY)INVALID_HANDLE_VALUE;
        }

        if (lrcRet == ERROR_FILE_NOT_FOUND)
        {
            hrc = E_ABORT;
            break;
        }

        /*
         * We need to check 'hkey' after we check 'lrcRet' to distinguish the case
         * of failed SetupDiOpenDevRegKey from the case when we timed out.
         */
        if (hkey == INVALID_HANDLE_VALUE)
            SetErrBreak(("SetupDiOpenDevRegKey failed (%Rwc)", GetLastError()));

        if (lrcRet != ERROR_SUCCESS)
            SetErrBreak(("Querying NetCfgInstanceId failed (%Rwc)", lrcRet));

        NET_LUID luid;
        HRESULT hrcSMRes = vboxNetCfgWinGetInterfaceLUID(hkey, &luid);

        /* Close the key as soon as possible. See @bugref{7973}. */
        RegCloseKey(hkey);
        hkey = (HKEY)INVALID_HANDLE_VALUE;

        if (FAILED(hrcSMRes))
        {
            /*
             * The setting of Metric is not very important functionality,
             * So we will not break installation process due to this error.
             */
            NonStandardLogFlow(("vboxNetCfgWinCreateHostOnlyNetworkInterface: Warning! "
                                "vboxNetCfgWinGetInterfaceLUID failed, default metric for new interface will not be set: %Rhrc\n",
                                hrcSMRes));
        }
        else
        {
            /*
             * Set default metric value of interface to fix multicast issue
             * See @bugref{6379} for details.
             */
            hrcSMRes = vboxNetCfgWinSetupMetric(&luid);
            if (FAILED(hrcSMRes))
            {
                /*
                 * The setting of Metric is not very important functionality,
                 * So we will not break installation process due to this error.
                 */
                NonStandardLogFlow(("vboxNetCfgWinCreateHostOnlyNetworkInterface: Warning! "
                                    "vboxNetCfgWinSetupMetric failed, default metric for new interface will not be set: %Rhrc\n",
                                    hrcSMRes));
            }
        }

        /*
         * We need to query the device name after we have succeeded in querying its
         * instance ID to avoid similar waiting-and-retrying loop (see @bugref{7973}).
         */
        if (!SetupDiGetDeviceRegistryPropertyW(hDeviceInfo, &DeviceInfoData,
                                               SPDRP_FRIENDLYNAME , /* IN DWORD Property,*/
                                               NULL, /*OUT PDWORD PropertyRegDataType, OPTIONAL*/
                                               (PBYTE)wszDevName, /*OUT PBYTE PropertyBuffer,*/
                                               sizeof(wszDevName) - sizeof(WCHAR), /* IN DWORD PropertyBufferSize,*/
                                               NULL /*OUT PDWORD RequiredSize OPTIONAL*/ ))
        {
            DWORD dwErr = GetLastError();
            if (dwErr != ERROR_INVALID_DATA)
                SetErrBreak(("SetupDiGetDeviceRegistryProperty failed (%Rwc)", dwErr));

            RT_ZERO(wszDevName);
            if (!SetupDiGetDeviceRegistryPropertyW(hDeviceInfo, &DeviceInfoData,
                                                   SPDRP_DEVICEDESC, /* IN DWORD Property,*/
                                                   NULL, /*OUT PDWORD PropertyRegDataType, OPTIONAL*/
                                                   (PBYTE)wszDevName, /*OUT PBYTE PropertyBuffer,*/
                                                   sizeof(wszDevName) - sizeof(WCHAR), /* IN DWORD PropertyBufferSize,*/
                                                   NULL /*OUT PDWORD RequiredSize OPTIONAL*/ ))
                SetErrBreak(("SetupDiGetDeviceRegistryProperty failed (%Rwc)", GetLastError()));
        }

        /* No need to rename the device if the names match. */
        if (RTUtf16Cmp(bstrNewInterfaceName.raw(), wszDevName) == 0)
            bstrNewInterfaceName.setNull();

#ifdef VBOXNETCFG_DELAYEDRENAME
        /* Re-use wszDevName for device instance id retrieval. */
        DWORD cwcReturned = 0;
        RT_ZERO(wszDevName);
        if (!SetupDiGetDeviceInstanceIdW(hDeviceInfo, &DeviceInfoData, wszDevName, RT_ELEMENTS(wszDevName) - 1, &cwcReturned))
            SetErrBreak(("SetupDiGetDeviceInstanceId failed (%Rwc)", GetLastError()));
#endif /* VBOXNETCFG_DELAYEDRENAME */
    } while (0);

    /*
     * Cleanup.
     */
    if (hkey != INVALID_HANDLE_VALUE)
        RegCloseKey(hkey);

    if (pQueueCallbackContext)
        SetupTermDefaultQueueCallback(pQueueCallbackContext);

    if (hDeviceInfo != INVALID_HANDLE_VALUE)
    {
        /* an error has occurred, but the device is registered, we must remove it */
        if (lrcRet != ERROR_SUCCESS && fRegistered)
            SetupDiCallClassInstaller(DIF_REMOVE, hDeviceInfo, &DeviceInfoData);

        SetupDiDeleteDeviceInfo(hDeviceInfo, &DeviceInfoData);

        /* destroy the driver info list */
#if 0
        /* I've remove this, as I was consistently getting crashes in
         * SetupDiDestroyDeviceInfoList otherwise during MSI installation
         * (W10 build 19044, VBox r153431 + nocrt changes).
         *
         * Some details from windbg:
         *
         * (175e8.1596c): Access violation - code c0000005 (first chance)
         * First chance exceptions are reported before any exception handling.
         * This exception may be expected and handled.
         * SETUPAPI!DereferenceClassDriverList+0x4e:
         * 00007ffa`83e2a42a 834008ff        add     dword ptr [rax+8],0FFFFFFFFh ds:00000000`00000008=????????
         * 0:006> k
         *  # Child-SP          RetAddr           Call Site
         * 00 0000007e`ccd7c070 00007ffa`83e2a287 SETUPAPI!DereferenceClassDriverList+0x4e
         * 01 0000007e`ccd7c0a0 00007ffa`56b96bd3 SETUPAPI!SetupDiDestroyDriverInfoList+0x117
         * 02 0000007e`ccd7c0f0 00007ffa`56b982a3 MSIF170!vboxNetCfgWinCreateHostOnlyNetworkInterface+0xb23 [E:\vbox\svn\trunk\src\VBox\HostDrivers\VBoxNetFlt\win\cfg\VBoxNetCfg.cpp @ 3378]
         * 03 0000007e`ccd7ef10 00007ffa`56b92cb8 MSIF170!VBoxNetCfgWinCreateHostOnlyNetworkInterface+0x53 [E:\vbox\svn\trunk\src\VBox\HostDrivers\VBoxNetFlt\win\cfg\VBoxNetCfg.cpp @ 3479]
         * 04 0000007e`ccd7efc0 00007ffa`610f59d3 MSIF170!_createHostOnlyInterface+0x218 [E:\vbox\svn\trunk\src\VBox\Installer\win\InstallHelper\VBoxInstallHelper.cpp @ 1453]
         * 05 0000007e`ccd7f260 00007ffa`610d80ac msi!CallCustomDllEntrypoint+0x2b
         * 06 0000007e`ccd7f2d0 00007ffa`84567034 msi!CMsiCustomAction::CustomActionThread+0x34c
         * 07 0000007e`ccd7f8c0 00007ffa`849a2651 KERNEL32!BaseThreadInitThunk+0x14
         * 08 0000007e`ccd7f8f0 00000000`00000000 ntdll!RtlUserThreadStart+0x21
         * 0:006> r
         * rax=0000000000000000 rbx=0000025f4f03af90 rcx=00007ffa83f23b40
         * rdx=0000025f4f03af90 rsi=0000025f5108be50 rdi=0000025f4f066f10
         * rip=00007ffa83e2a42a rsp=0000007eccd7c070 rbp=00007ffa83f23000
         *  r8=0000007eccd7c0a8  r9=0000007eccd7c1f0 r10=0000000000000000
         * r11=0000007eccd7c020 r12=0000007eccd7c3d0 r13=00007ffa83f23000
         * r14=0000000000000000 r15=0000025f4f03af90
         * iopl=0         nv up ei pl nz na po nc
         * cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010206
         * SETUPAPI!DereferenceClassDriverList+0x4e:
         * 00007ffa`83e2a42a 834008ff        add     dword ptr [rax+8],0FFFFFFFFh ds:00000000`00000008=????????
         *
         */
        if (destroyList)
            SetupDiDestroyDriverInfoList(hDeviceInfo, &DeviceInfoData, SPDIT_CLASSDRIVER);
#else
        RT_NOREF(destroyList);
#endif

        /* clean up the device info set */
        SetupDiDestroyDeviceInfoList(hDeviceInfo);
    }

    /*
     * Return the network connection GUID on success.
     */
    if (SUCCEEDED(hrc))
    {
        /** @todo r=bird: Some please explain this mess. It's not just returning a
         *        GUID here, it's a bit more than that. */
        RENAMING_CONTEXT context;
        context.hr = E_FAIL;

        if (pGuid)
        {
            hrc = CLSIDFromString(wszCfgGuidString, (LPCLSID)pGuid);
            if (FAILED(hrc))
                NonStandardLogFlow(("CLSIDFromString failed, hrc (0x%x)\n", hrc));
        }

        INetCfg *pNetCfg = NULL;
        LPWSTR   pwszApp = NULL;
        HRESULT hrc2 = VBoxNetCfgWinQueryINetCfg(&pNetCfg, TRUE, L"VirtualBox Host-Only Creation",
                                                 30 * 1000, /* on Vista we often get 6to4svc.dll holding the lock, wait for 30 sec.  */
                                                 /** @todo special handling for 6to4svc.dll ???, i.e. several retrieves */
                                                 &pwszApp);
        if (hrc2 == S_OK)
        {
            if (bstrNewInterfaceName.isNotEmpty())
            {
                /* The assigned name does not match the desired one, rename the device */
                context.bstrName = bstrNewInterfaceName.raw();
                context.pGuid    = pGuid;
                hrc2 = vboxNetCfgWinEnumNetCfgComponents(pNetCfg, &GUID_DEVCLASS_NET,
                                                         vboxNetCfgWinRenameHostOnlyNetworkInterface, &context);
            }
            if (SUCCEEDED(hrc2))
                hrc2 = vboxNetCfgWinEnumNetCfgComponents(pNetCfg, &GUID_DEVCLASS_NETSERVICE,
                                                         vboxNetCfgWinAdjustHostOnlyNetworkInterfacePriority, pGuid);
            if (SUCCEEDED(hrc2))
                hrc2 = vboxNetCfgWinEnumNetCfgComponents(pNetCfg, &GUID_DEVCLASS_NETTRANS,
                                                         vboxNetCfgWinAdjustHostOnlyNetworkInterfacePriority, pGuid);
            if (SUCCEEDED(hrc2))
                hrc2 = vboxNetCfgWinEnumNetCfgComponents(pNetCfg, &GUID_DEVCLASS_NETCLIENT,
                                                         vboxNetCfgWinAdjustHostOnlyNetworkInterfacePriority, pGuid);
            if (SUCCEEDED(hrc2))
                hrc2 = pNetCfg->Apply();
            else
                NonStandardLogFlow(("Enumeration failed, hrc2=%Rhrc\n", hrc2));

            VBoxNetCfgWinReleaseINetCfg(pNetCfg, TRUE);
        }
        else if (hrc2 == NETCFG_E_NO_WRITE_LOCK && pwszApp)
        {
            NonStandardLogFlow(("Application '%ls' is holding the lock, failed\n", pwszApp));
            CoTaskMemFree(pwszApp);
            pwszApp = NULL;
        }
        else
            NonStandardLogFlow(("VBoxNetCfgWinQueryINetCfg failed, hrc2=%Rhrc\n", hrc2));

#ifndef VBOXNETCFG_DELAYEDRENAME
        /* If the device has been successfully renamed, replace the name now. */
        if (SUCCEEDED(hrc2) && SUCCEEDED(context.hr))
            RTUtf16Copy(wszDevName, RT_ELEMENTS(wszDevName), pBstrDesiredName);

        WCHAR wszConnectionName[128];
        hrc2 = VBoxNetCfgWinGenHostonlyConnectionName(wszDevName, wszConnectionName, RT_ELEMENTS(wszConnectionName), NULL);
        if (SUCCEEDED(hrc2))
            hrc2 = VBoxNetCfgWinRenameConnection(wszCfgGuidString, wszConnectionName);
#endif

        /*
         * Now, return the network connection GUID/name.
         */
        if (pBstrName)
        {
            *pBstrName = SysAllocString((const OLECHAR *)wszDevName);
            if (!*pBstrName)
            {
                NonStandardLogFlow(("SysAllocString failed\n"));
                hrc = E_OUTOFMEMORY;
            }
        }
    }

    if (pBstrErrMsg)
    {
        *pBstrErrMsg = NULL;
        if (bstrError.isNotEmpty())
            bstrError.detachToEx(pBstrErrMsg);
    }
    return hrc;
}

VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinCreateHostOnlyNetworkInterface(IN LPCWSTR pwszInfPath, IN bool fIsInfPathFile,
                                                                        IN BSTR pwszDesiredName, OUT GUID *pGuid,
                                                                        OUT BSTR *pBstrName, OUT BSTR *pBstrErrMsg)
{
    HRESULT hrc = vboxNetCfgWinCreateHostOnlyNetworkInterface(pwszInfPath, fIsInfPathFile, pwszDesiredName,
                                                              pGuid, pBstrName, pBstrErrMsg);
    if (hrc == E_ABORT)
    {
        NonStandardLogFlow(("Timed out while waiting for NetCfgInstanceId, try again immediately...\n"));

        /*
         * This is the first time we fail to obtain NetCfgInstanceId, let us
         * retry it once. It is needed to handle the situation when network
         * setup fails to recognize the arrival of our device node while it
         * is busy removing another host-only interface, and it gets stuck
         * with no matching network interface created for our device node.
         * See @bugref{7973} for details.
         */
        hrc = vboxNetCfgWinCreateHostOnlyNetworkInterface(pwszInfPath, fIsInfPathFile, pwszDesiredName,
                                                          pGuid, pBstrName, pBstrErrMsg);
        if (hrc == E_ABORT)
        {
            NonStandardLogFlow(("Timed out again while waiting for NetCfgInstanceId, try again after a while...\n"));

            /*
             * This is the second time we fail to obtain NetCfgInstanceId, let us
             * retry it once more. This time we wait to network setup service
             * to go down before retrying. Hopefully it will resolve all error
             * conditions. See @bugref{7973} for details.
             */
            SC_HANDLE hSCM = OpenSCManagerW(NULL, NULL, GENERIC_READ);
            if (hSCM)
            {
                SC_HANDLE hService = OpenServiceW(hSCM, L"NetSetupSvc", GENERIC_READ);
                if (hService)
                {
                    for (int retries = 0; retries < 60 && !vboxNetCfgWinIsNetSetupStopped(hService); ++retries)
                        Sleep(1000);
                    CloseServiceHandle(hService);
                    hrc = vboxNetCfgWinCreateHostOnlyNetworkInterface(pwszInfPath, fIsInfPathFile, pwszDesiredName,
                                                                      pGuid, pBstrName, pBstrErrMsg);
                }
                else
                    NonStandardLogFlow(("OpenService failed (%Rwc)\n", GetLastError()));
                CloseServiceHandle(hSCM);
            }
            else
                NonStandardLogFlow(("OpenSCManager failed (%Rwc)", GetLastError()));

            /* Give up and report the error. */
            if (hrc == E_ABORT)
            {
                if (pBstrErrMsg)
                {
                    com::Bstr bstrError;
                    bstrError.printfNoThrow("Querying NetCfgInstanceId failed (ERROR_FILE_NOT_FOUND)");
                    bstrError.detachToEx(pBstrErrMsg);
                }
                hrc = E_FAIL;
            }
        }
    }
    return hrc;
}

static HRESULT vboxNetCfgWinGetLoopbackMetric(OUT DWORD *Metric)
{
    Assert(g_pfnInitializeIpInterfaceEntry != NULL);
    Assert(g_pfnGetIpInterfaceEntry != NULL);

    MIB_IPINTERFACE_ROW row;
    RT_ZERO(row); /* paranoia */
    g_pfnInitializeIpInterfaceEntry(&row);

    row.Family = AF_INET;
    row.InterfaceLuid.Info.IfType = IF_TYPE_SOFTWARE_LOOPBACK;

    NETIO_STATUS dwErr = g_pfnGetIpInterfaceEntry(&row);
    if (dwErr == NO_ERROR)
    {
        *Metric = row.Metric;
        return S_OK;
    }
    return HRESULT_FROM_WIN32(dwErr);
}

static HRESULT vboxNetCfgWinSetInterfaceMetric(IN NET_LUID *pInterfaceLuid, IN DWORD metric)
{
    Assert(g_pfnInitializeIpInterfaceEntry != NULL);
    Assert(g_pfnSetIpInterfaceEntry != NULL);

    MIB_IPINTERFACE_ROW newRow;
    RT_ZERO(newRow); /* paranoia */
    g_pfnInitializeIpInterfaceEntry(&newRow);

    // identificate the interface to change
    newRow.InterfaceLuid = *pInterfaceLuid;
    newRow.Family = AF_INET;

    // changed settings
    newRow.UseAutomaticMetric = false;
    newRow.Metric = metric;

    // change settings
    NETIO_STATUS dwErr = g_pfnSetIpInterfaceEntry(&newRow);
    if (dwErr == NO_ERROR)
        return S_OK;
    return HRESULT_FROM_WIN32(dwErr);
}

static HRESULT vboxNetCfgWinSetupMetric(IN NET_LUID* pLuid)
{
    HRESULT  hrc  = E_FAIL;
    HMODULE  hmod = loadSystemDll(L"Iphlpapi.dll");
    if (hmod)
    {
        g_pfnInitializeIpInterfaceEntry = (PFNINITIALIZEIPINTERFACEENTRY)GetProcAddress(hmod, "InitializeIpInterfaceEntry");
        g_pfnGetIpInterfaceEntry        = (PFNGETIPINTERFACEENTRY)       GetProcAddress(hmod, "GetIpInterfaceEntry");
        g_pfnSetIpInterfaceEntry        = (PFNSETIPINTERFACEENTRY)       GetProcAddress(hmod, "SetIpInterfaceEntry");
        Assert(g_pfnInitializeIpInterfaceEntry);
        Assert(g_pfnGetIpInterfaceEntry);
        Assert(g_pfnSetIpInterfaceEntry);

        if (   g_pfnInitializeIpInterfaceEntry
            && g_pfnGetIpInterfaceEntry
            && g_pfnSetIpInterfaceEntry)
        {
            DWORD loopbackMetric = 0;
            hrc = vboxNetCfgWinGetLoopbackMetric(&loopbackMetric);
            if (SUCCEEDED(hrc))
                hrc = vboxNetCfgWinSetInterfaceMetric(pLuid, loopbackMetric - 1);
        }

        g_pfnInitializeIpInterfaceEntry = NULL;
        g_pfnSetIpInterfaceEntry        = NULL;
        g_pfnGetIpInterfaceEntry        = NULL;

        FreeLibrary(hmod);
    }
    return hrc;
}

static HRESULT vboxNetCfgWinGetInterfaceLUID(IN HKEY hKey, OUT NET_LUID *pLUID)
{
    if (pLUID == NULL)
        return E_INVALIDARG;

    DWORD dwLuidIndex = 0;
    DWORD cbSize      = sizeof(dwLuidIndex);
    DWORD dwValueType = REG_DWORD; /** @todo r=bird: This is output only. No checked after the call. So, only for debugging? */
    LSTATUS lrc = RegQueryValueExW(hKey, L"NetLuidIndex", NULL, &dwValueType, (LPBYTE)&dwLuidIndex, &cbSize);
    if (lrc == ERROR_SUCCESS)
    {
        DWORD dwIfType = 0;
        cbSize         = sizeof(dwIfType);
        dwValueType    = REG_DWORD; /** @todo r=bird: This is output only. No checked after the call. So, only for debugging? */
        lrc = RegQueryValueExW(hKey, L"*IfType", NULL, &dwValueType, (LPBYTE)&dwIfType, &cbSize);
        if (lrc == ERROR_SUCCESS)
        {
            RT_ZERO(*pLUID);
            pLUID->Info.IfType       = dwIfType;
            pLUID->Info.NetLuidIndex = dwLuidIndex;
            return S_OK;
        }
    }

    RT_ZERO(*pLUID);
    return HRESULT_FROM_WIN32(lrc);
}


#ifdef VBOXNETCFG_DELAYEDRENAME
VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinRenameHostOnlyConnection(IN const GUID *pGuid, IN LPCWSTR pwszId, OUT BSTR *pDevName)
{
    if (pDevName)
        *pDevName = NULL;

    HRESULT  hr = S_OK; /** @todo r=bird: ODD return status for SetupDiCreateDeviceInfoList failures! */
    HDEVINFO hDevInfo = SetupDiCreateDeviceInfoList(&GUID_DEVCLASS_NET, NULL);
    if (hDevInfo != INVALID_HANDLE_VALUE)
    {
        SP_DEVINFO_DATA DevInfoData = { sizeof(SP_DEVINFO_DATA) };
        if (SetupDiOpenDeviceInfo(hDevInfo, pwszId, NULL, 0, &DevInfoData))
        {
            WCHAR wszDevName[256 + 1] = {0};
            DWORD err = ERROR_SUCCESS;
            if (!SetupDiGetDeviceRegistryPropertyW(hDevInfo, &DevInfoData, SPDRP_FRIENDLYNAME, NULL /*PropertyRegDataType*/,
                                                   (PBYTE)wszDevName, sizeof(wszDevName) - sizeof(WCHAR) /*yes, bytes*/,
                                                   NULL /*RequiredSize*/))
            {
                err = GetLastError();
                if (err == ERROR_INVALID_DATA)
                {
                    RT_ZERO(wszDevName);
                    if (SetupDiGetDeviceRegistryPropertyW(hDevInfo, &DevInfoData, SPDRP_DEVICEDESC, NULL /*PropertyRegDataType*/,
                                                          (PBYTE)wszDevName, sizeof(wszDevName) - sizeof(WCHAR) /*yes, bytes*/,
                                                          NULL /*RequiredSize*/))
                        err = ERROR_SUCCESS;
                    else
                        err = GetLastError();
                }
            }
            if (err == ERROR_SUCCESS)
            {
                WCHAR wszConnectionNewName[128] = {0};
                hr = VBoxNetCfgWinGenHostonlyConnectionName(wszDevName, wszConnectionNewName,
                                                            RT_ELEMENTS(wszConnectionNewName), NULL);
                if (SUCCEEDED(hr))
                {
                    WCHAR wszGuid[50];
                    int cbWGuid = StringFromGUID2(*pGuid, wszGuid, RT_ELEMENTS(wszGuid));
                    if (cbWGuid)
                    {
                        hr = VBoxNetCfgWinRenameConnection(wszGuid, wszConnectionNewName);
                        if (FAILED(hr))
                            NonStandardLogFlow(("VBoxNetCfgWinRenameHostOnlyConnection: VBoxNetCfgWinRenameConnection failed (0x%x)\n", hr));
                    }
                    else
                    {
                        err = GetLastError();
                        hr = HRESULT_FROM_WIN32(err);
                        if (SUCCEEDED(hr))
                            hr = E_FAIL;
                        NonStandardLogFlow(("StringFromGUID2 failed err=%u, hr=0x%x\n", err, hr));
                    }
                }
                else
                    NonStandardLogFlow(("VBoxNetCfgWinRenameHostOnlyConnection: VBoxNetCfgWinGenHostonlyConnectionName failed (0x%x)\n", hr));
                if (SUCCEEDED(hr) && pDevName)
                {
                    *pDevName = SysAllocString((const OLECHAR *)wszDevName);
                    if (!*pDevName)
                    {
                        NonStandardLogFlow(("SysAllocString failed\n"));
                        hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
                    }
                }
            }
            else
            {
                hr = HRESULT_FROM_WIN32(err);
                NonStandardLogFlow(("VBoxNetCfgWinRenameHostOnlyConnection: SetupDiGetDeviceRegistryPropertyW failed (0x%x)\n", err));
            }
        }
        else
        {
            DWORD err = GetLastError();
            hr = HRESULT_FROM_WIN32(err);
            NonStandardLogFlow(("VBoxNetCfgWinRenameHostOnlyConnection: SetupDiOpenDeviceInfo failed (0x%x)\n", err));
        }
        SetupDiDestroyDeviceInfoList(hDevInfo);
    }

    return hr;
}
#endif /* VBOXNETCFG_DELAYEDRENAME */

#undef SetErrBreak

