全國最多中醫師線上諮詢網站-台灣中醫網
發文 回覆 瀏覽次數:9962
推到 Plurk!
推到 Facebook!

想多了解有關用程式讀取、控制PCI介面......請各位前輩幫忙

尚未結案
lee708
一般會員


發表:6
回覆:5
積分:7
註冊:2002-11-24

發送簡訊給我
#1 引用回覆 回覆 發表時間:2003-03-13 18:02:41 IP:210.202.xxx.xxx 未訂閱
想多了解有關用程式讀取、控制PCI介面的影像擷取卡...... 請各位前輩幫忙推薦一些書或網站 我只要知道可以從哪下手就行了 因為我現在完全不知如何開始 謝謝  >
taishyang
站務副站長


發表:377
回覆:5485
積分:4552
註冊:2002-10-08

發送簡訊給我
#2 引用回覆 回覆 發表時間:2003-03-13 20:35:35 IP:140.135.xxx.xxx 未訂閱
lee708您好:    
引言:想多了解有關用程式讀取、控制PCI介面的影像擷取卡
請問您的目的是否是為了擷取攝影機的影像呢? 如果是的話可以到[影音多媒體區]裡面有相當多精采詳盡的討論< >< > 順心
lee708
一般會員


發表:6
回覆:5
積分:7
註冊:2002-11-24

發送簡訊給我
#3 引用回覆 回覆 發表時間:2003-03-18 14:40:11 IP:210.202.xxx.xxx 未訂閱
謝謝taishyang大大的告知 可是我想知道更多一點相關資訊 不只是影像擷取的部分 我想學習更多一點的相關資訊 不知道這樣會不會太貪心了 謝謝
axsoft
版主


發表:681
回覆:1056
積分:969
註冊:2002-03-13

發送簡訊給我
#4 引用回覆 回覆 發表時間:2003-03-19 11:40:01 IP:61.218.xxx.xxx 未訂閱

實戰DeviceIoControl 之一:通過API訪問設備驅動程序

作者:bhw98 資料來源:www.CSDN.net Q 在NT/2000/XP中,我想用VC編寫應用程序訪問硬件設備,如獲取磁盤參數、讀寫絕對扇區數據、測試光驅實際速度等,該從哪裡入手呢? A 在NT/2000/XP中,應用程序可以通過API函數DeviceIoControl來實現對設備的訪問—獲取信息,發送命令,交換數據等。利用該接口函數向指定的設備驅動發送正確的控制碼及數據,然後分析它的響應,就可以達到我們的目的。 DeviceIoControl的函數原型為 BOOL DeviceIoControl( HANDLE hDevice, // 設備句柄 DWORD dwIoControlCode, // 控制碼 LPVOID lpInBuffer, // 輸入數據緩衝區指針 DWORD nInBufferSize, // 輸入數據緩衝區長度 LPVOID lpOutBuffer, // 輸出數據緩衝區指針 DWORD nOutBufferSize, // 輸出數據緩衝區長度 LPDWORD lpBytesReturned, // 輸出數據實際長度單元長度 LPOVERLAPPED lpOverlapped // 重疊操作結構指針 ); 設備句柄用來標識你所訪問的設備。 發送不同的控制碼,可以調用設備驅動程序的不同類型的功能。在頭文件winioctl.h中,預定義的標準設備控制碼,都以IOCTL或FSCTL開頭。例如,IOCTL_DISK_GET_DRIVE_GEOMETRY是對物理驅動器取結構參數(介質類型、柱面數、每柱面磁道數、每磁道扇區數等)的控制碼,FSCTL_LOCK_VOLUME是對邏輯驅動器的卷加鎖的控制碼。 輸入輸出數據緩衝區是否需要,是何種結構,以及佔多少字節空間,完全由不同設備的不同操作類型決定。在頭文件winioctl.h中,已經為標準設備預定義了一些輸入輸出數據結構。重疊操作結構指針設置為NULL,DeviceIoControl將進行阻塞調用;否則,應在編程時按異步操作設計。 Q 設備句柄是從哪裡獲得的? A 設備句柄可以用API函數CreateFile獲得。它的原型為 HANDLE CreateFile( LPCTSTR lpFileName, // 文件名 DWORD dwDesiredAccess, // 訪問方式 DWORD dwShareMode, // 共享方式 LPSECURITY_ATTRIBUTES lpSecurityAttributes, // 安全描述符指針 DWORD dwCreationDisposition, // 創建方式 DWORD dwFlagsAndAttributes, // 文件屬性及標誌 HANDLE hTemplateFile // 模板文件的句柄 ); CreateFile這個函數用處很多,這裡我們用它「打開」設備驅動程序,得到設備的句柄。操作完成後用CloseHandle關閉設備句柄。 與普通文件名有所不同,設備驅動的「文件名」形式固定為「\\.\DeviceName」(注意在C程序中該字符串寫法為「\\\\.\\DeviceName」),DeviceName必須與設備驅動程序內規定的設備名稱一致。 一般地,調用CreateFile獲得設備句柄時,訪問方式參數設置為0或GENERIC_READ|GENERIC_WRITE,共享方式參數設置為FILE_SHARE_READ|FILE_SHARE_WRITE,創建方式參數設置為OPEN_EXISTING,其它參數設置為0或NULL。 Q 可是,我怎麼知道設備名稱是什麼呢? A 一些存儲設備的名稱是微軟規定好的,不可能有什麼變化。大體列出如下 軟盤驅動器 A:, B: 邏輯驅動器 C:, D:, E:, …… 物理驅動器 PHYSICALDRIVEx CD-ROM, DVD/ROM CDROMx 磁帶機 TAPEx 其中,物理驅動器不包括軟驅和光驅。邏輯驅動器可以是IDE/SCSI/PCMCIA/USB接口的硬盤分區(卷)、光驅、MO、CF卡等,甚至是虛擬盤。x=0,1,2 …… 其它的設備名稱需通過驅動接口的GUID調用設備管理函數族取得,這裡暫不討論。 Q 請舉一個簡單的例子說明如何通過DeviceIoControl訪問設備驅動程序。 A 這裡有一個從MSDN上摘抄來的demo程序,演示在NT/2000/XP中如何通過DeviceIoControl獲取硬盤的基本參數。
/* The code of interest is in the subroutine GetDriveGeometry. The 
   code in main shows how to interpret the results of the IOCTL call. */    #include 
#include     BOOL GetDriveGeometry(DISK_GEOMETRY *pdg)
{
  HANDLE hDevice;               // handle to the drive to be examined 
  BOOL bResult;                 // results flag
  DWORD junk;                   // discard results      hDevice = CreateFile("\\\\.\\PhysicalDrive0",  // drive to open
                    0,                // no access to the drive
                    FILE_SHARE_READ | // share mode
                    FILE_SHARE_WRITE, 
                    NULL,             // default security attributes
                    OPEN_EXISTING,    // disposition
                    0,                // file attributes
                    NULL);            // do not copy file attributes      if (hDevice == INVALID_HANDLE_VALUE) // cannot open the drive
  {
    return (FALSE);
  }      bResult = DeviceIoControl(hDevice,  // device to be queried
      IOCTL_DISK_GET_DRIVE_GEOMETRY,  // operation to perform
                             NULL, 0, // no input buffer
                            pdg, sizeof(*pdg),     // output buffer
                            &junk,                 // # bytes returned
                            (LPOVERLAPPED) NULL);  // synchronous I/O      CloseHandle(hDevice);      return (bResult);
}    int main(int argc, char *argv[])
{
  DISK_GEOMETRY pdg;            // disk drive geometry structure
  BOOL bResult;                 // generic results flag
  ULONGLONG DiskSize;           // size of the drive, in bytes      bResult = GetDriveGeometry (&pdg);      if (bResult) 
  {
    printf("Cylinders = %I64d\n", pdg.Cylinders);
    printf("Tracks per cylinder = %ld\n", (ULONG) pdg.TracksPerCylinder);
    printf("Sectors per track = %ld\n", (ULONG) pdg.SectorsPerTrack);
    printf("Bytes per sector = %ld\n", (ULONG) pdg.BytesPerSector);        DiskSize = pdg.Cylinders.QuadPart * (ULONG)pdg.TracksPerCylinder *
      (ULONG)pdg.SectorsPerTrack * (ULONG)pdg.BytesPerSector;
    printf("Disk size = %I64d (Bytes) = %I64d (Mb)\n", DiskSize,
           DiskSize / (1024 * 1024));
  } 
  else 
  {
    printf ("GetDriveGeometry failed. Error %ld.\n", GetLastError ());
  }      return ((int)bResult);
}
Q 如果將設備名換成「A:」就可以取A盤參數,換成「CDROM0」就可以取CDROM參數,是這樣嗎? A 這個問題暫不回答你。請動手試一下看看。 現在我們總結一下通過DeviceIoControl訪問設備驅動程序的「三步曲」:首先用CreateFile取得設備句柄,然後用DeviceIoControl與設備進行I/O,最後別忘記用CloseHandle關閉設備句柄。 聯盟----Visita網站http://www.vista.org.tw ---[ 發問前請先找找舊文章 ]--- 發表人 - axsoft 於 2003/03/19 12:10:48
axsoft
版主


發表:681
回覆:1056
積分:969
註冊:2002-03-13

發送簡訊給我
#5 引用回覆 回覆 發表時間:2003-03-19 11:46:27 IP:61.218.xxx.xxx 未訂閱

實戰DeviceIoControl 之二:獲取軟盤/硬盤/光盤的參數

作者:bhw98 資料來源:www.CSDN.net Q 在MSDN的那個demo中,將設備名換成「A:」取A盤參數,先用資源管理器讀一下盤,再運行這個程序可以成功,但換一張盤後就失敗;換成「CDROM0」取CDROM參數,無論如何都不行。這個問題如何解決呢? A 取軟盤參數是從軟盤上讀取格式化後的信息,也就是必須執行讀操作,這一點與硬盤不同。將CreateFile中的訪問方式改為GENERIC_READ就行了。 IOCTL_DISK_GET_DRIVE_GEOMETRY這個I/O控制碼,對軟盤和硬盤有效,但對一些可移動媒介如CD/DVD-ROM、TAPE等就不管用了。要取CDROM參數,還得另闢蹊徑。IOCTL_STORAGE_GET_MEDIA_TYPES_EX能夠幫我們解決問題。 Q 使用這些I/O控制碼,需要什麼樣的輸入輸出數據格式呢?
A DeviceIoControl使用這兩個控制碼時,都不需要輸入數據。
IOCTL_DISK_GET_DRIVE_GEOMETRY直接輸出一個DISK_GEOMETRY結構:    typedef struct _DISK_GEOMETRY { 
  LARGE_INTEGER  Cylinders; // 柱面數 
  MEDIA_TYPE  MediaType;  // 介質類型
  DWORD  TracksPerCylinder;  // 每柱面的磁道數
  DWORD  SectorsPerTrack;       // 每磁道的扇區數
  DWORD  BytesPerSector;  // 每扇區的字節數
} DISK_GEOMETRY;     IOCTL_STORAGE_GET_MEDIA_TYPES_EX輸出一個GET_MEDIA_TYPES結構:    typedef struct _GET_MEDIA_TYPES {
  DWORD DeviceType;   // 設備類型
  DWORD MediaInfoCount;   // 介質信息條數
  DEVICE_MEDIA_INFO MediaInfo[1]; // 介質信息
} GET_MEDIA_TYPES;    讓我們來看一下DEVICE_MEDIA_INFO結構的定義:    typedef struct _DEVICE_MEDIA_INFO {
    union {
        struct {
            LARGE_INTEGER Cylinders;  // 柱面數 
            STORAGE_MEDIA_TYPE MediaType;  // 介質類型
            DWORD TracksPerCylinder;   // 每柱面的磁道數
            DWORD SectorsPerTrack;  // 每磁道的扇區數
            DWORD BytesPerSector;   // 每扇區的字節數
            DWORD NumberMediaSides;  // 介質面數
            DWORD MediaCharacteristics;  // 介質特性
        } DiskInfo;    // 硬盤信息
        struct {
            LARGE_INTEGER Cylinders;  // 柱面數
            STORAGE_MEDIA_TYPE MediaType;  // 介質類型
            DWORD TracksPerCylinder;   // 每柱面的磁道數
            DWORD SectorsPerTrack;  // 每磁道的扇區數
            DWORD BytesPerSector;   // 每扇區的字節數
            DWORD NumberMediaSides;  // 介質面數
            DWORD MediaCharacteristics;  // 介質特性
        } RemovableDiskInfo;   // 「可移動盤」信息
        struct {
            STORAGE_MEDIA_TYPE MediaType;  // 介質類型
            DWORD   MediaCharacteristics;  // 介質特性
            DWORD   CurrentBlockSize;   // 塊的大小
        } TapeInfo;    // 磁帶信息
    } DeviceSpecific;
} DEVICE_MEDIA_INFO;
其中CD-ROM屬於「可移動盤」的範圍。請注意,GET_MEDIA_TYPES結構本身只定義了一條DEVICE_MEDIA_INFO,額外的DEVICE_MEDIA_INFO需要緊接此結構的另外的空間。 Q 調用方法我瞭解了,請用VC舉個例子來實現我所期待已久的功能吧? A 好,現在就演示一下如何取軟盤/硬盤/光盤的參數。測試時,記得要有軟盤/光盤插在驅動器裡喔! 首先,用MFC AppWizard生成一個單文檔的應用程序,取名為DiskGeometry,讓它的View基於CEditView。 然後,添加以下的.h和.cpp文件。
    //////////////////////////////////////////////////////////////////////////////
// GetDiskGeometry.h
//////////////////////////////////////////////////////////////////////////////    #if !defined(GET_DISK_GEOMETRY_H__)
#define GET_DISK_GEOMETRY_H__    #if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000    #include < winioctl.h >    BOOL GetDriveGeometry(const char* filename, DISK_GEOMETRY *pdg);    #endif // !defined(GET_DISK_GEOMETRY_H__)    //////////////////////////////////////////////////////////////////////////////
// GetDiskGeometry.cpp
//////////////////////////////////////////////////////////////////////////////    #include "stdafx.h"
#include "GetDiskGeometry.h"    // IOCTL_STORAGE_GET_MEDIA_TYPES_EX可能返回不止一條DEVICE_MEDIA_INFO,故定義足夠的空間
#define MEDIA_INFO_SIZE  sizeof(GET_MEDIA_TYPES) 15*sizeof(DEVICE_MEDIA_INFO)    // filename -- 用於設備的文件名
// pdg -- 參數緩衝區指針
BOOL GetDriveGeometry(const char* filename, DISK_GEOMETRY *pdg)
{
 HANDLE hDevice;         // 設備句柄
 BOOL bResult;           // DeviceIoControl的返回結果
 GET_MEDIA_TYPES *pmt;   // 內部用的輸出緩衝區
 DWORD dwOutBytes;       // 輸出數據長度     // 打開設備
 hDevice = ::CreateFile(filename,                           // 文件名
   GENERIC_READ,                              // 軟驅需要讀盤
   FILE_SHARE_READ | FILE_SHARE_WRITE,        // 共享方式
   NULL,                                      // 默認的安全描述符
   OPEN_EXISTING,                             // 創建方式
   0,                                         // 不需設置文件屬性
   NULL);                                     // 不需參照模板文件     if(hDevice == INVALID_HANDLE_VALUE)
 {
  // 設備無法打開...
  return FALSE;
 }     // 用IOCTL_DISK_GET_DRIVE_GEOMETRY取磁盤參數
 bResult = ::DeviceIoControl(hDevice,                   // 設備句柄
   IOCTL_DISK_GET_DRIVE_GEOMETRY,         // 取磁盤參數
   NULL, 0,                               // 不需要輸入數據
   pdg, sizeof(DISK_GEOMETRY),            // 輸出數據緩衝區
   &dwOutBytes,                           // 輸出數據長度
   (LPOVERLAPPED)NULL);                   // 用同步I/O     // 如果失敗,再用IOCTL_STORAGE_GET_MEDIA_TYPES_EX取介質類型參數
 if(!bResult)
 {
  pmt = (GET_MEDIA_TYPES *)new BYTE[MEDIA_INFO_SIZE];      bResult = ::DeviceIoControl(hDevice,                 // 設備句柄
    IOCTL_STORAGE_GET_MEDIA_TYPES_EX,    // 取介質類型參數
    NULL, 0,                             // 不需要輸入數據
    pmt, MEDIA_INFO_SIZE,                // 輸出數據緩衝區
    &dwOutBytes,                         // 輸出數據長度
    (LPOVERLAPPED)NULL);                 // 用同步I/O       if(bResult)
  {
   // 注意到結構DEVICE_MEDIA_INFO是在結構DISK_GEOMETRY的基礎上擴充的
   // 為簡化程序,用memcpy代替如下多條賦值語句:
   // pdg->MediaType = (MEDIA_TYPE)pmt->MediaInfo[0].DeviceSpecific.DiskInfo.MediaType;
   // pdg->Cylinders = pmt->MediaInfo[0].DeviceSpecific.DiskInfo.Cylinders;
   // pdg->TracksPerCylinder = pmt->MediaInfo[0].DeviceSpecific.DiskInfo.TracksPerCylinder;
   // ... ...
   ::memcpy(pdg, pmt->MediaInfo, sizeof(DISK_GEOMETRY));
  }      delete pmt;
 }     // 關閉設備句柄
 ::CloseHandle(hDevice);     return (bResult);
}    然後,在Toolbar的IDR_MAINFRAME上添加一個按鈕,ID為ID_GET_DISK_GEOMETRY。打開ClassWizard,在DiskGeometryView中    添加ID_GET_DISK_GEOMETRY的映射函數OnGetDiskGeometry。打開DiskGeometryView.cpp,包含頭文件GetDiskGeometry.h。    在OnGetDiskGeometry中,添加以下代碼     const char *szDevName[]=
 {
  "\\\\.\\A:",
  "\\\\.\\B:",
  "\\\\.\\PhysicalDrive0",
  "\\\\.\\PhysicalDrive1",
  "\\\\.\\PhysicalDrive2",
  "\\\\.\\PhysicalDrive3",
  "\\\\.\\Cdrom0",
  "\\\\.\\Cdrom1",
 };
 DISK_GEOMETRY dg;
 ULONGLONG DiskSize;
 BOOL bResult;
 CString strMsg;
 CString strTmp;     for(int i=0; i
 CEdit& Edit = GetEditCtrl();     Edit.SetWindowText(strMsg);    最後,最後幹什麼呢?編譯,運行......        聯盟----Visita網站http://www.vista.org.tw  
---[ 發問前請先找找舊文章 ]---
        
axsoft
版主


發表:681
回覆:1056
積分:969
註冊:2002-03-13

發送簡訊給我
#6 引用回覆 回覆 發表時間:2003-03-19 12:36:36 IP:61.218.xxx.xxx 未訂閱

實戰DeviceIoControl 之三:製作磁盤鏡像文件

作者:bhw98 資料來源:www.CSDN.net Q DOS命令DISKCOPY給我很深的印象,現在也有許多「克隆」軟件,可以對磁盤進行全盤複製。我想,要製作磁盤鏡像文件,DeviceIoControl應該很有用武之地吧? A 是的。這裡舉一個製作軟盤鏡像文件,功能類似於「DISKCOPY」的例子。 本例實現其功能的核心代碼如下:
// 打開磁盤
HANDLE OpenDisk(LPCTSTR filename)
{
 HANDLE hDisk;     // 打開設備
 hDisk = ::CreateFile(filename,   // 文件名
  GENERIC_READ | GENERIC_WRITE,  // 讀寫方式
  FILE_SHARE_READ | FILE_SHARE_WRITE, // 共享方式
  NULL,     // 默認的安全描述符
  OPEN_EXISTING,    // 創建方式
  0,     // 不需設置文件屬性
  NULL);     // 不需參照模板文件     return hDisk;
}    // 獲取磁盤參數
BOOL GetDiskGeometry(HANDLE hDisk, PDISK_GEOMETRY lpGeometry)
{
    DWORD dwOutBytes;
 BOOL bResult;     // 用IOCTL_DISK_GET_DRIVE_GEOMETRY取磁盤參數
 bResult = ::DeviceIoControl(hDisk,  // 設備句柄
  IOCTL_DISK_GET_DRIVE_GEOMETRY,  // 取磁盤參數
  NULL, 0,    // 不需要輸入數據
  lpGeometry, sizeof(DISK_GEOMETRY), // 輸出數據緩衝區
  &dwOutBytes,    // 輸出數據長度
  (LPOVERLAPPED)NULL);   // 用同步I/O     return bResult;
}    // 從指定磁道開始讀磁盤
BOOL ReadTracks(HANDLE hDisk, PDISK_GEOMETRY lpGeometry, LPVOID pBuf, DWORD dwStartCylinder, DWORD     dwCylinderNumber)
{
    DWORD VirtBufSize;
    DWORD BytesRead;
                
 // 大小
 VirtBufSize =  lpGeometry->TracksPerCylinder * lpGeometry->SectorsPerTrack * lpGeometry->BytesPerSector;     // 偏移
 ::SetFilePointer(hDisk, VirtBufSize*dwStartCylinder, NULL, FILE_BEGIN);     return ::ReadFile(hDisk, pBuf, VirtBufSize*dwCylinderNumber, &BytesRead, NULL);
}    // 從指定磁道開始寫磁盤
BOOL WriteTracks(HANDLE hDisk, PDISK_GEOMETRY lpGeometry, LPVOID pBuf, DWORD dwStartCylinder, DWORD     dwCylinderNumber)
{
    DWORD VirtBufSize;
    DWORD BytesWritten;     // 大小
 VirtBufSize =  lpGeometry->TracksPerCylinder * lpGeometry->SectorsPerTrack * lpGeometry->BytesPerSector;     // 偏移
 ::SetFilePointer(hDisk, VirtBufSize*dwStartCylinder, NULL, FILE_BEGIN);        return ::WriteFile(hDisk, pBuf, VirtBufSize*dwCylinderNumber, &BytesWritten, NULL);
}    // 從指定磁道開始格式化磁盤
BOOL LowLevelFormatTracks(HANDLE hDisk, PDISK_GEOMETRY lpGeometry, DWORD dwStartCylinder, DWORD     dwCylinderNumber)
{
    FORMAT_PARAMETERS FormatParameters;
    PBAD_TRACK_NUMBER lpBadTrack;
    DWORD dwOutBytes;
    DWORD dwBufSize;
    BOOL bResult;        FormatParameters.MediaType = lpGeometry->MediaType;
    FormatParameters.StartCylinderNumber = dwStartCylinder;
    FormatParameters.EndCylinderNumber = dwStartCylinder   dwCylinderNumber - 1;
    FormatParameters.StartHeadNumber = 0;
    FormatParameters.EndHeadNumber = lpGeometry->TracksPerCylinder - 1;        dwBufSize = lpGeometry->TracksPerCylinder * sizeof(BAD_TRACK_NUMBER);        lpBadTrack = (PBAD_TRACK_NUMBER) new BYTE[dwBufSize];
                
 // 用IOCTL_DISK_FORMAT_TRACKS對連續磁道進行低級格式化
 bResult = ::DeviceIoControl(hDisk,   // 設備句柄
  IOCTL_DISK_FORMAT_TRACKS,   // 低級格式化
  &FormatParameters, sizeof(FormatParameters), // 輸入數據緩衝區
  lpBadTrack, dwBufSize,    // 輸出數據緩衝區
  &dwOutBytes,     // 輸出數據長度
  (LPOVERLAPPED)NULL);    // 用同步I/O        delete lpBadTrack;        return bResult;
}    // 將卷鎖定
BOOL LockVolume(HANDLE hDisk)
{
    DWORD dwOutBytes;
    BOOL bResult;     // 用FSCTL_LOCK_VOLUME鎖卷
 bResult = ::DeviceIoControl(hDisk,  // 設備句柄
  FSCTL_LOCK_VOLUME,   // 鎖卷
  NULL, 0,    // 不需要輸入數據
  NULL, 0,    // 不需要輸出數據
  &dwOutBytes,    // 輸出數據長度
  (LPOVERLAPPED)NULL);   // 用同步I/O        return bResult;
}    // 將卷解鎖
BOOL UnlockVolume(HANDLE hDisk)
{
    DWORD dwOutBytes;
    BOOL bResult;     // 用FSCTL_UNLOCK_VOLUME開卷鎖
 bResult = ::DeviceIoControl(hDisk,  // 設備句柄
  FSCTL_UNLOCK_VOLUME,   // 開卷鎖
  NULL, 0,    // 不需要輸入數據
  NULL, 0,    // 不需要輸出數據
  &dwOutBytes,    // 輸出數據長度
  (LPOVERLAPPED)NULL);   // 用同步I/O        return bResult;
}    // 將卷卸下
// 該操作使系統重新辨識磁盤,等效於重新插盤
BOOL DismountVolume(HANDLE hDisk)
{
    DWORD dwOutBytes;
    BOOL bResult;     // 用FSCTL_DISMOUNT_VOLUME卸卷
 bResult = ::DeviceIoControl(hDisk,  // 設備句柄
  FSCTL_DISMOUNT_VOLUME,   // 卸卷
  NULL, 0,    // 不需要輸入數據
  NULL, 0,    // 不需要輸出數據
  &dwOutBytes,    // 輸出數據長度
  (LPOVERLAPPED)NULL);   // 用同步I/O        return bResult;
}
將軟盤保存成鏡像文件的步驟簡單描述為: 1、創建空的鏡像文件。 2、調用OpenDisk打開軟盤。成功轉3,失敗轉8。 3、調用LockVolume將卷鎖定。成功轉4,失敗轉7。 4、調用GetDiskGeometry獲取參數。成功轉5,失敗轉6。 5、將磁盤參數寫入鏡像文件作為文件頭。調用ReadTracks按柱面讀出數據,保存在鏡像文件中。循環次數等於柱面數。 6、調用UnlockVolume將卷解鎖。 7、調用CloseDisk關閉軟盤。 8、關閉鏡像文件。 將鏡像文件載入軟盤的步驟簡單描述為: 1、打開鏡像文件。 2、調用OpenDisk打開軟盤。成功轉3,失敗轉11。 3、調用LockVolume將卷鎖定。成功轉4,失敗轉10。 4、調用GetDiskGeometry獲取參數。成功轉5,失敗轉9。 5、從鏡像文件中讀出文件頭,判斷兩個磁盤參數是否一致。不一致轉6,否則轉7。 6、調用LowLevelFormatTracks按柱面格式化軟盤。循環次數等於柱面數。成功轉7,失敗轉8。 7、從鏡像文件中讀出數據,並調用WriteTracks按柱面寫入磁盤。循環次數等於柱面數。 8、調用DismountVolume將卷卸下。 9、調用UnlockVolume將卷解鎖。 10、調用CloseDisk關閉軟盤。 11、關閉鏡像文件。 Q 我注意到,磁盤讀寫和格式化是按柱面進行的,有什麼道理嗎? A 沒有特別的原因,只是因為在這個例子中可以方便地顯示處理進度。 有一點需要特別提及,按絕對地址讀寫磁盤數據時,「最小單位」是扇區,地址一定要與扇區對齊,長度也要等於扇區長度的整數倍。比如,每扇區512字節,那麼起始地址和數據長度都應能被512整除才行。 Q 我忽然產生了一個偉大的想法,用絕對地址讀寫的方式使用磁盤,包括U盤啦,MO啦,而不是用現成的文件系統,那不是可以將數據保密了嗎? A 當然,只要你喜歡。可千萬別在你的系統盤上做試驗,否則......可別怪bhw98沒有提醒過你喔! Q 我知道怎麼測試光驅的傳輸速度了,就用上面的方法,讀出一定長度數據,除以所需時間,應該可以吧? A 可以。但取光盤參數時要用IOCTL_STORAGE_GET_MEDIA_TYPES_EX,我們已經探討過的。 聯盟----Visita網站http://www.vista.org.tw ---[ 發問前請先找找舊文章 ]---
axsoft
版主


發表:681
回覆:1056
積分:969
註冊:2002-03-13

發送簡訊給我
#7 引用回覆 回覆 發表時間:2003-03-19 12:41:08 IP:61.218.xxx.xxx 未訂閱

實戰DeviceIoControl 之四:獲取硬盤的詳細信息

作者:bhw98 資料來源:www.CSDN.net Q 用IOCTL_DISK_GET_DRIVE_GEOMETRY或IOCTL_STORAGE_GET_MEDIA_TYPES_EX只能得到很少的磁盤參數,我想獲得包括硬盤序列號在內的更加詳細的信息,有什麼辦法呀? A 確實,用你所說的I/O控制碼,只能得到最基本的磁盤參數。獲取磁盤出廠信息的I/O控制碼,微軟在VC/MFC環境中沒有開放,在DDK中可以發現一些線索。早先,Lynn McGuire寫了一個很出名的獲取IDE硬盤詳細信息的程序DiskID32,下面的例子是在其基礎上經過增刪和改進而成的。 本例中,我們要用到ATA/APAPI的IDENTIFY DEVICE指令。ATA/APAPI是國際組織T13起草和發佈的IDE/EIDE/UDMA硬盤及其它可移動存儲設備與主機接口的標準,至今已經到了ATA/APAPI-7版本。該接口標準規定了ATA/ATAPI設備的輸入輸出寄存器和指令集。欲瞭解更詳細的ATA/ATAPI技術資料,可訪問T13的站點。 用到的常量及數據結構有以下一些:
// IOCTL控制碼
// #define DFP_SEND_DRIVE_COMMAND   0x0007c084
#define  DFP_SEND_DRIVE_COMMAND   CTL_CODE(IOCTL_DISK_BASE, 0x0021, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
// #define DFP_RECEIVE_DRIVE_DATA   0x0007c088
#define  DFP_RECEIVE_DRIVE_DATA   CTL_CODE(IOCTL_DISK_BASE, 0x0022, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)    #define  FILE_DEVICE_SCSI         0x0000001b
#define  IOCTL_SCSI_MINIPORT_IDENTIFY      ((FILE_DEVICE_SCSI << 16)   0x0501)
#define  IOCTL_SCSI_MINIPORT 0x0004D008  //  see NTDDSCSI.H for definition    // ATA/ATAPI指令
#define  IDE_ATA_IDENTIFY    0xEC   //  ATA的ID指令(IDENTIFY DEVICE)    // IDE命令寄存器
typedef struct _IDEREGS
{
   BYTE bFeaturesReg;       // 特徵寄存器(用於SMART命令)
   BYTE bSectorCountReg;    // 扇區數目寄存器
   BYTE bSectorNumberReg;   // 開始扇區寄存器
   BYTE bCylLowReg;         // 開始柱面低字節寄存器
   BYTE bCylHighReg;        // 開始柱面高字節寄存器
   BYTE bDriveHeadReg;      // 驅動器/磁頭寄存器
   BYTE bCommandReg;        // 指令寄存器
   BYTE bReserved;          // 保留
} IDEREGS, *PIDEREGS, *LPIDEREGS;    // 從驅動程序返回的狀態
typedef struct _DRIVERSTATUS
{
   BYTE  bDriverError;      // 錯誤碼
   BYTE  bIDEStatus;        // IDE狀態寄存器
   BYTE  bReserved[2];      // 保留
   DWORD  dwReserved[2];    // 保留
} DRIVERSTATUS, *PDRIVERSTATUS, *LPDRIVERSTATUS;    // IDE設備IOCTL輸入數據結構
typedef struct _SENDCMDINPARAMS
{
   DWORD     cBufferSize;   // 緩衝區字節數
   IDEREGS   irDriveRegs;   // IDE寄存器組
   BYTE bDriveNumber;       // 驅動器號
   BYTE bReserved[3];       // 保留
   DWORD     dwReserved[4]; // 保留
   BYTE      bBuffer[1];    // 輸入緩衝區(此處象徵性地包含1字節)
} SENDCMDINPARAMS, *PSENDCMDINPARAMS, *LPSENDCMDINPARAMS;    // IDE設備IOCTL輸出數據結構
typedef struct _SENDCMDOUTPARAMS
{
   DWORD         cBufferSize;   // 緩衝區字節數
   DRIVERSTATUS  DriverStatus;  // 驅動程序返回狀態
   BYTE          bBuffer[1];    // 輸入緩衝區(此處象徵性地包含1字節)
} SENDCMDOUTPARAMS, *PSENDCMDOUTPARAMS, *LPSENDCMDOUTPARAMS;    // IDE的ID命令返回的數據
// 共512字節(256個WORD),這裡僅定義了一些感興趣的項(基本上依據ATA/ATAPI-4)
typedef struct _IDINFO
{
 USHORT  wGenConfig;     // WORD 0: 基本信息字
 USHORT  wNumCyls;     // WORD 1: 柱面數
 USHORT  wReserved2;     // WORD 2: 保留
 USHORT  wNumHeads;     // WORD 3: 磁頭數
 USHORT  wReserved4;        // WORD 4: 保留
 USHORT  wReserved5;        // WORD 5: 保留
 USHORT  wNumSectorsPerTrack;  // WORD 6: 每磁道扇區數
 USHORT  wVendorUnique[3];   // WORD 7-9: 廠家設定值
 CHAR    sSerialNumber[20];   // WORD 10-19:序列號
 USHORT  wBufferType;    // WORD 20: 緩衝類型
 USHORT  wBufferSize;    // WORD 21: 緩衝大小
 USHORT  wECCSize;     // WORD 22: ECC校驗大小
 CHAR    sFirmwareRev[8];   // WORD 23-26: 固件版本
 CHAR    sModelNumber[40];   // WORD 27-46: 內部型號
 USHORT  wMoreVendorUnique;   // WORD 47: 廠家設定值
 USHORT  wReserved48;    // WORD 48: 保留
 struct {
  USHORT  reserved1:8;
  USHORT  DMA:1;     // 1=支持DMA
  USHORT  LBA:1;     // 1=支持LBA
  USHORT  DisIORDY:1;    // 1=可不使用IORDY
  USHORT  IORDY:1;    // 1=支持IORDY
  USHORT  SoftReset:1;   // 1=需要ATA軟啟動
  USHORT  Overlap:1;    // 1=支持重疊操作
  USHORT  Queue:1;    // 1=支持命令隊列
  USHORT  InlDMA:1;    // 1=支持交叉存取DMA
 } wCapabilities;     // WORD 49: 一般能力
 USHORT  wReserved1;     // WORD 50: 保留
 USHORT  wPIOTiming;     // WORD 51: PIO時序
 USHORT  wDMATiming;     // WORD 52: DMA時序
 struct {
  USHORT  CHSNumber:1;   // 1=WORD 54-58有效
  USHORT  CycleNumber:1;   // 1=WORD 64-70有效
  USHORT  UnltraDMA:1;   // 1=WORD 88有效
  USHORT  reserved:13;
 } wFieldValidity;     // WORD 53: 後續字段有效性標誌
 USHORT  wNumCurCyls;    // WORD 54: CHS可尋址的柱面數
 USHORT  wNumCurHeads;    // WORD 55: CHS可尋址的磁頭數
 USHORT  wNumCurSectorsPerTrack;  // WORD 56: CHS可尋址每磁道扇區數
 USHORT  wCurSectorsLow;    // WORD 57: CHS可尋址的扇區數低位字
 USHORT  wCurSectorsHigh;   // WORD 58: CHS可尋址的扇區數高位字
 struct {
  USHORT  CurNumber:8;   // 當前一次性可讀寫扇區數
  USHORT  Multi:1;    // 1=已選擇多扇區讀寫
  USHORT  reserved1:7;
 } wMultSectorStuff;     // WORD 59: 多扇區讀寫設定
 ULONG  dwTotalSectors;    // WORD 60-61: LBA可尋址的扇區數
 USHORT  wSingleWordDMA;    // WORD 62: 單字節DMA支持能力
 struct {
  USHORT  Mode0:1;    // 1=支持模式0 (4.17Mb/s)
  USHORT  Mode1:1;    // 1=支持模式1 (13.3Mb/s)
  USHORT  Mode2:1;    // 1=支持模式2 (16.7Mb/s)
  USHORT  Reserved1:5;
  USHORT  Mode0Sel:1;    // 1=已選擇模式0
  USHORT  Mode1Sel:1;    // 1=已選擇模式1
  USHORT  Mode2Sel:1;    // 1=已選擇模式2
  USHORT  Reserved2:5;
 } wMultiWordDMA;     // WORD 63: 多字節DMA支持能力
 struct {
  USHORT  AdvPOIModes:8;   // 支持高級POI模式數
  USHORT  reserved:8;
 } wPIOCapacity;      // WORD 64: 高級PIO支持能力
 USHORT  wMinMultiWordDMACycle;  // WORD 65: 多字節DMA傳輸週期的最小值
 USHORT  wRecMultiWordDMACycle;  // WORD 66: 多字節DMA傳輸週期的建議值
 USHORT  wMinPIONoFlowCycle;   // WORD 67: 無流控制時PIO傳輸週期的最小值
 USHORT  wMinPOIFlowCycle;   // WORD 68: 有流控制時PIO傳輸週期的最小值
 USHORT  wReserved69[11];   // WORD 69-79: 保留
 struct {
  USHORT  Reserved1:1;
  USHORT  ATA1:1;     // 1=支持ATA-1
  USHORT  ATA2:1;     // 1=支持ATA-2
  USHORT  ATA3:1;     // 1=支持ATA-3
  USHORT  ATA4:1;     // 1=支持ATA/ATAPI-4
  USHORT  ATA5:1;     // 1=支持ATA/ATAPI-5
  USHORT  ATA6:1;     // 1=支持ATA/ATAPI-6
  USHORT  ATA7:1;     // 1=支持ATA/ATAPI-7
  USHORT  ATA8:1;     // 1=支持ATA/ATAPI-8
  USHORT  ATA9:1;     // 1=支持ATA/ATAPI-9
  USHORT  ATA10:1;    // 1=支持ATA/ATAPI-10
  USHORT  ATA11:1;    // 1=支持ATA/ATAPI-11
  USHORT  ATA12:1;    // 1=支持ATA/ATAPI-12
  USHORT  ATA13:1;    // 1=支持ATA/ATAPI-13
  USHORT  ATA14:1;    // 1=支持ATA/ATAPI-14
  USHORT  Reserved2:1;
 } wMajorVersion;     // WORD 80: 主版本
 USHORT  wMinorVersion;    // WORD 81: 副版本
 USHORT  wReserved82[6];    // WORD 82-87: 保留
 struct {
  USHORT  Mode0:1;    // 1=支持模式0 (16.7Mb/s)
  USHORT  Mode1:1;    // 1=支持模式1 (25Mb/s)
  USHORT  Mode2:1;    // 1=支持模式2 (33Mb/s)
  USHORT  Mode3:1;    // 1=支持模式3 (44Mb/s)
  USHORT  Mode4:1;    // 1=支持模式4 (66Mb/s)
  USHORT  Mode5:1;    // 1=支持模式5 (100Mb/s)
  USHORT  Mode6:1;    // 1=支持模式6 (133Mb/s)
  USHORT  Mode7:1;    // 1=支持模式7 (166Mb/s) ???
  USHORT  Mode0Sel:1;    // 1=已選擇模式0
  USHORT  Mode1Sel:1;    // 1=已選擇模式1
  USHORT  Mode2Sel:1;    // 1=已選擇模式2
  USHORT  Mode3Sel:1;    // 1=已選擇模式3
  USHORT  Mode4Sel:1;    // 1=已選擇模式4
  USHORT  Mode5Sel:1;    // 1=已選擇模式5
  USHORT  Mode6Sel:1;    // 1=已選擇模式6
  USHORT  Mode7Sel:1;    // 1=已選擇模式7
 } wUltraDMA;      // WORD 88:  Ultra DMA支持能力
 USHORT    wReserved89[167];   // WORD 89-255
} IDINFO, *PIDINFO;    // SCSI驅動所需的輸入輸出共用的結構
typedef struct _SRB_IO_CONTROL
{
   ULONG HeaderLength;  // 頭長度
   UCHAR Signature[8];  // 特徵名稱
   ULONG Timeout;   // 超時時間
   ULONG ControlCode;  // 控制碼
   ULONG ReturnCode;  // 返回碼
   ULONG Length;   // 緩衝區長度
} SRB_IO_CONTROL, *PSRB_IO_CONTROL;    需要引起注意的是IDINFO第57-58 WORD (CHS可尋址的扇區數),因為不滿足32位對齊的要求,不可定義為一個ULONG字段。Lynn McGuire的程序裡正是由於定義為一個ULONG字段,導致該結構不可用。    以下是核心代碼:    // 打開設備
// filename: 設備的「文件名」
HANDLE OpenDevice(LPCTSTR filename)
{
 HANDLE hDevice;     // 打開設備
 hDevice= ::CreateFile(filename,   // 文件名
  GENERIC_READ | GENERIC_WRITE,  // 讀寫方式
  FILE_SHARE_READ | FILE_SHARE_WRITE, // 共享方式
  NULL,     // 默認的安全描述符
  OPEN_EXISTING,    // 創建方式
  0,     // 不需設置文件屬性
  NULL);     // 不需參照模板文件     return hDevice;
}    // 向驅動發「IDENTIFY DEVICE」命令,獲得設備信息
// hDevice: 設備句柄
// pIdInfo:  設備信息結構指針
BOOL IdentifyDevice(HANDLE hDevice, PIDINFO pIdInfo)
{
 PSENDCMDINPARAMS pSCIP;  // 輸入數據結構指針
 PSENDCMDOUTPARAMS pSCOP; // 輸出數據結構指針
 DWORD dwOutBytes;   // IOCTL輸出數據長度
 BOOL bResult;    // IOCTL返回值     // 申請輸入/輸出數據結構空間
    pSCIP = (PSENDCMDINPARAMS)::GlobalAlloc(LMEM_ZEROINIT, sizeof(SENDCMDINPARAMS)-1);
    pSCOP = (PSENDCMDOUTPARAMS)::GlobalAlloc(LMEM_ZEROINIT, sizeof(SENDCMDOUTPARAMS) sizeof(IDINFO)-1);     // 指定ATA/ATAPI命令的寄存器值
// pSCIP->irDriveRegs.bFeaturesReg = 0;
// pSCIP->irDriveRegs.bSectorCountReg = 0;
// pSCIP->irDriveRegs.bSectorNumberReg = 0;
// pSCIP->irDriveRegs.bCylLowReg = 0;
// pSCIP->irDriveRegs.bCylHighReg = 0;
// pSCIP->irDriveRegs.bDriveHeadReg = 0;
 pSCIP->irDriveRegs.bCommandReg = IDE_ATA_IDENTIFY;     // 指定輸入/輸出數據緩衝區大小
 pSCIP->cBufferSize = 0;
 pSCOP->cBufferSize = sizeof(IDINFO);     // IDENTIFY DEVICE
 bResult = ::DeviceIoControl(hDevice,  // 設備句柄
  DFP_RECEIVE_DRIVE_DATA,   // 指定IOCTL
  pSCIP, sizeof(SENDCMDINPARAMS) - 1, // 輸入數據緩衝區
  pSCOP, sizeof(SENDCMDOUTPARAMS)   sizeof(IDINFO) - 1, // 輸出數據緩衝區
  &dwOutBytes,    // 輸出數據長度
  (LPOVERLAPPED)NULL);   // 用同步I/O     // 複製設備參數結構
 ::memcpy(pIdInfo, pSCOP->bBuffer, sizeof(IDINFO));     // 釋放輸入/輸出數據空間
 ::GlobalFree(pSCOP);
 ::GlobalFree(pSCIP);     return bResult;
}    // 向SCSI MINI-PORT驅動發「IDENTIFY DEVICE」命令,獲得設備信息
// hDevice: 設備句柄
// pIdInfo:  設備信息結構指針
BOOL IdentifyDeviceAsScsi(HANDLE hDevice, int nDrive, PIDINFO pIdInfo)
{
 PSENDCMDINPARAMS pSCIP;  // 輸入數據結構指針
 PSENDCMDOUTPARAMS pSCOP; // 輸出數據結構指針
 PSRB_IO_CONTROL pSRBIO;  // SCSI輸入輸出數據結構指針
 DWORD dwOutBytes;   // IOCTL輸出數據長度
 BOOL bResult;    // IOCTL返回值     // 申請輸入/輸出數據結構空間
    pSRBIO = (PSRB_IO_CONTROL)::GlobalAlloc(LMEM_ZEROINIT, sizeof(SRB_IO_CONTROL) sizeof(SENDCMDOUTPARAMS) sizeof(IDINFO)-1);
    pSCIP = (PSENDCMDINPARAMS)((char *)pSRBIO sizeof(SRB_IO_CONTROL));
    pSCOP = (PSENDCMDOUTPARAMS)((char *)pSRBIO sizeof(SRB_IO_CONTROL));     // 填充輸入/輸出數據
 pSRBIO->HeaderLength = sizeof(SRB_IO_CONTROL);
 pSRBIO->Timeout = 10000;
 pSRBIO->Length = sizeof(SENDCMDOUTPARAMS) sizeof(IDINFO)-1;
 pSRBIO->ControlCode = IOCTL_SCSI_MINIPORT_IDENTIFY;
 ::strncpy ((char *)pSRBIO->Signature, "SCSIDISK", 8);     // 指定ATA/ATAPI命令的寄存器值
// pSCIP->irDriveRegs.bFeaturesReg = 0;
// pSCIP->irDriveRegs.bSectorCountReg = 0;
// pSCIP->irDriveRegs.bSectorNumberReg = 0;
// pSCIP->irDriveRegs.bCylLowReg = 0;
// pSCIP->irDriveRegs.bCylHighReg = 0;
// pSCIP->irDriveRegs.bDriveHeadReg = 0;
 pSCIP->irDriveRegs.bCommandReg = IDE_ATA_IDENTIFY;
 pSCIP->bDriveNumber = nDrive;     // IDENTIFY DEVICE
 bResult = ::DeviceIoControl(hDevice,  // 設備句柄
  IOCTL_SCSI_MINIPORT,   // 指定IOCTL
  pSRBIO, sizeof(SRB_IO_CONTROL)  sizeof(SENDCMDINPARAMS) - 1, // 輸入數據緩衝區
  pSRBIO, sizeof(SRB_IO_CONTROL)  sizeof(SENDCMDOUTPARAMS)   sizeof(IDINFO) - 1, // 輸出數據緩衝區
  &dwOutBytes,  // 輸出數據長度
  (LPOVERLAPPED)NULL); // 用同步I/O
 
 // 複製設備參數結構
 ::memcpy(pIdInfo, pSCOP->bBuffer, sizeof(IDINFO));     // 釋放輸入/輸出數據空間
 ::GlobalFree(pSRBIO);     return bResult;
}    // 將串中的字符兩兩顛倒
// 原因是ATA/ATAPI中的WORD,與Windows採用的字節順序相反
// 驅動程序中已經將收到的數據全部反過來,我們來個負負得正
void AdjustString(char* str, int len)
{
 char ch;
 int i;     // 兩兩顛倒
 for(i=0;i=0 && str[i]==' ')
 {
  str[i] = '\0';
  i--;
 }
}    // 讀取IDE硬盤的設備信息,必須有足夠權限
// nDrive: 驅動器號(0=第一個硬盤,1=0=第二個硬盤,......)
// pIdInfo: 設備信息結構指針
BOOL GetPhysicalDriveInfoInNT(int nDrive, PIDINFO pIdInfo)
{
 HANDLE hDevice;   // 設備句柄
 BOOL bResult;   // 返回結果
 char szFileName[20]; // 文件名     ::sprintf(szFileName,"\\\\.\\PhysicalDrive%d", nDrive);     hDevice = ::OpenDevice(szFileName);     if(hDevice == INVALID_HANDLE_VALUE)
 {
  return FALSE;
 }     // IDENTIFY DEVICE
 bResult = ::IdentifyDevice(hDevice, pIdInfo);     if(bResult)
 {
  // 調整字符串
  ::AdjustString(pIdInfo->sSerialNumber, 20);
  ::AdjustString(pIdInfo->sModelNumber, 40);
  ::AdjustString(pIdInfo->sFirmwareRev, 8);
 }     ::CloseHandle (hDevice);     return bResult;
}    // 用SCSI驅動讀取IDE硬盤的設備信息,不受權限制約
// nDrive: 驅動器號(0=Primary Master, 1=Promary Slave, 2=Secondary master, 3=Secondary slave)
// pIdInfo: 設備信息結構指針
BOOL GetIdeDriveAsScsiInfoInNT(int nDrive, PIDINFO pIdInfo)
{
 HANDLE hDevice;   // 設備句柄
 BOOL bResult;   // 返回結果
 char szFileName[20]; // 文件名     ::sprintf(szFileName,"\\\\.\\Scsi%d:", nDrive/2);     hDevice = ::OpenDevice(szFileName);
      
 if(hDevice == INVALID_HANDLE_VALUE)
 {
  return FALSE;
 }     // IDENTIFY DEVICE
 bResult = ::IdentifyDeviceAsScsi(hDevice, nDrive%2, pIdInfo);     // 檢查是不是空串
 if(pIdInfo->sModelNumber[0]=='\0')
 {
  bResult = FALSE;
 }     if(bResult)
 {
  // 調整字符串
  ::AdjustString(pIdInfo->sSerialNumber, 20);
  ::AdjustString(pIdInfo->sModelNumber, 40);
  ::AdjustString(pIdInfo->sFirmwareRev, 8);
 }     return bResult;
}
Q 我注意到ATA/ATAPI裡,以及DiskID32里,有一個「IDENTIFY PACKET DEVICE」指令,與「IDENTIFY DEVICE」有什麼區別? A IDENTIFY DEVICE專門用於固定硬盤,而IDENTIFY PACKET DEVICE用於可移動存儲設備如CDROM、CF、MO、ZIP、TAPE等。因為驅動程序的原因,實際上用本例的方法,不管是IDENTIFY DEVICE也好,IDENTIFY PACKET DEVICE也好,獲取可移動存儲設備的詳細信息,一般是做不到的。而且除了IDE硬盤,對SCSI、USB等接口的硬盤也不起作用。除非廠商提供的驅動支持這樣的功能。 Q ATA/ATAPI有很多指令,如READ SECTORS, WRITE SECTORS, SECURITY, SLEEP, STANDBY等,利用上述方法,是否可進行相應操作? A 應該沒問題。但切記,要慎重慎重再慎重! Q 關於權限問題,請解釋一下好嗎? A 在NT/2000/XP下,administrator可以管理設備,上述兩種訪問驅動的方法都行。但在user身份下,或者登錄到域後,用戶無法訪問PhysicalDrive驅動的核心層,但SCSI MINI-PORT驅動卻可以。目前是可以,不知道Windows以後的版本是否支持。因為這肯定是一個安全隱患。 另外,我們著重討論NT/2000/XP中DeviceIoControl的應用,如果需要在98/ME中得到包括硬盤序列號在內的更加詳細的信息,請參考DiskID32。 源碼:IdeDiskInfo.zip (25KB) Lynn McGuire的DiskID32.zip(30KB) T13官方網站 http://www.t13.org 聯盟----Visita網站http://www.vista.org.tw ---[ 發問前請先找找舊文章 ]--- 發表人 - axsoft 於 2003/03/19 12:42:42
axsoft
版主


發表:681
回覆:1056
積分:969
註冊:2002-03-13

發送簡訊給我
#8 引用回覆 回覆 發表時間:2003-03-19 12:47:43 IP:61.218.xxx.xxx 未訂閱

實戰DeviceIoControl 之五:列舉已安裝的存儲設備

作者:bhw98 資料來源:www.CSDN.net Q 前幾次我們討論的都是設備名比較清楚的情況,有了設備名(路徑),就可以直接調用CreateFile打開設備,進行它所支持的I/O操作了。如果事先並不能確切知道設備名,如何去訪問設備呢? A 訪問設備必須用設備句柄,而得到設備句柄必須知道設備路徑,這個套路以你我之力是改變不了的。每個設備都有它所屬類型的GUID,我們順著這個GUID就能獲得設備路徑。 GUID是同類或同種設備的全球唯一識別碼,它是一個128 bit(16字節)的整形數,真實面目為
typedef struct _GUID
{
    unsigned long  Data1;
    unsigned short Data2;
    unsigned short Data3;
    unsigned char  Data4[8];
} GUID, *PGUID;
例如,Disk類的GUID為「53f56307-b6bf-11d0-94f2-00a0c91efb8b」,在我們的程序裡可以定義為 const GUID DiskClassGuid = {0x53f56307L, 0xb6bf, 0x11d0, {0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b)}; 或者用一個宏來定義 DEFINE_GUID(DiskClassGuid, 0x53f56307L, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b); 通過GUID找出設備路徑,需要用到一組設備管理的API函數 SetupDiGetClassDevs, SetupDiEnumDeviceInterfaces, SetupDiGetInterfaceDeviceDetail, SetupDiDestroyDeviceInfoList, 以及結構SP_DEVICE_INTERFACE_DATA, SP_DEVICE_INTERFACE_DETAIL_DATA。 有關信息請查閱MSDN,這裡就不詳細介紹了。 實現GUID到設備路徑的代碼如下:
// SetupDiGetInterfaceDeviceDetail所需要的輸出長度,定義足夠大
#define INTERFACE_DETAIL_SIZE (1024)    // 根據GUID獲得設備路徑
// lpGuid: GUID指針
// pszDevicePath: 設備路徑指針的指針
// 返回: 成功得到的設備路徑個數,可能不止1個
int GetDevicePath(LPGUID lpGuid, LPTSTR* pszDevicePath)
{
 HDEVINFO hDevInfoSet;
 SP_DEVICE_INTERFACE_DATA ifdata;
 PSP_DEVICE_INTERFACE_DETAIL_DATA pDetail;
 int nCount;
 BOOL bResult;     // 取得一個該GUID相關的設備信息集句柄
 hDevInfoSet = ::SetupDiGetClassDevs(lpGuid,  // class GUID 
  NULL,     // 無關鍵字 
  NULL,     // 不指定父窗口句柄 
  DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); // 目前存在的設備     // 失敗...
 if(hDevInfoSet == INVALID_HANDLE_VALUE)
 {
  return 0;
 }     // 申請設備接口數據空間
 pDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA)::GlobalAlloc(LMEM_ZEROINIT, INTERFACE_DETAIL_SIZE);
 
 pDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);     nCount = 0;
 bResult = TRUE;
 
 // 設備序號=0,1,2... 逐一測試設備接口,到失敗為止
 while (bResult)
 {
  ifdata.cbSize=sizeof(ifdata);
 
  // 枚舉符合該GUID的設備接口
  bResult = ::SetupDiEnumDeviceInterfaces(
   hDevInfoSet,  // 設備信息集句柄
   NULL,   // 不需額外的設備描述
   lpGuid,   // GUID
   (ULONG)nCount,  // 設備信息集裡的設備序號
   &ifdata);  // 設備接口信息      if(bResult)
  {
   // 取得該設備接口的細節(設備路徑)
   bResult = SetupDiGetInterfaceDeviceDetail(
    hDevInfoSet,  // 設備信息集句柄
    &ifdata,  // 設備接口信息
    pDetail,  // 設備接口細節(設備路徑)
    INTERFACE_DETAIL_SIZE, // 輸出緩衝區大小
    NULL,   // 不需計算輸出緩衝區大小(直接用設定值)
    NULL);   // 不需額外的設備描述       if(bResult)
   {
    // 複製設備路徑到輸出緩衝區
    ::strcpy(pszDevicePath[nCount], pDetail->DevicePath);        // 調整計數值
    nCount  ;
   }
  }
 }     // 釋放設備接口數據空間
 ::GlobalFree(pDetail);     // 關閉設備信息集句柄
 ::SetupDiDestroyDeviceInfoList(hDevInfoSet);     return nCount;
}    調用GetDevicePath函數時要注意,pszDevicePath是個指向字符串指針的指針,例如可以這樣     int i;
 char* szDevicePath[MAX_DEVICE];  // 設備路徑     // 分配需要的空間
 for(i=0; i
 // 釋放空間
 for(i=0;i
// IOCTL控制碼
#define IOCTL_STORAGE_QUERY_PROPERTY   CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS)    // 存儲設備的總線類型
typedef enum _STORAGE_BUS_TYPE {
    BusTypeUnknown = 0x00,
    BusTypeScsi,
    BusTypeAtapi,
    BusTypeAta,
    BusType1394,
    BusTypeSsa,
    BusTypeFibre,
    BusTypeUsb,
    BusTypeRAID,
    BusTypeMaxReserved = 0x7F
} STORAGE_BUS_TYPE, *PSTORAGE_BUS_TYPE;    // 查詢存儲設備屬性的類型
typedef enum _STORAGE_QUERY_TYPE {
    PropertyStandardQuery = 0,          // 讀取描述
    PropertyExistsQuery,                // 測試是否支持
    PropertyMaskQuery,                  // 讀取指定的描述
    PropertyQueryMaxDefined  // 驗證數據
} STORAGE_QUERY_TYPE, *PSTORAGE_QUERY_TYPE;    // 查詢存儲設備還是適配器屬性
typedef enum _STORAGE_PROPERTY_ID {
    StorageDeviceProperty = 0,   // 查詢設備屬性
    StorageAdapterProperty   // 查詢適配器屬性
} STORAGE_PROPERTY_ID, *PSTORAGE_PROPERTY_ID;    // 查詢屬性輸入的數據結構
typedef struct _STORAGE_PROPERTY_QUERY {
 STORAGE_PROPERTY_ID PropertyId;  // 設備/適配器
 STORAGE_QUERY_TYPE QueryType;  // 查詢類型 
    UCHAR AdditionalParameters[1];  // 額外的數據(僅定義了象徵性的1個字節)
} STORAGE_PROPERTY_QUERY, *PSTORAGE_PROPERTY_QUERY;    // 查詢屬性輸出的數據結構
typedef struct _STORAGE_DEVICE_DESCRIPTOR {
    ULONG Version;  // 版本
    ULONG Size;   // 結構大小
    UCHAR DeviceType;  // 設備類型
    UCHAR DeviceTypeModifier; // SCSI-2額外的設備類型
    BOOLEAN RemovableMedia; // 是否可移動
    BOOLEAN CommandQueueing; // 是否支持命令隊列
    ULONG VendorIdOffset;  // 廠家設定值的偏移
    ULONG ProductIdOffset;  // 產品ID的偏移
    ULONG ProductRevisionOffset; // 產品版本的偏移
    ULONG SerialNumberOffset;  // 序列號的偏移
    STORAGE_BUS_TYPE BusType;  // 總線類型
    ULONG RawPropertiesLength;  // 額外的屬性數據長度
    UCHAR RawDeviceProperties[1]; // 額外的屬性數據(僅定義了象徵性的1個字節)
} STORAGE_DEVICE_DESCRIPTOR, *PSTORAGE_DEVICE_DESCRIPTOR;    // 取設備屬性信息
// hDevice -- 設備句柄
// pDevDesc -- 輸出的設備描述和屬性信息緩衝區指針(包含連接在一起的兩部分)
BOOL GetDriveProperty(HANDLE hDevice, PSTORAGE_DEVICE_DESCRIPTOR pDevDesc)
{
 STORAGE_PROPERTY_QUERY Query; // 查詢輸入參數
 DWORD dwOutBytes;    // IOCTL輸出數據長度
 BOOL bResult;     // IOCTL返回值     // 指定查詢方式
 Query.PropertyId = StorageDeviceProperty;
 Query.QueryType = PropertyStandardQuery;     // 用IOCTL_STORAGE_QUERY_PROPERTY取設備屬性信息
 bResult = ::DeviceIoControl(hDevice,  // 設備句柄
  IOCTL_STORAGE_QUERY_PROPERTY,  // 取設備屬性信息
  &Query, sizeof(STORAGE_PROPERTY_QUERY), // 輸入數據緩衝區
  pDevDesc, pDevDesc->Size,  // 輸出數據緩衝區
  &dwOutBytes,    // 輸出數據長度
  (LPOVERLAPPED)NULL);   // 用同步I/O      return bResult;
}
Q 我用這個方法從IOCTL_STORAGE_QUERY_PROPERTY返回的數據中,沒有得到CDROM和USB接口的外置硬盤的序列號、產品ID等信息。但從設備路徑上看,明明是有這些信息的,為什麼它沒有填充到STORAGE_DEVICE_DESCRIPTOR中呢?再就是為什麼硬盤序列號本是「D22P7KHE 」,為什麼它填充的是「3146563447534558202020202020202020202020」這種形式呢? A 對這兩個問題我也是心存疑惑,但又不敢妄加猜測,正琢磨著向微軟請教呢。 源碼:StorageEnum.zip (23KB) 聯盟----Visita網站http://www.vista.org.tw ---[ 發問前請先找找舊文章 ]---
axsoft
版主


發表:681
回覆:1056
積分:969
註冊:2002-03-13

發送簡訊給我
#9 引用回覆 回覆 發表時間:2003-03-19 12:53:07 IP:61.218.xxx.xxx 未訂閱

實戰DeviceIoControl 之六:訪問物理端口

關鍵字:DeviceIoControl,物理端口,設備驅動程序,IOPM,CMOS,speaker 作者:bhw98 資料來源:www.CSDN.net Q 在NT/2000/XP中,如何讀取CMOS數據? Q 在NT/2000/XP中,如何控制speaker發聲? Q 在NT/2000/XP中,如何直接訪問物理端口? A 看似小小問題,難倒多少好漢! NT/2000/XP從安全性、可靠性、穩定性上考慮,應用程序和操作系統是分開的,操作系統代碼運行在核心態,有權訪問系統數據和硬件,能執行特權指令;應用程序運行在用戶態,能夠使用的接口和訪問系統數據的權限都受到嚴格限制。當用戶程序調用系統服務時,處理器捕獲該調用,然後把調用的線程切換到核心態。當系統服務完成後,操作系統將線程描述表切換回用戶態,調用者繼續運行。 想在用戶態應用程序中實現I/O讀寫,直接存取硬件,可以通過編寫驅動程序,實現CreateFile、CloseHandle、 DeviceIOControl、ReadFile、WriteFile等功能。從Windows 2000開始,引入WDM核心態驅動程序的概念。 下面是我寫的一個非常簡單的驅動程序,可實現字節型端口I/O。
#include < ntddk.h >    // 設備類型定義
// 0-32767被Microsoft佔用,用戶自定義可用32768-65535
#define FILE_DEVICE_MYPORT 0x0000f000    // I/O控制碼定義
// 0-2047被Microsoft佔用,用戶自定義可用2048-4095 
#define MYPORT_IOCTL_BASE 0xf00    #define IOCTL_MYPORT_READ_BYTE  CTL_CODE(FILE_DEVICE_MYPORT, MYPORT_IOCTL_BASE, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_MYPORT_WRITE_BYTE  CTL_CODE(FILE_DEVICE_MYPORT, MYPORT_IOCTL_BASE 1, METHOD_BUFFERED, FILE_ANY_ACCESS)    // IOPM是65536個端口的位屏蔽矩陣,包含8192字節(8192 x 8 = 65536)
// 0 bit: 允許應用程序訪問對應端口
// 1 bit: 禁止應用程序訪問對應端口    #define IOPM_SIZE 8192    typedef UCHAR IOPM[IOPM_SIZE];    IOPM *pIOPM = NULL;    // 設備名(要求以UNICODE表示)
const WCHAR NameBuffer[] = L"\\Device\\MyPort";
const WCHAR DOSNameBuffer[] = L"\\DosDevices\\MyPort";    // 這是兩個在ntoskrnl.exe中的未見文檔的服務例程
// 沒有現成的已經說明它們原型的頭文件,我們自己聲明
void Ke386SetIoAccessMap(int, IOPM *);
void Ke386IoSetAccessProcess(PEPROCESS, int);    // 函數原型預先說明
NTSTATUS MyPortDispatch(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
void MyPortUnload(IN PDRIVER_OBJECT DriverObject);    // 驅動程序入口,由系統自動調用,就像WIN32應用程序的WinMain
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
 PDEVICE_OBJECT deviceObject;
 NTSTATUS status;
 UNICODE_STRING uniNameString, uniDOSString;     // 為IOPM分配內存
 pIOPM = MmAllocateNonCachedMemory(sizeof(IOPM));
 if(pIOPM == 0)
 {
  return STATUS_INSUFFICIENT_RESOURCES;
 }     // IOPM全部初始化為0(允許訪問所有端口)
 RtlZeroMemory(pIOPM, sizeof(IOPM));
    
 // 將IOPM加載到當前進程
 Ke386IoSetAccessProcess(PsGetCurrentProcess(), 1);
    Ke386SetIoAccessMap(1, pIOPM);     // 指定驅動名字
 RtlInitUnicodeString(&uniNameString, NameBuffer);
 RtlInitUnicodeString(&uniDOSString, DOSNameBuffer);     // 創建設備
 status = IoCreateDevice(DriverObject, 0,
     &uniNameString,
     FILE_DEVICE_MYPORT,
     0, FALSE, &deviceObject);     if(!NT_SUCCESS(status))
 {
  return status;
 }     // 創建WIN32應用程序需要的符號連接
 status = IoCreateSymbolicLink (&uniDOSString, &uniNameString);     if (!NT_SUCCESS(status))
 {
  return status;
 }     // 指定驅動程序有關操作的模塊入口(函數指針)
 // 涉及以下兩個模塊:MyPortDispatch和MyPortUnload
    DriverObject->MajorFunction[IRP_MJ_CREATE]         =
    DriverObject->MajorFunction[IRP_MJ_CLOSE]          =
    DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = MyPortDispatch;
 DriverObject->DriverUnload = MyPortUnload;        return STATUS_SUCCESS;
}    // IRP處理模塊
NTSTATUS MyPortDispatch(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
  PIO_STACK_LOCATION IrpStack;
  ULONG              dwInputBufferLength;
  ULONG              dwOutputBufferLength;
  ULONG              dwIoControlCode;
  PULONG               pvIOBuffer;
  NTSTATUS           ntStatus;      // 填充幾個默認值
  Irp->IoStatus.Status = STATUS_SUCCESS; // 返回狀態
  Irp->IoStatus.Information = 0;   // 輸出長度
  
  IrpStack = IoGetCurrentIrpStackLocation(Irp);      // Get the pointer to the input/output buffer and it's length      // 輸入輸出共用的緩衝區
  // 因為我們在IOCTL中指定了METHOD_BUFFERED,
  pvIOBuffer = Irp->AssociatedIrp.SystemBuffer;     switch (IrpStack->MajorFunction)
 {
  case IRP_MJ_CREATE:  // 與WIN32應用程序中的CreateFile對應
   break;
    
  case IRP_MJ_CLOSE:  // 與WIN32應用程序中的CloseHandle對應
   break;
  
  case IRP_MJ_DEVICE_CONTROL:  // 與WIN32應用程序中的DeviceIoControl對應
   dwIoControlCode = IrpStack->Parameters.DeviceIoControl.IoControlCode;
   switch (dwIoControlCode)
   {
    // 我們約定,緩衝區共兩個DWORD,第一個DWORD為端口,第二個DWORD為數據
    // 一般做法是專門定義一個結構,此處簡單化處理了
    case IOCTL_MYPORT_READ_BYTE:  // 從端口讀字節
     pvIOBuffer[1] = _inp(pvIOBuffer[0]);
     Irp->IoStatus.Information = 8;  // 輸出長度為8
     break;
    case IOCTL_MYPORT_WRITE_BYTE:  // 寫字節到端口
     _outp(pvIOBuffer[0], pvIOBuffer[1]);
     break;
    default:  // 不支持的IOCTL
     Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
   }
 }     ntStatus = Irp->IoStatus.Status;     IoCompleteRequest (Irp, IO_NO_INCREMENT);     return ntStatus;
}    // 刪除驅動
void MyPortUnload(IN PDRIVER_OBJECT DriverObject)
{
 UNICODE_STRING uniDOSString;     if(pIOPM)
 {
  // 釋放IOPM佔用的空間
  MmFreeNonCachedMemory(pIOPM, sizeof(IOPM));
 }     RtlInitUnicodeString(&uniDOSString, DOSNameBuffer);     // 刪除符號連接和設備
 IoDeleteSymbolicLink (&uniDOSString);
 IoDeleteDevice(DriverObject->DeviceObject);
}    下面給出實現設備驅動程序的動態加載的源碼。動態加載的好處是,你不用做任何添加新硬件的操作,也不用編輯註冊表,更不用重新啟動計算機。    // 安裝驅動並啟動服務
// lpszDriverPath:  驅動程序路徑
// lpszServiceName: 服務名 
BOOL StartDriver(LPCTSTR lpszDriverPath, LPCTSTR lpszServiceName)
{
 SC_HANDLE hSCManager;  // 服務控制管理器句柄
 SC_HANDLE hService;  // 服務句柄
 BOOL bResult = FALSE;  // 返回值     // 打開服務控制管理器
 hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);     // 打開失敗...
 if(hSCManager)
 {
  // 創建服務
  hService = CreateService(hSCManager,
     lpszServiceName,
     lpszServiceName,
     SERVICE_ALL_ACCESS,
     SERVICE_KERNEL_DRIVER,
     SERVICE_DEMAND_START,
     SERVICE_ERROR_NORMAL,
     lpszDriverPath,
     NULL,
     NULL,
     NULL,
     NULL,
     NULL);      if(hService)
  {
   // 啟動服務
      bResult = StartService(hService, 0, NULL);
 
   // 關閉服務句柄
   CloseServiceHandle(hService);
  }
  
  // 關閉服務控制管理器句柄
  CloseServiceHandle(hSCManager);
 }     return bResult;
}    // 停止服務並卸下驅動
// lpszServiceName: 服務名 
BOOL StopDriver(LPCTSTR lpszServiceName)
{
 SC_HANDLE hSCManager;  // 服務控制管理器句柄
 SC_HANDLE hService;  // 服務句柄
 BOOL bResult = FALSE;  // 返回值
 SERVICE_STATUS ServiceStatus;     // 打開服務控制管理器
 hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);     // 打開失敗...
 if(hSCManager)
 {
  // 打開服務
  hService = OpenService(hSCManager, lpszServiceName, SERVICE_ALL_ACCESS);      if(hService)
  {
   // 停止服務
   bResult = ControlService(hService, SERVICE_CONTROL_STOP, &ServiceStatus);       // 刪除服務
   bResult = bResult && DeleteService(hService);       // 關閉服務句柄
   CloseServiceHandle(hService);
  }      // 關閉服務控制管理器句柄
  CloseServiceHandle(hSCManager);
 }     return bResult;
}    應用程序實現端口I/O的接口如下:    // 全局的設備句柄
HANDLE hMyPort;    // 打開設備
// lpszDevicePath: 設備的路徑
HANDLE OpenDevice(LPCTSTR lpszDevicePath)
{
 HANDLE hDevice;     // 打開設備
 hDevice= ::CreateFile(lpszDevicePath,  // 設備路徑
  GENERIC_READ | GENERIC_WRITE,  // 讀寫方式
  FILE_SHARE_READ | FILE_SHARE_WRITE, // 共享方式
  NULL,     // 默認的安全描述符
  OPEN_EXISTING,    // 創建方式
  0,     // 不需設置文件屬性
  NULL);     // 不需參照模板文件     return hDevice;
}    // 打開端口驅動
BOOL OpenMyPort()
{
 BOOL bResult;     // 設備名為"MyPort",驅動程序位於Windows的"system32\drivers"目錄中
 bResult = StartDriver("system32\\drivers\\MyPort.sys", "MyPort");     // 設備路徑為"\\.\MyPort"
 if(bResult) hMyPort=OpenDevice("\\\\.\\MyPort");     return (bResult && hMyPort!=INVALID_HANDLE_VALUE);
}    // 關閉端口驅動
BOOL CloseMyPort()
{
    return (CloseHandle(hMyPort) && StopDriver("MyPort"));
}    // 從指定端口讀一個字節
// port: 端口
BYTE ReadPortByte(WORD port)
{
 DWORD buf[2];   // 輸入輸出緩衝區   
 DWORD dwOutBytes;  // IOCTL輸出數據長度     buf[0] = port;   // 第一個DWORD是端口
// buf[1] = 0;   // 第二個DWORD是數據     // 用IOCTL_MYPORT_READ_BYTE讀端口
 ::DeviceIoControl(hMyPort,  // 設備句柄
  IOCTL_MYPORT_READ_BYTE,  // 取設備屬性信息
  buf, sizeof(buf),  // 輸入數據緩衝區
  buf, sizeof(buf),  // 輸出數據緩衝區
  &dwOutBytes,   // 輸出數據長度
  (LPOVERLAPPED)NULL);  // 用同步I/O      return (BYTE)buf[1];
}    // 將一個字節寫到指定端口
// port: 端口
// data: 字節數據
void WritePortByte(WORD port, BYTE data)
{
 DWORD buf[2];   // 輸入輸出緩衝區   
 DWORD dwOutBytes;  // IOCTL輸出數據長度     buf[0] = port;   // 第一個DWORD是端口
 buf[1] = data;   // 第二個DWORD是數據     // 用IOCTL_MYPORT_WRITE_BYTE寫端口
 ::DeviceIoControl(hMyPort,  // 設備句柄
  IOCTL_MYPORT_WRITE_BYTE, // 取設備屬性信息
  buf, sizeof(buf),  // 輸入數據緩衝區
  buf, sizeof(buf),  // 輸出數據緩衝區
  &dwOutBytes,   // 輸出數據長度
  (LPOVERLAPPED)NULL);  // 用同步I/O
}    有了ReadPortByte和WritePortByte這兩個函數,我們就能很容易地操縱CMOS和speaker了(關於CMOS值的含義以及定時器寄存器定義,請參考相應的硬件資料):    // 0x70是CMOS索引端口(只寫)
// 0x71是CMOS數據端口
BYTE ReadCmos(BYTE index)
{
 BYTE data;     ::WritePortByte(0x70, index);
 
 data = ::ReadPortByte(0x71);     return data;
}    // 0x61是speaker控制端口
// 0x43是8253/8254定時器控制端口
// 0x42是8253/8254定時器通道2的端口
void Sound(DWORD freq )
{
 BYTE data;     if(freq>=20 && freq<=20000)
 {
  freq = 1193181 / freq;      data = ::ReadPortByte(0x61);      if((data & 3) == 0)
  {
   ::WritePortByte(0x61, data | 3);
   ::WritePortByte(0x43, 0xb6);
  }      ::WritePortByte(0x42, (BYTE)(freq%6));
  ::WritePortByte(0x42, (BYTE)(freq/256));
 }
}    void NoSound( void )
{
 BYTE data;     data = ::ReadPortByte(0x61);     ::WritePortByte(0x61, data & 0xfc);
}    // 讀出CMOS 128個字節
 for(int i=0;i<128;i  )
 {
  BYTE data = ::ReadCmos(i);
  ... ...
 }    // 用C調演奏「多-來-米」     // 1 = 262 Hz
 ::Sound(262);
 ::Sleep(200);
 ::NoSound();     // 2 = 288 Hz
 ::Sound(288);
 ::Sleep(200);
 ::NoSound();     // 3 = 320 Hz
 ::Sound(320);
 ::Sleep(200);
 ::NoSound(); 
Q 就是個簡單的端口I/O,這麼麻煩才能實現,搞得俺頭腦稀昏,有沒有簡潔明瞭的辦法啊? A 上面的例子,之所以從編寫驅動程序,到安裝驅動,到啟動服務,到打開設備,到訪問設備,一直到讀寫端口,這樣一路下來,是為了揭示在NT/2000/XP中硬件訪問技術的本質。假如將所有過程封裝起來,只提供OpenMyPort, CloseMyPort, ReadPortByte, WritePortByte甚至更高層的ReadCmos、WriteCmos、Sound、NoSound給你調用,是不是會感覺清爽許多? 實際上,我們平常做的基於一定硬件的二次開發,一般會先安裝驅動程序(DRV)和用戶接口的運行庫(DLL),然後在此基礎上開發出我們的應用程序(APP)。DRV、DLL、APP三者分別運行在核心態、核心態/用戶態聯絡帶、用戶態。比如買了一塊圖像採集卡,要先安裝核心驅動,它的「Development Tool Kit」,提供類似於PCV_Initialize, PCV_Capture等的API,就是扮演核心態和用戶態聯絡員的角色。我們根本不需要CreateFile、CloseHandle、 DeviceIOControl、ReadFile、WriteFile等較低層次的直接調用。 Yariv Kaplan寫過一個WinIO的例子,能實現對物理端口和內存的訪問,提供了DRV、DLL、APP三方面的源碼,有興趣的話可以深入研究一下。 驅動程序源碼:MyPort.zip (3KB, 編譯環境: VC6 2000DDK) 演示程序源碼:MyPortIo.zip (22KB, 含MyPort.sys, 該文件需複製到windows的system32\drivers目錄中) Yariv Kaplan的主頁:http://www.internals.com [作者後記] 「實戰DeviceIoControl系列」,到此告一段落了。 所謂「實戰DeviceIoControl」,其實名不副實,並不是一步一步地介紹一個大型應用的開發,限於篇幅,只是列舉一些用到DeviceIoControl的場合的例子而已。對涉及硬件的開發人員,DeviceIoControl是一個非常重要的API,其基本用處是聯絡設備驅動和應用程序,像本例中編寫設備的用戶接口時用到的情形比較多。如果只是限於開發應用程序,可能永遠都用不到,也可能被迫使用(如獲取硬盤序列號)。 聯盟----Visita網站http://www.vista.org.tw ---[ 發問前請先找找舊文章 ]--- 發表人 - axsoft 於 2003/03/19 12:55:31
系統時間:2017-10-19 4:05:34
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!