2011-07-12 11 views
4

Python 3.2では、ctypes.windll.kernel32.DeviceIoControl関数によって返されたデータからStructureオブジェクトを作成しています。これが完了したら、Structureフィールドにアクセスしてデータを返すことができます。しかし、私が何かをすると、ファイルを開くなどのメモリを使用すると、構造内のデータが変更されます。私が結果に貼り付けた出力の最初の部分では、予想されることがあります。ただし、ファイルが開かれ、構造体のフィールドが再度印刷された後、値が変更されています。なぜデータが変更されているのか、それが起こるのを止める方法はわかりません。さらに多くのメモリを割り当てると、Pythonのctypes構造が上書きされる

構造:

class DISK_GEOMETRY(ctypes.Structure): 
    ''' 
    Disk Geometry Data Structure 
    http://msdn.microsoft.com/en-us/library/aa363972(v=vs.85).aspx 
    ''' 
    _fields_ = [("Cylinders", wintypes.LARGE_INTEGER), 
       ("MediaType", wintypes.BYTE), #MEDIA_TYPE 
       ("TracksPerCylinder", wintypes.DWORD), 
       ("SectorsPerTrack", wintypes.DWORD), 
       ("BytesPerSector", wintypes.DWORD)] 


class DISK_GEOMETRY_EX(ctypes.Structure): 
    ''' 
    Disk Geometry EX Data Structure 
    http://msdn.microsoft.com/en-us/library/aa363970(v=vs.85).aspx 
    ''' 
    _fields_ = [("Geometry", DISK_GEOMETRY), 
       ("DiskSize", wintypes.LARGE_INTEGER), 
       ("Data[1]", wintypes.BYTE)] 

のDeviceIoControl:

class DeviceIoControl: 
    def __init__(self, path): 
     self.path = path 

    def __DeviceIoControl(self, devicehandle, IoControlCode, input, output): 
     ''' 
     DeviceIoControl Function 
     http://msdn.microsoft.com/en-us/library/aa363216(v=vs.85).aspx 
     ''' 
     DevIoCtl = ctypes.windll.kernel32.DeviceIoControl 
     DevIoCtl.argtypes = [ 
      wintypes.HANDLE, #HANDLE hDevice 
      wintypes.DWORD, #DWORD dwIoControlCode 
      wintypes.LPVOID, #LPVOID lpInBuffer 
      wintypes.DWORD, #DWORD nInBufferSize 
      wintypes.LPVOID, #LPVOID lpOutBuffer 
      wintypes.DWORD, #DWORD nOutBufferSize 
      ctypes.POINTER(wintypes.DWORD), #LPDWORD lpBytesReturned 
      wintypes.LPVOID] #LPOVERLAPPED lpOverlapped 
     DevIoCtl.restype = wintypes.BOOL 

     if isinstance(output, int): 
      output = ctypes.create_string_buffer(output) 

     input_size = len(input) if input is not None else 0 
     output_size = len(output) 
     assert isinstance(output, ctypes.Array) 

     BytesReturned = wintypes.DWORD() 

     status = DevIoCtl(devicehandle, IoControlCode, input, input_size, output, output_size, BytesReturned, None) 
     return output[:BytesReturned.value] if status is not 0 else -1 

    def GetDriveGeometry(self): 
     diskhandle = winapi.CreateHandle(
       self.path, 
       winapi.NULL, 
       winapi.FILE_SHARE_READ|winapi.FILE_SHARE_WRITE, 
       winapi.LPSECURITY_ATTRIBUTES(), 
       winapi.OPEN_EXISTING, 
       winapi.FILE_ATTRIBUTE_NORMAL, 
       winapi.NULL) 
     if diskhandle == winapi.INVALID_HANDLE_VALUE: 
      return -1 

     temp = ctypes.cast(self.__DeviceIoControl(diskhandle, winioctl.IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, None, 1024), ctypes.POINTER(winioctl.DISK_GEOMETRY_EX)).contents 
     winapi.CloseHandle(diskhandle) 
     return temp 

メイン:

device = DeviceIoControl(r"\\.\PhysicalDrive0") 
devicegeo = device.GetDriveGeometry() 
print("Disk Size: " +str(devicegeo.DiskSize)) 
print("BytesPerSector: "+str(devicegeo.Geometry.BytesPerSector)) 
print("Cylinders: "+str(devicegeo.Geometry.Cylinders)) 
print("MediaType: "+str(hex(devicegeo.Geometry.MediaType))) 
print("CtypesAddressOf: "+str(ctypes.addressof(devicegeo))) 

with open(r"\\.\PhysicalDrive0", 'rb') as f: 
    f.seek(0) 
    MBRdata = f.read(512) 
print("\nOpened a file\n")   

print("Disk Size: "+str(devicegeo.DiskSize)) 
print("BytesPerSector: "+str(devicegeo.Geometry.BytesPerSector)) 
print("Cylinders: "+str(devicegeo.Geometry.Cylinders)) 
print("MediaType: "+str(hex(devicegeo.Geometry.MediaType))) 
print("CtypesAddressOf: "+str(ctypes.addressof(devicegeo))) 

出力:

Disk Size: 80000000000 
BytesPerSector: 512 
Cylinders: 9726 
MediaType: 0xc 
CtypesAddressOf: 12322040 

Opened a file 

Disk Size: 0 
BytesPerSector: 1 
Cylinders: 2170477562872987649 
MediaType: -0x40 
CtypesAddressOf: 12322040 
+1

'device'が何であるか、その' GetDriveGeometry'が何をするか(正確には、 'ctypes'がその戻り値を容易にするもの)に依存します。基本的なネイティブ実装が管理するメモリのビューを返すことができ、それらを所有することは意図していない可能性があります。 – Santa

+0

@Santaこれらの機能の背後にあるコードを質問に追加しました。 – Shawn

+0

@Shawn 'winapi'インポートはどこから来ますか?インポートされたパッケージのエイリアスですか? – swdev

答えて

2

のいくつかの観測は:

  1. DevIoCtlbyref(BytesReturned)と呼ばれるべきです。
  2. ctypes.castの最初の引数は、 "ポインタとして解釈できるオブジェクト"でなければなりません。 ただし、キャストしているのは未加工のbytesオブジェクトです(output[:BytesReturned.value])。
  3. __DeviceIoControlから返されたものは、新しいPython bytesオブジェクトです。 ctypes配列オブジェクトへの元の参照が範囲外になりました。したがって、ガベージコレクションや再利用が可能である可能性があります。

FWIW、私はWindowsのIOCTLディスパッチctypesを使用して周りにちょうどそれを実行しています。また、\\.\PysicalDrive0IOCTL_DISK_GET_DRIVE_GEOMETRYを使用します。

this gistを作成しました。

関連する問題