二度と使用されないデバイスのレジストリ情報を削除する方法

適用先
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 によって再度使用されなくなるため、バックアップ ソフトウェアは Windows から LUN 情報をクリーンする必要があります。

追加情報

新しいデバイスがコンピューターに接続されると、Windows はデバイスに関する情報をシステム レジストリに記録します。 ほとんどのデバイスでは、この手順で問題は発生しません。 ただし、ストレージ デバイスがファイバー チャネルまたは iSCSI を介して LUN によって提示された後、デバイスがコンピューターによって再び検出されることはありません。 たとえば、デバイスはシリアル番号または SCSI ページ0x80および0x83によって識別される場合があります。

このような状況では、レジストリには、再び表示されない可能性があるデバイスのエントリが含まれている可能性があります。 これらのエントリはレジストリ内の領域を占有するだけでなく、最終的には操作上の問題を引き起こす可能性があります。 たとえば、プラグ アンド プレイ機能のインデックスでは 4 桁の 10 進値が使用されるため、デバイス 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 コンソール アプリケーション] をクリックし、[ 名前 ] テキスト ボックスに「クリーンアップ」と入力し、[OK] をクリック します

  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. [OK] を 2 回クリックします。

  13. プロジェクトをビルドします。