... ovvero qualche appunto sul modulo ctypes. Il modulo ctypes permette di richiamare librerie dinamiche (DLL in Windows) in maniera molto semplice, aprendo così alla possibilità di richiamare codice scritto in altri linguaggi; naturalmente esistono diversi altri sistemi di integrazione ma questo è sicuramente quello più immediato.
Gli scenari in cui si può dover richiamare funzioni C sono diversi: potreste avere del vecchio codice il cui porting in puro Python richiederebbe troppo tempo, oppure potreste dovervi interfacciare con una libreria di terze parti oppure ancora potreste aver voi stessi creato una libreria C che volete testare in maniera veloce grazie a Python.

connect 420 280

Tempo fa ho dovuto lavorare sul porting di una serie di librerie C e C++ da Windows a Linux, sia a 32 bit che a 64 bit.
In questa attività il problema non era tanto la ricompilazione quanto la ricostruzione di tutta una serie di applicazioni di corredo a tali librerie (programmi dimostrativi e programmi di test); principalmente, però, avevo la necessità di creare "al volo" dei programmi di test aggiuntivi per verificare le funzionalità delle librerie. A tutto questo si aggiunga il fatto che i programmi di test volevo che potessero essere utilizzati prontamente sia a 32 bit che a 64 bit, senza necessità di ricompilazione: utilizzare degli script Python era la soluzione che mi avrebbe fatto risparmiare molto tempo!

Qui di seguito presento un semplice esempio in Windows (in Linux ce ne sono già tanti!), richiamato da Python 3; verranno illustrati la creazione della DLL, la scelta fra 32 bit e 64 bit, il passaggio di parametri stringa e, ovviamente, l'utilizzo da Python.

CREAZIONE DLL

Per creare la DLL ho utilizzato Visual Studio 2015 (community edition):

- creiamo il nuovo progetto con File -> New -> Project;
- selezioniamo Visual C++ Project, Win32, Win32 Project;
- diamo un nome alla libreria (nell'esempio qui sotto l'ho chiamata MiaLib) e una posizione su disco;
- premiamo quindi OK;
- selezioniamo DLL come tipo applicazione;
- selezioniamo Empty Project;
- premiamo Finish.

Ora nel progetto così creato andiamo ad aggiungere un nuovo file C (nel Solution Explorer clicchiamo Add -> Add New Item e diamo un nome al nuovo file, ad esempio MiaLib.c); a questo punto inseriamo il codice:

#include <windows.h>

__declspec(dllexport) int msg(char *text, char *caption)
{
return MessageBox(NULL, text, caption, MB_OKCANCEL);
}

Si tratta di una semplice funzione (chiamata msg()) che richiama la message box standard di Windows: questo ci permetterà di capire facilmente se la chiamata funziona oppure no!

Prima di compilare e provare ci sono almeno due aspetti da chiarire:

1) l'interprete Python è a 32 bit o a 64 bit?
2) stiamo lavorando con stringhe Unicode o semplicemente ANSI?

32 bit o 64 bit?

Ricordo che non è possibile richiamare librerie a 32 bit da processi a 64 bit e viceversa: il nostro script Python girerà a 32 bit o 64 bit a seconda dell'interprete utilizzato e di conseguenza in Visual Studio dovremo compilare la libreria utilizzando la piattaforma "x86" se l'interprete è a 32 bit o la piattaforma "x64" se l'interprete è a 64 bit.

In Linux questo problema è meno sentito: in un sistema a 64 bit compiliamo le librerie (file .so) di default a 64 bit e l'interprete Python molto probabilmente lavorerà a 64 bit (perchè è tipicamente un pacchetto della distribuzione).

In Windows, invece, in un sistema a 64 bit il nostro Visual Studio compilerà di default comunque a 32 bit e l'interprete Python, installato manualmente, potrebbe essere sia a 32 bit che a 64 bit.

Ma come si può determinare quale architettura stiamo usando? Basta lanciare l'interprete! Ad esempio, sulla mia postazione ho ottenuto:

Python 3.5.1 (v3.5.1:37a07cee5969, Dec 6 2015, 01:54:25) [MSC v.1900 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.

Se lo volete fare a run-time, perchè magari compilate due librerie, una a 32 bit e una a 64 bit e volete scegliere dinamicamente quale usare, potete seguire le indicazioni presenti in questa discussione https://stackoverflow.com/questions/1842544/how-do-i-detect-if-python-is-running-as-a-64-bit-application?noredirect=1&lq=1

UNICODE o ANSI?

Le stringhe di Python 3 sono UNICODE mentre molto probabilmente in C avrete lavorato con stringhe ANSI: in Visual Studio questa scelta può essere fatta nelle proprietà generali del progetto, alla voce "Character Set". Nell'esempio abbiamo scelto di lavorare con stringhe ANSI, quindi abbiamo impostato "Character Set"="Not Set".
Di seguito, nello script Python vedremo come settare tali stringhe.

SCRIPT PYTHON

Qui di seguito lo script Python che carica la libreria ed esegue due chiamate alla funzione msg():

import ctypes

#--------------------------------------------------
def main():
MiaLib = ctypes.cdll.LoadLibrary('MiaLib.dll')

res = MiaLib.msg(b'text',b'caption')
print ("msg res = "+str(res))

mytext = "Bel lavoro"
res = MiaLib.msg(mytext.encode('utf-8'),b'caption')
print ("msg res = "+str(res))

#--------------------------------------------------
if __name__ == '__main__':
main()

Da notare, all'inizio, la import del modulo ctypes; la libreria viene caricata utilizzando ctypes.cdll.LoadLibrary() che provvederà a caricare in memoria la libreria: fatto questo è possibile richiamare la nostra funzione C esportata dalla DLL.

In questo esempio ci sono due chiamate alla funzione msg(): la prima con valori costanti in cui si può notare l'utilizzo del prefisso b per definire una stringa di byte mentre nella seconda chiamata ho introdotto una conversione da stringa UNICODE a stringa ANSI usando il metodo encode().
Naturalmente queste conversioni sono necessarie perchè la nostra libreria C non lavora con stringhe UNICODE .

CONCLUSIONI

Ho presentato solo un semplice esempio ma sono sicuro che ha reso bene l'idea sulla semplicità del modulo ctypes; se avete qualche domanda potete raggiungermi tramite la pagina dei contatti di questo sito.

FONTI

Esistono molte risorse sul web, a partire dalla documentazione ufficiale https://docs.python.org/3/library/ctypes.html.
Segnalo in particolare questa pagina http://wolfprojects.altervista.org/articles/dll-in-c-for-python/: davvero un bel lavoro!