gpt4 book ai didi

python - Py_InitModule4()在嵌入式Python解释器(使用Cython)上返回NULL

转载 作者:太空宇宙 更新时间:2023-11-04 04:03:17 25 4
gpt4 key购买 nike

我正在用Cython编写一个C插件。最后,这应该将Python解释器嵌入到Windows上的iTunes中。为了使其工作,需要一个bootstrapper。它实现了iTunes插件入口点,初始化或完成,或者需要什么,然后调用Cython生成的代码。
我在Windows 7 64Bit和CPython 2.7上使用MinGW gcc 4.6.2。
序言
Windows上的iTunes插件入口点称为iTunesPluginMain。据我所知,实现插件的共享库并不是在iTunes运行时一直保存的,因此一旦调用入口点,就不能存储任何全局变量。这就是为什么iTunes希望开发人员存储一个指向句柄的指针,该句柄在每次调用void*时都被传递。
iTunesPluginMain会调用几个通知,例如初始化和清理。
引导程序如下所示:

/** coding: utf-8
file: bootstrap.c
Copyright (c) 2012 by Niklas Rosenstein

This file implements the bootstrapper for loading the PyTunes plugin. **/

#include <Python.h>
#include <stdlib.h>
#include <windows.h>
#include <iTunesVisualAPI/iTunesAPI.h>

#include "pytunes.c"

#define EXPORT(type) __declspec(dllexport) type

extern void initpytunes(void);
extern OSStatus PyTunes_Main(OSType, PluginMessageInfo*, void*);

EXPORT(OSStatus) iTunesPluginMain(OSType message, PluginMessageInfo* msgInfo, void* refCon) {

OSStatus status = unimpErr;
char handlePyMain = 1;

switch(message) {
case kPluginInitMessage: {
// Sent to notify the plugin that this is the first time it is loaded
// and should register itself to iTunes

char** argv = malloc(sizeof(char*) * 1);
argv[0] = malloc(sizeof(char) * 256);

// WinAPI call, retrieves the path to iTunes.exe
GetModuleFileName(0, argv[0], 256);

// Initialize the Python-interpreter
Py_SetProgramName(argv[0]);
PyEval_InitThreads();
Py_Initialize();
PySys_SetArgvEx(1, argv, 0);
handlePyMain = 1;

free(argv[0]);
free(argv);
break;
}

case kPluginCleanupMessage: {
// Sent to cleanup the memory when the plugin gets unload

status = PyTunes_Main(message, msgInfo, refCon);
handlePyMain = 0;
Py_Finalize();
break;
}

default: break;
}

if (handlePyMain != 0) {
initpytunes();
status = PyTunes_Main(message, msgInfo, refCon);
}

return status;
}

iTunesPluginMain由Cython生成。现在引导程序应该做的是:
确定iTunes想告诉插件什么
如果iTunes通知它初始化,它将通过Windows API调用检索 pytunes.c的路径并初始化Python解释器。
如果iTunes通知它进行清理(例如iTunes关闭),它将完成Python解释器。请注意,“Cython调用”是在这种情况发生之前完成的,并且 iTunes.exe被设置为零,因此当解释器已经完成时不会再次执行它。
如果 handlePyMain未设置为零(表示不应执行Cython调用),则调用由Cython生成的 handlePyMaininitpytunes。调用 PyTunes_Main是必要的,因为Cython在那里初始化全局变量。 initpytunes是插件功能的Cython实现。
Cython实现
PyTunes_Main中实现的对 PyTunes_Main的调用运行顺利。下面的实现在我的桌面上打开一个文件并向其中写入一条消息。
cimport iTunesVisualAPI     as itapi

cdef public itapi.OSStatus PyTunes_Main(itapi.OSType message,
itapi.PluginMessageInfo* msgInfo,
void* refCon):
fl = open("C:/Users/niklas/Desktop/feedback.txt", "w")
print >> fl, "Greetings from PyTunes!"
fl.close()
return itapi.unimpErr

当我启动iTunes时,会创建文件并将文本写入其中。
pytunes.pyx包含使API可用于Cython的 iTunesVisalAPI.pxd声明,但这在这里不太重要。
问题描述
例如,在Cython中导入 cdef extern from "iTunesVisualAPI/iTunesVisualAPI.h"模块并使用它时,就会出现问题。简单示例:
cimport iTunesVisualAPI     as itapi

import sys

cdef public itapi.OSStatus PyTunes_Main(itapi.OSType message,
itapi.PluginMessageInfo* msgInfo,
void* refCon):
fl = open("C:/Users/niklas/Desktop/feedback.txt", "w")
print >> fl, sys
fl.close()
return itapi.unimpErr

这会导致iTunes崩溃。这是完整的gdb会话,它将告诉我们实际的问题是什么。
C:\Program Files (x86)\iTunes>gdb -q iTunes.exe
Reading symbols from c:\program files (x86)\itunes\iTunes.exe...(no debugging symbols found)...done.
(gdb) b pytunes.c:553
No symbol table is loaded. Use the "file" command.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (pytunes.c:553) pending.
(gdb) r
Starting program: c:\program files (x86)\itunes\iTunes.exe
[New Thread 3244.0x3a8]
[New Thread 3244.0xd90]
[New Thread 3244.0x11c0]
[New Thread 3244.0x125c]
[New Thread 3244.0x1354]
[New Thread 3244.0x690]
[New Thread 3244.0x3d8]
[New Thread 3244.0xdb8]
[New Thread 3244.0xe74]
[New Thread 3244.0xf2c]
[New Thread 3244.0x13c0]
[New Thread 3244.0x1038]
[New Thread 3244.0x12b4]
[New Thread 3244.0x101c]
[New Thread 3244.0x10b0]
[New Thread 3244.0x140]
[New Thread 3244.0x10e4]
[New Thread 3244.0x848]
[New Thread 3244.0x1b0]
[New Thread 3244.0xc84]
[New Thread 3244.0xd5c]
[New Thread 3244.0x12dc]
[New Thread 3244.0x12fc]
[New Thread 3244.0xf84]
warning: ASL checking for logging parameters in environment variable "iTunes.exe.log"

warning: ASL checking for logging parameters in environment variable "asl.log"

BFD: C:\Windows\SysWOW64\WMVCORE.DLL: Warning: Ignoring section flag IMAGE_SCN_MEM_NOT_PAGED in section .reloc

Breakpoint 1, PyTunes_Main (__pyx_v_message=1768843636, __pyx_v_msgInfo=0xd7e798, __pyx_v_refCon=0x0)
at C:/Users/niklas/Desktop/pytunes/pytunes/build-cython/pytunes.c:553
553 __pyx_t_1 = __Pyx_GetName(__pyx_m, __pyx_n_s__sys); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno
= 75; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
(gdb) print __pyx_m
$1 = (PyObject *) 0x0
(gdb) print __pyx_n_s__sys
$2 = (PyObject *) 0x92f42c0
(gdb) print __pyx_t_1
$3 = (PyObject *) 0x0
(gdb) step
__Pyx_GetName (dict=0x0, name=0x92f42c0) at C:/Users/niklas/Desktop/pytunes/pytunes/build-cython/pytunes.c:788
788 result = PyObject_GetAttr(dict, name);
(gdb) step

Program received signal SIGSEGV, Segmentation fault.
0x1e089f57 in python27!PyObject_GetAttr () from C:\Windows\SysWOW64\python27.dll
(gdb)

旁注:第553行是Python语句 sys由Cython处理的行。您可以在 paste.pocoo.org上找到 print >> fl, sys的完整生成源代码。
调试会话告诉我们 pytunes.c与Cython代码中的 __pyx_m_t模块一起在上下文中使用(为什么?)。无论如何,它是一个空指针。它应该在第699行初始化。 sys显然返回NULL,因此应该在 Py_InitModule4内引发ImportError。(您可以在第751行找到相应的 initpytunes实现)。
为了检查这一点,我对代码进行了一些修改,结果在该上下文中是“肯定的”。
    if (handlePyMain != 0) {
initpytunes();
if (PyErr_Occurred()) {
PyObject* exception, *value, *traceback;
PyErr_Fetch(&exception, &value, &traceback);
PyObject* errString = PyObject_Str(exception);

// WinAPI call
MessageBox(NULL, PyString_AsString(errString), "PyErr_Occurred()?", 0);
status = paramErr;
}
else {
// WinAPI call
MessageBox(NULL, "No error, calling PyTunes_Main.", "PyPyErr_Occurred()?", 0);
status = PyTunes_Main(message, msgInfo, refCon);
}
}

问题
你知道还是知道我做错了什么?也许我初始化Python解释器时出错了?最奇怪的是,我有一个工作的原型。但我再也不能让它工作了!(见下文)
参考文献和注释
您可能想查看完整的源代码。您可以找到工作原型 hereVirustotal)和实际项目 hereVirustotal)。(两者都链接到mediafire.com)
由于不允许我将itunesvisualdk与它一起发布, here是一个从apple.com下载它的链接。
请不要评论“为什么不使用原型?”或者类似的。它是一个原型,我写的原型脏兮兮的,通常我在重写整个东西时会取得更好的效果。
感谢大家看了这篇文章,仔细阅读,花时间帮助我解决问题。:-)
-尼古拉斯

最佳答案

ImportError表示Python无法导入模块。检查异常的值以查看未找到哪个模块。
Moduleinit函数返回void,因此您应该始终在它之后调用PyErr_Occurred()以检查它是否失败。如果发生错误,您必须处理它,最好以某种方式向用户显示它。如果stdout可用,PyErr_Print()将打印出完整的回溯。
我不确定iTunes插件是如何工作的,但如果它确实在调用之间卸载了DLL,那么Python也将被卸载,其状态将丢失。在每次调用Py_Initialize()时都需要调用Py_Finalize()iTunesPluginMain(),这意味着所有Python对象也将丢失。很可能不是你想要的。
防止这种情况的一个办法是在kPluginInitMessage中重新打开插件DLL,然后在kPluginCleanupMessage中关闭它。Windows会跟踪进程打开DLL的次数。只有当计数达到0时,才会卸载DLL因此,如果在DLL中调用LoadLibrary(),则计数将增加到2,并且只有在iTunes和代码调用FreeLibrary()之后,DLL才会被卸载。
注意,这只是一个(未经测试的)想法。

关于python - Py_InitModule4()在嵌入式Python解释器(使用Cython)上返回NULL,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9319315/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com