none
DeviceIoControl()在Win7下得到ERROR_ACCESS_DENIED? RRS feed

  • 問題

  • 假設我現在一個隨身碟是X磁碟機,要對它寫入:

    SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sSPTDWB; // 有填值進去
    ULONG length = sizeof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER);
    DWORD dwRet = 0;
    
    HANDLE USBHandle = CreateFile(_T("\\\\.\\X:"), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
    
    DeviceIoControl(USBHandle, IOCTL_SCSI_PASS_THROUGH_DIRECT, &sSPTDWB, length, &sSPTDWB, length, &dwRet, FALSE);
    


    專案的UAC Execution Level已經設為「requireAdministrator」。
    在Win XP之下運作正常,但是在Win7下、DeviceIoControl()會fail,GetLastError()所得到是ERROR_ACCESS_DENIED!

    請問大家,這是怎麼回事?該如何做才能在Win7之下執行?

    2010年12月7日 上午 06:52

解答

  • 終於解決了!感謝key in workman提供方向!

    順便把我的程式摘要列出來:

    SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sSPTDWB; // 有填值進去
    ULONG length = sizeof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER);
    DWORD dwRet = 0;
    
    HANDLE USBHandle = CreateFile(_T("\\\\.\\X:"), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
    
    DeviceIoControl(USBHandle, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &BytesReturned, NULL);
    
    DeviceIoControl(USBHandle, IOCTL_SCSI_PASS_THROUGH_DIRECT, &sSPTDWB, length, &sSPTDWB, length, &dwRet, FALSE);
    

    關閉的control code要用FSCTL_LOCK_VOLUME或FSCTL_UNMOUNT_VOLUME都可以,重點就是在於要先lock或unmount volume。

    相關可參考WriteFile()的說明,在Remarks的最後面有提到:

    To prevent these problems, the following changes have been made in Windows Vista and later:

     

    • A write on a volume handle will succeed if the volume is not mounted by a file system, or if one of the following conditions is true:

       

      • The sectors to be written to are boot sectors.
      • The sectors to be written to reside outside of file system space.
      • You have explicitly locked or dismounted the volume by using FSCTL_LOCK_VOLUME or FSCTL_DISMOUNT_VOLUME.
      • The volume has no file system. (In other words, it has been mounted as a RAW volume.)
    2011年2月10日 上午 10:14

所有回覆

  • 請用系統管理員身份執行您的程式, 就不會有這個問題了
    2010年12月7日 上午 07:37
  • 謝謝回覆,不過以系統管理員身份執行、還是不行...

    其實project setting的UAC就已經有設為requireAdministrator了,執行時就會先詢問是否確定要執行;以往的程式、遇到Vista/Win7,requireAdministrator已經可以解決了,但這次就還是不行?:(

    但在測試中、我又發現另一個現象,只要隨身碟在windows下是顯示未格式化,在Vista/Win7下也可以燒得進檔案!

    例如說我先在Win XP下:

    HANDLE USBHandle = CreateFile(_T("\\\\.\\X:"), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
    
    UCHAR cDataW[128][512];
    WriteFile(USBHandle, cDataW[0], nValidDataLength, &dwRet, NULL);

    總之就是用WriteFile()亂寫一些東西進去,好像是打壞了隨身碟的system block、還是block 0什麼的?會把隨身碟變成「未格式化」,原本的Volume Label也會不見;接著再到Vista/Win7上面去執行寫入的程式,就發現這次能寫得進去、不會再fail在DeviceIoControl()!而且確定檔案真的是有寫進去成功,但只是隨身碟仍然是「未格式化」。

    這樣一來,難道問題又變成是「NAND Flash在某種狀況下,在Vista/Win7下、會造成DeviceIoControl()因為ERROR_ACCESS_DENIED而fail」?!這...越來越不懂了...@@

    2010年12月7日 上午 08:43
  • 在vista/win7是不允許對格式化後的磁碟下0x2a這個scsi command的,這時中間的driver會將這組command檔掉,但未格式化的磁碟就允許了,我猜是微軟怕你也對系統磁碟做相同的動作而毀了整個系統
    2010年12月22日 上午 01:55
  • 在vista/win7是不允許對格式化後的磁碟下0x2a這個scsi command的,這時中間的driver會將這組command檔掉,但未格式化的磁碟就允許了,我猜是微軟怕你也對系統磁碟做相同的動作而毀了整個系統


    0x2A是指 SCSI_PASS_THROUGH_DIRECT.Cdb[0] = SCSIOP_WRITE 的SCSIOP_WRITE嗎?

    但要寫入、就只能是SCSIOP_WRITE...而現在的需求,不能允許把隨身碟變成未格式化、或者Volume Label不見。

    那問題還是回到原點...到底在Vista/Win7下,要怎樣才能用DeviceIoControl()寫檔到隨身碟???>_<

    2011年2月8日 上午 08:23
  • 建議你看你要問微軟看看嗎 ?

    反正發問錢由公司出,相對的你有問公司才會知道設計軟體的難度.

    假如你要問我建議做法 請回覆我我在提給你

    2011年2月9日 上午 07:02
  • 教教我吧,有什麼可行的作法?

    問MS的話...不知道要多少錢?

    2011年2月9日 上午 07:13
  • 你說了 我只是給你參考方法?

    1.在你 使用DeviceIoControl要求裝置io時要記得先把io釋放

     

    DeviceIoControl(USBHandle, IOCTL_SCSI_PASS_THROUGH_DIRECT, &sSPTDWB, length, &sSPTDWB, length, &dwRet, FALSE);

    2.當你使用完後 也做IO釋放動作.

    http://support.microsoft.com/kb/137247/zh-tw

    http://support.microsoft.com/kb/259573/zh-tw

    但要注意這項條件

    你IOCTL_SCSI_PASS_THROUGH_DIRECT 關閉 的 fLAG 我還沒查到 可能在你程式碼下方就有

     

    2011年2月9日 上午 07:37
  • http://msdn.microsoft.com/en-us/library/ff560519(v=vs.85).aspx 這篇或許對你有幫助'

    以下是IOCT 所有求控制參數希望對你有用試一下就知道

    #define IOCTL_SCSI_PASS_THROUGH      

    #define IOCTL_SCSI_MINIPORT            

    #define IOCTL_SCSI_GET_INQUIRY_DATA 

    #define IOCTL_SCSI_GET_CAPABILITIES  

    #define IOCTL_SCSI_PASS_THROUGH_DIRECT 

    #define IOCTL_SCSI_GET_ADDRESS         

    #define IOCTL_SCSI_RESCAN_BUS          

    #define IOCTL_SCSI_GET_DUMP_POINTERS 

    #define IOCTL_SCSI_FREE_DUMP_POINTERS  

    #define IOCTL_IDE_PASS_THROUGH         

    #define IOCTL_ATA_PASS_THROUGH       

    #define IOCTL_ATA_PASS_THROUGH_DIRECT  

    2011年2月9日 上午 08:04
  • 個人比較建議方法

    First, call GetVolumePathName. Then, call CreateFile to open the volume using the path. Next, use IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS with the volume handle to obtain the disk number and use the disk number to construct the disk path, such as "\\?\PhysicalDriveX". Finally, use IOCTL_DISK_GET_DRIVE_LAYOUT_EX to obtain the partition list, and check the PartitionType for each entry in the partition list.

    http://msdn.microsoft.com/en-us/library/aa363785(v=VS.85).aspx

    參考網址

    2011年2月9日 上午 08:17
  • 大約用法如下 ..................以下是cd-rom使用方式

     

     

    HANDLE  hCD, hFile;
       DWORD   dwNotUsed;

       //  檔案寫入CD-ROM.
       hFile = CreateFile ("sector.dat",
                           GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
                           FILE_ATTRIBUTE_NORMAL, NULL);

       // 系統 F:是 CD-ROM
      
       hCD = CreateFile ("\\\\.\\F:", GENERIC_READ,
                         FILE_SHARE_READ|FILE_SHARE_WRITE,
                         NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
                         NULL);

      
       if (hCD != INVALID_HANDLE_VALUE)
       {
          DISK_GEOMETRY         dgCDROM;
          PREVENT_MEDIA_REMOVAL pmrLockCDROM;

          // 移除鎖定 CD-ROM 取消使用
          pmrLockCDROM.PreventMediaRemoval = TRUE;
          DeviceIoControl (hCD, IOCTL_CDROM_MEDIA_REMOVAL,
                           &pmrLockCDROM, sizeof(pmrLockCDROM), NULL,
                           0, &dwNotUsed, NULL);

          // 取得扇形區大小
          if (DeviceIoControl (hCD, IOCTL_CDROM_GET_DRIVE_GEOMETRY,
                               NULL, 0, &dgCDROM, sizeof(dgCDROM),
                               &dwNotUsed, NULL))
          {
             LPBYTE lpSector;
             DWORD  dwSize = 2 * dgCDROM.BytesPerSector; 

            
             lpSector = VirtualAlloc (NULL, dwSize,
                                      MEM_COMMIT|MEM_RESERVE,
                                      PAGE_READWRITE);

             // 移動到16 扇形區讀取
             SetFilePointer (hCD, dgCDROM.BytesPerSector * 16,
                             NULL, FILE_BEGIN);

             //讀取跟比較檔案.
             if (ReadFile (hCD, lpSector, dwSize, &dwNotUsed, NULL))
                WriteFile (hFile, lpSector, dwSize, &dwNotUsed, NULL);

             VirtualFree (lpSector, 0, MEM_RELEASE);
          }

          //解除鎖定CD-ROM.
          pmrLockCDROM.PreventMediaRemoval = FALSE;
          DeviceIoControl (hCD, IOCTL_CDROM_MEDIA_REMOVAL,
                           &pmrLockCDROM, sizeof(pmrLockCDROM), NULL,
                           0, &dwNotUsed, NULL);

          CloseHandle (hCD);
          CloseHandle (hFile);
       }

        

    2011年2月10日 上午 01:41
  • 太感謝你的熱心回覆!我趕緊來試試看!

    2011年2月10日 上午 06:00
  • 終於解決了!感謝key in workman提供方向!

    順便把我的程式摘要列出來:

    SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sSPTDWB; // 有填值進去
    ULONG length = sizeof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER);
    DWORD dwRet = 0;
    
    HANDLE USBHandle = CreateFile(_T("\\\\.\\X:"), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
    
    DeviceIoControl(USBHandle, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &BytesReturned, NULL);
    
    DeviceIoControl(USBHandle, IOCTL_SCSI_PASS_THROUGH_DIRECT, &sSPTDWB, length, &sSPTDWB, length, &dwRet, FALSE);
    

    關閉的control code要用FSCTL_LOCK_VOLUME或FSCTL_UNMOUNT_VOLUME都可以,重點就是在於要先lock或unmount volume。

    相關可參考WriteFile()的說明,在Remarks的最後面有提到:

    To prevent these problems, the following changes have been made in Windows Vista and later:

     

    • A write on a volume handle will succeed if the volume is not mounted by a file system, or if one of the following conditions is true:

       

      • The sectors to be written to are boot sectors.
      • The sectors to be written to reside outside of file system space.
      • You have explicitly locked or dismounted the volume by using FSCTL_LOCK_VOLUME or FSCTL_DISMOUNT_VOLUME.
      • The volume has no file system. (In other words, it has been mounted as a RAW volume.)
    2011年2月10日 上午 10:14
  • 這篇真棒:D

    不收錄不行^^

    2011年2月14日 上午 01:40
  • 您好!

    我在win7下也遇到了类似问题, XP下正常,在win7下调用DeviceIoControl后返回2404错误, 方便时请给予问题解决建议,谢谢!  MSN:xiangsuccess@hotmail.com

    2011年9月23日 上午 01:59