如何删除永远不会再次使用的设备的注册表信息

应用对象
Windows Server 2008 Datacenter Windows Server 2008 Datacenter without Hyper-V Windows Server 2008 Enterprise Windows Server 2008 Enterprise without Hyper-V Windows Server 2008 for Itanium-Based Systems Windows Server 2008 Standard Windows Server 2008 Standard without Hyper-V Windows Server 2008 R2 Datacenter Windows Server 2008 R2 Enterprise Windows Server 2008 R2 Standard Windows Server 2012 Standard Windows Server 2012 Standard Windows Server 2012 Datacenter Windows Server 2012 Datacenter Windows Server 2016, all editions Windows Server 2016 Standard Windows Server 2016 Essentials Windows Server 2016 Windows Server 2016 Standard edition Nano Server installation option Windows Server 2016 Datacenter edition Nano Server installation option

简介

当存储设备连接到 Windows 时,即使只是短暂的,Windows 也会为设备创建注册表信息。 随着时间的推移,注册表可能包含许多永远不会再次使用的设备的条目。 本文介绍如何从系统注册表中删除此信息。

软件负责在存储设备和 Windows 之间建立连接,以正确清理设备的信息。 此过程是必需的,因为 Windows 不知道何时暂时或永久删除存储设备。 但建立连接的软件通常知道这一点。 例如,如果备份软件装载逻辑单元号 (LUN) 进行备份,然后卸载 LUN,则备份软件将负责清理 Windows 中的 LUN 信息,因为 Windows 将不再使用相同的存储设备。

详细信息

当新设备连接到计算机时,Windows 在系统注册表中记录有关设备的信息。 对于大多数设备,此过程不会造成问题。 但是,在 LUN 通过光纤通道或 iSCSI 提供存储设备后,计算机可能再也不会遇到该设备。 例如,设备可能由序列号或 SCSI 页0x80和0x83标识。

在这种情况下,注册表可能包含可能永远不会再次出现的设备的条目。 这些条目不仅占用注册表中的空间,而且最终可能会导致操作问题。 例如,由于即插即用功能的索引使用四位数的十进制值,因此连接设备 10,001 时可能会出现问题。

若要解决即插即用功能中的此限制,可能需要在设备是不再存在的硬盘驱动器时从注册表中删除设备信息。 可以使用 Microsoft DevNodeClean 实用工具执行此操作。

如何针对 Windows Server 2003、Windows Server 2008、Windows Server 2008 R2 和 Visual Studio 2005 生成

若要清理GUID_DEVCLASS_DISKDRIVE磁盘类 GUID 和 GUID_DEVCLASS_VOLUME 磁盘类 GUID 的注册表,请执行以下步骤。

注意 建议对此任务使用 DevNodeClean 实用工具。 以下步骤和步骤 7 中的代码示例仅供参考。

  1. 调用 SetupDiGetClassDevs 函数以获取与 GUID 关联的类的信息。
  2. 调用 SetupDiEnumDeviceInfo 函数以获取当前类中每个设备的实例信息。
  3. 调用 CM_Get_DevNode_Status 函数以查看当前设备信息是否表示不存在的设备。 确定函数状态是等于CR_NO_SUCH_DEVINST还是CR_NO_SUCH_VALUE。
  4. (可选)对于不存在的设备,调用 CM_Get_Device_ID 函数以获取设备实例 ID 并在删除信息之前显示 ID。
  5. 对于不存在的设备,请使用在步骤 1 中获取的类信息和在步骤 2 中获取的实例信息。 调用 SetupDiCallClassInstaller (DIF_REMOVE, ...) 函数以从注册表中删除信息。
  6. 处理当前类中的所有设备后,调用 SetupDiDestroyDeviceInfoList 函数进行清理。

注意 在某些情况下,可能不仅需要清理GUID_DEVCLASS_DISKDRIVE和GUID_DEVCLASS_VOLUME磁盘类 GUID 的注册表,还需要清理GUID_DEVCLASS_SCSIADAPTER和GUID_DEVCLASS_VOLUMESNAPSHOT磁盘类 GUID 的注册表。 为此,必须更改以下示例代码中的 DiskClassesToClean 定义。

以下 Win32 控制台应用程序是清理注册表的应用程序的示例。 若要使用此应用程序,请执行以下步骤。

Microsoft 的编程示例仅用于说明,不做任何明示或暗示的保证。 这包括但不限于特定用途的适销性或适用性的隐含保证。 本文假定你熟悉所演示的编程语言以及用于创建和调试过程的工具。 Microsoft 支持工程师可以帮助解释特定过程的功能。 但他们不会修改这些示例以提供附加功能或构造满足你的特定要求的过程。 

  1. 在 Microsoft Visual Studio 2005 中,单击“文件”菜单上的“新建”,然后单击“项目”。

  2. 展开 “Visual C++”,然后单击“ Win32”。

  3. 单击“ Win32 控制台应用程序”,在 “名称 ”文本框中键入“清理”,然后单击“ 确定”。

  4. “Win32 应用程序向导”对话框中单击“完成”。

  5. 在“解决方案资源管理器”中,展开“源Files”,右键单击“Cleanup.cpp”,然后单击“查看代码”。

  6. 找到以下代码:

    int _tmain(int argc, _TCHAR* argv[])
    {
    return 0;
    }
    
    
  7. 将步骤 6 中找到的代码替换为以下代码。

    /**************************************************************************************************/     
    /*                                                                                                */     
    /* Copyright (c) 2007 Microsoft Corporation.  All Rights Reserved                                 */     
    /*                                                                                                */     
    /**************************************************************************************************/     
    
    #pragma warning( disable : 4201 ) // nonstandard extension used : nameless strut/union
    
    #include <windows.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <stddef.h>
    #include <tchar.h>
    #include <setupapi.h>
    #include <cfgmgr32.h>
    #include <initguid.h>
    #include <devguid.h>
    
    #define SIZECHARS(x) (sizeof((x))/sizeof(TCHAR))
    #define arraysize(p) (sizeof(p)/sizeof((p)[0]))
    
    CONST GUID *DiskClassesToClean[2] = {
        &GUID_DEVCLASS_DISKDRIVE,
        &GUID_DEVCLASS_VOLUME
    };
    
    /**************************************************************************************************/
    /*                                                                                                */
    /* The user must be member of Administrator group and must have backup and restore permissions         */
    /* (SE_BACKUP_NAME and SE_RESTORE_NAME). No check for these is performed in this example.              */
    /*                                                                                                */
    /**************************************************************************************************/
    int
    __cdecl
    main(
         IN int    ArgC,
         IN char * pArgV[]
        )
    {
        HDEVINFO DeviceInfoSet;
        SP_DEVINFO_DATA DeviceInfoData;
        ULONG DevicesRemoved = 0,
              i,
              MemberIndex,
              Status, 
              Problem,
              ulClassesToCleanIdx;
        BOOL bDoRemove = TRUE;
        CONFIGRET cr;
        TCHAR DeviceInstanceId[MAX_DEVICE_ID_LEN];
        OSVERSIONINFO osvi;
        const GUID ** ClassesToClean;
    
        //
        // Parse parameters.
        //
        for (i = 1; i < (ULONG)ArgC; i++) {
            //
            // Check for help.
            //
            if ( (lstrcmpi(pArgV[i], TEXT("-?")) == 0) ||
                    (lstrcmpi(pArgV[i], TEXT("/?")) == 0) ){
    
                printf("\nCleanUp will remove phantom storage device nodes from this machine.\n\n");
                printf("Usage:  CleanUp \n");
                printf("\twhere /n displays but does not remove the phantom devnodes.\n");
                printf("\nBackup and Restore privileges are required to run this utility.\n");
                return 0;
            }
    
            //
            // Check for -n, which means just list the devices that we would remove.
            //
            if ( (lstrcmpi(pArgV[i], TEXT("-n")) == 0) ||
                 (lstrcmpi(pArgV[i], TEXT("/n")) == 0) ) {
                bDoRemove = FALSE;
            }
        }
    
        //
        // Run only on Windows XP/2003 (version 5.1) or later.
        //
    
        ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
        osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
    
        if (!GetVersionEx(&osvi)) {
            printf("CleanUp:  Unable to verify Windows version, exiting...\n");
            return -1;
        }
    
        if ((osvi.dwMajorVersion == 5) &&
            ((osvi.dwMinorVersion == 1) || (osvi.dwMinorVersion == 2))) {
        }
        else if (osvi.dwMajorVersion>=6) {
        }
        else
        {
            printf("CleanUp:  This utility is  designed to run on Windows XP/2003 and later\n");
            return -1;
        }
    
        ClassesToClean = DiskClassesToClean;
        ulClassesToCleanIdx = arraysize(DiskClassesToClean);
    
        for (i=0; (i<ulClassesToCleanIdx) && (bDoRemove); i++) {
    
            DeviceInfoSet = SetupDiGetClassDevs(ClassesToClean[i],
                                                NULL,
                                                NULL,
                                                0
                                                );
    
            if (INVALID_HANDLE_VALUE!=DeviceInfoSet) {
    
                DeviceInfoData.cbSize = sizeof(DeviceInfoData);
                MemberIndex = 0;
    
                while (SetupDiEnumDeviceInfo(DeviceInfoSet,
                                             MemberIndex++,
                                             &DeviceInfoData
                                             )) {
    
                    //
                    // Determine whether this device is a phantom.
                    //
                    cr = CM_Get_DevNode_Status(&Status,
                                               &Problem,
                                               DeviceInfoData.DevInst,
                                               0
                                               );
    
                    if ((cr == CR_NO_SUCH_DEVINST) ||
                        (cr == CR_NO_SUCH_VALUE)) {
                        //
                        // This is a phantom. Now get the DeviceInstanceId so we
                        // can display this as output, then delete the phantom if requested.
                        //
                        if (CM_Get_Device_ID(DeviceInfoData.DevInst,
                                             DeviceInstanceId,
                                             SIZECHARS(DeviceInstanceId),
                                             0) == CR_SUCCESS) {
    
                            if (bDoRemove) {
                                printf("DevNodePhantomCleaner:  %s will be removed.\n",
                                       DeviceInstanceId);
    
                                //
                                // Call DIF_REMOVE to remove the device's hardware
                                // and software registry keys.
                                //
                                if (SetupDiCallClassInstaller(DIF_REMOVE,
                                                              DeviceInfoSet,
                                                              &DeviceInfoData
                                                              )) {
                                    DevicesRemoved++;
                                } else {
                                    printf("CleanUp:  Error 0x%X removing phantom\n",
                                           GetLastError);
                                }
                            } else {
                                printf("CleanUp:  %s would have been removed.\n",
                                       DeviceInstanceId);
                            }
                        }
                    }
                }
    
                SetupDiDestroyDeviceInfoList(DeviceInfoSet);
            }
        }
    
        return DevicesRemoved;
    }
    
  8. 单击“ 调试 ”菜单,然后单击“ 开始调试”。

如何为 Windows Server 2012 和 Visual Studio 2012 生成

若要针对 Windows Server 2012 生成并Microsoft Visual Studio 2012,请执行以下步骤。

注意 建议对此任务使用 DevNodeClean 实用工具。 以下步骤和步骤 7 中的代码示例仅供参考。

  1. 在 Microsoft Visual Studio 2012 中,单击“文件”菜单上的“新建”,然后单击“项目”。

  2. 在“ 新建项目 ”对话框中,在“ 名称 ”字段中键入“清理”,然后双击“ Win32 项目”。

  3. 在 Win32 应用程序向导中,单击“ 下一步”。

  4. 在“ 应用程序类型”下,单击以选择“ 控制台应用程序”,然后单击“ 完成”。

  5. 在“解决方案资源管理器”中,展开“源Files”,右键单击“Cleanup.cpp”,然后单击“查看代码”。

  6. 找到以下代码:

    int _tmain(int argc, _TCHAR* argv[])
    {
    return 0;
    }
    
    
  7. 将步骤 6 中找到的代码替换为以下代码。

    //DevPhantomClnr.cpp : Defines the entry point for the console application.
    // 
    
    #include "stdafx.h"
    
    /**************************************************************************************************/     
    /*                                                                                                */     
    /* Copyright (c) 2007 Microsoft Corporation.  All Rights Reserved                                 */     
    /*                                                                                                */     
    /**************************************************************************************************/     
    
    #pragma warning( disable : 4201 ) // nonstandard extension used : nameless strut/union
    
    #include <windows.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <stddef.h>
    #include <tchar.h>
    #include <setupapi.h>
    #include <cfgmgr32.h>
    #include <initguid.h>
    #include <devguid.h>
    
    #define SIZECHARS(x) (sizeof((x))/sizeof(TCHAR))
    #define arraysize(p) (sizeof(p)/sizeof((p)[0]))
    
    CONST GUID *DiskClassesToClean[2] = {
        &GUID_DEVCLASS_DISKDRIVE,
        &GUID_DEVCLASS_VOLUME
    };
    
    /**************************************************************************************************/
    /*                                                                                                */
    /* The user must be member of Administrator group and must have backup and restore permissions         */
    /* (SE_BACKUP_NAME and SE_RESTORE_NAME). No check for these is performed in this example.              */
    /*                                                                                                */
    /**************************************************************************************************/
    int
    __cdecl
    main(
         IN int    ArgC,
         IN LPCWSTR pArgV[]
        )
    {
        HDEVINFO DeviceInfoSet;
        SP_DEVINFO_DATA DeviceInfoData;
        ULONG DevicesRemoved = 0,
              i,
              MemberIndex,
              Status, 
              Problem,
              ulClassesToCleanIdx;
        BOOL bDoRemove = TRUE;
        CONFIGRET cr;
        TCHAR DeviceInstanceId[MAX_DEVICE_ID_LEN];
        OSVERSIONINFO osvi;
        const GUID ** ClassesToClean;
    
        //
        // Parse parameters.
        //
        for (i = 1; i < (ULONG)ArgC; i++) {
            //
            // Check for help.
            //
            if ( (lstrcmpi(pArgV[i], L"-?") == 0) ||
                    (lstrcmpi(pArgV[i], L"/?") == 0) ){
    
                printf("\nDevNodePhantomCleaner will remove phantom storage device nodes from this machine.\n\n");
                printf("Usage:  nDevNodePhantomCleaner \n");
                printf("\tWhere /n displays but does not remove the phantom devnodes.\n");
                printf("\nBackup and Restore privileges are required to run this utility.\n");
                return 0;
            }
    
            //
            // Check for -n, which means just list the devices that we would remove.
            //
            if ( (lstrcmpi(pArgV[i], L"-n") == 0) ||
                 (lstrcmpi(pArgV[i], L"/n") == 0) ) {
                bDoRemove = FALSE;
            }
        }
    
        //
        // Run only on Windows XP/2003 (version 5.1) or later.
        //
    
        ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
        osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
    
        if (!GetVersionEx(&osvi)) {
            printf("DevNodePhantomCleaner:  Unable to verify Windows version, exiting...\n");
            return -1;
        }
    
        if ((osvi.dwMajorVersion == 5) &&
            ((osvi.dwMinorVersion == 1) || (osvi.dwMinorVersion == 2))) {
    
        // 5.1 || 5.2
    
        }
    
        else if (osvi.dwMajorVersion>=6) {
    
        //Nothing special on 6.x
    
        }
    
        else
        {
            printf("DevNodePhantomCleaner:  This utility is  designed to run on Windows XP/2003 and later\n");
            return -1;
        }
    
        ClassesToClean = DiskClassesToClean;
        ulClassesToCleanIdx = arraysize(DiskClassesToClean);
    
        for (i=0; (i<ulClassesToCleanIdx) && (bDoRemove); i++) {
    
            DeviceInfoSet = SetupDiGetClassDevs(ClassesToClean[i],
                                                NULL,
                                                NULL,
                                                0
                                                );
    
            if (INVALID_HANDLE_VALUE!=DeviceInfoSet) {
    
                DeviceInfoData.cbSize = sizeof(DeviceInfoData);
                MemberIndex = 0;
    
                while (SetupDiEnumDeviceInfo(DeviceInfoSet,
                                             MemberIndex++,
                                             &DeviceInfoData
                                             )) {
    
                    //
                    // Determine whether this device is a phantom.
                    //
                    cr = CM_Get_DevNode_Status(&Status,
                                               &Problem,
                                               DeviceInfoData.DevInst,
                                               0
                                               );
    
                    if ((cr == CR_NO_SUCH_DEVINST) ||
                        (cr == CR_NO_SUCH_VALUE)) {
                        //
                        // This is a phantom. Now get the DeviceInstanceId so we
                        // can display this as output, then delete the phantom if requested.
                        //
                        if (CM_Get_Device_ID(DeviceInfoData.DevInst,
                                             DeviceInstanceId,
                                             SIZECHARS(DeviceInstanceId),
                                             0) == CR_SUCCESS) {
    
                            if (bDoRemove) {
                                printf("DevNodePhantomCleaner:  %ws will be removed.\n",
                                       DeviceInstanceId);
    
                                //
                                // Call DIF_REMOVE to remove the device's hardware
                                // and software registry keys.
                                //
                                if (SetupDiCallClassInstaller(DIF_REMOVE,
                                                              DeviceInfoSet,
                                                              &DeviceInfoData
                                                              )) {
                                    DevicesRemoved++;
                                } else {
                                    printf("DevNodePhantomCleaner:  Error 0x%x removing phantom\n",
                                           GetLastError());
                                }
                            } else {
                                printf("DevNodePhantomCleaner:  %ws would have been removed.\n",
                                       DeviceInstanceId);
                            }
                        }
                    }
                }
    
                SetupDiDestroyDeviceInfoList(DeviceInfoSet);
            }
        }
    
        return DevicesRemoved;
    }
    
  8. 在解决方案资源管理器中,右键单击“清理”,然后单击“属性”。

  9. 展开 “配置属性”,展开“ 链接器”,然后单击“ 输入”。

  10. 选择“ 其他依赖项”,单击向下箭头,然后选择“ 编辑”。

  11. 在“ 其他依赖项 ”对话框中,键入 setupapi.lib 和 cfgmgr32.lib。

  12. 单击“确定”两次。

  13. 生成项目。