2016-11-17 2 views
1

J'ai un appareil qui enregistre des données spectroscopiques et est contrôlé par une application tierce. Pour l'automatisation, je veux utiliser l'interface COM de l'application pour récupérer les données en Python. Comme il n'y a pas de documentation adéquate pour l'utilisation de l'API de Python, je collectionnais le code suivant à partir de différentes sources Web, qui obtient avec succès la première image:Comment introspecter win32com wrapper?

comtypes.client.GetModule(('{1A762221-D8BA-11CF-AFC2-508201C10000}', 3, 11)) 
import comtypes.gen.WINX32Lib as WinSpecLib 
win32com.client.pythoncom.CoInitialize() 
doc = win32com.client.Dispatch("WinX32.DocFile") 

buffer = ctypes.c_float() 
frame = 1 
spectrum = doc.GetFrame(frame, buffer) 

Cependant, l'appel à GetFrame est incompatible avec sa définition dans Visual de base, qui est fourni par le fabricant:

Sub GetFrame(frame As Integer, buffer As Variant) 

GetFrame copie les données d'un document dans un tableau Visual Basic. Si buffer est un Variant vide, GetFrame crée un tableau de la taille et du type de données appropriés et définit le tampon pour qu'il pointe dessus avant de copier les données.

Cela signifie que dans Visual Basic est rempli la buffer variable des données alors que la fonction GetFrame n'a pas de valeur de retour, alors qu'en Python buffer reste inchangé mais la fonction ne GetFrame renvoyer les données réelles. Je ne m'intéresserais pas à de telles subtilités, si je n'avais pas observé les plantages aléatoires de mon programme en lançant un MemoryError et en indiquant ainsi une fuite de mémoire à ce point précis du code. Donc, mon soupçon est que pour chaque appel à GetFrame de la mémoire est allouée pour le tampon, mais jamais libéré, parce que win32com en quelque sorte foiré l'emballage de l'API. Ce raisonnement m'amène à ma vraie question: comment puis-je introspecter cette enveloppe et comprendre ce qu'elle fait? Jusqu'à présent, je n'ai trouvé aucune indication que le code généré par win32com est stocké dans un fichier, mais peut-être que je n'ai pas regardé les bons endroits.

En IPython j'ai essayé aussi d'obtenir des informations en utilisant doc.GetFrame??, mais il n'a retourné aucun mise en œuvre:

Signature: doc.GetFrame(frame=<PyOleMissing object at 0x06F20BC8>, FrameVariant=<PyOleMissing object at 0x06F20BC8>) 
Docstring: <no docstring> 
File:  c:\programming\python\src\<comobject winx32.docfile> 
Type:  method 

Que puis-je essayer d'obtenir plus d'informations sur l'emballage API?

Répondre

1

En essayant plus, j'ai finalement été capable de trouver une solution à mon problème. La première réalisation importante a été la découverte que l'appel EnsureDispatch au lieu de Dispatch me donne accès à l'encapsuleur généré par win32com.

>>> import win32com.client 
>>> doc = win32com.client.gencache.EnsureDispatch ("WinX32.DocFile") 
>>> print(doc.GetFrame.__module__) 
'win32com.gen_py.1A762221-D8BA-11CF-AFC2-508201C10000x0x3x12.IDocFile4' 

Dans mon cas, le fichier correspondant est situé dans le dossier suivant:

C:\WinPython\WinPython-32bit-3.5.2.2\python-3.5.2\Lib\site-packages\win32com\gen_py\1A762221-D8BA-11CF-AFC2-508201C10000x0x3x12 

La mise en œuvre de GetFrame se présente comme suit.

def GetFrame(self, frame=defaultNamedNotOptArg, FrameVariant=defaultNamedNotOptArg): 
    'Get Frame Data' 
    return self._ApplyTypes_(10, 1, (24, 0), ((2, 1), (16396, 3)), 'GetFrame', None, frame, FrameVariant) 

Donc la magie est dans la méthode _ApplyTypes_. Cette méthode est elle-même définie dans win32com\client\__init__. Nous pouvons voir que tout est fondamentalement passé à InvokeTypes.Selon le message this sur la liste de diffusion Python-win32, InvokeTypes est très similaire à Invoke, qui à son tour est une ré-implémentation de IDispatch::Invoke. Le code source de l'implémentation C++ intégrée dans Python peut être trouvé here. Passer en revue cette implémentation C++ explique également ce qui m'a dérangé dans ma question initiale: La version Python de Invoke transforme explicitement les arguments parref en valeurs de retour. Donc, au moins, il ne devrait pas y avoir de fuite de mémoire, ce que je soupçonnais au début.

Maintenant, que pouvons-nous apprendre sur les types d'arguments? Les informations nécessaires sont stockées dans le tuple ((2, 1), (16396, 3)). Nous avons deux arguments, dont le premier est un argument d'entrée seulement (indiqué par 1), tandis que le second est un argument d'entrée et de sortie (indiqué par 3 = 1 | 2). Selon l'entrée du blog this, les premiers chiffres respectifs nous indiquent le type de type Variant attendu.

Nous pouvons rechercher dans la liste this, ce que les chiffres signifient réellement. Le premier argument est un int16 signé, ce qui est logique, car il spécifie le numéro d'image. Le deuxième nombre a la signification suivante.

16396 = 0x400c = VT_VARIANT | VT_BYREF 

Le documentation nous dit, ce VT_VARIANT signifie réellement.

Soit le type spécifié, ou le type de l'élément ou champ contenu DOIT être VARIANT

Pas super instructif, mais quand même. Il semble que le choix de passer un ctypes.c_float n'est pas vraiment un bon choix. Au lieu de cela, je passe maintenant une variante, comme je devrais probablement, inspiré par la discussion this.

var = win32com.client.VARIANT(pythoncom.VT_VARIANT | pythoncom.VT_NULL | pythoncom.VT_BYREF, None) 
spectrum = doc.GetFrame(frame, var) 

Depuis ce changement, je n'ai pas les accidents observés plus de cette partie de code, de sorte que la question initiale est résolu pour moi.