2010-06-12 505 views
5

我試圖使用PySerial用下面的代碼在我的Windows 7 PC連接到我的手機:如何解決「[錯誤6]句柄無效。」與PySerial

import wmi 
import serial 

c = wmi.WMI() 
modem = c.query("SELECT * FROM Win32_POTSModem").pop() 
ser = serial.Serial(modem.AttachedTo, modem.MaxBaudRateToSerialPort) 

try: 
    ser.write('at \r\n') 
    print ser.readline() 
finally: 
    ser.close() 

,但得到的寫調用以下錯誤:

Traceback (most recent call last): 
    File "D:\Alasdair\Documents\Python Scripts\Phone Interface\test.py", line 14, in <module> 
    ser.write('at \r\n') 
    File "C:\Python26\Lib\site-packages\serial\serialwin32.py", line 255, in write 
    raise SerialException("WriteFile failed (%s)" % ctypes.WinError()) 
SerialException: WriteFile failed ([Error 6] The handle is invalid.) 

我已經嘗試連接TeraTerm並且工作正常,所以連接到手機本身並不是問題。

我一直在尋找四處尋找解決方案,但沒有拿出任何有效的東西。有任何想法嗎?

回答

4

我在Windows 7 64位,與Python 2.6,它給我同樣的錯誤。

ser = serial.Serial(3,115200,timeout=1) 
ser.read() 
#or ser.write("whatever") 

Traceback (most recent call last): 
    File "<pyshell#3>", line 1, in <module> 
    ser.read(1) 
    File "build\bdist.win-amd64\egg\serial\serialwin32.py", line 236, in read 
    raise SerialException("ReadFile failed (%s)" % ctypes.WinError()) 
SerialException: ReadFile failed ([Error 6] The handle is invalid.) 

當使用類似的程序使用c庫時,相同的端口響應正確。這裏發生了什麼?聽起來像是pyserial或ctypes中的一個bug。你也使用64位嗎?

在pyserial編寫的源代碼看起來很簡單

def write(self, data): 
     """Output the given string over the serial port.""" 
     if not self.hComPort: raise portNotOpenError 
     #~ if not isinstance(data, (bytes, bytearray)): 
      #~ raise TypeError('expected %s or bytearray, got %s' % (bytes, type(data))) 
     # convert data (needed in case of memoryview instance: Py 3.1 io lib), ctypes doesn't like memoryview 
     data = bytes(data) 
     if data: 
      #~ win32event.ResetEvent(self._overlappedWrite.hEvent) 
      n = win32.DWORD() 
      err = win32.WriteFile(self.hComPort, data, len(data), ctypes.byref(n), self._overlappedWrite) 
      if not err and win32.GetLastError() != win32.ERROR_IO_PENDING: 
       raise SerialException("WriteFile failed (%s)" % ctypes.WinError()) 

或許與64位ctypes的一個問題?


更新: 至少給我一個64位的問題。我剛剛安裝了一個x86版本的python(這次是3.1),現在它工作正常。顯然64位ctypes只能導入64位庫。聽起來很奇怪,但是無法訪問操作系統庫。

+0

好的,謝謝。如您所懷疑的,我正在使用64位版本的Python。我會給它一個32位版本。 – alnorth29 2010-07-26 15:25:24

6

我剛在64位窗口(XP,Vista和7)上解決了這個問題。

此問題是由無效的句柄轉換引起的,由於舊的python-win32函數而丟棄64位值的高32位。 如果您遇到這種問題,請使用win32file等模塊中包含的新的python-win32函數。 請在site-packages \ serial \ serialwin32.py上輸入以下代碼。

#! python 
# Python Serial Port Extension for Win32, Linux, BSD, Jython 
# serial driver for win32 
# see __init__.py 
# 
# (C) 2001-2009 Chris Liechti <[email protected]> 
# this is distributed under a free software license, see license.txt 
# 
# Initial patch to use ctypes by Giovanni Bajo <[email protected]> 

import ctypes 
import win32 
import win32file 
import win32con 
import pywintypes 

from serialutil import * 

def device(portnum): 
    """Turn a port number into a device name""" 
    return 'COM%d' % (portnum+1) # numbers are transformed to a string 

class Win32Serial(SerialBase): 
    """Serial port implementation for Win32 based on ctypes.""" 

    BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 
       9600, 19200, 38400, 57600, 115200) 

    def __init__(self, *args, **kwargs): 
     self.hComPort = None 
     SerialBase.__init__(self, *args, **kwargs) 

    def open(self): 
     """Open port with current settings. This may throw a SerialException 
      if the port cannot be opened.""" 
     if self._port is None: 
      raise SerialException("Port must be configured before it can be used.") 
     # the "\\.\COMx" format is required for devices other than COM1-COM8 
     # not all versions of windows seem to support this properly 
     # so that the first few ports are used with the DOS device name 
     port = self.portstr 
     try: 
      if port.upper().startswith('COM') and int(port[3:]) > 8: 
       port = '\\\\.\\' + port 
     except ValueError: 
      # for like COMnotanumber 
      pass 
     self.hComPort = win32file.CreateFile(port, 
       win32con.GENERIC_READ | win32con.GENERIC_WRITE, 
       0, # exclusive access 
       None, # no security 
       win32con.OPEN_EXISTING, 
       win32con.FILE_ATTRIBUTE_NORMAL | win32con.FILE_FLAG_OVERLAPPED, 
       0) 
     if self.hComPort == win32.INVALID_HANDLE_VALUE: 
      self.hComPort = None # 'cause __del__ is called anyway 
      raise SerialException("could not open port %s: %s" % (self.portstr, ctypes.WinError())) 

     # Setup a 4k buffer 
     win32file.SetupComm(self.hComPort, 4096, 4096) 

     # Save original timeout values: 
     tos = win32file.GetCommTimeouts(self.hComPort) 
     self._orgTimeouts = win32.COMMTIMEOUTS(*tos) 
     self._rtsState = win32.RTS_CONTROL_ENABLE 
     self._dtrState = win32.DTR_CONTROL_ENABLE 

     self._reconfigurePort() 

     # Clear buffers: 
     # Remove anything that was there 
     win32file.PurgeComm(self.hComPort, 
          win32.PURGE_TXCLEAR | win32.PURGE_TXABORT | 
          win32.PURGE_RXCLEAR | win32.PURGE_RXABORT) 

     self._overlappedRead = pywintypes.OVERLAPPED() 
     self._overlappedRead.hEvent = win32.CreateEvent(None, 1, 0, None) 
     self._overlappedWrite = pywintypes.OVERLAPPED() 
     #~ self._overlappedWrite.hEvent = win32.CreateEvent(None, 1, 0, None) 
     self._overlappedWrite.hEvent = win32.CreateEvent(None, 0, 0, None) 
     self._isOpen = True 

    def _reconfigurePort(self): 
     """Set communication parameters on opened port.""" 
     if not self.hComPort: 
      raise SerialException("Can only operate on a valid port handle") 

     # Set Windows timeout values 
     # timeouts is a tuple with the following items: 
     # (ReadIntervalTimeout,ReadTotalTimeoutMultiplier, 
     # ReadTotalTimeoutConstant,WriteTotalTimeoutMultiplier, 
     # WriteTotalTimeoutConstant) 
     if self._timeout is None: 
      timeouts = (0, 0, 0, 0, 0) 
     elif self._timeout == 0: 
      timeouts = (win32.MAXDWORD, 0, 0, 0, 0) 
     else: 
      timeouts = (0, 0, int(self._timeout*1000), 0, 0) 
     if self._timeout != 0 and self._interCharTimeout is not None: 
      timeouts = (int(self._interCharTimeout * 1000),) + timeouts[1:] 

     if self._writeTimeout is None: 
      pass 
     elif self._writeTimeout == 0: 
      timeouts = timeouts[:-2] + (0, win32.MAXDWORD) 
     else: 
      timeouts = timeouts[:-2] + (0, int(self._writeTimeout*1000)) 
     win32file.SetCommTimeouts(self.hComPort, timeouts) 

     win32file.SetCommMask(self.hComPort, win32.EV_ERR) 

     # Setup the connection info. 
     # Get state and modify it: 
     comDCB = win32file.GetCommState(self.hComPort) 
     comDCB.BaudRate = self._baudrate 

     if self._bytesize == FIVEBITS: 
      comDCB.ByteSize  = 5 
     elif self._bytesize == SIXBITS: 
      comDCB.ByteSize  = 6 
     elif self._bytesize == SEVENBITS: 
      comDCB.ByteSize  = 7 
     elif self._bytesize == EIGHTBITS: 
      comDCB.ByteSize  = 8 
     else: 
      raise ValueError("Unsupported number of data bits: %r" % self._bytesize) 

     if self._parity == PARITY_NONE: 
      comDCB.Parity  = win32.NOPARITY 
      comDCB.fParity  = 0 # Disable Parity Check 
     elif self._parity == PARITY_EVEN: 
      comDCB.Parity  = win32.EVENPARITY 
      comDCB.fParity  = 1 # Enable Parity Check 
     elif self._parity == PARITY_ODD: 
      comDCB.Parity  = win32.ODDPARITY 
      comDCB.fParity  = 1 # Enable Parity Check 
     elif self._parity == PARITY_MARK: 
      comDCB.Parity  = win32.MARKPARITY 
      comDCB.fParity  = 1 # Enable Parity Check 
     elif self._parity == PARITY_SPACE: 
      comDCB.Parity  = win32.SPACEPARITY 
      comDCB.fParity  = 1 # Enable Parity Check 
     else: 
      raise ValueError("Unsupported parity mode: %r" % self._parity) 

     if self._stopbits == STOPBITS_ONE: 
      comDCB.StopBits  = win32.ONESTOPBIT 
     elif self._stopbits == STOPBITS_ONE_POINT_FIVE: 
      comDCB.StopBits  = win32.ONE5STOPBITS 
     elif self._stopbits == STOPBITS_TWO: 
      comDCB.StopBits  = win32.TWOSTOPBITS 
     else: 
      raise ValueError("Unsupported number of stop bits: %r" % self._stopbits) 

     comDCB.fBinary   = 1 # Enable Binary Transmission 
     # Char. w/ Parity-Err are replaced with 0xff (if fErrorChar is set to TRUE) 
     if self._rtscts: 
      comDCB.fRtsControl = win32.RTS_CONTROL_HANDSHAKE 
     else: 
      comDCB.fRtsControl = self._rtsState 
     if self._dsrdtr: 
      comDCB.fDtrControl = win32.DTR_CONTROL_HANDSHAKE 
     else: 
      comDCB.fDtrControl = self._dtrState 
     comDCB.fOutxCtsFlow  = self._rtscts 
     comDCB.fOutxDsrFlow  = self._dsrdtr 
     comDCB.fOutX   = self._xonxoff 
     comDCB.fInX    = self._xonxoff 
     comDCB.fNull   = 0 
     comDCB.fErrorChar  = 0 
     comDCB.fAbortOnError = 0 
     comDCB.XonChar   = XON 
     comDCB.XoffChar   = XOFF 
     win32file.SetCommState(self.hComPort, comDCB) 

    #~ def __del__(self): 
     #~ self.close() 

    def close(self): 
     """Close port""" 
     if self._isOpen: 
      if self.hComPort: 
       # Restore original timeout values: 
       win32file.SetCommTimeouts(self.hComPort, self._orgTimeouts) 
       # Close COM-Port: 
       win32file.CloseHandle(self.hComPort) 
       win32file.CloseHandle(self._overlappedRead.hEvent) 
       win32file.CloseHandle(self._overlappedWrite.hEvent) 
       self.hComPort = None 
      self._isOpen = False 

    def makeDeviceName(self, port): 
     return device(port) 

    # - - - - - - - - - - - - - - - - - - - - - - - - 

    def inWaiting(self): 
     """Return the number of characters currently in the input buffer.""" 
     flags = win32.DWORD() 
     comstat = win32.COMSTAT() 
     if not win32file.ClearCommError(self.hComPort, ctypes.byref(flags), ctypes.byref(comstat)): 
      raise SerialException('call to ClearCommError failed') 
     return comstat.cbInQue 

    def read(self, size=1): 
     """Read size bytes from the serial port. If a timeout is set it may 
      return less characters as requested. With no timeout it will block 
      until the requested number of bytes is read.""" 
     if not self.hComPort: raise portNotOpenError 
     if size > 0: 
      win32.ResetEvent(self._overlappedRead.hEvent) 
      if not win32file.ClearCommError(self.hComPort): 
       raise SerialException('call to ClearCommError failed') 
      if self.timeout == 0: 
       n = min(comstat.cbInQue, size) 
       if n > 0: 
        rc,buf = win32file.ReadFile(self.hComPort, n, self._overlappedRead) 
        if win32.GetLastError() != win32.ERROR_IO_PENDING: 
         raise SerialException("ReadFile failed (%s)" % ctypes.WinError()) 
        err = win32.WaitForSingleObject(self._overlappedRead.hEvent, win32.INFINITE) 
        read = buf[:rc] 
       else: 
        read = bytes() 
      else: 
       rc,buf = win32file.ReadFile(self.hComPort, size, self._overlappedRead) 
       rc = win32file.GetOverlappedResult(self.hComPort, self._overlappedRead, True) 
       read = buf[:rc] 
     else: 
      read = bytes() 
     return bytes(read) 

    def write(self, data): 
     """Output the given string over the serial port.""" 
     if not self.hComPort: raise portNotOpenError 
     #~ if not isinstance(data, (bytes, bytearray)): 
      #~ raise TypeError('expected %s or bytearray, got %s' % (bytes, type(data))) 
     # convert data (needed in case of memoryview instance: Py 3.1 io lib), ctypes doesn't like memoryview 
     if data: 
      #~ win32event.ResetEvent(self._overlappedWrite.hEvent) 
      err,n = win32file.WriteFile(self.hComPort, data, self._overlappedWrite) 
      if not err and win32.GetLastError() != win32.ERROR_IO_PENDING: 
       raise SerialException("WriteFile failed (%s)" % ctypes.WinError()) 
      # Wait for the write to complete. 
      #~ win32.WaitForSingleObject(self._overlappedWrite.hEvent, win32.INFINITE) 
      n = win32file.GetOverlappedResult(self.hComPort, self._overlappedWrite, True) 
      if n != len(data): 
       raise writeTimeoutError 
      return n 
     else: 
      return 0 

    def flushInput(self): 
     """Clear input buffer, discarding all that is in the buffer.""" 
     if not self.hComPort: raise portNotOpenError 
     win32.PurgeComm(self.hComPort, win32.PURGE_RXCLEAR | win32.PURGE_RXABORT) 

    def flushOutput(self): 
     """Clear output buffer, aborting the current output and 
     discarding all that is in the buffer.""" 
     if not self.hComPort: raise portNotOpenError 
     win32.PurgeComm(self.hComPort, win32.PURGE_TXCLEAR | win32.PURGE_TXABORT) 

    def sendBreak(self, duration=0.25): 
     """Send break condition. Timed, returns to idle state after given duration.""" 
     if not self.hComPort: raise portNotOpenError 
     import time 
     win32.SetCommBreak(self.hComPort) 
     time.sleep(duration) 
     win32.ClearCommBreak(self.hComPort) 

    def setBreak(self, level=1): 
     """Set break: Controls TXD. When active, to transmitting is possible.""" 
     if not self.hComPort: raise portNotOpenError 
     if level: 
      win32.SetCommBreak(self.hComPort) 
     else: 
      win32.ClearCommBreak(self.hComPort) 

    def setRTS(self, level=1): 
     """Set terminal status line: Request To Send""" 
     if not self.hComPort: raise portNotOpenError 
     if level: 
      self._rtsState = win32.RTS_CONTROL_ENABLE 
      win32.EscapeCommFunction(self.hComPort, win32.SETRTS) 
     else: 
      self._rtsState = win32.RTS_CONTROL_DISABLE 
      win32.EscapeCommFunction(self.hComPort, win32.CLRRTS) 

    def setDTR(self, level=1): 
     """Set terminal status line: Data Terminal Ready""" 
     if not self.hComPort: raise portNotOpenError 
     if level: 
      self._dtrState = win32.DTR_CONTROL_ENABLE 
      win32.EscapeCommFunction(self.hComPort, win32.SETDTR) 
     else: 
      self._dtrState = win32.DTR_CONTROL_DISABLE 
      win32.EscapeCommFunction(self.hComPort, win32.CLRDTR) 

    def _GetCommModemStatus(self): 
     stat = win32.DWORD() 
     win32.GetCommModemStatus(self.hComPort, ctypes.byref(stat)) 
     return stat.value 

    def getCTS(self): 
     """Read terminal status line: Clear To Send""" 
     if not self.hComPort: raise portNotOpenError 
     return win32.MS_CTS_ON & self._GetCommModemStatus() != 0 

    def getDSR(self): 
     """Read terminal status line: Data Set Ready""" 
     if not self.hComPort: raise portNotOpenError 
     return win32.MS_DSR_ON & self._GetCommModemStatus() != 0 

    def getRI(self): 
     """Read terminal status line: Ring Indicator""" 
     if not self.hComPort: raise portNotOpenError 
     return win32.MS_RING_ON & self._GetCommModemStatus() != 0 

    def getCD(self): 
     """Read terminal status line: Carrier Detect""" 
     if not self.hComPort: raise portNotOpenError 
     return win32.MS_RLSD_ON & self._GetCommModemStatus() != 0 

    # - - platform specific - - - - 

    def setXON(self, level=True): 
     """Platform specific - set flow state.""" 
     if not self.hComPort: raise portNotOpenError 
     if level: 
      win32.EscapeCommFunction(self.hComPort, win32.SETXON) 
     else: 
      win32.EscapeCommFunction(self.hComPort, win32.SETXOFF) 

    def outWaiting(self): 
     """return how many characters the in the outgoing buffer""" 
     flags = win32.DWORD() 
     comstat = win32.COMSTAT() 
     if not win32.ClearCommError(self.hComPort, ctypes.byref(flags), ctypes.byref(comstat)): 
      raise SerialException('call to ClearCommError failed') 
     return comstat.cbOutQue 

# assemble Serial class with the platform specific implementation and the base 
# for file-like behavior. for Python 2.6 and newer, that provide the new I/O 
# library, derive from io.RawIOBase 
try: 
    import io 
except ImportError: 
    # classic version with our own file-like emulation 
    class Serial(Win32Serial, FileLike): 
     pass 
else: 
    # io library present 
    class Serial(Win32Serial, io.RawIOBase): 
     pass 

# Nur Testfunktion!! 
if __name__ == '__main__': 
    s = Serial(0) 
    sys.stdout.write("%s\n" % s) 

    s = Serial() 
    sys.stdout.write("%s\n" % s) 

    s.baudrate = 19200 
    s.databits = 7 
    s.close() 
    s.port = 0 
    s.open() 
    sys.stdout.write("%s\n" % s) 
+1

種類的殭屍帖子,但我試過,現在我得到一個新的錯誤,具體來說,self.port.flushInput() 文件「C:\ Python27 \ lib \ site-packages \ serial \ serialwin32。py「,行273,齊平 輸入 win32.PurgeComm(self.hComPort,win32.PURGE_RXCLEAR | win32.PURGE_RXABORT) ctypes.ArgumentError:參數1::錯誤的類型 – camperdave 2011-05-02 07:03:42

+0

同樣的問題@camperdave,有沒有人知道這一點? – Legogris 2012-02-05 13:13:32

+0

我也有類似的問題,我認爲這個補丁有一段時間和一個地方已經過去了,試着2.6,然後再回到2.4。 – meawoppl 2012-07-23 18:41:49