INNLEDNING
Når en lagringsenhet er koblet til Windows, selv om det bare er kortvarig, oppretter Windows registerinformasjon for enheten. Over tid kan registret inneholde mange oppføringer for enheter som aldri kommer til å bli brukt igjen. Denne artikkelen beskriver hvordan du fjerner denne informasjonen fra systemregisteret.
Det er ansvaret til programvaren som oppretter forbindelsen mellom lagringsenheten og Windows, å rydde opp i informasjonen for enheten på riktig måte. Denne prosessen er nødvendig fordi Windows ikke vet når en lagringsenhet fjernes midlertidig eller permanent. Men programvaren som etablerer tilkoblingen, vet vanligvis dette. Hvis programvaren for sikkerhetskopiering for eksempel monterer logiske enhetsnumre (LUN-er) for sikkerhetskopieringsformål og deretter demonterer LUN-ene, vil det være sikkerhetskopieringsprogramvarens ansvar å rydde opp i LUN-informasjonen fra Windows, fordi den samme lagringsenheten ikke lenger brukes igjen av Windows.
Mer informasjon
Når en ny enhet kobles til en datamaskin, registrerer Windows informasjon om enheten i systemregisteret. Denne prosedyren utgjør ikke et problem for de fleste enheter. Etter at en lagringsenhet presenteres av et LUN gjennom en fiberkanal eller gjennom iSCSI, kan det imidlertid hende at enheten aldri oppdages igjen av datamaskinen. En enhet kan for eksempel identifiseres med et serienummer eller SCSI-sider 0x80 og 0x83.
I denne situasjonen kan registret inneholde oppføringer for enheter som kanskje aldri vises igjen. Ikke bare tar disse oppføringene plass i registret, disse oppføringene kan etter hvert forårsake driftsproblemer. Fordi for eksempel indekser for Plug and Play-funksjonalitet bruker firesifrede desimalverdier, kan det oppstå et problem når enhet 10 001 er tilkoblet.
Hvis du vil løse denne begrensningen i Plug and Play-funksjonaliteten, kan du fjerne enhetsinformasjon fra registret når enheten er en harddisk som ikke lenger finnes. Du kan gjøre dette ved hjelp av Microsoft DevNodeClean-verktøyet .
Slik bygger du for Windows Server 2003, Windows Server 2008, Windows Server 2008 R2 og Visual Studio 2005
Følg disse trinnene for å rense registeret for GUID-en for GUID_DEVCLASS_DISKDRIVE diskklasse og for GUID-en for GUID_DEVCLASS_VOLUME diskklasse.
Obs! Vi anbefaler at du bruker DevNodeClean-verktøyet for denne oppgaven. De følgende trinnene og kodeeksemplet i trinn 7 er bare ment å være til informasjon.
- Kall SetupDiGetClassDevs-funksjonen for å hente informasjon for klassen som er knyttet til GUID-en.
- Kall opp SetupDiEnumDeviceInfo-funksjonen for å hente forekomstinformasjon for hver enhet i gjeldende klasse.
- Kall CM_Get_DevNode_Status-funksjonen for å se om den gjeldende enhetsinformasjonen representerer en fraværende enhet. Avgjør om funksjonsstatusen er lik CR_NO_SUCH_DEVINST eller CR_NO_SUCH_VALUE.
- Hvis enheten mangler, kan du eventuelt kalle CM_Get_Device_ID-funksjonen for å hente enhetsforekomst-ID-en og vise ID-en før du fjerner informasjonen.
- For den fraværende enheten bruker du klasseinformasjonen som du innhentet i trinn 1, og forekomstinformasjonen som du fikk i trinn 2. Kall opp SetupDiCallClassInstaller(DIF_REMOVE, ...)-funksjonen for å fjerne informasjonen fra registret.
- Når alle enhetene i gjeldende klasse er håndtert, kaller du SetupDiDestroyDeviceInfoList-funksjonen for å rydde opp.
Obs! I enkelte scenarier må du kanskje rense registeret, ikke bare for GUID-er for GUID_DEVCLASS_DISKDRIVE og GUID_DEVCLASS_VOLUME diskklasse, men også for GUID-er for GUID_DEVCLASS_SCSIADAPTER og GUID_DEVCLASS_VOLUMESNAPSHOT diskklasse. Hvis du vil gjøre dette, må du endre DiskClassesToClean-definisjonen i følgende eksempelkode.
Følgende Win32-konsollprogram er et eksempel på et program som renser registeret. Følg disse trinnene for å bruke dette programmet.
Microsoft tilbyr programmeringseksempler bare for illustrasjon, uten garanti enten uttrykt eller underforstått. Dette omfatter, men er ikke begrenset til, de underforståtte garantiene om salgbarhet eller egnethet for et bestemt formål. Denne artikkelen antar at du er kjent med programmeringsspråket som demonstreres, og med verktøyene som brukes til å opprette og feilsøke prosedyrer. Microsofts kundestøtteteknikere kan bidra til å forklare funksjonaliteten til en bestemt fremgangsmåte. De vil imidlertid ikke endre disse eksemplene for å gi ekstra funksjonalitet eller konstruere fremgangsmåter for å oppfylle dine spesifikke krav.
Klikk Ny påFil-menyen i Microsoft Visual Studio 2005, og klikk deretter Prosjekt.
Utvid Visual C++, og klikk deretter Win32.
Klikk på Win32-konsollprogram, skriv inn Opprydding i navnetekstboksen, og klikk deretter på OK.
Klikk Fullfør i dialogboksen i Win32-programveiviseren .
Utvid Kilde-Files i Løsningsutforsker, høyreklikk Cleanup.cpp, og klikk deretter Vis kode.
Finn følgende kode:
int _tmain(int argc, _TCHAR* argv[]) { return 0; }Erstatt koden du fant i trinn 6 med følgende kode.
/**************************************************************************************************/ /* */ /* 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; }Klikk på Feilsøking-menyen , og klikk deretter på Start feilsøking.
Slik bygger du for Windows Server 2012 og Visual Studio 2012
Følg denne fremgangsmåten for å bygge for Windows Server 2012 og Microsoft Visual Studio 2012.
Obs! Vi anbefaler at du bruker DevNodeClean-verktøyet for denne oppgaven. De følgende trinnene og kodeeksemplet i trinn 7 er bare ment å være til informasjon.
Klikk Ny påFil-menyen i Microsoft Visual Studio 2012, og klikk deretter Prosjekt.
Skriv inn Opprydding i Navn-feltet i dialogboksen Nytt prosjekt, og dobbeltklikk deretter Win32-prosjekt.
Klikk Neste i Win32-programveiviseren.
Klikk for å velge Konsollprogram under Programtype, og klikk deretter Fullfør.
Utvid Kilde-Files i Løsningsutforsker, høyreklikk Cleanup.cpp, og klikk deretter Vis kode.
Finn følgende kode:
int _tmain(int argc, _TCHAR* argv[]) { return 0; }Erstatt koden du fant i trinn 6 med følgende kode.
//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; }Høyreklikk Opprydding i Løsningsutforsker, og klikk deretter Egenskaper.
Utvid Konfigurasjonsegenskaper, utvid Kobling, og klikk deretter Inndata.
Velg Flere avhengigheter, klikk pil ned, og velg deretter Rediger.
Skriv inn setupapi.lib og cfgmgr32.lib i dialogboksen Flere avhengigheter .
Klikk OK to ganger.
Bygge prosjektet.