// InstallUtils.cpp : Defines the initialization routines for the DLL.
//

#include "stdafx.h"
#include "InstallUtils.h"
#include "WaitThread.h"
#include "RebootPromptDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

//
//TODO: If this DLL is dynamically linked against the MFC DLLs,
//		any functions exported from this DLL which call into
//		MFC must have the AFX_MANAGE_STATE macro added at the
//		very beginning of the function.
//
//		For example:
//
//		extern "C" BOOL PASCAL EXPORT ExportedFunction()
//		{
//			AFX_MANAGE_STATE(AfxGetStaticModuleState());
//			// normal function body here
//		}
//
//		It is very important that this macro appear in each
//		function, prior to any calls into MFC.  This means that
//		it must appear as the first statement within the 
//		function, even before any object variable declarations
//		as their constructors may generate calls into the MFC
//		DLL.
//
//		Please see MFC Technical Notes 33 and 58 for additional
//		details.
//

// CInstallUtilsApp

BEGIN_MESSAGE_MAP(CInstallUtilsApp, CWinApp)
END_MESSAGE_MAP()


// CInstallUtilsApp construction

CInstallUtilsApp::CInstallUtilsApp()
: m_hInstallDB(NULL)
, m_sCopyRightDates(_T("2015"))
, m_sVersionHTMLString(_T(""))
, m_nRetCode(0)
, m_sModuleFileName(_T(""))
, m_qwFileVersion(0)
, m_bRebootNeed(false)
, m_sModuleFolder(_T(""))
, m_sDriverFolder(_T(""))
, m_sInfFilePath(_T(""))
, m_bTestModeInstalled(false)
, m_bTestModeInstalling(false)
, m_bRootCertExist(false)
, m_bRootCertInstalled(false)
, m_bTrustedPublisherCertExist(false)
, m_bTrustedPublisherCertInstalled(false)
, m_sCoInstallerPath(_T(""))
, m_hCoInatallerModule(NULL)
, m_pfnWdfPreDeviceInstall(NULL)
, m_pfnWdfPostDeviceInstall(NULL)
, m_pfnWdfPreDeviceInstallEx(NULL)
, m_pfnWdfPreDeviceRemove(NULL)
, m_pfnWdfPostDeviceRemove(NULL)
, m_sOEMInfFile(_T(""))
, m_sDriversFolder(_T(""))
, m_pDeviceKeyDacl(NULL)
, m_hProcessToken(NULL)
, m_pAdminsSid((SID*) m_abAdminsSidBuffer)
, m_dwAdminsSidSize(SECURITY_MAX_SID_SIZE)
, m_pWorldSid((SID*) m_abWorldSidBuffer)
, m_dwWorldSidSize(SECURITY_MAX_SID_SIZE)
, m_hParentWindow(NULL)
, m_dwProcessID(0)
{
	// TODO: add construction code here,
	// Place all significant initialization in InitInstance
}


// The one and only CInstallUtilsApp object
CInstallUtilsApp theApp;


// CInstallUtilsApp initialization

BOOL CInstallUtilsApp::InitInstance()
{
    BOOL                bRet;
    DWORD               dwVerInfoLen = 0, dwVerHandle = 1, dwNameSize;
    TCHAR               sModuleFileName[MAX_PATH];
    PVOID               pVerInfoData;
    TCHAR               sBS[2] = { _T('\\'), 0 };
    VS_FIXEDFILEINFO*   pFileVerInfo = NULL;
    UINT                nFILen = 0;
    DWORD               anFileVersion[4];
    int                 nCharPos;
    DWORD               dwRetCode, dwErrorCode;
    EXPLICIT_ACCESS     aExplicitAccess[2];
    bool                bBool;
    CWinApp::InitInstance();
    //AfxMessageBox(_T("Подключаем отладчик (InitInstance)"));
    //bRet = AfxOleInit();
    //ASSERT(bRet);
    // Измените раздел реестра, в котором хранятся параметры
    // TODO: следует изменить эту строку на что-нибудь подходящее,
    // например на название организации
    SetRegistryKey(_T("Real Time Systems DD\\Public DNS WS System"));
    bRet = WriteProfileInt(
        _T("RebootParameters"),
        _T("dwRebootPurpose"),
        0
        );
    bRet = GetModuleHandleEx(
        GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
        (LPCTSTR)&theApp,
        &m_hDllModule
        );
    ASSERT(bRet);
    dwNameSize = GetModuleFileName(
        m_hDllModule,
        sModuleFileName,
        sizeof sModuleFileName / sizeof TCHAR
        );
    if(dwNameSize == 0 )
    {
        m_nRetCode = GetLastError();
        ASSERT(FALSE);
        return FALSE;
    }
    m_sModuleFileName = sModuleFileName;
    nCharPos = m_sModuleFileName.ReverseFind(_T('\\'));
    m_sModuleFolder = m_sModuleFileName.Left(nCharPos);
    m_sDriverFolder = m_sModuleFolder + _T("\\dnsfirewall Package");
    m_sInfFilePath = m_sDriverFolder + _T("\\dnsfirewall.inf");
    dwVerInfoLen = GetFileVersionInfoSize(
        sModuleFileName,
        &dwVerHandle
        );
    if(dwVerInfoLen == 0)
    {
        m_nRetCode = GetLastError();
        ASSERT(FALSE);
        return FALSE;
    }
    pVerInfoData = _alloca(dwVerInfoLen);
    bRet = GetFileVersionInfo(
        sModuleFileName,
        dwVerHandle,
        dwVerInfoLen,
        pVerInfoData
        );
    if(!bRet)
    {
        m_nRetCode = GetLastError();
        ASSERT(FALSE);
        return FALSE;
    }
    bRet = VerQueryValue(
        pVerInfoData,
        sBS,
        (LPVOID*)&pFileVerInfo,
        &nFILen
        );
    if(!bRet)
    {
        m_nRetCode = GetLastError();
        ASSERT(FALSE);
        return FALSE;
    }
    m_qwFileVersion = pFileVerInfo->dwFileVersionMS;
    m_qwFileVersion = (m_qwFileVersion << 32) | pFileVerInfo->dwFileVersionLS;
    anFileVersion[0] = pFileVerInfo->dwFileVersionMS >> 16;
    anFileVersion[1] = pFileVerInfo->dwFileVersionMS & 0xFFFF;
    anFileVersion[2] = pFileVerInfo->dwFileVersionLS >> 16;
    anFileVersion[3] = pFileVerInfo->dwFileVersionLS & 0xFFFF;
    m_sVersionHTMLString.Format(
        _T("%u.%u.%.4u.%.4u &beta;"),
        anFileVersion[0],
        anFileVersion[1],
        anFileVersion[2],
        anFileVersion[3]
        );
    bRet = CreateWellKnownSid(
        WinBuiltinAdministratorsSid,
        NULL,
        m_pAdminsSid,
        &m_dwAdminsSidSize
        );
    dwErrorCode = ::GetLastError();
    ASSERT(bRet);
    if (!bRet)
    {
        m_nRetCode = dwErrorCode;
        return FALSE;
    }
    bRet = CreateWellKnownSid(
        WinWorldSid,
        NULL,
        m_pWorldSid,
        &m_dwWorldSidSize
        );
    dwErrorCode = ::GetLastError();
    ASSERT(bRet);
    if (!bRet)
    {
        m_nRetCode = dwErrorCode;
        return FALSE;
    }
    bRet = OpenProcessToken(
        GetCurrentProcess(),
        TOKEN_ADJUST_PRIVILEGES,
        &m_hProcessToken
        );
    dwErrorCode = ::GetLastError();
    ASSERT(bRet);
    if (!bRet)
    {
        m_nRetCode = dwErrorCode;
        return FALSE;
    }
    bBool = SetProcessPrivilege(SE_TAKE_OWNERSHIP_NAME, true);
    ASSERT(bBool);
    if (!bBool)
    {
        m_nRetCode = -1;
        return FALSE;
    }
    bBool = SetProcessPrivilege(SE_RESTORE_NAME, true);
    ASSERT(bBool);
    if (!bBool)
    {
        m_nRetCode = -1;
        return FALSE;
    }
    bBool = SetProcessPrivilege(SE_BACKUP_NAME, true);
    ASSERT(bBool);
    if (!bBool)
    {
        m_nRetCode = -1;
        return FALSE;
    }
    // Initialize an EXPLICIT_ACCESS structure for an ACE.
    // The ACE will allow Everyone read access to the key.
    RtlZeroMemory(aExplicitAccess, 2 * sizeof(EXPLICIT_ACCESS));
    aExplicitAccess[0].grfAccessPermissions = KEY_READ;
    aExplicitAccess[0].grfAccessMode = SET_ACCESS;
    aExplicitAccess[0].grfInheritance = NO_INHERITANCE;
    aExplicitAccess[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
    aExplicitAccess[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
    aExplicitAccess[0].Trustee.ptstrName = (LPTSTR) m_pWorldSid;
    // Initialize an EXPLICIT_ACCESS structure for an ACE.
    // The ACE will allow the Administrators group full access to
    // the key.
    aExplicitAccess[1].grfAccessPermissions = KEY_ALL_ACCESS;
    aExplicitAccess[1].grfAccessMode = SET_ACCESS;
    aExplicitAccess[1].grfInheritance = NO_INHERITANCE;
    aExplicitAccess[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
    aExplicitAccess[1].Trustee.TrusteeType = TRUSTEE_IS_GROUP;
    aExplicitAccess[1].Trustee.ptstrName = (LPTSTR) m_pAdminsSid;
    // Create a new ACL that contains the new ACEs.
    dwRetCode = SetEntriesInAcl(
        2,
        aExplicitAccess,
        NULL,
        &m_pDeviceKeyDacl
        );
    ASSERT(ERROR_SUCCESS == dwRetCode);
    if (!(ERROR_SUCCESS == dwRetCode))
    {
        m_nRetCode = dwRetCode;
        return FALSE;
    }
    //m_hParentWindow = FindWindowW(
    //    NULL,
    //    _T("Public DNS WS System Bundle Setup")
    //    );
    m_dwProcessID = GetCurrentProcessId();
    return TRUE;
}


UINT __stdcall InstallDnsFirewallDriver(MSIHANDLE hInstall)
{
    return theApp.mInstallDnsFirewallDriver(hInstall);
}

UINT __stdcall UninstallServicesAndDrivers(MSIHANDLE hInstall)
{
    return theApp.mUninstallServicesAndDrivers(hInstall);
}


int CInstallUtilsApp::ExitInstance()
{
    CWinApp::ExitInstance();
    return m_nRetCode;
}


void CInstallUtilsApp::mfDeleteKey(
    HKEY hRootKey,
    LPCTSTR szKeyPath
    )
{
    LONG        nRegRet;
    DWORD       dwSKIndex;
    DWORD       nNameLength;
    TCHAR       szSKName[2048];
    CRegKey     oRegKey;
    CString     sKeyObjectName;
    CString     sSubKeyPath;
    DWORD       dwRetCode;
    nRegRet = oRegKey.Open(
        hRootKey,
        szKeyPath,
        KEY_READ
        );
    if (ERROR_FILE_NOT_FOUND == nRegRet)
    {
        return;
    }
    ASSERT(ERROR_SUCCESS == nRegRet);
    nRegRet = oRegKey.Close();
    ASSERT(ERROR_SUCCESS == nRegRet);
    sKeyObjectName = L"";
    if (HKEY_CLASSES_ROOT == hRootKey)
    {
        sKeyObjectName = L"CLASSES_ROOT\\";
    }
    else if (HKEY_CURRENT_USER == hRootKey)
    {
        sKeyObjectName = L"CURRENT_USER\\";
    }
    else if (HKEY_LOCAL_MACHINE == hRootKey)
    {
        sKeyObjectName = L"MACHINE\\";
    }
    else if (HKEY_USERS == hRootKey)
    {
        sKeyObjectName = L"USERS\\";
    }
    else
    {
        ASSERT(false);
        return;
    }
    sKeyObjectName += szKeyPath;
    // Set the owner in the object's security descriptor.
    dwRetCode = SetNamedSecurityInfo(
        (LPWSTR) (LPCWSTR) sKeyObjectName,
        SE_REGISTRY_KEY,
        OWNER_SECURITY_INFORMATION,
        m_pAdminsSid,
        NULL,
        NULL,
        NULL
        );
    ASSERT(ERROR_SUCCESS == dwRetCode);
    // Try again to modify the object's DACL,
    // now that we are the owner.
    dwRetCode = SetNamedSecurityInfo(
        (LPWSTR) (LPCWSTR) sKeyObjectName,
        SE_REGISTRY_KEY,
        DACL_SECURITY_INFORMATION,
        NULL,
        NULL,
        m_pDeviceKeyDacl,
        NULL
        );
    ASSERT(ERROR_SUCCESS == dwRetCode);
    nRegRet = oRegKey.Open(
        hRootKey,
        szKeyPath
        );
    ASSERT(ERROR_SUCCESS == nRegRet);
    for (dwSKIndex = 0;; dwSKIndex++)
    {
        nNameLength = 2048;
        nRegRet = oRegKey.EnumKey(
            dwSKIndex,
            szSKName,
            &nNameLength
            );
        if (ERROR_NO_MORE_ITEMS == nRegRet)
        {
            break;
        }
        ASSERT(ERROR_SUCCESS == nRegRet);
        sSubKeyPath = szKeyPath;
        sSubKeyPath += _T('\\');
        sSubKeyPath += szSKName;
        mfDeleteKey(
            hRootKey,
            sSubKeyPath
            );
    }
    nRegRet = RegDeleteTree(
        oRegKey,
        NULL
        );
    ASSERT(ERROR_SUCCESS == nRegRet);
}


bool CInstallUtilsApp::SetProcessPrivilege(
    LPCTSTR lpszPrivilege,
    bool bEnablePrivilege
    )
{
    // Пример от MICROSOFT
    TOKEN_PRIVILEGES tp;
    LUID    luid;
    BOOL    bRet;
    DWORD   dwErrorCode;
    bRet = LookupPrivilegeValue(
        NULL,
        lpszPrivilege,
        &luid
        );
    dwErrorCode = ::GetLastError();
    ASSERT(bRet);
    if (!bRet)
    {
        return false;
    }
    tp.PrivilegeCount = 1;
    tp.Privileges[0].Luid = luid;
    if (bEnablePrivilege)
    {
        tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    }
    else
    {
        tp.Privileges[0].Attributes = 0;
    }
    // Enable the privilege or disable all privileges.
    bRet = AdjustTokenPrivileges(
        m_hProcessToken,
        FALSE,
        &tp,
        sizeof(TOKEN_PRIVILEGES),
        (PTOKEN_PRIVILEGES) NULL,
        (PDWORD) NULL
        );
    dwErrorCode = ::GetLastError();
    ASSERT(bRet);
    if (!bRet)
    {
        return false;
    }
    return true;
}


UINT CInstallUtilsApp::mInstallDnsFirewallDriver(MSIHANDLE hInstall)
{
    HRESULT                 hr;
    bool                    bReturnValue;
    bool                    bTestMode;
    ISWbemLocatorPtr        spLoc;
    ISWbemServicesPtr       spSvc;
    ISWbemObjectPtr         spBcdStoreClass;
    ISWbemMethodSetPtr      spBcdStoreClassMethods;
    ISWbemMethodSetPtr      spObjectMethods;
    ISWbemMethodPtr         spMethodObject;
    ISWbemObjectPtr         spInParamsObject;
    ISWbemPropertySetPtr    spInParams;
    ISWbemPropertyPtr       spParameter;
    ISWbemObjectPtr         spOutParamsObject;
    ISWbemPropertySetPtr    spOutParams;
    ISWbemObjectPtr         spBcdStoreObject;
    ISWbemObjectPtr         spCurrentLoader;
    ISWbemSecurityPtr       spServiceSecurity;
    ISWbemObjectPtr         spElemenObject;
    ISWbemPropertySetPtr    spElementProperties;
    ISWbemPropertyPtr       spElementProperty;
    ISWbemPrivilegeSetPtr   spServSWbemPrivilegeSet;
    ISWbemPrivilegePtr      spPrivilege;
    _variant_t              vElementProperty;
    DWORD                   dwErrorCode, dwMsgLength;
    CString                 sMessageText, sErrorMessage;
    LPVOID                  lpMsgBuf = NULL;
    INT_PTR                 nMessageBoxRet;
    BOOL                    bRet;
    CRebootPromptDlg*       pRebootPromptDlg = NULL;
    CString                 sCommandLine;
    CRegKey                 oRunOnceKey;
    LONG                    nRetCode;
    STARTUPINFO             stStartupInfo;
    PROCESS_INFORMATION     stProcessInformation;
    hInstall;
    //AfxMessageBox(_T("Подключаем отладчик (mInstallDnsFirewallDriver)"));
    hr = CoInitializeEx(
        NULL,
        COINIT_MULTITHREADED
        );
    // Проверка и установка BCD-элемента "TESTSIGNING" в текущий загрузчик
    hr = spLoc.CreateInstance(
        __uuidof(SWbemLocator)
        );
    if (FAILED(hr))
    {
        dwMsgLength = ::FormatMessage(
            FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
            NULL,
            hr,
            MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
            (LPTSTR)&lpMsgBuf,
            0,
            NULL
            );
        if (dwMsgLength == 0)
        {
            sErrorMessage.Format(
                _T("Ошибка вызова функции \"FormatMessage\"! Код ошибки = %u."),
                ::GetLastError()
                );
        }
        else
        {
            sErrorMessage = (LPCTSTR)lpMsgBuf;
            ::LocalFree((HLOCAL)lpMsgBuf);
        }
        sMessageText.Format(
            _T("\
Ошибка создания SWbemLocator - объекта!!\n\
\tКод ошибки = 0x%08X\n\
\tОписание ошибки: %s\n\
"),
            hr,
            (LPCTSTR)sErrorMessage
            );
        AfxMessageBox(
            sMessageText,
            MB_OK | MB_ICONEXCLAMATION
            );
        m_nRetCode = hr;
        return hr;
    }
    // Connect to the root\wmi namespace with the current user.
    try
    {
        spSvc = spLoc->ConnectServer(
            _bstr_t(),
            _bstr_t(L"Root\\WMI"),
            _bstr_t(),
            _bstr_t(),
            _bstr_t(),
            _bstr_t(),
            0,
            (IDispatch*)NULL
            );
    }
    catch (_com_error oError)
    {
        hr = oError.Error();
        sErrorMessage = (LPCTSTR)oError.Description();
        sMessageText.Format(
            _T("\
Ошибка подсоединения к сервису \"ROOT\\WMI\"!!\n\
\tКод ошибки = 0x%08X\n\
\tОписание ошибки: %s\n\
"),
            hr,
            (LPCTSTR)sErrorMessage
            );
        AfxMessageBox(
            sMessageText,
            MB_OK | MB_ICONEXCLAMATION
            );
        m_nRetCode = hr;
        return hr;
    }
    try
    {
        spServiceSecurity = spSvc->Security_;
    }
    catch (_com_error oError)
    {
        hr = oError.Error();
        sErrorMessage = (LPCTSTR)oError.Description();
        sMessageText.Format(
            _T("\
Ошибка получения объекта свойства \"Security_\"!!\n\
\tКод ошибки = 0x%08X\n\
\tОписание ошибки: %s\n\
"),
            hr,
            (LPCTSTR)sErrorMessage
            );
        AfxMessageBox(
            sMessageText,
            MB_OK | MB_ICONEXCLAMATION
            );
        m_nRetCode = hr;
        return hr;
    }
    spServiceSecurity->ImpersonationLevel = wbemImpersonationLevelDelegate;
    spServSWbemPrivilegeSet = spServiceSecurity->Privileges;
    spPrivilege = spServSWbemPrivilegeSet->Add(
        wbemPrivilegeEnableDelegation,
        VARIANT_TRUE
        );
    spPrivilege = spServSWbemPrivilegeSet->Add(
        wbemPrivilegeSystemEnvironment,
        VARIANT_TRUE
        );
    spPrivilege = spServSWbemPrivilegeSet->Add(
        wbemPrivilegeSystemProfile,
        VARIANT_TRUE
        );
    spPrivilege = spServSWbemPrivilegeSet->Add(
        wbemPrivilegeCreatePermanent,
        VARIANT_TRUE
        );
    try
    {
        spBcdStoreClass = spSvc->Get(
            L"BcdStore",
            0,
            (IDispatch*)NULL
            );
    }
    catch (_com_error oError)
    {
        hr = oError.Error();
        sErrorMessage = (LPCTSTR)oError.Description();
        sMessageText.Format(
            _T("\
Ошибка получения инстанции класса \"BcdStore\"!!\n\
\tКод ошибки = 0x%08X\n\
\tОписание ошибки: %s\n\
"),
            hr,
            (LPCTSTR)sErrorMessage
            );
        AfxMessageBox(
            sMessageText,
            MB_OK | MB_ICONEXCLAMATION
            );
        m_nRetCode = hr;
        return hr;
    }
    try
    {
        spBcdStoreClassMethods = spBcdStoreClass->Methods_;
    }
    catch (_com_error oError)
    {
        hr = oError.Error();
        sErrorMessage = (LPCTSTR)oError.Description();
        sMessageText.Format(
            _T("\
Ошибка получения массива методов класса \"BcdStore\"!!\n\
\tКод ошибки = 0x%08X\n\
\tОписание ошибки: %s\n\
"),
            hr,
            (LPCTSTR)sErrorMessage
            );
        AfxMessageBox(
            sMessageText,
            MB_OK | MB_ICONEXCLAMATION
            );
        m_nRetCode = hr;
        return hr;
    }
    try
    {
        spMethodObject = spBcdStoreClassMethods->Item(
            L"OpenStore",
            0
            );
    }
    catch (_com_error oError)
    {
        hr = oError.Error();
        sErrorMessage = (LPCTSTR)oError.Description();
        sMessageText.Format(
            _T("\
Ошибка получения метода \"OpenStore\" класса \"BcdStore\"!!\n\
\tКод ошибки = 0x%08X\n\
\tОписание ошибки: %s\n\
"),
            hr,
            (LPCTSTR)sErrorMessage
            );
        AfxMessageBox(
            sMessageText,
            MB_OK | MB_ICONEXCLAMATION
            );
        m_nRetCode = hr;
        return hr;
    }
    try
    {
        spInParamsObject = spMethodObject->InParameters->SpawnInstance_(
            0
            );
    }
    catch (_com_error oError)
    {
        hr = oError.Error();
        sErrorMessage = (LPCTSTR)oError.Description();
        sMessageText.Format(
            _T("\
Ошибка получения объекта параметров метода \"OpenStore\" класса \"BcdStore\"!!\n\
\tКод ошибки = 0x%08X\n\
\tОписание ошибки: %s\n\
"),
            hr,
            (LPCTSTR)sErrorMessage
            );
        AfxMessageBox(
            sMessageText,
            MB_OK | MB_ICONEXCLAMATION
            );
        m_nRetCode = hr;
        return hr;
    }
    try
    {
        spInParams = spInParamsObject->Properties_;
    }
    catch (_com_error oError)
    {
        hr = oError.Error();
        sErrorMessage = (LPCTSTR)oError.Description();
        sMessageText.Format(
            _T("\
Ошибка получения массива входных параметров метода \"OpenStore\" класса \"BcdStore\"!!\n\
\tКод ошибки = 0x%08X\n\
\tОписание ошибки: %s\n\
"),
            hr,
            (LPCTSTR)sErrorMessage
            );
        AfxMessageBox(
            sMessageText,
            MB_OK | MB_ICONEXCLAMATION
            );
        m_nRetCode = hr;
        return hr;
    }
    try
    {
        spParameter = spInParams->Item(
            L"File",
            0
            );
    }
    catch (_com_error oError)
    {
        hr = oError.Error();
        sErrorMessage = (LPCTSTR)oError.Description();
        sMessageText.Format(
            _T("\
Ошибка получения объекта параметра \"File\"!!\n\
\tКод ошибки = 0x%08X\n\
\tОписание ошибки: %s\n\
"),
        hr,
        (LPCTSTR)sErrorMessage
        );
        AfxMessageBox(
            sMessageText,
            MB_OK | MB_ICONEXCLAMATION
            );
        m_nRetCode = hr;
        return hr;
    }
    spParameter->PutValue(&_variant_t(L"").GetVARIANT());
    try
    {
        spOutParamsObject = spBcdStoreClass->ExecMethod_(
            L"OpenStore",
            spInParamsObject,
            0,
            NULL
            );
    }
    catch (_com_error oError)
    {
        hr = oError.Error();
        sErrorMessage = (LPCTSTR)oError.Description();
        sMessageText.Format(
            _T("\
Ошибка вызова метода \"OpenStore\"!!\n\
\tКод ошибки = 0x%08X\n\
\tОписание ошибки: %s\n\
"),
            hr,
            (LPCTSTR)sErrorMessage
            );
        AfxMessageBox(
            sMessageText,
            MB_OK | MB_ICONEXCLAMATION
            );
        m_nRetCode = hr;
        return hr;
    }
    try
    {
        spOutParams = spOutParamsObject->Properties_;
    }
    catch (_com_error oError)
    {
        hr = oError.Error();
        sErrorMessage = (LPCTSTR)oError.Description();
        sMessageText.Format(
            _T("\
Ошибка получения массива выходных параметров метода \"OpenStore\" класса \"BcdStore\"!!\n\
\tКод ошибки = 0x%08X\n\
\tОписание ошибки: %s\n\
"),
            hr,
            (LPCTSTR)sErrorMessage
            );
        AfxMessageBox(
            sMessageText,
            MB_OK | MB_ICONEXCLAMATION
            );
        m_nRetCode = hr;
        return hr;
    }
    try
    {
        spParameter = spOutParams->Item(
            L"ReturnValue",
            0
            );
    }
    catch (_com_error oError)
    {
        hr = oError.Error();
        sErrorMessage = (LPCTSTR)oError.Description();
        sMessageText.Format(
            _T("\
Ошибка получения объекта параметра \"ReturnValue\"!!\n\
\tКод ошибки = 0x%08X\n\
\tОписание ошибки: %s\n\
"),
            hr,
            (LPCTSTR)sErrorMessage
            );
        AfxMessageBox(
            sMessageText,
            MB_OK | MB_ICONEXCLAMATION
            );
        m_nRetCode = hr;
        return hr;
    }
    bReturnValue = spParameter->GetValue();
    if (!bReturnValue)
    {
        sMessageText = _T("Ошибка открытия системного BCD-хранилища!!");
        AfxMessageBox(
            sMessageText,
            MB_OK | MB_ICONEXCLAMATION
            );
        m_nRetCode = ERROR_INVALID_DATA;
        return ERROR_INVALID_DATA;
    }
    try
    {
        spParameter = spOutParams->Item(
            L"Store",
            0
            );
    }
    catch (_com_error oError)
    {
        hr = oError.Error();
        sErrorMessage = (LPCTSTR)oError.Description();
        sMessageText.Format(
            _T("\
Ошибка получения объекта параметра \"Store\"!!\n\
\tКод ошибки = 0x%08X\n\
\tОписание ошибки: %s\n\
"),
            hr,
            (LPCTSTR)sErrorMessage
            );
        AfxMessageBox(
            sMessageText,
            MB_OK | MB_ICONEXCLAMATION
            );
        m_nRetCode = hr;
        return hr;
    }
    spBcdStoreObject = spParameter->GetValue();
    try
    {
        spMethodObject = spBcdStoreClassMethods->Item(
            L"OpenObject",
            0
            );
    }
    catch (_com_error oError)
    {
        hr = oError.Error();
        sErrorMessage = (LPCTSTR)oError.Description();
        sMessageText.Format(
            _T("\
Ошибка получения метода \"OpenObject\" класса \"BcdStore\"!!\n\
\tКод ошибки = 0x%08X\n\
\tОписание ошибки: %s\n\
"),
            hr,
            (LPCTSTR)sErrorMessage
            );
        AfxMessageBox(
            sMessageText,
            MB_OK | MB_ICONEXCLAMATION
            );
        m_nRetCode = hr;
        return hr;
    }
    try
    {
        spInParamsObject = spMethodObject->InParameters->SpawnInstance_(
            0
            );
    }
    catch (_com_error oError)
    {
        hr = oError.Error();
        sErrorMessage = (LPCTSTR)oError.Description();
        sMessageText.Format(
            _T("\
Ошибка получения объекта параметров метода \"OpenObject\" класса \"BcdStore\"!!\n\
\tКод ошибки = 0x%08X\n\
\tОписание ошибки: %s\n\
"),
            hr,
            (LPCTSTR)sErrorMessage
            );
        AfxMessageBox(
            sMessageText,
            MB_OK | MB_ICONEXCLAMATION
            );
        m_nRetCode = hr;
        return hr;
    }
    try
    {
        spInParams = spInParamsObject->Properties_;
    }
    catch (_com_error oError)
    {
        hr = oError.Error();
        sErrorMessage = (LPCTSTR)oError.Description();
        sMessageText.Format(
            _T("\
Ошибка получения массива входных параметров метода \"OpenObject\" класса \"BcdStore\"!!\n\
\tКод ошибки = 0x%08X\n\
\tОписание ошибки: %s\n\
"),
            hr,
            (LPCTSTR)sErrorMessage
            );
        AfxMessageBox(
            sMessageText,
            MB_OK | MB_ICONEXCLAMATION
            );
        m_nRetCode = hr;
        return hr;
    }
    try
    {
        spParameter = spInParams->Item(
            L"Id",
            0
            );
    }
    catch (_com_error oError)
    {
        hr = oError.Error();
        sErrorMessage = (LPCTSTR)oError.Description();
        sMessageText.Format(
            _T("\
Ошибка получения объекта параметра \"Id\"!!\n\
\tКод ошибки = 0x%08X\n\
\tОписание ошибки: %s\n\
"),
            hr,
            (LPCTSTR)sErrorMessage
            );
        AfxMessageBox(
            sMessageText,
            MB_OK | MB_ICONEXCLAMATION
            );
        m_nRetCode = hr;
        return hr;
    }
    spParameter->PutValue(&_variant_t(L"{fa926493-6f1c-4193-a414-58f0b2456d1e}").GetVARIANT());
    try
    {
        spOutParamsObject = spBcdStoreObject->ExecMethod_(
            L"OpenObject",
            spInParamsObject,
            0,
            NULL
            );
    }
    catch (_com_error oError)
    {
        hr = oError.Error();
        sErrorMessage = (LPCTSTR)oError.Description();
        sMessageText.Format(
            _T("\
Ошибка вызова метода \"OpenObject\"!!\n\
\tКод ошибки = 0x%08X\n\
\tОписание ошибки: %s\n\
"),
            hr,
            (LPCTSTR)sErrorMessage
            );
        AfxMessageBox(
            sMessageText,
            MB_OK | MB_ICONEXCLAMATION
            );
        m_nRetCode = hr;
        return hr;
    }
    try
    {
        spOutParams = spOutParamsObject->Properties_;
    }
    catch (_com_error oError)
    {
        hr = oError.Error();
        sErrorMessage = (LPCTSTR)oError.Description();
        sMessageText.Format(
            _T("\
Ошибка получения массива выходных параметров метода \"OpenObject\" класса \"BcdStore\"!!\n\
\tКод ошибки = 0x%08X\n\
\tОписание ошибки: %s\n\
"),
            hr,
            (LPCTSTR)sErrorMessage
            );
        AfxMessageBox(
            sMessageText,
            MB_OK | MB_ICONEXCLAMATION
            );
        m_nRetCode = hr;
        return hr;
    }
    try
    {
        spParameter = spOutParams->Item(
            L"ReturnValue",
            0
            );
    }
    catch (_com_error oError)
    {
        hr = oError.Error();
        sErrorMessage = (LPCTSTR)oError.Description();
        sMessageText.Format(
            _T("\
Ошибка получения объекта параметра \"ReturnValue\"!!\n\
\tКод ошибки = 0x%08X\n\
\tОписание ошибки: %s\n\
"),
            hr,
            (LPCTSTR)sErrorMessage
            );
        AfxMessageBox(
            sMessageText,
            MB_OK | MB_ICONEXCLAMATION
            );
        m_nRetCode = hr;
        return hr;
    }
    bReturnValue = spParameter->GetValue();
    if (!bReturnValue)
    {
        sMessageText = _T("Ошибка открытия объекта текущего загрузчика!!");
        AfxMessageBox(
            sMessageText,
            MB_OK | MB_ICONEXCLAMATION
            );
        AfxMessageBox(
            sMessageText,
            MB_OK | MB_ICONEXCLAMATION
            );
        m_nRetCode = ERROR_INVALID_DATA;
        return ERROR_INVALID_DATA;
    }
    try
    {
        spParameter = spOutParams->Item(
            L"Object",
            0
            );
    }
    catch (_com_error oError)
    {
        hr = oError.Error();
        sErrorMessage = (LPCTSTR)oError.Description();
        sMessageText.Format(
            _T("\
Ошибка получения объекта параметра \"Object\"!!\n\
\tКод ошибки = 0x%08X\n\
\tОписание ошибки: %s\n\
"),
            hr,
            (LPCTSTR)sErrorMessage
            );
        AfxMessageBox(
            sMessageText,
            MB_OK | MB_ICONEXCLAMATION
            );
        m_nRetCode = hr;
        return hr;
    }
    spCurrentLoader = spParameter->GetValue();
    try
    {
        spObjectMethods = spCurrentLoader->Methods_;
    }
    catch (_com_error oError)
    {
        hr = oError.Error();
        sErrorMessage = (LPCTSTR)oError.Description();
        sMessageText.Format(
            _T("\
Ошибка получения массива методов класса \"BcdObject\"!!\n\
\tКод ошибки = 0x%08X\n\
\tОписание ошибки: %s\n\
"),
            hr,
            (LPCTSTR)sErrorMessage
            );
        AfxMessageBox(
            sMessageText,
            MB_OK | MB_ICONEXCLAMATION
            );
        m_nRetCode = hr;
        return hr;
    }
    try
    {
        spMethodObject = spObjectMethods->Item(
            L"GetElement",
            0
            );
    }
    catch (_com_error oError)
    {
        hr = oError.Error();
        sErrorMessage = (LPCTSTR)oError.Description();
        sMessageText.Format(
            _T("\
Ошибка получения метода \"GetElement\" класса \"BcdObject\"!!\n\
\tКод ошибки = 0x%08X\n\
\tОписание ошибки: %s\n\
"),
            hr,
            (LPCTSTR)sErrorMessage
            );
        AfxMessageBox(
            sMessageText,
            MB_OK | MB_ICONEXCLAMATION
            );
        m_nRetCode = hr;
        return hr;
    }
    try
    {
        spInParamsObject = spMethodObject->InParameters->SpawnInstance_(
            0
            );
    }
    catch (_com_error oError)
    {
        hr = oError.Error();
        sErrorMessage = (LPCTSTR)oError.Description();
        sMessageText.Format(
            _T("\
Ошибка получения объекта входных параметров метода \"GetElement\" класса \"BcdObject\"!!\n\
\tКод ошибки = 0x%08X\n\
\tОписание ошибки: %s\n\
"),
            hr,
            (LPCTSTR)sErrorMessage
            );
        AfxMessageBox(
            sMessageText,
            MB_OK | MB_ICONEXCLAMATION
            );
        m_nRetCode = hr;
        return hr;
    }
    try
    {
        spInParams = spInParamsObject->Properties_;
    }
    catch (_com_error oError)
    {
        hr = oError.Error();
        sErrorMessage = (LPCTSTR)oError.Description();
        sMessageText.Format(
            _T("\
Ошибка получения массива входных параметров метода \"GetElement\" класса \"BcdObject\"!!\n\
\tКод ошибки = 0x%08X\n\
\tОписание ошибки: %s\n\
"),
            hr,
            (LPCTSTR)sErrorMessage
            );
        AfxMessageBox(
            sMessageText,
            MB_OK | MB_ICONEXCLAMATION
            );
        m_nRetCode = hr;
        return hr;
    }
    try
    {
        spParameter = spInParams->Item(
            L"Type",
            0
            );
        spParameter->PutValue(&_variant_t((DWORD)BcdLibraryBoolean_AllowPrereleaseSignatures).GetVARIANT());
        spOutParamsObject = spCurrentLoader->ExecMethod_(
            L"GetElement",
            spInParamsObject,
            0,
            NULL
            );
        spOutParams = spOutParamsObject->Properties_;
        spParameter = spOutParams->Item(
            L"ReturnValue",
            0
            );
        bReturnValue = spParameter->GetValue();
        if (!bReturnValue)
        {
            bTestMode = false;
        }
        else
        {
            spParameter = spOutParams->Item(
                L"Element",
                0
                );
            spElemenObject = spParameter->GetValue();
            spElementProperties = spElemenObject->Properties_;
            spElementProperty = spElementProperties->Item(
                L"Boolean",
                0
                );
            vElementProperty = spElementProperty->GetValue();
            bTestMode = vElementProperty;
        }
    }
    catch (_com_error oError)
    {
        bTestMode = false;
    }
    if (!bTestMode)
    {
        try
        {
            spMethodObject = spObjectMethods->Item(
                L"SetBooleanElement",
                0
                );
        }
        catch (_com_error oError)
        {
            hr = oError.Error();
            sErrorMessage = (LPCTSTR)oError.Description();
            sMessageText.Format(
                _T("\
Ошибка получения метода \"SetBooleanElement\" класса \"BcdObject\"!!\n\
\tКод ошибки = 0x%08X\n\
\tОписание ошибки: %s\n\
"),
                hr,
                (LPCTSTR)sErrorMessage
                );
            m_nRetCode = hr;
            return hr;
        }
        try
        {
            spInParamsObject = spMethodObject->InParameters->SpawnInstance_(
                0
                );
        }
        catch (_com_error oError)
        {
            hr = oError.Error();
            sErrorMessage = (LPCTSTR)oError.Description();
            sMessageText.Format(
                _T("\
Ошибка получения объекта входных параметров метода \"SetBooleanElement\" класса \"BcdObject\"!!\n\
\tКод ошибки = 0x%08X\n\
\tОписание ошибки: %s\n\
"),
                hr,
                (LPCTSTR)sErrorMessage
                );
            AfxMessageBox(
                sMessageText,
                MB_OK | MB_ICONEXCLAMATION
                );
            m_nRetCode = hr;
            return hr;
        }
        try
        {
            spInParams = spInParamsObject->Properties_;
        }
        catch (_com_error oError)
        {
            hr = oError.Error();
            sErrorMessage = (LPCTSTR)oError.Description();
            sMessageText.Format(
                _T("\
Ошибка получения массива входных параметров метода \"SetBooleanElement\" класса \"BcdObject\"!!\n\
\tКод ошибки = 0x%08X\n\
\tОписание ошибки: %s\n\
"),
                hr,
                (LPCTSTR)sErrorMessage
                );
            AfxMessageBox(
                sMessageText,
                MB_OK | MB_ICONEXCLAMATION
                );
            m_nRetCode = hr;
            return hr;
        }
        try
        {
            spParameter = spInParams->Item(
                L"Type",
                0
                );
        }
        catch (_com_error oError)
        {
            hr = oError.Error();
            sErrorMessage = (LPCTSTR)oError.Description();
            sMessageText.Format(
                _T("\
Ошибка получения объекта параметра \"Type\"!!\n\
\tКод ошибки = 0x%08X\n\
\tОписание ошибки: %s\n\
"),
                hr,
                (LPCTSTR)sErrorMessage
                );
            AfxMessageBox(
                sMessageText,
                MB_OK | MB_ICONEXCLAMATION
                );
            m_nRetCode = hr;
            return hr;
        }
        spParameter->PutValue(&_variant_t((DWORD)BcdLibraryBoolean_AllowPrereleaseSignatures).GetVARIANT());
        try
        {
            spParameter = spInParams->Item(
                L"Boolean",
                0
                );
        }
        catch (_com_error oError)
        {
            hr = oError.Error();
            sErrorMessage = (LPCTSTR)oError.Description();
            sMessageText.Format(
                _T("\
Ошибка получения объекта параметра \"Boolean\"!!\n\
\tКод ошибки = 0x%08X\n\
\tОписание ошибки: %s\n\
"),
                hr,
                (LPCTSTR)sErrorMessage
                );
            AfxMessageBox(
                sMessageText,
                MB_OK | MB_ICONEXCLAMATION
                );
            m_nRetCode = hr;
            return hr;
        }
        spParameter->PutValue(&_variant_t(VARIANT_TRUE).GetVARIANT());
        try
        {
            spOutParamsObject = spCurrentLoader->ExecMethod_(
                L"SetBooleanElement",
                spInParamsObject,
                0,
                NULL
                );
        }
        catch (_com_error oError)
        {
            hr = oError.Error();
            sErrorMessage = (LPCTSTR)oError.Description();
            sMessageText.Format(
                _T("\
Ошибка вызова метода \"SetBooleanElement\"!!\n\
\tКод ошибки = 0x%08X\n\
\tОписание ошибки: %s\n\
"),
                hr,
                (LPCTSTR)sErrorMessage
                );
            AfxMessageBox(
                sMessageText,
                MB_OK | MB_ICONEXCLAMATION
                );
            m_nRetCode = hr;
            return hr;
        }
        try
        {
            spOutParams = spOutParamsObject->Properties_;
        }
        catch (_com_error oError)
        {
            hr = oError.Error();
            sErrorMessage = (LPCTSTR)oError.Description();
            sMessageText.Format(
                _T("\
Ошибка получения массива выходных параметров метода \"GetElement\" класса \"BcdObject\"!!\n\
\tКод ошибки = 0x%08X\n\
\tОписание ошибки: %s\n\
"),
                hr,
                (LPCTSTR)sErrorMessage
                );
            AfxMessageBox(
                sMessageText,
                MB_OK | MB_ICONEXCLAMATION
                );
            m_nRetCode = hr;
            return hr;
        }
        try
        {
            spParameter = spOutParams->Item(
                L"ReturnValue",
                0
                );
        }
        catch (_com_error oError)
        {
            hr = oError.Error();
            sErrorMessage = (LPCTSTR)oError.Description();
            sMessageText.Format(
                _T("\
Ошибка получения объекта параметра \"ReturnValue\"!!\n\
\tКод ошибки = 0x%08X\n\
\tОписание ошибки: %s\n\
"),
                hr,
                (LPCTSTR)sErrorMessage
                );
            AfxMessageBox(
                sMessageText,
                MB_OK | MB_ICONEXCLAMATION
                );
            m_nRetCode = hr;
            return hr;
        }
        bReturnValue = spParameter->GetValue();
        if (!bReturnValue)
        {
            sMessageText = _T("Ошибка установки параметра \"TESTSIGNING\"!!");
            AfxMessageBox(
                sMessageText,
                MB_OK | MB_ICONEXCLAMATION
                );
            m_nRetCode = ERROR_INVALID_DATA;
            return ERROR_INVALID_DATA;
        }
        m_bRebootNeed = true;
        m_bTestModeInstalling = true;
        m_enRebootPurpose = enSetTesting;
        bRet = WriteProfileInt(
            _T("RebootParameters"),
            _T("dwRebootPurpose"),
            m_enRebootPurpose
            );
        ASSERT(bRet);
        bRet = WriteProfileString(
            _T("RebootParameters"),
            _T("sRebootPurposeDescription"),
            _T("Установлен параметр загрузки \"TESTSIGNING\".")
            );
        ASSERT(bRet);
        sCommandLine  = m_sModuleFolder;
        sCommandLine += _T("\\PostInstallUtils.exe -install");
        nRetCode = oRunOnceKey.Open(
            HKEY_LOCAL_MACHINE,
            _T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce")
            );
        ASSERT(ERROR_SUCCESS == nRetCode);
        nRetCode = oRunOnceKey.SetStringValue(
            _T("dnsfiewalInstallContinue"),
            sCommandLine
            );
        ASSERT(ERROR_SUCCESS == nRetCode);
        pRebootPromptDlg = new CRebootPromptDlg( );
        pRebootPromptDlg->m_sRebootPurposeText = _T("\
Установлен параметр загрузки \"TESTSIGNING\". Для пролжения установки драйвера \"dnsfirewall\" необходимо перезагрузить \
компьютер. Установка драйвера \"dnsfirewall\" автоматически продолжится после перезагрузки компютера, но для установки \
необходимы привилегии администратора, поэтому после перезагрузки нужно войти в систему с соответствующими правами.\
");
        nMessageBoxRet = pRebootPromptDlg->DoModal();
        delete pRebootPromptDlg;
        if (IDOK == nMessageBoxRet)
        {
            sCommandLine.Format(
                _T("%s\\WaitForReboot.exe %u"),
                (LPCTSTR) m_sModuleFolder,
                m_dwProcessID
                );
            RtlZeroMemory(
                &stStartupInfo,
                sizeof(stStartupInfo)
                );
            stStartupInfo.cb = sizeof(stStartupInfo);
            RtlZeroMemory(
                &stProcessInformation,
                sizeof(stProcessInformation)
                );
            bRet = CreateProcess(
                NULL,
                LPTSTR(LPCTSTR(sCommandLine)),
                NULL,
                NULL,
                FALSE,
                0,
                NULL,
                NULL,
                &stStartupInfo,
                &stProcessInformation
                );
            ASSERT(bRet);
            bRet = CloseHandle(stProcessInformation.hThread);
            bRet = CloseHandle(stProcessInformation.hProcess);
        }
        return ERROR_SUCCESS;
    }
    m_bTestModeInstalled = true;
    {
        // установка тестового сертификата в хранилище корневых сертификатов и в хранилище 
        // сертификатов доверенных издателей
        HCERTSTORE      hSource;
        HCERTSTORE      hDestination;
        CString         sSourceFileName;
        PCCERT_CONTEXT  pSourceContext;
        bool            bCertExist = false;
        sSourceFileName = m_sDriverFolder;
        sSourceFileName += _T("\\dnsfirewall.cer");
        hSource = CertOpenStore(
            CERT_STORE_PROV_FILENAME,
            PKCS_7_ASN_ENCODING,
            NULL,
            CERT_STORE_OPEN_EXISTING_FLAG,
            (LPCTSTR)sSourceFileName
            );
        ASSERT(NULL != hSource);
        pSourceContext = CertEnumCertificatesInStore(
            hSource,
            NULL
            );
        ASSERT(NULL != pSourceContext);
        hDestination = CertOpenStore(
            CERT_STORE_PROV_SYSTEM,
            PKCS_7_ASN_ENCODING,
            NULL,
            CERT_SYSTEM_STORE_LOCAL_MACHINE,
            _T("root")
            );
        ASSERT(NULL != hDestination);
        bRet = CertAddCertificateContextToStore(
            hDestination,
            pSourceContext,
            CERT_STORE_ADD_NEW,
            NULL
            );
        if (!bRet)
        {
            dwErrorCode = ::GetLastError();
            ASSERT(CRYPT_E_EXISTS == dwErrorCode);
            bRet = CertAddCertificateContextToStore(
                hDestination,
                pSourceContext,
                CERT_STORE_ADD_NEWER,
                NULL
                );
            bCertExist = true;
        }
        if (bCertExist && (!bRet))
        {
            dwErrorCode = ::GetLastError();
            ASSERT(CRYPT_E_EXISTS == dwErrorCode);
        }
        if (bCertExist)
        {
            m_bRootCertExist = true;
        }
        else
        {
            m_bRootCertInstalled = true;
        }
        bRet = CertCloseStore(
            hDestination,
            0
            );
        bCertExist = false;
        hDestination = CertOpenStore(
            CERT_STORE_PROV_SYSTEM,
            PKCS_7_ASN_ENCODING,
            NULL,
            CERT_SYSTEM_STORE_LOCAL_MACHINE,
            _T("trustedpublisher")
            );
        ASSERT(NULL != hDestination);
        bRet = CertAddCertificateContextToStore(
            hDestination,
            pSourceContext,
            CERT_STORE_ADD_NEW,
            NULL
            );
        if (!bRet)
        {
            dwErrorCode = ::GetLastError();
            ASSERT(CRYPT_E_EXISTS == dwErrorCode);
            bRet = CertAddCertificateContextToStore(
                hDestination,
                pSourceContext,
                CERT_STORE_ADD_NEWER,
                NULL
                );
            bCertExist = true;
        }
        if (bCertExist && (!bRet))
        {
            dwErrorCode = ::GetLastError();
            ASSERT(CRYPT_E_EXISTS == dwErrorCode);
        }
        if (bCertExist)
        {
            m_bTrustedPublisherCertExist = true;
        }
        else
        {
            m_bTrustedPublisherCertInstalled = true;
        }
        bRet = CertCloseStore(
            hDestination,
            0
            );
        bRet = CertFreeCertificateContext(
            pSourceContext
            );
        pSourceContext = NULL;
        bRet = CertCloseStore(
            hSource,
            0
            );
    }
    {
        // установка драйвера с помощью setup-API и WDF-API
        WDF_COINSTALLER_INSTALL_OPTIONS stClienOptions;
        HINF            hInfHandle;
        UINT            nErrorLine, nPathLength;
        INFCONTEXT      stInfContext;
        TCHAR           szTextBuffer[MAX_PATH];
        DWORD           dwRequiredSize;
        CString         sTextBuffer, sMajorVer, sMinorVer;
        int             nPos;
        long            nMajorVer, nMinorVer;
        ULONG           ulRetCode;
        HSPFILEQ        hFileQueue;
        PVOID           pDefaultCBContext;
        DWORD           dwInstallRet;
        HDEVINFO        hDevsInfoSet;
        SP_DEVINFO_DATA stDevInfoData;
        CONFIGRET       cr;
        CPath 			oPath;
        hInfHandle = SetupOpenInfFile(
            m_sInfFilePath,
            NULL,
            INF_STYLE_WIN4,
            &nErrorLine
            );
        if (INVALID_HANDLE_VALUE == hInfHandle)
        {
            dwErrorCode = ::GetLastError();
            dwMsgLength = ::FormatMessage(
                FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                NULL,
                HRESULT_FROM_SETUPAPI(dwErrorCode),
                MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
                (LPTSTR)&lpMsgBuf,
                0,
                NULL
                );
            if (dwMsgLength == 0)
            {
                sErrorMessage.Format(
                    _T("Ошибка вызова функции \"FormatMessage\"! Код ошибки = %u."),
                    ::GetLastError()
                    );
            }
            else
            {
                sErrorMessage = (LPCTSTR)lpMsgBuf;
                ::LocalFree((HLOCAL)lpMsgBuf);
            }
            sMessageText.Format(
                _T("\
Ошибка открытия INF-файла!!\n\
\tКод ошибки = %d (0x%08X): %s\n\
\tНомер строки INF-файла = %d\n\
"),
                dwErrorCode,
                dwErrorCode,
                (LPCTSTR)sErrorMessage,
                nErrorLine
                );
            AfxMessageBox(
                sMessageText,
                MB_OK | MB_ICONEXCLAMATION
                );
            m_nRetCode = dwErrorCode;
            return dwErrorCode;
        }
        bRet = SetupFindFirstLine(
            hInfHandle,
            _T("dnsfirewall_wdfsect"),
            _T("KmdfLibraryVersion"),
            &stInfContext
            );
        if (!bRet)
        {
            dwErrorCode = ::GetLastError();
            dwMsgLength = ::FormatMessage(
                FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                NULL,
                HRESULT_FROM_SETUPAPI(dwErrorCode),
                MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
                (LPTSTR)&lpMsgBuf,
                0,
                NULL
                );
            if (dwMsgLength == 0)
            {
                sErrorMessage.Format(
                    _T("Ошибка вызова функции \"FormatMessage\"! Код ошибки = %u."),
                    ::GetLastError()
                    );
            }
            else
            {
                sErrorMessage = (LPCTSTR)lpMsgBuf;
                ::LocalFree((HLOCAL)lpMsgBuf);
            }
            sMessageText.Format(
                _T("\
Ошибка поиска строки значения \"KmdfLibraryVersion\"!!\n\
\tКод ошибки = %d (0x%08X): %s\n\
"),
                dwErrorCode,
                dwErrorCode,
                (LPCTSTR)sErrorMessage
                );
            AfxMessageBox(
                sMessageText,
                MB_OK | MB_ICONEXCLAMATION
                );
            m_nRetCode = dwErrorCode;
            return dwErrorCode;
        }
        bRet = SetupGetStringField(
            &stInfContext,
            1,
            szTextBuffer,
            MAX_PATH,
            &dwRequiredSize
            );
        if (!bRet)
        {
            dwErrorCode = ::GetLastError();
            dwMsgLength = ::FormatMessage(
                FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                NULL,
                HRESULT_FROM_SETUPAPI(dwErrorCode),
                MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
                (LPTSTR)&lpMsgBuf,
                0,
                NULL
                );
            if (dwMsgLength == 0)
            {
                sErrorMessage.Format(
                    _T("Ошибка вызова функции \"FormatMessage\"! Код ошибки = %u."),
                    ::GetLastError()
                    );
            }
            else
            {
                sErrorMessage = (LPCTSTR)lpMsgBuf;
                ::LocalFree((HLOCAL)lpMsgBuf);
            }
            sMessageText.Format(
                _T("\
Ошибка получения строки значения \"KmdfLibraryVersion\"!!\n\
\tКод ошибки = %d (0x%08X): %s\n\
"),
                dwErrorCode,
                dwErrorCode,
                (LPCTSTR)sErrorMessage
                );
            AfxMessageBox(
                sMessageText,
                MB_OK | MB_ICONEXCLAMATION
                );
            m_nRetCode = dwErrorCode;
            return dwErrorCode;
        }
        sTextBuffer = szTextBuffer;
        nPos = sTextBuffer.Find(_T('.'));
        sMajorVer = sTextBuffer.Left(nPos);
        sMinorVer = sTextBuffer.Mid(nPos + 1);
        nMajorVer = _tstol(sMajorVer);
        nMinorVer = _tstol(sMinorVer);
        sTextBuffer.Format(
            _T("%02d%03d"),
            nMajorVer,
            nMinorVer
            );
        m_sCoInstallerPath = m_sDriverFolder;
        m_sCoInstallerPath += _T("\\WdfCoInstaller");
        m_sCoInstallerPath += sTextBuffer;
        m_sCoInstallerPath += _T(".dll");
        // загрузка КоИнсталлера
        m_hCoInatallerModule = LoadLibrary(m_sCoInstallerPath);
        if (NULL == m_hCoInatallerModule)
        {
            dwErrorCode = ::GetLastError();
            dwMsgLength = ::FormatMessage(
                FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                NULL,
                dwErrorCode,
                MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
                (LPTSTR)&lpMsgBuf,
                0,
                NULL
                );
            if (dwMsgLength == 0)
            {
                sErrorMessage.Format(
                    _T("Ошибка вызова функции \"FormatMessage\"! Код ошибки = %u."),
                    ::GetLastError()
                    );
            }
            else
            {
                sErrorMessage = (LPCTSTR)lpMsgBuf;
                ::LocalFree((HLOCAL)lpMsgBuf);
            }
            sMessageText.Format(
                _T("\
Ошибка загрузки \"КоИнсталлера\"!!\n\
\tКод ошибки = %d (0x%08X): %s\n\
"),
                dwErrorCode,
                dwErrorCode,
                (LPCTSTR)sErrorMessage
                );
            AfxMessageBox(
                sMessageText,
                MB_OK | MB_ICONEXCLAMATION
                );
            m_nRetCode = dwErrorCode;
            return dwErrorCode;
        }
        m_pfnWdfPreDeviceInstall = (PFN_WDFPREDEVICEINSTALL)GetProcAddress(
            m_hCoInatallerModule,
            "WdfPreDeviceInstall"
            );
        ASSERT(NULL != m_pfnWdfPreDeviceInstall);
        m_pfnWdfPostDeviceInstall = (PFN_WDFPOSTDEVICEINSTALL)GetProcAddress(
            m_hCoInatallerModule,
            "WdfPostDeviceInstall"
            );
        ASSERT(NULL != m_pfnWdfPostDeviceInstall);
        m_pfnWdfPreDeviceInstallEx = (PFN_WDFPREDEVICEINSTALLEX)GetProcAddress(
            m_hCoInatallerModule,
            "WdfPreDeviceInstallEx"
            );
        ASSERT(NULL != m_pfnWdfPreDeviceInstallEx);
        m_pfnWdfPreDeviceRemove = (PFN_WDFPREDEVICEREMOVE)GetProcAddress(
            m_hCoInatallerModule,
            "WdfPreDeviceRemove"
            );
        ASSERT(NULL != m_pfnWdfPreDeviceRemove);
        m_pfnWdfPostDeviceRemove = (PFN_WDFPOSTDEVICEREMOVE)GetProcAddress(
            m_hCoInatallerModule,
            "WdfPostDeviceRemove"
            );
        ASSERT(NULL != m_pfnWdfPostDeviceRemove);
        // прединсталляция драйвера
        WDF_COINSTALLER_INSTALL_OPTIONS_INIT(&stClienOptions);
        stClienOptions.ShowRebootPrompt = FALSE;
        ulRetCode = m_pfnWdfPreDeviceInstallEx(
            m_sInfFilePath,
            WDF_SECTION_NAME,
            &stClienOptions
            );
        if (ERROR_SUCCESS != ulRetCode)
        {
            if (ERROR_SUCCESS_REBOOT_REQUIRED == ulRetCode)
            {
                m_enRebootPurpose = enWdfInstall;
                m_bRebootNeed = true;
                bRet = WriteProfileInt(
                    _T("RebootParameters"),
                    _T("dwRebootPurpose"),
                    m_enRebootPurpose
                    );
                ASSERT(bRet);
                bRet = WriteProfileString(
                    _T("RebootParameters"),
                    _T("sRebootPurposeDescription"),
                    _T("\
Для установки драйвера \"dnsfirewall.sys\" необходима библиотека \"WDF\" (Windows Driver Framework).\
Библиотека упешно установлена в систему.\
")
                    );
                ASSERT(bRet);
            }
            else
            {
                dwErrorCode = ulRetCode;
                dwMsgLength = ::FormatMessage(
                    FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                    NULL,
                    dwErrorCode,
                    MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
                    (LPTSTR)&lpMsgBuf,
                    0,
                    NULL
                    );
                if (dwMsgLength == 0)
                {
                    sErrorMessage.Format(
                        _T("Ошибка вызова функции \"FormatMessage\"! Код ошибки = %u."),
                        ::GetLastError()
                        );
                }
                else
                {
                    sErrorMessage = (LPCTSTR)lpMsgBuf;
                    ::LocalFree((HLOCAL)lpMsgBuf);
                }
                sMessageText.Format(
                    _T("\
Ошибка вызова функции \"WdfPreDeviceInstallEx\"!!\n\
\tКод ошибки = %d (0x%08X): %s\n\
"),
                dwErrorCode,
                dwErrorCode,
                (LPCTSTR)sErrorMessage
                );
                bRet = FreeLibrary(m_hCoInatallerModule);
                SetupCloseInfFile(hInfHandle);
                AfxMessageBox(
                    sMessageText,
                    MB_OK | MB_ICONEXCLAMATION
                    );
                m_nRetCode = dwErrorCode;
                return dwErrorCode;
            }
        }
        // прединсталляция драйвера в хранилище драйверов
        bRet = SetupCopyOEMInf(
            m_sInfFilePath,
            m_sDriverFolder,
            SPOST_PATH,
            0,
            szTextBuffer,
            MAX_PATH,
            NULL,
            NULL
            );
        if (!bRet)
        {
            dwErrorCode = ::GetLastError();
            dwMsgLength = ::FormatMessage(
                FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                NULL,
                HRESULT_FROM_SETUPAPI(dwErrorCode),
                MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
                (LPTSTR)&lpMsgBuf,
                0,
                NULL
                );
            if (dwMsgLength == 0)
            {
                sErrorMessage.Format(
                    _T("Ошибка вызова функции \"FormatMessage\"! Код ошибки = %u."),
                    ::GetLastError()
                    );
            }
            else
            {
                sErrorMessage = (LPCTSTR)lpMsgBuf;
                ::LocalFree((HLOCAL)lpMsgBuf);
            }
            sMessageText.Format(
                _T("\
Ошибка вызова функции \"SetupCopyOEMInf\"!!\n\
\tКод ошибки = %d (0x%08X): %s\n\
"),
                dwErrorCode,
                dwErrorCode,
                (LPCTSTR)sErrorMessage
                );
            bRet = FreeLibrary(m_hCoInatallerModule);
            SetupCloseInfFile(hInfHandle);
            AfxMessageBox(
                sMessageText,
                MB_OK | MB_ICONEXCLAMATION
                );
            m_nRetCode = dwErrorCode;
            return dwErrorCode;
        }
        m_sOEMInfFile = szTextBuffer;
        // копирование файла драйвера в систему
        nPathLength = GetWindowsDirectory(
            szTextBuffer,
            MAX_PATH
            );
        ASSERT(nPathLength > 1);
        m_sDriversFolder = szTextBuffer;
        m_sDriversFolder += _T("\\System32\\drivers");
        hFileQueue = SetupOpenFileQueue();
        if (INVALID_HANDLE_VALUE == hFileQueue)
        {
            dwErrorCode = ::GetLastError();
            dwMsgLength = ::FormatMessage(
                FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                NULL,
                HRESULT_FROM_SETUPAPI(dwErrorCode),
                MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
                (LPTSTR)&lpMsgBuf,
                0,
                NULL
                );
            if (dwMsgLength == 0)
            {
                sErrorMessage.Format(
                    _T("Ошибка вызова функции \"FormatMessage\"! Код ошибки = %u."),
                    ::GetLastError()
                    );
            }
            else
            {
                sErrorMessage = (LPCTSTR)lpMsgBuf;
                ::LocalFree((HLOCAL)lpMsgBuf);
            }
            sMessageText.Format(
                _T("\
Ошибка открытия очереди устанавливаемых файлов!!\n\
\tКод ошибки = %d (0x%08X): %s\n\
"),
                dwErrorCode,
                dwErrorCode,
                (LPCTSTR)sErrorMessage
                );
            oPath = CPath(m_sOEMInfFile);
            oPath.StripPath();
            bRet = SetupUninstallOEMInf(
                oPath,
                SUOI_FORCEDELETE,
                NULL
                );
            bRet = FreeLibrary(m_hCoInatallerModule);
            SetupCloseInfFile(hInfHandle);
            AfxMessageBox(
                sMessageText,
                MB_OK | MB_ICONEXCLAMATION
                );
            m_nRetCode = dwErrorCode;
            return dwErrorCode;
        }
        bRet = SetupQueueDefaultCopy(
            hFileQueue,
            hInfHandle,
            m_sDriverFolder,
            _T("dnsfirewall.sys"),
            _T("dnsfirewall.sys"),
            SP_COPY_SOURCEPATH_ABSOLUTE
            );
        if (!bRet)
        {
            dwErrorCode = ::GetLastError();
            dwMsgLength = ::FormatMessage(
                FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                NULL,
                HRESULT_FROM_SETUPAPI(dwErrorCode),
                MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
                (LPTSTR)&lpMsgBuf,
                0,
                NULL
                );
            if (dwMsgLength == 0)
            {
                sErrorMessage.Format(
                    _T("Ошибка вызова функции \"FormatMessage\"! Код ошибки = %u."),
                    ::GetLastError()
                    );
            }
            else
            {
                sErrorMessage = (LPCTSTR)lpMsgBuf;
                ::LocalFree((HLOCAL)lpMsgBuf);
            }
            sMessageText.Format(
                _T("\
Ошибка копирования файла драйвера!!\n\
\tКод ошибки = %d (0x%08X): %s\n\
"),
                dwErrorCode,
                dwErrorCode,
                (LPCTSTR)sErrorMessage
                );
            oPath = CPath(m_sOEMInfFile);
            oPath.StripPath();
            bRet = SetupUninstallOEMInf(
                oPath,
                SUOI_FORCEDELETE,
                NULL
                );
            SetupCloseFileQueue(hFileQueue);
            bRet = FreeLibrary(m_hCoInatallerModule);
            SetupCloseInfFile(hInfHandle);
            AfxMessageBox(
                sMessageText,
                MB_OK | MB_ICONEXCLAMATION
                );
            m_nRetCode = dwErrorCode;
            return dwErrorCode;
        }
        pDefaultCBContext = SetupInitDefaultQueueCallback(NULL);
        ASSERT(NULL != pDefaultCBContext);
        bRet = SetupCommitFileQueue(
            NULL,
            hFileQueue,
            SetupDefaultQueueCallback,
            pDefaultCBContext
            );
        if (!bRet)
        {
            dwErrorCode = ::GetLastError();
            dwMsgLength = ::FormatMessage(
                FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                NULL,
                HRESULT_FROM_SETUPAPI(dwErrorCode),
                MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
                (LPTSTR)&lpMsgBuf,
                0,
                NULL
                );
            if (dwMsgLength == 0)
            {
                sErrorMessage.Format(
                    _T("Ошибка вызова функции \"FormatMessage\"! Код ошибки = %u."),
                    ::GetLastError()
                    );
            }
            else
            {
                sErrorMessage = (LPCTSTR)lpMsgBuf;
                ::LocalFree((HLOCAL)lpMsgBuf);
            }
            sMessageText.Format(
                _T("\
Ошибка подтверждения очереди файлов!!\n\
\tКод ошибки = %d (0x%08X): %s\n\
"),
                dwErrorCode,
                dwErrorCode,
                (LPCTSTR)sErrorMessage
                );
            SetupCloseFileQueue(hFileQueue);
            oPath = CPath(m_sOEMInfFile);
            oPath.StripPath();
            bRet = SetupUninstallOEMInf(
                oPath,
                SUOI_FORCEDELETE,
                NULL
                );
            bRet = FreeLibrary(m_hCoInatallerModule);
            SetupCloseInfFile(hInfHandle);
            AfxMessageBox(
                sMessageText,
                MB_OK | MB_ICONEXCLAMATION
                );
            m_nRetCode = dwErrorCode;
            return dwErrorCode;
        }
        SetupTermDefaultQueueCallback(pDefaultCBContext);
        SetupCloseFileQueue(hFileQueue);
        // инсталяция сервиса драйвера
        hDevsInfoSet = SetupDiCreateDeviceInfoList(
            NULL,
            NULL
            );
        if (INVALID_HANDLE_VALUE == hDevsInfoSet)
        {
            dwErrorCode = ::GetLastError();
            dwMsgLength = ::FormatMessage(
                FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                NULL,
                HRESULT_FROM_SETUPAPI(dwErrorCode),
                MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
                (LPTSTR)&lpMsgBuf,
                0,
                NULL
                );
            if (dwMsgLength == 0)
            {
                sErrorMessage.Format(
                    _T("Ошибка вызова функции \"FormatMessage\"! Код ошибки = %u."),
                    ::GetLastError()
                    );
            }
            else
            {
                sErrorMessage = (LPCTSTR)lpMsgBuf;
                ::LocalFree((HLOCAL)lpMsgBuf);
            }
            sMessageText.Format(
                _T("\
Ошибка вызова функции \"SetupDiCreateDeviceInfoList\"!!\n\
\tКод ошибки = 0x%08X\n\
\tОписание ошибки: %s\n\
                "),
                dwErrorCode,
                (LPCTSTR)sErrorMessage
                );
            oPath = CPath(m_sOEMInfFile);
            oPath.StripPath();
            bRet = SetupUninstallOEMInf(
                oPath,
                SUOI_FORCEDELETE,
                NULL
                );
            bRet = FreeLibrary(m_hCoInatallerModule);
            SetupCloseInfFile(hInfHandle);
            AfxMessageBox(
                sMessageText,
                MB_OK | MB_ICONEXCLAMATION
                );
            m_nRetCode = dwErrorCode;
            return dwErrorCode;
        }
        stDevInfoData.cbSize = sizeof(stDevInfoData);
        bRet = SetupDiCreateDeviceInfo(
            hDevsInfoSet,
            DEVICE_KEY_INSTANCE,
            &GUID_NULL,
            NULL,
            NULL,
            0,
            &stDevInfoData
            );
        if (!bRet)
        {
            dwErrorCode = ::GetLastError();
            dwMsgLength = ::FormatMessage(
                FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                NULL,
                HRESULT_FROM_SETUPAPI(dwErrorCode),
                MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
                (LPTSTR)&lpMsgBuf,
                0,
                NULL
                );
            if (dwMsgLength == 0)
            {
                sErrorMessage.Format(
                    _T("Ошибка вызова функции \"FormatMessage\"! Код ошибки = %u."),
                    ::GetLastError()
                    );
            }
            else
            {
                sErrorMessage = (LPCTSTR)lpMsgBuf;
                ::LocalFree((HLOCAL)lpMsgBuf);
            }
            sMessageText.Format(
                _T("\
Ошибка вызова функции \"SetupDiCreateDeviceInfo\"!!\n\
\tКод ошибки = 0x%08X\n\
\tОписание ошибки: %s\n\
"),
                dwErrorCode,
                (LPCTSTR)sErrorMessage
                );
            bRet = SetupDiDestroyDeviceInfoList(hDevsInfoSet);
            oPath = CPath(m_sOEMInfFile);
            oPath.StripPath();
            bRet = SetupUninstallOEMInf(
                oPath,
                SUOI_FORCEDELETE,
                NULL
                );
            bRet = FreeLibrary(m_hCoInatallerModule);
            SetupCloseInfFile(hInfHandle);
            AfxMessageBox(
                sMessageText,
                MB_OK | MB_ICONEXCLAMATION
                );
            m_nRetCode = dwErrorCode;
            return dwErrorCode;
        }
        cr = CM_Add_ID(
            stDevInfoData.DevInst,
            DEVICE_ID_HARDWARE,
            CM_ADD_ID_HARDWARE
            );
        ASSERT(CR_SUCCESS == cr);
        bRet = SetupDiBuildDriverInfoList(
            hDevsInfoSet,
            &stDevInfoData,
            SPDIT_COMPATDRIVER
            );
        if (!bRet)
        {
            dwErrorCode = ::GetLastError();
            dwMsgLength = ::FormatMessage(
                FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                NULL,
                HRESULT_FROM_SETUPAPI(dwErrorCode),
                MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
                (LPTSTR)&lpMsgBuf,
                0,
                NULL
                );
            if (dwMsgLength == 0)
            {
                sErrorMessage.Format(
                    _T("Ошибка вызова функции \"FormatMessage\"! Код ошибки = %u."),
                    ::GetLastError()
                    );
            }
            else
            {
                sErrorMessage = (LPCTSTR)lpMsgBuf;
                ::LocalFree((HLOCAL)lpMsgBuf);
            }
            sMessageText.Format(
                _T("\
Ошибка вызова функции \"SetupDiBuildDriverInfoList\"!!\n\
\tКод ошибки = 0x%08X\n\
\tОписание ошибки: %s\n\
"),
                dwErrorCode,
                (LPCTSTR)sErrorMessage
                );
            bRet = SetupDiDeleteDeviceInfo(
                hDevsInfoSet,
                &stDevInfoData
                );
            bRet = SetupDiDestroyDeviceInfoList(hDevsInfoSet);
            oPath = CPath(m_sOEMInfFile);
            oPath.StripPath();
            bRet = SetupUninstallOEMInf(
                oPath,
                SUOI_FORCEDELETE,
                NULL
                );
            bRet = FreeLibrary(m_hCoInatallerModule);
            SetupCloseInfFile(hInfHandle);
            AfxMessageBox(
                sMessageText,
                MB_OK | MB_ICONEXCLAMATION
                );
            m_nRetCode = dwErrorCode;
            return dwErrorCode;
        }
        bRet = SetupInstallServicesFromInfSection(
            hInfHandle,
            SERVICE_SECTION_NAME,
            0
            );
        dwInstallRet = GetLastError();
        if (ERROR_SUCCESS_REBOOT_REQUIRED == dwInstallRet)
        {
            m_enRebootPurpose = enDriverService;
            m_bRebootNeed = true;
            bRet = WriteProfileInt(
                _T("RebootParameters"),
                _T("dwRebootPurpose"),
                m_enRebootPurpose
                );
            ASSERT(bRet);
            bRet = WriteProfileString(
                _T("RebootParameters"),
                _T("sRebootPurposeDescription"),
                _T("\
Инсталяция сервиса драйвера \"dnsfirewall.sys\" потребовала перезагрузки системы.\
")
                );
            ASSERT(bRet);
        }
        else if (ERROR_SUCCESS != dwInstallRet)
        {
            dwErrorCode = dwInstallRet;
            dwMsgLength = ::FormatMessage(
                FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                NULL,
                HRESULT_FROM_SETUPAPI(dwErrorCode),
                MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
                (LPTSTR)&lpMsgBuf,
                0,
                NULL
                );
            if (dwMsgLength == 0)
            {
                sErrorMessage.Format(
                    _T("Ошибка вызова функции \"FormatMessage\"! Код ошибки = %u."),
                    ::GetLastError()
                    );
            }
            else
            {
                sErrorMessage = (LPCTSTR)lpMsgBuf;
                ::LocalFree((HLOCAL)lpMsgBuf);
            }
            sMessageText.Format(
                _T("\
Ошибка инсталяции сервиса драйвера!!\n\
\tКод ошибки = %d (0x%08X): %s\n\
"),
                dwErrorCode,
                dwErrorCode,
                (LPCTSTR)sErrorMessage
                );
            bRet = SetupDiDestroyDriverInfoList(
                hDevsInfoSet,
                &stDevInfoData,
                SPDIT_COMPATDRIVER
                );
            bRet = SetupDiDeleteDeviceInfo(
                hDevsInfoSet,
                &stDevInfoData
                );
            bRet = SetupDiDestroyDeviceInfoList(hDevsInfoSet);
            oPath = CPath(m_sOEMInfFile);
            oPath.StripPath();
            bRet = SetupUninstallOEMInf(
                oPath,
                SUOI_FORCEDELETE,
                NULL
                );
            bRet = FreeLibrary(m_hCoInatallerModule);
            SetupCloseInfFile(hInfHandle);
            AfxMessageBox(
                sMessageText,
                MB_OK | MB_ICONEXCLAMATION
                );
            m_nRetCode = dwErrorCode;
            return dwErrorCode;
        }
        bRet = SetupDiDestroyDriverInfoList(
            hDevsInfoSet,
            &stDevInfoData,
            SPDIT_COMPATDRIVER
            );
        bRet = SetupDiDeleteDeviceInfo(
            hDevsInfoSet,
            &stDevInfoData
            );
        bRet = SetupDiDestroyDeviceInfoList(hDevsInfoSet);
        SetupCloseInfFile(hInfHandle);
        //
        // POST-INSTALL for WDF support
        //
        ulRetCode = m_pfnWdfPostDeviceInstall(
            m_sInfFilePath,
            WDF_SECTION_NAME
            );
        ASSERT(ERROR_SUCCESS == ulRetCode);
        bRet = FreeLibrary(m_hCoInatallerModule);
        if (m_bRebootNeed)
        {
            INT_PTR             nDlgResult;
            CWnd*               pParentWindow = NULL;
            BOOL                bRet;
            if (NULL != m_hParentWindow)
            {
                pParentWindow = new CWnd;
                bRet = pParentWindow->Attach(m_hParentWindow);
            }
            CRebootPromptDlg    oRebootPromptDlg(pParentWindow);
            switch (m_enRebootPurpose)
            {
//            case enSetTesting:
//                oRebootPromptDlg.m_sRebootPurpose = _T("Установлен параметр загрузки \"TESTSIGNING\".");
//                oRebootPromptDlg.m_sRebootPromptHtml = _T("\
//Если нажмёте кнопку <i>&quot;Перезагрузка&quot;</i>, то перезагрузка компьютера будет запланирована \
//на окончание инсталляции всего пакета. \
//Иначе, если нажмёте кнопку <i>&quot;Отмена&quot;</i>, то перезагрузку нужно произвести \
//после инсталляции всего пакета вручную.\
//");
//                break;
            case enWdfInstall:
                oRebootPromptDlg.m_sRebootPurposeText = _T("\
Для установки драйвера \"dnsfirewall.sys\" необходима библиотека \"WDF\" (Windows Driver Framework). \
Библиотека упешно установлена в систему.\
");
                oRebootPromptDlg.m_sRebootPromptText = _T("\
Если нажмёте кнопку \"Перезагрузка\", то перезагрузка компьютера будет запланирована \
на окончание инсталляции всего пакета. \
Иначе, если нажмёте кнопку \"Отмена\", то перезагрузку нужно произвести \
после инсталляции всего пакета вручную.\
");
                break;
            case enDriverStore:
                oRebootPromptDlg.m_sRebootPurposeText = _T("\
Установка пакета драйвера \"dnsfirewall.sys\" в храниище драйверов потребовала перезагрузки системы.\
");
                oRebootPromptDlg.m_sRebootPromptText = _T("\
Если нажмёте кнопку \"Перезагрузка\", то перезагрузка компьютера будет запланирована \
на окончание инсталляции всего пакета. \
Иначе, если нажмёте кнопку \"Отмена\", то перезагрузку нужно произвести \
после инсталляции всего пакета вручную.\
");
                break;
            case enDriverService:
                oRebootPromptDlg.m_sRebootPurposeText = _T("\
Инсталяция сервиса драйвера \"dnsfirewall.sys\" потребовала перезагрузки системы.\
");
                oRebootPromptDlg.m_sRebootPromptText = _T("\
Если нажмёте кнопку \"Перезагрузка\", то перезагрузка компьютера будет запланирована \
на окончание инсталляции всего пакета. \
Иначе, если нажмёте кнопку \"Отмена\", то перезагрузку нужно произвести \
после инсталляции всего пакета вручную.\
");
                break;
            default:
                ASSERT(FALSE);
                m_nRetCode = 0;
                return FALSE;
            }
            nDlgResult = oRebootPromptDlg.DoModal();
            if (NULL != pParentWindow)
            {
                pParentWindow->Detach();
                delete pParentWindow;
            }
            if (IDOK == nDlgResult)
            {
                sCommandLine.Format(
                    _T("%s\\WaitForReboot.exe %u"),
                    (LPCTSTR)m_sModuleFolder,
                    m_dwProcessID
                    );
                RtlZeroMemory(
                    &stStartupInfo,
                    sizeof(stStartupInfo)
                    );
                stStartupInfo.cb = sizeof(stStartupInfo);
                RtlZeroMemory(
                    &stProcessInformation,
                    sizeof(stProcessInformation)
                    );
                bRet = CreateProcess(
                    NULL,
                    LPTSTR(LPCTSTR(sCommandLine)),
                    NULL,
                    NULL,
                    FALSE,
                    0,
                    NULL,
                    NULL,
                    &stStartupInfo,
                    &stProcessInformation
                    );
                ASSERT(bRet);
                bRet = CloseHandle(stProcessInformation.hThread);
                bRet = CloseHandle(stProcessInformation.hProcess);
            }
        }
    }
    return ERROR_SUCCESS;
}


UINT CInstallUtilsApp::mUninstallServicesAndDrivers(MSIHANDLE hInstall)
{
    UINT                uiRetCode = ERROR_SUCCESS;
    SC_HANDLE           schSCManager;
    SC_HANDLE           schService;
    BOOL                bRet;
    DWORD               dwErrorCode, dwMsgLength, dwStopCount = 0;
    LPVOID              lpMsgBuf = NULL;
    CString             sErrorMessage, sMessageText;
    LONG                nRet;
    bool                bValidRegistry;
    CRegKey             oDnsDbgKey;
    CWaitThread*        pWaitThread = NULL;
    DWORD               dwWaitCode;
    HRESULT             hr;
    hInstall;
    //AfxMessageBox(_T("Подключаем отладчик (mUninstallServicesAndDrivers)"));
    hr = CoInitializeEx(
        NULL,
        COINIT_MULTITHREADED
        );
    // удаление службы фильтрации адресов DNS-сервера
    nRet = oDnsDbgKey.Open(
        HKEY_LOCAL_MACHINE,
        _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\dns.exe"),
        KEY_ALL_ACCESS
        );
    bValidRegistry = (ERROR_SUCCESS == nRet);
    if (bValidRegistry)
    {
        nRet = oDnsDbgKey.DeleteValue(
            _T("Debugger")
            );
        bValidRegistry = (ERROR_SUCCESS == nRet);
        if (bValidRegistry)
        {
            schSCManager = OpenSCManager(
                NULL,                 // local machine
                NULL,                 // ServicesActive database
                SC_MANAGER_ALL_ACCESS // full access rights
                );
            ASSERT(NULL != schSCManager);
            schService = OpenService(
                schSCManager,
                _T("DNS"),
                SERVICE_ALL_ACCESS
                );
            ASSERT(NULL != schService);
            bRet = CloseServiceHandle(schSCManager);
            for (dwStopCount = 0; dwStopCount < 120; dwStopCount++)
            {
                // перезапуск сервиса "DNS"
                DWORD                   cbBytesNeeded;
                SERVICE_STATUS          stServiceStatus;
                SERVICE_STATUS_PROCESS  stServiceProcessStatus;
                RtlZeroMemory(
                    &stServiceProcessStatus,
                    sizeof(stServiceProcessStatus)
                    );
                RtlZeroMemory(
                    &stServiceStatus,
                    sizeof(stServiceStatus)
                    );
                bRet = QueryServiceStatusEx(
                    schService,
                    SC_STATUS_PROCESS_INFO,
                    (LPBYTE)&stServiceProcessStatus,
                    sizeof(stServiceProcessStatus),
                    &cbBytesNeeded
                    );
                ASSERT(bRet);
                if (SERVICE_RUNNING == stServiceProcessStatus.dwCurrentState)
                {
                    if (0 == dwStopCount)
                    {
                        CWnd*   pParent = NULL;
                        if (NULL != m_hParentWindow)
                        {
                            pParent = new CWnd;
                            bRet = pParent->Attach(m_hParentWindow);
                        }
                        pWaitThread = new CWaitThread(
                            _T("Завершение сервиса фильтрации адресов DNS-сервера может занять несколько минут!"),
                            pParent
                            );
                        bRet = pWaitThread->CreateThread();
                        ASSERT(bRet);
                        bRet = ControlService(
                            schService,
                            SERVICE_CONTROL_STOP,
                            &stServiceStatus
                            );
                        if (!bRet)
                        {
                            dwErrorCode = ::GetLastError();
                            bRet = pWaitThread->PostThreadMessage(
                                WM_QUIT,
                                0,
                                0
                                );
                            pWaitThread = NULL;
                            dwMsgLength = ::FormatMessage(
                                FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                                NULL,
                                dwErrorCode,
                                MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
                                (LPTSTR)&lpMsgBuf,
                                0,
                                NULL
                                );
                            if (dwMsgLength == 0)
                            {
                                sErrorMessage.Format(
                                    _T("Ошибка вызова функции \"FormatMessage\"! Код ошибки = %u."),
                                    ::GetLastError()
                                    );
                            }
                            else
                            {
                                sErrorMessage = (LPCTSTR)lpMsgBuf;
                                ::LocalFree((HLOCAL)lpMsgBuf);
                            }
                            sMessageText.Format(
                                _T("\
Ошибка вызова функции \"ControlService\"!\n\
\tКод ошибки = 0x%08X\n\
\tОписание ошибки: %s\n\
"),
                                dwErrorCode,
                                (LPCTSTR)sErrorMessage
                                );
                            AfxMessageBox(
                                sMessageText,
                                MB_OK | MB_ICONEXCLAMATION
                                );
                            break;
                        }
                    }
                    dwWaitCode = SleepEx(1000, TRUE);
                }
                else
                {
                    if (NULL != pWaitThread)
                    {
                        bRet = pWaitThread->PostThreadMessage(
                            WM_QUIT,
                            0,
                            0
                            );
                        pWaitThread = NULL;
                    }
                    break;
                }
            }
            if (dwStopCount >= 120)
            {
                if (NULL != pWaitThread)
                {
                    bRet = pWaitThread->PostThreadMessage(
                        WM_QUIT,
                        0,
                        0
                        );
                    pWaitThread = NULL;
                    sMessageText = _T("Stop service command limit exceeded.");
                    AfxMessageBox(
                        sMessageText,
                        MB_OK | MB_ICONEXCLAMATION
                        );
                }
            }
            bRet = StartService(
                schService,
                0,
                NULL
                );
            if (!bRet)
            {
                dwErrorCode = ::GetLastError();
                dwMsgLength = ::FormatMessage(
                    FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                    NULL,
                    dwErrorCode,
                    MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
                    (LPTSTR)&lpMsgBuf,
                    0,
                    NULL
                    );
                if (dwMsgLength == 0)
                {
                    sErrorMessage.Format(
                        _T("Ошибка вызова функции \"FormatMessage\"! Код ошибки = %u."),
                        ::GetLastError()
                        );
                }
                else
                {
                    sErrorMessage = (LPCTSTR)lpMsgBuf;
                    ::LocalFree((HLOCAL)lpMsgBuf);
                }
                sMessageText.Format(
                    _T("\
Ошибка вызова функции \"StartService\"!!\n\
\tКод ошибки = 0x%08X\n\
\tОписание ошибки: %s\n\
"),
                    dwErrorCode,
                    (LPCTSTR)sErrorMessage
                    );
                AfxMessageBox(
                    sMessageText,
                    MB_OK | MB_ICONEXCLAMATION
                    );
            }
            bRet = CloseServiceHandle(schService);
        }
    }
    {
        // удаление пакета драйвера из системы
        SP_DRVINFO_DATA         stDrvInfoData;
        PBYTE                   pDrvinfoDetailDataBuffer = NULL;
        PSP_DRVINFO_DETAIL_DATA pDrvinfoDetailData = NULL;
        DWORD                   dwDriverInfoDetailDataSize = sizeof(SP_DRVINFO_DETAIL_DATA);
        CString                 sOemInfPath;
        CString                 sOemInfName;
        bool                    bDeviceNotInstalled = false;
        HDEVINFO                hDevsInfoSet;
        SP_DEVINFO_DATA         stDevInfoData;
        CONFIGRET               cr;
        DWORD                   dwRequiredSize = 0;
        SC_HANDLE               schSCManager;
        SC_HANDLE               schService;
        int                     nPos;
        HINF                    hInfHandle;
        CString                 sTextBuffer, sMajorVer, sMinorVer;
        ULONG                   ulRetCode;
        HSPFILEQ                hFileQueue;
        PVOID                   pDefaultCBContext;
        DWORD                   dwMsgLength;
        DWORD                   dwErrorCode = ERROR_SUCCESS;
        LPVOID                  lpMsgBuf = NULL;
        CString                 sErrorMessage, sMessageText;
        UINT                    nErrorLine;
        INFCONTEXT              stInfContext;
        TCHAR                   szTextBuffer[MAX_PATH];
        long                    nMajorVer, nMinorVer;
        DWORD                   dwInstallRet;
        schSCManager = OpenSCManager(
            NULL,                 // local machine
            NULL,                 // ServicesActive database
            SC_MANAGER_ALL_ACCESS // full access rights
            );
        ASSERT(NULL != schSCManager);
        // удаление драйвера с помощью Setup API и WDF
        hDevsInfoSet = SetupDiCreateDeviceInfoList(
            NULL,
            NULL
            );
        if (INVALID_HANDLE_VALUE == hDevsInfoSet)
        {
            dwErrorCode = ::GetLastError();
            dwMsgLength = ::FormatMessage(
                FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                NULL,
                HRESULT_FROM_SETUPAPI(dwErrorCode),
                MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
                (LPTSTR)&lpMsgBuf,
                0,
                NULL
                );
            if (dwMsgLength == 0)
            {
                sErrorMessage.Format(
                    _T("Ошибка вызова функции \"FormatMessage\"! Код ошибки = %u."),
                    ::GetLastError()
                    );
            }
            else
            {
                sErrorMessage = (LPCTSTR)lpMsgBuf;
                ::LocalFree((HLOCAL)lpMsgBuf);
            }
            sMessageText.Format(
                _T("\
Ошибка вызова функции \"SetupDiCreateDeviceInfoList\"!!\n\
\tКод ошибки = 0x%08X\n\
\tОписание ошибки: %s\n\
"),
            dwErrorCode,
            (LPCTSTR)sErrorMessage
            );
            bRet = CloseServiceHandle(schSCManager);
            AfxMessageBox(
                sMessageText,
                MB_OK | MB_ICONEXCLAMATION
                );
            m_nRetCode = dwErrorCode;
            return dwErrorCode;
        }
        stDevInfoData.cbSize = sizeof(stDevInfoData);
        bRet = SetupDiOpenDeviceInfo(
            hDevsInfoSet,
            DEVICE_KEY_INSTANCE,
            NULL,
            0,
            &stDevInfoData
            );
        if (!bRet)
        {
            bDeviceNotInstalled = true;
            bRet = SetupDiCreateDeviceInfo(
                hDevsInfoSet,
                DEVICE_KEY_INSTANCE,
                &GUID_NULL,
                NULL,
                NULL,
                0,
                &stDevInfoData
                );
            if (!bRet)
            {
                dwErrorCode = ::GetLastError();
                dwMsgLength = ::FormatMessage(
                    FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                    NULL,
                    HRESULT_FROM_SETUPAPI(dwErrorCode),
                    MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
                    (LPTSTR)&lpMsgBuf,
                    0,
                    NULL
                    );
                if (dwMsgLength == 0)
                {
                    sErrorMessage.Format(
                        _T("Ошибка вызова функции \"FormatMessage\"! Код ошибки = %u."),
                        ::GetLastError()
                        );
                }
                else
                {
                    sErrorMessage = (LPCTSTR)lpMsgBuf;
                    ::LocalFree((HLOCAL)lpMsgBuf);
                }
                sMessageText.Format(
                    _T("\
Ошибка вызова функции \"SetupDiCreateDeviceInfo\"!!\n\
\tКод ошибки = 0x%08X\n\
\tОписание ошибки: %s\n\
"),
                    dwErrorCode,
                    (LPCTSTR)sErrorMessage
                    );
                bRet = SetupDiDestroyDeviceInfoList(hDevsInfoSet);
                bRet = CloseServiceHandle(schSCManager);
                AfxMessageBox(
                    sMessageText,
                    MB_OK | MB_ICONEXCLAMATION
                    );
                m_nRetCode = dwErrorCode;
                return dwErrorCode;
            }
        }
        cr = CM_Add_ID(
            stDevInfoData.DevInst,
            DEVICE_ID_HARDWARE,
            CM_ADD_ID_HARDWARE
            );
        ASSERT(CR_SUCCESS == cr);
        if (CR_SUCCESS != cr)
        {
            sMessageText.Format(
                _T("0x%08X"),
                cr
                );
            bRet = SetupDiDeleteDeviceInfo(
                hDevsInfoSet,
                &stDevInfoData
                );
            bRet = SetupDiDestroyDeviceInfoList(hDevsInfoSet);
            bRet = CloseServiceHandle(schSCManager);
            AfxMessageBox(
                sMessageText,
                MB_OK | MB_ICONEXCLAMATION
                );
            m_nRetCode = cr;
            return dwErrorCode;
        }
        bRet = SetupDiBuildDriverInfoList(
            hDevsInfoSet,
            &stDevInfoData,
            SPDIT_COMPATDRIVER
            );
        if (!bRet)
        {
            dwErrorCode = ::GetLastError();
            dwMsgLength = ::FormatMessage(
                FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                NULL,
                HRESULT_FROM_SETUPAPI(dwErrorCode),
                MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
                (LPTSTR)&lpMsgBuf,
                0,
                NULL
                );
            if (dwMsgLength == 0)
            {
                sErrorMessage.Format(
                    _T("Ошибка вызова функции \"FormatMessage\"! Код ошибки = %u."),
                    ::GetLastError()
                    );
            }
            else
            {
                sErrorMessage = (LPCTSTR)lpMsgBuf;
                ::LocalFree((HLOCAL)lpMsgBuf);
            }
            sMessageText.Format(
                _T("\
Ошибка вызова функции \"SetupDiBuildDriverInfoList\"!!\n\
\tКод ошибки = 0x%08X\n\
\tОписание ошибки: %s\n\
"),
            dwErrorCode,
            (LPCTSTR)sErrorMessage
            );
            bRet = SetupDiDeleteDeviceInfo(
                hDevsInfoSet,
                &stDevInfoData
                );
            bRet = SetupDiDestroyDeviceInfoList(hDevsInfoSet);
            bRet = CloseServiceHandle(schSCManager);
            AfxMessageBox(
                sMessageText,
                MB_OK | MB_ICONEXCLAMATION
                );
            m_nRetCode = dwErrorCode;
            return dwErrorCode;
        }
        RtlZeroMemory(
            &stDrvInfoData,
            sizeof(stDrvInfoData)
            );
        stDrvInfoData.cbSize = sizeof(stDrvInfoData);
        bRet = SetupDiEnumDriverInfo(
            hDevsInfoSet,
            &stDevInfoData,
            SPDIT_COMPATDRIVER,
            0,
            &stDrvInfoData
            );
        if (!bRet)
        {
            dwErrorCode = ::GetLastError();
            dwMsgLength = ::FormatMessage(
                FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                NULL,
                HRESULT_FROM_SETUPAPI(dwErrorCode),
                MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
                (LPTSTR)&lpMsgBuf,
                0,
                NULL
                );
            if (dwMsgLength == 0)
            {
                sErrorMessage.Format(
                    _T("Ошибка вызова функции \"FormatMessage\"! Код ошибки = %u."),
                    ::GetLastError()
                    );
            }
            else
            {
                sErrorMessage = (LPCTSTR)lpMsgBuf;
                ::LocalFree((HLOCAL)lpMsgBuf);
            }
            sMessageText.Format(
                _T("\
Ошибка вызова функции \"SetupDiEnumDriverInfo\"!!\n\
\tКод ошибки = 0x%08X\n\
\tОписание ошибки: %s\n\
"),
                dwErrorCode,
                (LPCTSTR)sErrorMessage
                );
            bRet = SetupDiDestroyDriverInfoList(
                hDevsInfoSet,
                &stDevInfoData,
                SPDIT_COMPATDRIVER
                );
            bRet = SetupDiDeleteDeviceInfo(
                hDevsInfoSet,
                &stDevInfoData
                );
            bRet = SetupDiDestroyDeviceInfoList(hDevsInfoSet);
            bRet = CloseServiceHandle(schSCManager);
            AfxMessageBox(
                sMessageText,
                MB_OK | MB_ICONEXCLAMATION
                );
            m_nRetCode = dwErrorCode;
            return dwErrorCode;
        }
        pDrvinfoDetailDataBuffer = new BYTE[dwDriverInfoDetailDataSize];
        pDrvinfoDetailData = (PSP_DRVINFO_DETAIL_DATA)pDrvinfoDetailDataBuffer;
        pDrvinfoDetailData->cbSize = sizeof(SP_DRVINFO_DETAIL_DATA);
        bRet = SetupDiGetDriverInfoDetail(
            hDevsInfoSet,
            &stDevInfoData,
            &stDrvInfoData,
            pDrvinfoDetailData,
            dwDriverInfoDetailDataSize,
            &dwRequiredSize
            );
        if (!bRet)
        {
            dwErrorCode = ::GetLastError();
            if (ERROR_INSUFFICIENT_BUFFER == dwErrorCode)
            {
                delete pDrvinfoDetailDataBuffer;
                pDrvinfoDetailDataBuffer = new BYTE[dwRequiredSize];
                pDrvinfoDetailData = (PSP_DRVINFO_DETAIL_DATA)pDrvinfoDetailDataBuffer;
                pDrvinfoDetailData->cbSize = sizeof(SP_DRVINFO_DETAIL_DATA);
                bRet = SetupDiGetDriverInfoDetail(
                    hDevsInfoSet,
                    &stDevInfoData,
                    &stDrvInfoData,
                    pDrvinfoDetailData,
                    dwRequiredSize,
                    NULL
                    );
                if (!bRet)
                {
                    dwErrorCode = ::GetLastError();
                }
            }
        }
        if (!bRet)
        {
            dwMsgLength = ::FormatMessage(
                FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                NULL,
                HRESULT_FROM_SETUPAPI(dwErrorCode),
                MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
                (LPTSTR)&lpMsgBuf,
                0,
                NULL
                );
            if (dwMsgLength == 0)
            {
                sErrorMessage.Format(
                    _T("Ошибка вызова функции \"FormatMessage\"! Код ошибки = %u."),
                    ::GetLastError()
                    );
            }
            else
            {
                sErrorMessage = (LPCTSTR)lpMsgBuf;
                ::LocalFree((HLOCAL)lpMsgBuf);
            }
            sMessageText.Format(
                _T("\
Ошибка вызова функции \"SetupDiGetDriverInfoDetail\"!!\n\
\tКод ошибки = 0x%08X\n\
\tОписание ошибки: %s\n\
"),
                dwErrorCode,
                (LPCTSTR)sErrorMessage
                );
            delete pDrvinfoDetailDataBuffer;
            bRet = SetupDiDestroyDriverInfoList(
                hDevsInfoSet,
                &stDevInfoData,
                SPDIT_COMPATDRIVER
                );
            bRet = SetupDiDeleteDeviceInfo(
                hDevsInfoSet,
                &stDevInfoData
                );
            bRet = SetupDiDestroyDeviceInfoList(hDevsInfoSet);
            bRet = CloseServiceHandle(schSCManager);
            AfxMessageBox(
                sMessageText,
                MB_OK | MB_ICONEXCLAMATION
                );
            m_nRetCode = dwErrorCode;
            return dwErrorCode;
        }
        sOemInfPath = pDrvinfoDetailData->InfFileName;
        delete pDrvinfoDetailDataBuffer;
        nPos = sOemInfPath.ReverseFind(_T('\\'));
        sOemInfName = sOemInfPath.Mid(nPos + 1);
        hInfHandle = SetupOpenInfFile(
            m_sInfFilePath,
            _T("WDF_Legacy_Class"),
            INF_STYLE_WIN4,
            &nErrorLine
            );
        if (INVALID_HANDLE_VALUE == hInfHandle)
        {
            dwErrorCode = ::GetLastError();
            dwMsgLength = ::FormatMessage(
                FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                NULL,
                HRESULT_FROM_SETUPAPI(dwErrorCode),
                MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
                (LPTSTR)&lpMsgBuf,
                0,
                NULL
                );
            if (dwMsgLength == 0)
            {
                sErrorMessage.Format(
                    _T("Ошибка вызова функции \"FormatMessage\"! Код ошибки = %u."),
                    ::GetLastError()
                    );
            }
            else
            {
                sErrorMessage = (LPCTSTR)lpMsgBuf;
                ::LocalFree((HLOCAL)lpMsgBuf);
            }
            sMessageText.Format(
                _T("\
Ошибка открытия INF-файла!!\n\
\tКод ошибки = %d (0x%08X): %s\n\
\tНомер строки INF-файла = %d\n"),
                dwErrorCode,
                dwErrorCode,
                (LPCTSTR)sErrorMessage,
                nErrorLine
                );
            bRet = SetupDiDestroyDriverInfoList(
                hDevsInfoSet,
                &stDevInfoData,
                SPDIT_COMPATDRIVER
                );
            bRet = SetupDiDeleteDeviceInfo(
                hDevsInfoSet,
                &stDevInfoData
                );
            bRet = SetupDiDestroyDeviceInfoList(hDevsInfoSet);
            bRet = CloseServiceHandle(schSCManager);
            AfxMessageBox(
                sMessageText,
                MB_OK | MB_ICONEXCLAMATION
                );
            m_nRetCode = dwErrorCode;
            return dwErrorCode;
        }
        bRet = SetupFindFirstLine(
            hInfHandle,
            _T("dnsfirewall_wdfsect"),
            _T("KmdfLibraryVersion"),
            &stInfContext
            );
        if (!bRet)
        {
            dwErrorCode = ::GetLastError();
            dwMsgLength = ::FormatMessage(
                FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                NULL,
                HRESULT_FROM_SETUPAPI(dwErrorCode),
                MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
                (LPTSTR)&lpMsgBuf,
                0,
                NULL
                );
            if (dwMsgLength == 0)
            {
                sErrorMessage.Format(
                    _T("Ошибка вызова функции \"FormatMessage\"! Код ошибки = %u."),
                    ::GetLastError()
                    );
            }
            else
            {
                sErrorMessage = (LPCTSTR)lpMsgBuf;
                ::LocalFree((HLOCAL)lpMsgBuf);
            }
            sMessageText.Format(
                _T("\
Ошибка поиска строки значения \"KmdfLibraryVersion\"!!\n\
\tКод ошибки = %d (0x%08X): %s\n\
"),
                dwErrorCode,
                dwErrorCode,
                (LPCTSTR)sErrorMessage
                );
            SetupCloseInfFile(hInfHandle);
            bRet = SetupDiDestroyDriverInfoList(
                hDevsInfoSet,
                &stDevInfoData,
                SPDIT_COMPATDRIVER
                );
            bRet = SetupDiDeleteDeviceInfo(
                hDevsInfoSet,
                &stDevInfoData
                );
            bRet = SetupDiDestroyDeviceInfoList(hDevsInfoSet);
            bRet = CloseServiceHandle(schSCManager);
            AfxMessageBox(
                sMessageText,
                MB_OK | MB_ICONEXCLAMATION
                );
            m_nRetCode = dwErrorCode;
            return dwErrorCode;
        }
        bRet = SetupGetStringField(
            &stInfContext,
            1,
            szTextBuffer,
            MAX_PATH,
            &dwRequiredSize
            );
        if (!bRet)
        {
            dwErrorCode = ::GetLastError();
            dwMsgLength = ::FormatMessage(
                FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                NULL,
                HRESULT_FROM_SETUPAPI(dwErrorCode),
                MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
                (LPTSTR)&lpMsgBuf,
                0,
                NULL
                );
            if (dwMsgLength == 0)
            {
                sErrorMessage.Format(
                    _T("Ошибка вызова функции \"FormatMessage\"! Код ошибки = %u."),
                    ::GetLastError()
                    );
            }
            else
            {
                sErrorMessage = (LPCTSTR)lpMsgBuf;
                ::LocalFree((HLOCAL)lpMsgBuf);
            }
            sMessageText.Format(
                _T("\
Ошибка получения строки значения \"KmdfLibraryVersion\"!!\n\
\tКод ошибки = %d (0x%08X): %s\n\
"),
                dwErrorCode,
                dwErrorCode,
                (LPCTSTR)sErrorMessage
                );
            SetupCloseInfFile(hInfHandle);
            bRet = SetupDiDestroyDriverInfoList(
                hDevsInfoSet,
                &stDevInfoData,
                SPDIT_COMPATDRIVER
                );
            bRet = SetupDiDeleteDeviceInfo(
                hDevsInfoSet,
                &stDevInfoData
                );
            bRet = SetupDiDestroyDeviceInfoList(hDevsInfoSet);
            bRet = CloseServiceHandle(schSCManager);
            AfxMessageBox(
                sMessageText,
                MB_OK | MB_ICONEXCLAMATION
                );
            m_nRetCode = dwErrorCode;
            return dwErrorCode;
        }
        sTextBuffer = szTextBuffer;
        nPos = sTextBuffer.Find(_T('.'));
        sMajorVer = sTextBuffer.Left(nPos);
        sMinorVer = sTextBuffer.Mid(nPos + 1);
        nMajorVer = _tstol(sMajorVer);
        nMinorVer = _tstol(sMinorVer);
        sTextBuffer.Format(
            _T("%02d%03d"),
            nMajorVer,
            nMinorVer
            );
        m_sCoInstallerPath = m_sDriverFolder;
        m_sCoInstallerPath += _T("\\WdfCoInstaller");
        m_sCoInstallerPath += sTextBuffer;
        m_sCoInstallerPath += _T(".dll");
        // загрузка КоИнсталлера
        m_hCoInatallerModule = LoadLibrary(m_sCoInstallerPath);
        if (NULL == m_hCoInatallerModule)
        {
            dwErrorCode = ::GetLastError();
            dwMsgLength = ::FormatMessage(
                FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                NULL,
                dwErrorCode,
                MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
                (LPTSTR)&lpMsgBuf,
                0,
                NULL
                );
            if (dwMsgLength == 0)
            {
                sErrorMessage.Format(
                    _T("Ошибка вызова функции \"FormatMessage\"! Код ошибки = %u."),
                    ::GetLastError()
                    );
            }
            else
            {
                sErrorMessage = (LPCTSTR)lpMsgBuf;
                ::LocalFree((HLOCAL)lpMsgBuf);
            }
            sMessageText.Format(
                _T("\
Ошибка загрузки \"КоИнсталлера\"!!\n\
\tКод ошибки = %d (0x%08X): %s\n\
"),
                dwErrorCode,
                dwErrorCode,
                (LPCTSTR)sErrorMessage
                );
            SetupCloseInfFile(hInfHandle);
            bRet = SetupDiDestroyDriverInfoList(
                hDevsInfoSet,
                &stDevInfoData,
                SPDIT_COMPATDRIVER
                );
            bRet = SetupDiDeleteDeviceInfo(
                hDevsInfoSet,
                &stDevInfoData
                );
            bRet = SetupDiDestroyDeviceInfoList(hDevsInfoSet);
            bRet = CloseServiceHandle(schSCManager);
            AfxMessageBox(
                sMessageText,
                MB_OK | MB_ICONEXCLAMATION
                );
            m_nRetCode = dwErrorCode;
            return dwErrorCode;
        }
        m_pfnWdfPreDeviceInstall = (PFN_WDFPREDEVICEINSTALL)GetProcAddress(
            m_hCoInatallerModule,
            "WdfPreDeviceInstall"
            );
        ASSERT(NULL != m_pfnWdfPreDeviceInstall);
        m_pfnWdfPostDeviceInstall = (PFN_WDFPOSTDEVICEINSTALL)GetProcAddress(
            m_hCoInatallerModule,
            "WdfPostDeviceInstall"
            );
        ASSERT(NULL != m_pfnWdfPostDeviceInstall);
        m_pfnWdfPreDeviceInstallEx = (PFN_WDFPREDEVICEINSTALLEX)GetProcAddress(
            m_hCoInatallerModule,
            "WdfPreDeviceInstallEx"
            );
        ASSERT(NULL != m_pfnWdfPreDeviceInstallEx);
        m_pfnWdfPreDeviceRemove = (PFN_WDFPREDEVICEREMOVE)GetProcAddress(
            m_hCoInatallerModule,
            "WdfPreDeviceRemove"
            );
        ASSERT(NULL != m_pfnWdfPreDeviceRemove);
        m_pfnWdfPostDeviceRemove = (PFN_WDFPOSTDEVICEREMOVE)GetProcAddress(
            m_hCoInatallerModule,
            "WdfPostDeviceRemove"
            );
        ASSERT(NULL != m_pfnWdfPostDeviceRemove);
        //
        // PRE-REMOVE of WDF support
        //
        ulRetCode = m_pfnWdfPreDeviceRemove(
            m_sInfFilePath,
            WDF_SECTION_NAME
            );
        if (ERROR_SUCCESS != ulRetCode)
        {
            dwMsgLength = ::FormatMessage(
                FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                NULL,
                ulRetCode,
                MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
                (LPTSTR)&lpMsgBuf,
                0,
                NULL
                );
            if (dwMsgLength == 0)
            {
                sErrorMessage.Format(
                    _T("Ошибка вызова функции \"FormatMessage\"! Код ошибки = %u."),
                    ::GetLastError()
                    );
            }
            else
            {
                sErrorMessage = (LPCTSTR)lpMsgBuf;
                ::LocalFree((HLOCAL)lpMsgBuf);
            }
            sMessageText.Format(
                _T("\
Ошибка вызова функции \"WdfPreDeviceRemove\"!!\n\
\tКод ошибки = %d (0x%08X): %s\n\
"),
                ulRetCode,
                ulRetCode,
                (LPCTSTR)sErrorMessage
                );
            bRet = FreeLibrary(m_hCoInatallerModule);
            SetupCloseInfFile(hInfHandle);
            bRet = SetupDiDestroyDriverInfoList(
                hDevsInfoSet,
                &stDevInfoData,
                SPDIT_COMPATDRIVER
                );
            bRet = SetupDiDeleteDeviceInfo(
                hDevsInfoSet,
                &stDevInfoData
                );
            bRet = SetupDiDestroyDeviceInfoList(hDevsInfoSet);
            bRet = CloseServiceHandle(schSCManager);
            AfxMessageBox(
                sMessageText,
                MB_OK | MB_ICONEXCLAMATION
                );
            m_nRetCode = ulRetCode;
            return ulRetCode;
        }
        // деинсталляция сервиса драйвера
        schService = OpenService(
            schSCManager,
            DRIVER_SERVICE_NAME,
            SERVICE_ALL_ACCESS
            );
        if (NULL != schService)
        {
            bRet = CloseServiceHandle(schService);
            bRet = SetupInstallServicesFromInfSection(
                hInfHandle,
                SERVICE_DELETE_SECTION_NAME,
                SPSVCINST_STOPSERVICE
                );
            dwInstallRet = GetLastError();
            if (ERROR_SUCCESS_REBOOT_REQUIRED == dwInstallRet)
            {
                m_enRebootPurpose = enDeleteDriverService;
                m_bRebootNeed = true;
                bRet = WriteProfileInt(
                    _T("RebootParameters"),
                    _T("dwRebootPurpose"),
                    m_enRebootPurpose
                    );
                ASSERT(bRet);
                bRet = WriteProfileString(
                    _T("RebootParameters"),
                    _T("sRebootPurposeDescription"),
                    _T("\
Удаление сервиса драйвера \"dnsfirewall.sys\" потребовало перезагрузки системы.\
")
);
                ASSERT(bRet);
            }
            else if (ERROR_SUCCESS != dwInstallRet)
            {
                dwErrorCode = dwInstallRet;
                dwMsgLength = ::FormatMessage(
                    FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                    NULL,
                    HRESULT_FROM_SETUPAPI(dwErrorCode),
                    MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
                    (LPTSTR)&lpMsgBuf,
                    0,
                    NULL
                    );
                if (dwMsgLength == 0)
                {
                    sErrorMessage.Format(
                        _T("Ошибка вызова функции \"FormatMessage\"! Код ошибки = %u."),
                        ::GetLastError()
                        );
                }
                else
                {
                    sErrorMessage = (LPCTSTR)lpMsgBuf;
                    ::LocalFree((HLOCAL)lpMsgBuf);
                }
                sMessageText.Format(
                    _T("\
Ошибка удаления сервиса драйвера!!\n\
\tКод ошибки = %d (0x%08X): %s\n\
Потребуется перезагрука компьютера.\n\
"),
                    dwErrorCode,
                    dwErrorCode,
                    (LPCTSTR)sErrorMessage
                    );
                m_enRebootPurpose = enDeleteDriverService;
                AfxMessageBox(
                    sMessageText,
                    MB_OK | MB_ICONEXCLAMATION
                    );
            }
        }
        // удаление узлового ключа драйвера
        mfDeleteKey(
            HKEY_LOCAL_MACHINE,
            DEVICE_KEY
            );
        bRet = SetupDiDestroyDriverInfoList(
            hDevsInfoSet,
            &stDevInfoData,
            SPDIT_COMPATDRIVER
            );
        bRet = SetupDiDeleteDeviceInfo(
            hDevsInfoSet,
            &stDevInfoData
            );
        bRet = SetupDiDestroyDeviceInfoList(hDevsInfoSet);
        bRet = CloseServiceHandle(schSCManager);
        //
        // POST-REMOVE of WDF support
        //
        ulRetCode = m_pfnWdfPostDeviceRemove(
            m_sInfFilePath,
            WDF_SECTION_NAME
            );
        ASSERT(ERROR_SUCCESS == ulRetCode);
        bRet = FreeLibrary(m_hCoInatallerModule);
        // удаление файла драйвера
        hFileQueue = SetupOpenFileQueue();
        if (INVALID_HANDLE_VALUE == hFileQueue)
        {
            ulRetCode = ::GetLastError();
            dwMsgLength = ::FormatMessage(
                FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                NULL,
                HRESULT_FROM_SETUPAPI(ulRetCode),
                MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                (LPTSTR)&lpMsgBuf,
                0,
                NULL
                );
            if (dwMsgLength == 0)
            {
                sErrorMessage.Format(
                    _T("Ошибка вызова функции \"FormatMessage\"! Код ошибки = %u."),
                    ::GetLastError()
                    );
            }
            else
            {
                sErrorMessage = (LPCTSTR)lpMsgBuf;
                ::LocalFree((HLOCAL)lpMsgBuf);
            }
            sMessageText.Format(
                _T("\
Ошибка открытия очереди устанавливаемых файлов!!\n\
\tКод ошибки = %d (0x%08X): %s\n\
Файл драйвера не удалён из папки драйверов!\n\
Можете удалить его вручную позже.\n\
"),
                ulRetCode,
                ulRetCode,
                (LPCTSTR)sErrorMessage
                );
            SetupCloseInfFile(hInfHandle);
            AfxMessageBox(
                sMessageText,
                MB_OK | MB_ICONEXCLAMATION
                );
            m_nRetCode = ulRetCode;
            return ulRetCode;
        }
        bRet = SetupQueueDeleteSection(
            hFileQueue,
            hInfHandle,
            NULL,
            _T("Drivers_Dir")
            );
        if (!bRet)
        {
            ulRetCode = ::GetLastError();
            dwMsgLength = ::FormatMessage(
                FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                NULL,
                HRESULT_FROM_SETUPAPI(ulRetCode),
                MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                (LPTSTR)&lpMsgBuf,
                0,
                NULL
                );
            if (dwMsgLength == 0)
            {
                sErrorMessage.Format(
                    _T("Ошибка вызова функции \"FormatMessage\"! Код ошибки = %u."),
                    ::GetLastError()
                    );
            }
            else
            {
                sErrorMessage = (LPCTSTR)lpMsgBuf;
                ::LocalFree((HLOCAL)lpMsgBuf);
            }
            sMessageText.Format(
                _T("\
Ошибка удаления секции [Drivers_Dir]!!\n\
\tКод ошибки = %d (0x%08X): %s\n\
Файл драйвера не удалён из папки драйверов!\n\
Можете удалить его вручную позже.\n\
"),
                ulRetCode,
                ulRetCode,
                (LPCTSTR)sErrorMessage
                );
            m_bRebootNeed = true;
            m_enRebootPurpose = enDeleteDriverFile;
            AfxMessageBox(
                sMessageText,
                MB_OK | MB_ICONEXCLAMATION
                );
        }
        else
        {
            pDefaultCBContext = SetupInitDefaultQueueCallback(NULL);
            ASSERT(NULL != pDefaultCBContext);
            bRet = SetupCommitFileQueue(
                NULL,
                hFileQueue,
                SetupDefaultQueueCallback,
                pDefaultCBContext
                );
            if (!bRet)
            {
                ulRetCode = ::GetLastError();
                dwMsgLength = ::FormatMessage(
                    FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                    NULL,
                    HRESULT_FROM_SETUPAPI(ulRetCode),
                    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                    (LPTSTR)&lpMsgBuf,
                    0,
                    NULL
                    );
                if (dwMsgLength == 0)
                {
                    sErrorMessage.Format(
                        _T("Ошибка вызова функции \"FormatMessage\"! Код ошибки = %u."),
                        ::GetLastError()
                        );
                }
                else
                {
                    sErrorMessage = (LPCTSTR)lpMsgBuf;
                    ::LocalFree((HLOCAL)lpMsgBuf);
                }
                sMessageText.Format(
                    _T("\
Ошибка подтверждения очереди файлов!!\n\
\tКод ошибки = %d (0x%08X): %s\n\
Файл драйвера не удалён из папки драйверов!\n\
Можете удалить его вручную позже.\n\
"),
                    ulRetCode,
                    ulRetCode,
                    (LPCTSTR)sErrorMessage
                    );
                m_bRebootNeed = true;
                m_enRebootPurpose = enDeleteDriverFile;
                AfxMessageBox(
                    sMessageText,
                    MB_OK | MB_ICONEXCLAMATION
                    );
            }
            SetupTermDefaultQueueCallback(pDefaultCBContext);
        }
        SetupCloseFileQueue(hFileQueue);
        SetupCloseInfFile(hInfHandle);
        // удаление пакета драйвера из хранилища драйверов
        bRet = SetupUninstallOEMInf(
            sOemInfName,
            SUOI_FORCEDELETE,
            NULL
            );
        if (!bRet)
        {
            ulRetCode = ::GetLastError();
            dwMsgLength = ::FormatMessage(
                FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                NULL,
                HRESULT_FROM_SETUPAPI(ulRetCode),
                MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                (LPTSTR)&lpMsgBuf,
                0,
                NULL
                );
            if (dwMsgLength == 0)
            {
                sErrorMessage.Format(
                    _T("Ошибка вызова функции \"FormatMessage\"! Код ошибки = %u."),
                    ::GetLastError()
                    );
            }
            else
            {
                sErrorMessage = (LPCTSTR)lpMsgBuf;
                ::LocalFree((HLOCAL)lpMsgBuf);
            }
            sMessageText.Format(
                _T("\
Ошибка удаление пакета драйвера из хранилища драйверов!\n\
\tКод ошибки = %d (0x%08X): %s\n\
Пакет драйвера не удалён из хранилища драйверов!\n\
Можете удалить его вручную позже.\n\
"),
                ulRetCode,
                ulRetCode,
                (LPCTSTR)sErrorMessage
                );
            AfxMessageBox(
                sMessageText,
                MB_OK | MB_ICONEXCLAMATION
                );
        }
    }
    if (m_bRebootNeed)
    {
        INT_PTR             nDlgResult;
        CWnd*               pParentWindow = NULL;
        if (NULL != m_hParentWindow)
        {
            pParentWindow = new CWnd;
            bRet = pParentWindow->Attach(m_hParentWindow);
        }
        CRebootPromptDlg    oRebootPromptDlg(pParentWindow);
        switch (m_enRebootPurpose)
        {
        case enStopPluginService:
            oRebootPromptDlg.m_sRebootPurposeText = _T("\
Попытка остановки сервиса подмены DNS-пакетов привела к необходимости перезагрузки компьютера. \
");
            oRebootPromptDlg.m_sRebootPromptText = _T("\
Если нажмёте кнопку \"Перезагрузка\", то перезагрузка компьютера будет запланирована \
на окончание деинсталляции всего пакета. \
Иначе, если нажмёте кнопку \"Отмена\", то перезагрузку нужно произвести \
после деинсталляции всего пакета вручную.\
");
            break;
        case enDeletePluginService:
            oRebootPromptDlg.m_sRebootPurposeText = _T("\
Попытка удаления сервиса подмены DNS-пакетов привела к необходимости перезагрузки компьютера. \
");
            oRebootPromptDlg.m_sRebootPromptText = _T("\
Если нажмёте кнопку \"Перезагрузка\", то перезагрузка компьютера будет запланирована \
на окончание деинсталляции всего пакета. \
Иначе, если нажмёте кнопку \"Отмена\", то перезагрузку нужно произвести \
после деинсталляции всего пакета вручную.\
");
            break;
        case enStopDriverService:
            oRebootPromptDlg.m_sRebootPurposeText = _T("\
Попытка остановки сервиса драйвера привела к необходимости перезагрузки компьютера. \
");
            oRebootPromptDlg.m_sRebootPromptText = _T("\
Если нажмёте кнопку \"Перезагрузка\", то перезагрузка компьютера будет запланирована \
на окончание деинсталляции всего пакета. \
Иначе, если нажмёте кнопку \"Отмена\", то перезагрузку нужно произвести \
после деинсталляции всего пакета вручную.\
");
            break;
        case enDeleteDriverService:
            oRebootPromptDlg.m_sRebootPurposeText = _T("\
Попытка удаления сервиса драйвера привела к необходимости перезагрузки компьютера. \
");
            oRebootPromptDlg.m_sRebootPromptText = _T("\
Если нажмёте кнопку \"Перезагрузка\", то перезагрузка компьютера будет запланирована \
на окончание деинсталляции всего пакета. \
Иначе, если нажмёте кнопку \"Отмена\", то перезагрузку нужно произвести \
после деинсталляции всего пакета вручную.\
");
            break;
        case enDeleteDriverFile:
            oRebootPromptDlg.m_sRebootPurposeText = _T("\
Попытка удаления файла драйвера привела к необходимости перезагрузки компьютера. \
");
            oRebootPromptDlg.m_sRebootPromptText = _T("\
Если нажмёте кнопку \"Перезагрузка\", то перезагрузка компьютера будет запланирована \
на окончание деинсталляции всего пакета. \
Иначе, если нажмёте кнопку \"Отмена\", то перезагрузку нужно произвести \
после деинсталляции всего пакета вручную.\
");
            break;
        default:
            ASSERT(FALSE);
            return uiRetCode;
        }
        nDlgResult = oRebootPromptDlg.DoModal();
        if (IDOK == nDlgResult)
        {
            CString                 sCommandLine, sSourceFileName, sTargetFileName;
            STARTUPINFO             stStartupInfo;
            PROCESS_INFORMATION     stProcessInformation;
            sSourceFileName  = m_sModuleFolder;
            sSourceFileName += _T("\\WaitForReboot.exe");
            sTargetFileName  = _tgetenv(_T("TEMP"));
            sTargetFileName += _T("\\WaitForReboot.exe");
            bRet = CopyFile(
                sSourceFileName,
                sTargetFileName,
                FALSE
                );
            ASSERT(bRet);
            sCommandLine.Format(
                _T("%s %u"),
                (LPCTSTR) sTargetFileName,
                m_dwProcessID
                );
            RtlZeroMemory(
                &stStartupInfo,
                sizeof(stStartupInfo)
                );
            stStartupInfo.cb = sizeof(stStartupInfo);
            RtlZeroMemory(
                &stProcessInformation,
                sizeof(stProcessInformation)
                );
            bRet = CreateProcess(
                NULL,
                LPTSTR(LPCTSTR(sCommandLine)),
                NULL,
                NULL,
                FALSE,
                0,
                NULL,
                NULL,
                &stStartupInfo,
                &stProcessInformation
                );
            ASSERT(bRet);
            bRet = CloseHandle(stProcessInformation.hThread);
            bRet = CloseHandle(stProcessInformation.hProcess);
        }
        if (NULL != pParentWindow)
        {
            pParentWindow->Detach();
            delete pParentWindow;
        }
    }
    return uiRetCode;
}
