就是一个u盘,每次直接从机器上拔下来的时候,总会有系统提示设备错误,为了避免这种错误,需要先把设备从系统中删除,点击右下角图标当然可以完成,但如何自动完成,即在程序中调用什么函数可以完成这个功能?

解决方案 »

  1.   

    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.
      

  2.   

    试了一下提供的那几个DeviceIoControl函数,可以把光驱弹出来,但对u盘却没什么反应。
      

  3.   

    #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); 
    }
      

  4.   

    运行这段程序,是不是还要安装DDK,需要提供给程序的所谓<Hardware_ID>应该写什么?在系统中哪里可以得到这个ID?
      

  5.   

    上面的程序在bcb下可以运行,但删除的是驱动器,即H:盘,但右下角的图标仍在,直接拔除依然会有提示。点击右下角图标时弹出的删除提示以前是3个设备,执行程序(Remove USBSTOR\GenDisk)后有两个,一个是USB Mass Storage Device,和以前一样,Generic Volume-(H:)和TGE USB Device不见了,变成了一个未知设备。有什么办法可以删除USB Mass Storage Device设备?使用Remove abcde,也就是不存在的设备可以得到一个列表,所有插上u盘后增加的设备不论删除哪个都是一个结果。
      

  6.   

    看看U盘的实际硬件ID,就是Enum\USB\下的东西例如Vid_0e68&Pid_0100,然后删除这个硬件ID看看
      

  7.   

    下面两个是利用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呢?
      

  8.   

    关注!
    kezhu(死猪)这个想法很好呀,
    对了,请较一下,在WIN98下,不用删除设备,直接就可以拔掉U盘,2000下为何要这样呢?
      

  9.   

    把USB\Vid_0c76&Pid_0003&Rev_0100或USB\Vid_0c76&Pid_0003直接写到程序里就可以删除这个设备了,直接拔除u盘也不出错误提示了,但在重新插上后系统会提示发现新设备,正在安装驱动程序等,如何让这些提示不出现,毕竟让用户看到这些不好。
      

  10.   

    还有一个问题,在程序中
    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时),没有什么设备序号加一的操作吧?
      

  11.   

    删除的是驱动程序,当然设备也先被卸载了。另外你说:
    ////////////////////////////////////////////////////////////////////////////////
    还有一个问题,在程序中
    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)的水平应该不错,代码很清晰流畅。
      

  12.   

    谢谢,我现在是使用DDK的函数来停用和启用设备,始终无法做到和点击图标一样的删除设备效果,虽然断电后不提示了,但总觉得不是很好。
      

  13.   

    DDK中应该有这样的函数可以实现把
      

  14.   

    我以前也做过这个,微软比较龌龊,在MSDN里告诉别人要用SetupDI的一套函数,可它自己却没用,经过我在2k下用s-ice跟踪,ms使用CM_Query_And_Remove_SubTree来弹出设备,即一套老api,具体你再参考msdn,经我测试结果,效果很好,和点击图标的效果一样
      

  15.   

    能告诉我如何调用的吗?这个函数的第一个参数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,不知该怎么办?按照以往的经验,这种函数不可能单独调用,一定是一系列相关函数的调用,能贴出来吗?谢谢。
      

  16.   

    关注!!!偶也想知道roger_ding(海天一色)的方法,能把核心的代码贴出来看看吗?
      

  17.   

    还是用前面的办法,用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下没有任何反应(盘符、图标都没有任何变化),不知为何?
      

  18.   

    是我记错了,应该是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,你自己再试试看)
      

  19.   

    这是我在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的版本问题?(我已经忘了我这个是什么时候装的了)
      

  20.   

    在我的c:\ntddk下的install.htm中,头一段话就是:
    Getting Started 
    The Microsoft&reg; Windows&reg; 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了,可为什么会找不到函数呢?
      

  21.   

    实在不行,你可以调用GetProcAddress,在Setupapi.dll里