【发布时间】:2012-03-08 07:48:34
【问题描述】:
我正在使用 Cython 编写 C 插件。最后,这应该将 Python-Interpreter 嵌入到 Windows 上的 iTunes 中。为此,需要bootstrapper。它实现了 iTunes 插件入口点,初始化或完成,或任何需要的,然后调用从 Cython 生成的代码。
我在 Windows 7 64Bit 和 CPython 2.7 上使用 MinGW gcc 4.6.2。
序言
Windows 上的 iTunes 插件入口点称为 iTunesPluginMain。据我了解,实现该插件的共享库不一直保持 iTunes 运行,因此您可以不在条目后存储任何全局变量-point 被调用。这就是为什么 iTunes 希望开发人员存储一个指向句柄的 void* 指针,该句柄在每次调用 iTunesPluginMain 时都会传递给该句柄。
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;
}
pytunes.c 由 Cython 生成。现在引导程序做什么,或者应该做什么,如下:
-
确定 iTunes 想要告诉插件什么
- 如果 iTunes 通知它初始化,它会通过 Windows API 调用检索到
iTunes.exe的路径并初始化 Python 解释器。 - 如果 iTunes 通知它进行清理(例如 iTunes 关闭),它会最终确定 Python 解释器。请注意,“Cython 调用”在此之前完成,
handlePyMain设置为零,因此在解释器已经完成时不会再次执行。
- 如果 iTunes 通知它初始化,它会通过 Windows API 调用检索到
如果
handlePyMain未设置为零,表示不应执行 Cython 调用,则调用从 Cython 生成的initpytunes和PyTunes_Main。调用initpytunes是必要的,因为 Cython 在那里对全局变量进行初始化。PyTunes_Main最终是插件功能的 Cython 实现。
Cython 实现
在pytunes.pyx 中实现的对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 时,会创建文件并将文本写入其中。
iTunesVisalAPI.pxd 包含 cdef extern from "iTunesVisualAPI/iTunesVisualAPI.h" 声明以使 API 可用于 Cython,但这在这里不太重要。
问题描述
例如,当在 Cython 中导入 sys 模块并使用它时,就会出现问题。简单例子:
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-session,它会告诉我们问题到底是什么。
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 语句 print >> fl, sys 由 Cython 处理的行。您可以在paste.pocoo.org 上找到pytunes.c 的完整生成源代码。
调试会话告诉我们 __pyx_m_t 在 Cython 代码中使用 sys 模块的上下文中使用(为什么?)。无论如何,它是一个 NULL 指针。它应该在 699 行初始化。 Py_InitModule4 显然返回 NULL,因此应该在 initpytunes 内引发 ImportError。 (可以在751行找到goto __pyx_L1_error的对应实现。
为了检查这一点,我稍微修改了代码,结果在该上下文中是“肯定的”。
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 解释器错误?最奇怪的部分是,我有一个工作原型。但我不能让它工作了! (见下文)
参考资料和注释
您可能想查看完整的源代码。您可以找到工作原型here (Virustotal) 和实际项目here (Virustotal)。 (两者都是 mediafire.com 的链接)
由于我不允许随其分发 iTunesVisualSDK,here 是从 apple.com 下载它的链接。
请不要评论“为什么不使用原型?”或类似的。它是一个原型,而我写的原型又脏又脏,通常我在重写整个东西时会取得更好的结果。
感谢所有看到此内容、仔细阅读并投入时间帮助我解决问题的人。 :-)
-尼克拉斯
【问题讨论】:
-
将异常的值添加到 msgbox。这可能会给你一个线索。也许它找不到标准库。该值将告诉您它无法导入哪个模块。
-
另外,我知道你还没有达到那一点,但是 Python 使用全局状态,所以如果 iTunes 决定在调用 main 之间卸载你的 dll,你可能(将会)遇到麻烦。
-
@yak 天哪!这太丢人了!我非常确信
pytunes模块的创建失败,我没有查看异常的值。No module named iTunesVis,真丢人。cimport iTunesVis as it是pytunes.pxd的行之一。由于iTunesVis.pxd中有一个 Python 类声明,Cython 需要一个名为 iTunesVis 的模块。删除 Python 声明或完全删除 cimport 可以修复它。非常感谢,我真的应该在问这个问题之前就这样做.. >.> 也许你想写一个答案?免费声望哈哈。 -
@yak 嗯,我实际上并没有考虑过。这是否意味着我需要初始化 和在 every上完成解释器> 打电话给
iTunesPluginMain?