http://support.microsoft.com/support/kb/articles/Q165/7/21.asp 165721 - HOWTO: Ejecting Removable Media in Windows NT/Windows ... HOWTO: Ejecting Removable Media in Windows NT/Windows 2000/Windows XP. http://support.microsoft.com/support/kb/articles/Q168/1/80.ASP HOWTO: Eject Removable Media on Windows 95/98/Me.
#include <stdio.h> #include <tchar.h> // Make all functions UNICODE safe. #include <windows.h> #include <setupapi.h> // for SetupDiXxx functions. int DisplayError(TCHAR * ErrorName) /*++ Routine Description: This Routine will display the LastError in human readable form when possible. If the return value is a 32-bit number, and falls in the range: ERROR_NO_ASSOCIATED_CLASS 0xE0000200 To ERROR_CANT_REMOVE_DEVINST 0xE0000232 The values defined in setupapi.h can help to determine the error. Start by searching for the text string ERROR_NO_ASSOCIATED_CLASS.
Arguments:
ErrorName: Human readable description of the last Function called.Return Value:
--*/ { HDEVINFO DeviceInfoSet; SP_DEVINFO_DATA DeviceInfoData; DWORD i,err; // // Verify the Arguments. // if (argc < 2) { _tprintf(TEXT("usage: remove <Hardware_ID>\n")); return 1; // Remove Failure } // // Create a Device Information Set with all present devices. // DeviceInfoSet = SetupDiGetClassDevs(NULL, // All Classes 0, 0, DIGCF_ALLCLASSES | DIGCF_PRESENT ); // All devices present on system if (DeviceInfoSet == INVALID_HANDLE_VALUE) { DisplayError(TEXT("GetClassDevs(All Present Devices)")); return 1; }
// // Enumerate through all Devices. // DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA); for (i=0;SetupDiEnumDeviceInfo(DeviceInfoSet,i,&DeviceInfoData);i++) { DWORD DataT; LPTSTR p,buffer = NULL; DWORD buffersize = 0;
// // We won't know the size of the HardwareID buffer until we call // this function. So call it with a null to begin with, and then // use the required buffer size to Alloc the nessicary space. // Keep calling we have success or an unknown failure. // while (!SetupDiGetDeviceRegistryProperty( DeviceInfoSet, &DeviceInfoData, SPDRP_HARDWAREID, &DataT, (PBYTE)buffer, buffersize, &buffersize)) { if (GetLastError() == ERROR_INVALID_DATA) { // // May be a Legacy Device with no HardwareID. Continue. // break; } else if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { // // We need to change the buffer size. // if (buffer) LocalFree(buffer); buffer = LocalAlloc(LPTR,buffersize); } else { // // Unknown Failure. // DisplayError(TEXT("GetDeviceRegistryProperty")); goto cleanup_DeviceInfo; } } if (GetLastError() == ERROR_INVALID_DATA) continue;
// // Compare each entry in the buffer multi-sz list with our HardwareID. // for (p=buffer;*p&&(p<&buffer[buffersize]);p+=lstrlen(p)+sizeof(TCHAR)) { _tprintf(TEXT("Compare device ID: [%s]\n"),p); if (!_tcscmp(argv[1],p)) { _tprintf(TEXT("Found! [%s]\n"),p); // // Worker function to remove device. // if (!SetupDiCallClassInstaller(DIF_REMOVE, DeviceInfoSet, &DeviceInfoData)) { DisplayError(TEXT("CallClassInstaller(REMOVE)")); } break; } } if (buffer) LocalFree(buffer); } if ((GetLastError()!=NO_ERROR)&&(GetLastError()!=ERROR_NO_MORE_ITEMS)) { DisplayError(TEXT("EnumDeviceInfo")); }
上面的程序在bcb下可以运行,但删除的是驱动器,即H:盘,但右下角的图标仍在,直接拔除依然会有提示。点击右下角图标时弹出的删除提示以前是3个设备,执行程序(Remove USBSTOR\GenDisk)后有两个,一个是USB Mass Storage Device,和以前一样,Generic Volume-(H:)和TGE USB Device不见了,变成了一个未知设备。有什么办法可以删除USB Mass Storage Device设备?使用Remove abcde,也就是不存在的设备可以得到一个列表,所有插上u盘后增加的设备不论删除哪个都是一个结果。
还有一个问题,在程序中 for (p=buffer;*p&&(p<&buffer[buffersize]);p+=lstrlen(p)+sizeof(TCHAR)) { if (!_tcscmp(argv[1],p)) 使用指针变量p以及输入参数来查找准备删除的设备,但在删除时 if (!SetupDiCallClassInstaller(DIF_REMOVE, DeviceInfoSet, &DeviceInfoData)) 却没有用到p,那系统是如何确认要删除的设备呢?或者说如何确认当前找到的设备呢? 设备号不一样时(即if!=0时),没有什么设备序号加一的操作吧? Top
能告诉我如何调用的吗?这个函数的第一个参数IN DEVINST dnAncestor应该写什么?我该如何得到这个“Caller-supplied device instance handle to the device at the root of the subtree to be removed”,按提示看CM_Get_Child,但还是需要提供一个IN DEVINST dnDevInst,不知该怎么办?按照以往的经验,这种函数不可能单独调用,一定是一系列相关函数的调用,能贴出来吗?谢谢。
在我的c:\ntddk下的install.htm中,头一段话就是: Getting Started The Microsoft® Windows® 2000 Driver Development Kit (DDK) provides a development environment for the creation of Windows 2000 and WDM drivers. The DDK provides additional driver-specific headers, libraries, sources, tools, and documentation that are used to develop drivers for Windows 2000. 应该也是2000的ddk了,可为什么会找不到函数呢?
作 者: kezhu (死猪)
信 誉 值: 98
所属论坛: VC/MFC 硬件/系统
问题点数: 100
回复次数: 33
发表时间: 2003-7-14 18:18:30
就是一个u盘,每次直接从机器上拔下来的时候,总会有系统提示设备错误,为了避免这种错误,需要先把设备从系统中删除,点击右下角图标当然可以完成,但如何自动完成,即在程序中调用什么函数可以完成这个功能?
回复人: DeautyFan(痴情浪子) ( ) 信誉:100 2003-7-14 18:52:14 得分:0
关注
Top
回复人: kezhu(死猪) ( ) 信誉:98 2003-7-15 9:34:54 得分:0
ding
Top
回复人: masterz() ( ) 信誉:203 2003-7-15 12:59:49 得分:0
http://support.microsoft.com/support/kb/articles/Q165/7/21.asp
165721 - HOWTO: Ejecting Removable Media in Windows NT/Windows ...
HOWTO: Ejecting Removable Media in Windows NT/Windows 2000/Windows XP. http://support.microsoft.com/support/kb/articles/Q168/1/80.ASP
HOWTO: Eject Removable Media on Windows 95/98/Me.
Top
回复人: kezhu(死猪) ( ) 信誉:98 2003-7-15 15:20:42 得分:0
试了一下提供的那几个DeviceIoControl函数,可以把光驱弹出来,但对u盘却没什么反应。
Top
回复人: kezhu(死猪) ( ) 信誉:98 2003-7-16 10:57:26 得分:0
ding
Top
回复人: i_tingfeng(鹰眼魔) ( ) 信誉:100 2003-7-16 19:30:33 得分:0
还没搞定吗?
Top
回复人: kezhu(死猪) ( ) 信誉:98 2003-7-17 13:09:37 得分:0
当然没有了
Top
回复人: xpxia(华龙) ( ) 信誉:99 2003-7-17 20:17:33 得分:0
Continue...
Top
回复人: afc(afc) ( ) 信誉:105 2003-7-17 22:16:13 得分:50
#include <stdio.h>
#include <tchar.h> // Make all functions UNICODE safe.
#include <windows.h>
#include <setupapi.h> // for SetupDiXxx functions.
int DisplayError(TCHAR * ErrorName)
/*++
Routine Description: This Routine will display the LastError in human readable
form when possible. If the return value is a 32-bit number, and falls in the range:
ERROR_NO_ASSOCIATED_CLASS 0xE0000200
To
ERROR_CANT_REMOVE_DEVINST 0xE0000232
The values defined in setupapi.h can help to determine the error.
Start by searching for the text string ERROR_NO_ASSOCIATED_CLASS.
Arguments:
ErrorName: Human readable description of the last Function called.Return Value:
Allways returns FALSE.
--*/
{
DWORD Err = GetLastError();
LPVOID lpMessageBuffer = NULL;
if (FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
Err,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMessageBuffer,
0,
NULL ))
_tprintf(TEXT("%s FAILURE: %s\n"),ErrorName,(TCHAR *)lpMessageBuffer);
else
_tprintf(TEXT("%s FAILURE: (0x%08x)\n"),ErrorName,Err);
if (lpMessageBuffer) LocalFree( lpMessageBuffer ); // Free system buffer
SetLastError(Err);
return FALSE;
}int __cdecl _tmain(int argc, _TCHAR **argv, _TCHAR **envp)
/*++
Routine Discription: Entry point to Remove.exe.
Parse the command line, call subroutines.Arguments:
Standard console 'c' application arguments. argv[1] - PnP HardwareID of devices to remove.Return Value:
Standard Console ERRORLEVEL values: 0 - Remove Successfull
2 - Remove Failure.
--*/
{
HDEVINFO DeviceInfoSet;
SP_DEVINFO_DATA DeviceInfoData;
DWORD i,err; //
// Verify the Arguments.
//
if (argc < 2)
{
_tprintf(TEXT("usage: remove <Hardware_ID>\n"));
return 1; // Remove Failure
} //
// Create a Device Information Set with all present devices.
//
DeviceInfoSet = SetupDiGetClassDevs(NULL, // All Classes
0,
0,
DIGCF_ALLCLASSES | DIGCF_PRESENT ); // All devices present on system
if (DeviceInfoSet == INVALID_HANDLE_VALUE)
{
DisplayError(TEXT("GetClassDevs(All Present Devices)"));
return 1;
}
//
// Enumerate through all Devices.
//
DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
for (i=0;SetupDiEnumDeviceInfo(DeviceInfoSet,i,&DeviceInfoData);i++)
{
DWORD DataT;
LPTSTR p,buffer = NULL;
DWORD buffersize = 0;
//
// We won't know the size of the HardwareID buffer until we call
// this function. So call it with a null to begin with, and then
// use the required buffer size to Alloc the nessicary space.
// Keep calling we have success or an unknown failure.
//
while (!SetupDiGetDeviceRegistryProperty(
DeviceInfoSet,
&DeviceInfoData,
SPDRP_HARDWAREID,
&DataT,
(PBYTE)buffer,
buffersize,
&buffersize))
{
if (GetLastError() == ERROR_INVALID_DATA)
{
//
// May be a Legacy Device with no HardwareID. Continue.
//
break;
}
else if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
//
// We need to change the buffer size.
//
if (buffer)
LocalFree(buffer);
buffer = LocalAlloc(LPTR,buffersize);
}
else
{
//
// Unknown Failure.
//
DisplayError(TEXT("GetDeviceRegistryProperty"));
goto cleanup_DeviceInfo;
}
} if (GetLastError() == ERROR_INVALID_DATA)
continue;
//
// Compare each entry in the buffer multi-sz list with our HardwareID.
//
for (p=buffer;*p&&(p<&buffer[buffersize]);p+=lstrlen(p)+sizeof(TCHAR))
{
_tprintf(TEXT("Compare device ID: [%s]\n"),p); if (!_tcscmp(argv[1],p))
{
_tprintf(TEXT("Found! [%s]\n"),p); //
// Worker function to remove device.
//
if (!SetupDiCallClassInstaller(DIF_REMOVE,
DeviceInfoSet,
&DeviceInfoData))
{
DisplayError(TEXT("CallClassInstaller(REMOVE)"));
}
break;
}
} if (buffer) LocalFree(buffer);
} if ((GetLastError()!=NO_ERROR)&&(GetLastError()!=ERROR_NO_MORE_ITEMS))
{
DisplayError(TEXT("EnumDeviceInfo"));
}
//
// Cleanup.
//
cleanup_DeviceInfo:
err = GetLastError();
SetupDiDestroyDeviceInfoList(DeviceInfoSet);
return !(err == NO_ERROR);
}
运行这段程序,是不是还要安装DDK,需要提供给程序的所谓<Hardware_ID>应该写什么?在系统中哪里可以得到这个ID?
Top
回复人: kezhu(死猪) ( ) 信誉:98 2003-7-18 15:53:52 得分:0
上面的程序在bcb下可以运行,但删除的是驱动器,即H:盘,但右下角的图标仍在,直接拔除依然会有提示。点击右下角图标时弹出的删除提示以前是3个设备,执行程序(Remove USBSTOR\GenDisk)后有两个,一个是USB Mass Storage Device,和以前一样,Generic Volume-(H:)和TGE USB Device不见了,变成了一个未知设备。有什么办法可以删除USB Mass Storage Device设备?使用Remove abcde,也就是不存在的设备可以得到一个列表,所有插上u盘后增加的设备不论删除哪个都是一个结果。
Top
回复人: afc(afc) ( ) 信誉:105 2003-7-18 21:17:57 得分:0
看看U盘的实际硬件ID,就是Enum\USB\下的东西例如Vid_0e68&Pid_0100,然后删除这个硬件ID看看
Top
回复人: kezhu(死猪) ( ) 信誉:98 2003-7-21 9:22:01 得分:0
下面两个是利用remove abcde得到的,和你说的比较相像,
Compare device ID: [USB\Vid_0c76&Pid_0003&Rev_0100]
Compare device ID: [USB\Vid_0c76&Pid_0003]
看来我的id应该是USB\Vid_0c76&Pid_0003,但如果我输入remove USB\Vid_0c76&Pid_0003或remove USB\Vid_0c76&Pid_0003&Rev_0100结果系统都提示:
'Pid_0003' 不是内部或外部命令,也不是可运行的程序或批处理文件。
系统把Pid_0003和Rev_0100当作另一个命令了。那该如何删除这个硬件ID呢?
Top
回复人: acev(睡眠不足(多看编程贴子;少灌水...)) ( ) 信誉:100 2003-7-21 9:29:12 得分:0
关注!
kezhu(死猪)这个想法很好呀,
对了,请较一下,在WIN98下,不用删除设备,直接就可以拔掉U盘,2000下为何要这样呢?
Top
回复人: godsmile(笑非哮,世间还有我这号) ( ) 信誉:100 2003-7-21 9:48:50 得分:0
GZ
UP
Top
回复人: kezhu(死猪) ( ) 信誉:98 2003-7-21 10:35:44 得分:0
把USB\Vid_0c76&Pid_0003&Rev_0100或USB\Vid_0c76&Pid_0003直接写到程序里就可以删除这个设备了,直接拔除u盘也不出错误提示了,但在重新插上后系统会提示发现新设备,正在安装驱动程序等,如何让这些提示不出现,毕竟让用户看到这些不好。
Top
回复人: kezhu(死猪) ( ) 信誉:98 2003-7-21 11:05:02 得分:0
还有一个问题,在程序中
for (p=buffer;*p&&(p<&buffer[buffersize]);p+=lstrlen(p)+sizeof(TCHAR))
{
if (!_tcscmp(argv[1],p))
使用指针变量p以及输入参数来查找准备删除的设备,但在删除时
if (!SetupDiCallClassInstaller(DIF_REMOVE,
DeviceInfoSet,
&DeviceInfoData))
却没有用到p,那系统是如何确认要删除的设备呢?或者说如何确认当前找到的设备呢?
设备号不一样时(即if!=0时),没有什么设备序号加一的操作吧?
Top
回复人: kezhu(死猪) ( ) 信誉:98 2003-7-22 11:29:18 得分:0
这段程序删除的是设备还是驱动?有一次系统提示需重新启动才能生效。
Top
回复人: MoreBug(bugandbug) ( ) 信誉:100 2003-10-25 2:41:59 得分:0
删除的是驱动程序,当然设备也先被卸载了。另外你说:
////////////////////////////////////////////////////////////////////////////////
还有一个问题,在程序中
for (p=buffer;*p&&(p<&buffer[buffersize]);p+=lstrlen(p)+sizeof(TCHAR))
{
if (!_tcscmp(argv[1],p))
使用指针变量p以及输入参数来查找准备删除的设备,但在删除时
if (!SetupDiCallClassInstaller(DIF_REMOVE,
DeviceInfoSet,
&DeviceInfoData))
却没有用到p,那系统是如何确认要删除的设备呢?或者说如何确认当前找到的设备呢?
设备号不一样时(即if!=0时),没有什么设备序号加一的操作吧?////////////////////////////////////////////////////////////////////////////////
是因为你没有真正看懂程序。再仔细看看,查一下DDK的帮助文件。
(其实在程序的枚举过程中就已经比较了当前枚举到的设备是否为想要查找的设备:
1、while (!SetupDiGetDeviceRegistryProperty(
DeviceInfoSet,
&DeviceInfoData,
SPDRP_HARDWAREID,
&DataT,
(PBYTE)buffer,
buffersize,
&buffersize))2、if (!_tcscmp(argv[1],p)))afc(afc)的水平应该不错,代码很清晰流畅。
Top
回复人: kezhu(死猪) ( ) 信誉:98 2003-10-26 16:15:38 得分:0
谢谢,我现在是使用DDK的函数来停用和启用设备,始终无法做到和点击图标一样的删除设备效果,虽然断电后不提示了,但总觉得不是很好。
Top
回复人: ljzcom() ( ) 信誉:92 2003-10-28 11:46:59 得分:0
DDK中应该有这样的函数可以实现把
Top
回复人: autotest() ( ) 信誉:100 2003-10-29 8:52:25 得分:0
能做到不删除驱动,直接停用吗?
Top
回复人: yifeng_ch(yifeng) ( ) 信誉:64 2003-10-29 9:10:39 得分:0
收藏。
Top
回复人: kezhu(死猪) ( ) 信誉:98 2003-10-30 10:01:24 得分:0
能告诉我如何调用的吗?这个函数的第一个参数IN DEVINST dnAncestor应该写什么?我该如何得到这个“Caller-supplied device instance handle to the device at the root of the subtree to be removed”,按提示看CM_Get_Child,但还是需要提供一个IN DEVINST dnDevInst,不知该怎么办?按照以往的经验,这种函数不可能单独调用,一定是一系列相关函数的调用,能贴出来吗?谢谢。
Top
回复人: poweruser(Loading......) ( ) 信誉:99 2003-10-30 13:04:38 得分:0
关注!!!偶也想知道roger_ding(海天一色)的方法,能把核心的代码贴出来看看吗?
Top
回复人: kezhu(死猪) ( ) 信誉:98 2003-10-30 15:36:06 得分:0
还是用前面的办法,用setupdi系列函数找到设备的DeviceInfoData,然后
PNP_VETO_TYPE pnpVetoType;
char s[MAX_PATH];
CM_Query_And_Remove_SubTree
(DeviceInfoData.DevInst,&pnpVetoType,s,MAX_PATH,CM_REMOVE_UI_OK);现象是u盘的盘符不见了,右下角图标消失了,但在设备管理器中没有变化,并且可以马上利用扫描硬件改动激活它(盘符再次出现),如果用鼠标单机右下角图标的方式,设备管理器中存在,但被打了个叹号,用扫描硬件改动也激活不了,只能拔下再插上。我是在xp中试的,放到2000下没有任何反应(盘符、图标都没有任何变化),不知为何?
Top
回复人: roger_ding(海天一色) ( ) 信誉:98 2003-10-31 14:47:18 得分:50
是我记错了,应该是CM_Request_Device_Eject,
需要注意的是第一个入参DEVINST dnAncestor应该是“USB\VID_046D&PID_C001\...”这类设备的句柄(来源于DeviceInfoData.DevInst),
CM_Request_Device_Eject(deviceInfoData.DevInst, 0, byBuffer, sizeof(byBuffer), 0);
至于有时已经安全弹出了,但资源管理器中的盘符仍然存在的话,可以通过SHChangeNotify来刷新
(在Win2000,WinXP下调试通过,Win9x中倒可能是CM_Query_And_Remove_SubTree,你自己再试试看)
Top
回复人: kezhu(死猪) ( ) 信誉:98 2003-10-31 15:53:31 得分:0
这是我在msdn上查到的函数格式,
CMAPI CONFIGRET WINAPI
CM_Request_Device_Eject(
IN DEVINST dnDevInst,
OUT PPNP_VETO_TYPE pVetoType,
OUT LPTSTR pszVetoName,
IN ULONG ulNameLength,
IN ULONG ulFlags
);
OUT PPNP_VETO_TYPE pVetoType可以直接写0吗?还有就是程序连接时提示:
testCMDlg.obj : error LNK2001: unresolved external symbol __imp__CM_Request_Device_EjectA@20
Debug/testCM.exe : fatal error LNK1120: 1 unresolved externals
Error executing link.exe.testCM.exe - 2 error(s), 0 warning(s)
好像没有找到这个函数的实现部分,但如果编译CM_Query_And_Remove_SubTree就没事,程序开始已经添加了头文件:
#include <setupapi.h>
#include <cfgmgr32.h>
#pragma comment(lib,"setupapi.lib")
#pragma comment(lib,"cfgmgr32.lib")
环境中已经添加了c:\ntddk下相应的inc和lib\i386\free目录,是不是ddk的版本问题?(我已经忘了我这个是什么时候装的了)
Top
回复人: roger_ding(海天一色) ( ) 信誉:98 2003-10-31 16:02:58 得分:0
我用的是Win2000的ddk
Top
回复人: kezhu(死猪) ( ) 信誉:98 2003-10-31 16:15:30 得分:0
在我的c:\ntddk下的install.htm中,头一段话就是:
Getting Started
The Microsoft® Windows® 2000 Driver Development Kit (DDK) provides a development environment for the creation of Windows 2000 and WDM drivers. The DDK provides additional driver-specific headers, libraries, sources, tools, and documentation that are used to develop drivers for Windows 2000. 应该也是2000的ddk了,可为什么会找不到函数呢?
Top
回复人: roger_ding(海天一色) ( ) 信誉:98 2003-11-1 2:30:26 得分:0
实在不行,你可以调用GetProcAddress,在Setupapi.dll里
Top
回复人: kezhu(死猪) ( ) 信誉:98 2003-11-2 15:27:40 得分:0
只能这样了,不过调用的结果确实和点击图标一样,谢谢。