logo
Contato | Sobre...        
rebarba rebarba

Rodrigo Strauss :: Blog

follow us in feedly

Acessando a API Win32 usando Python

Como muitos devem saber, eu voltei a trabalhar com drivers. Meu projeto específico aqui na Axur é o AMS, um software que (simplificadamente) monitora o acesso à file servers. Durante a caçada à um bug eu precisei fazer um programa de testes para reproduzir um cenário descrito no nosso controle de bugs. Nesse cenário, eu precisa acessar certos arquivos com flags específicas da função CreateFile da API Win32.

Fácil. É só abrir o Visual C++ e escrever algumas linhas. Mas Python é mais indicado para programinhas pequenos de teste (não preciso de performance e o fato do código fonte ser aberto não é um problema). Então decidi fazer em Python, e vou mostrar aqui as duas opções que eu conheço para isso.

A primeira delas é usar o ctypes, que vem na distribuição padrão do Python 2.5. "Batteries included", lembra? Essa biblioteca provê um acesso MUITO fácil à código C (DLLs, APIs, etc) usando Python. Sinceramente, nunca vi nada tão fácil e prático, nem em VB ou C#. O código fica simples assim:

>>> import ctypes
>>> CreateFile = ctypes.windll.kernel32.CreateFile
Traceback (most recent call last):
  File "<interactive input>", line 1, in <module>
  File "C:\Python25\lib\ctypes\__init__.py", line 353, in __getattr__
    func = self.__getitem__(name)
  File "C:\Python25\lib\ctypes\__init__.py", line 358, in __getitem__
    func = self._FuncPtr((name_or_ordinal, self))
AttributeError: function 'CreateFile' not found

>>> # ooops. Lembra das funções ANSI e UNICODE? Já falei detalhadamente sobre isso
>>> # nos tutoriais de WinDbg (que pretendo retomar agora que voltei a fazer drivers)
>>> CreateFile = ctypes.windll.kernel32.CreateFileA
>>> CreateFile
<_FuncPtr object at 0x012DF288>
>>>

Simples assim. A variável CreateFile já é um ponteiro para a função CreateFile da API. Como exemplo vamos ler o arquivo boot.ini:

>>> # ** CONTINUAÇÃO**
>>> # uma ajudinha. Copiei isso do winnt.h. Como são defines, 
>>> # não dá pra "importar" como as funções
>>> GENERIC_READ = 0x80000000
>>> GENERIC_WRITE = 0x40000000
>>> FILE_SHARE_READ = 0x00000001
>>> OPEN_EXISTING = 3
>>> h = CreateFile(r'c:\boot.ini', GENERIC_READ, FILE_SHARE_READ,
     None, OPEN_EXISTING, None, None)
>>> # se for erro, retorna INVALID_HANDLE_VALUE, que é 0xFFFFFFFF
>>> h
532
>>> # tudo ok. vamos ler
>>> ReadFile = ctypes.windll.kernel32.ReadFile
>>> # preciso de um buffer para ler o arquivo.
>>> buffer = ctypes.create_string_buffer(4096)
>>> buffer
<ctypes.c_char_Array_4096 object at 0x012DDE40>
>>> #preciso também passar o ponteiro de um LONG (int) para saber o quanto foi lido
>>> BytesRead = ctypes.c_long()
>>> BytesRead
c_long(0)
>>> ret = ReadFile(h, buffer, len(buffer), ctypes.byref(BytesRead), None)
>>> ret
1
>>> # funcionou
>>> print buffer.value
[boot loader]
timeout=30
default=multi(0)disk(0)rdisk(0)partition(1)\WINDOWS
[operating systems]
multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Microsoft Windows XP Professional" 
/noexecute=optin /fastdetect
 
>>> #bytes lidos:
>>> print BytesRead.value
211
>>> # Não preciso pegar o ponteiro de função em uma variável,
>>> # posso chamar diretamente
>>> ctypes.windll.kernel32.CloseHandle(h)
1
>>>

A segunda opção é usar o pacote PyWin32, que é um wrapper para a API Win32 feito em Python. Ele encpasula praticamente toda a API Win32, toda a runtime COM e a MFC. Esse pacote vem com uma IDE para Python muito boa, chamada PythonWin. Ela é feita em Python usando MFC.

Talk is cheap, show me the code:

>>> import win32file
>>> h = win32file.CreateFile(r'c:\boot.ini', win32file.GENERIC_READ, 
win32file.FILE_SHARE_READ, None, win32file.OPEN_EXISTING, 0, 0)
>>> h
<PyHANDLE:504>
>>> err, str = win32file.ReadFile(h, 1024)
>>> err
0

>>> print str
[boot loader]
timeout=30
default=multi(0)disk(0)rdisk(0)partition(1)\WINDOWS
[operating systems]
multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Microsoft Windows XP Professional" 
/noexecute=optin /fastdetect
>>> Não precisamos fechar o handle, o tipo PyHANDLE cuida disso
>>> Use "del h" se quiser fechar na hora

Algumas coisas são mais fáceis usando PyWin32, mas temos também algumas desvantagens. As assinaturas das funções não são exatamente as mesmas da API e esse pacote vem separado da distribuição padrão do Python.


Em 20/10/2007 23:56, por Rodrigo Strauss


  
 
 
Comentários
Thyago | em 20/10/2007 | #
Poderia também usar um intepretador de C/C++, http://www.ddj.com/cpp/184402054

Existem outros alem do ch...
Algo a dizer?
Nome:


Site:


E-mail:


Escreva o número vinte e seis:


 Não mostre meu e-mail no site, não serve pra nada mesmo...

Comentário





Os comentários devem ser sobre assuntos relativos ao post, eu provavelmente apagarei comentários totalmente offtopic. Se quiser me enviar uma mensagem, use o formulário de contato. E não esqueça: isso é um site pessoal e eu me reservo o direito de apagar qualquer comentário ofensivo ou inapropriado.
rebarba rebarba
  ::::