
| ■ | 実践Python拡張モジュールづくり (MS-Windows編) |
●ソースプログラムを書く
このページで使うサンプルも、UNIX編と同じ「escaper」というモジュールです。
手間を惜しんだ? あー、そうですよそうですよ。ちっ(変な仕草)
#include "Python.h"
#include <ctype.h>
#include <string.h>
#include <windows.h>
#define DLLEXPORT __declspec(dllexport)
/* 引数は
* escaper.escape("string")
* escaper.escape("string", 進数(8 or 16)
* の2通りを可能とします。上の場合は16進数モードになります。
*/
static PyObject* escaper_escape(PyObject* self, PyObject* args){
int radi;
int i;
int srclen;
char* srcp;
PyObject* outobj;
/* 引数のデータ型「s#」は文字列本体とその長さの組を表します */
if(! PyArg_ParseTuple(args, "s#i", & srcp, & srclen, & radi)){
if(! PyArg_ParseTuple(args, "s#", & srcp, & srclen))
return NULL;
else
radi = 16;
}
if(radi != 8 && radi != 16){
PyErr_SetString(PyExc_AttributeError,
"radix parameter must be one of: 8, 16.");
return NULL;
}
{
char* p, * q, * resultp;
int i;
/* 元の文字列の最大4倍の長さになる可能性があります */
if((resultp = (char* )malloc(srclen * 4)) == NULL){
PyErr_SetString(PyExc_MemoryError, "Ootto! out of memory.");
return NULL;
}
for(i=0, p=srcp, q=resultp; i<srclen; i++){
if(isprint(*p))
*q++ = *p++;
else{
char tbuf[10], * r;
if(radi == 16){
sprintf(tbuf, "%02x", *p); *q++ = '\\'; *q++ = 'x';
}
else{
sprintf(tbuf, "%03o", *p); *q++ = '\\';
}
for(r=tbuf; *r; *q++ = *r++) ;
p++;
}
}
*q = '\0';
outobj = Py_BuildValue("s", resultp);
free( (char* ) resultp );
return outobj;
}
}
static PyMethodDef defs[] = {
{"escape", escaper_escape, METH_VARARGS},
{NULL, NULL}
};
DLLEXPORT void APIENTRY initescaper(void){
Py_InitModule("escaper", defs);
}
/* end. */
|
ソースプログラムの名前は
「escapermodule.c」でUNIXのときと同じです(*1)。
C言語的なプログラム・コードも全く同じでよいのですが、
MS-Windowsでは、
PythonモジュールをCで書いた場合はダイナミックリンクライブラリ
(DLL)としてビルドします。その際、
DLL特有のクセに対応するため、UNIXのときと比べて幾つか書き足す内容があります
(上の赤字部分)。
初期化関数の型修飾ですが、__declspec(dllexport)
もAPIENTRYも必須です。どちらかを欠くと、
DLLはビルドできますが、
Pythonからインポートしようとすると、
「兄貴、escapermodule.dllには関数initescaperが見つからないっすよ」
という超兄貴なメッセージが出てしまいます。
さて、ソースプログラムが書けたところでビルドですが、
まずPythonのWindowsバイナリ配布に含まれるライブラリ(python22.lib)
はVC++用なので、BC++に付属のimplib
(インポートライブラリ ユーティリティ)を使って、
Python22.dllのBC++用のインポートライブラリを作ってしまいます。
この辺りから外道技なので、うまくいかないかも…かも…です。
> C:\Borland\Bcc55\implib -a python22imp.lib C:\Windows\System32\Python22.dll |
Python22.dll はインストール時に上のように C:\Windows\System32
にコピーされると思うので存在することを確認してみてください。
コマンドラインの
-aは、インポートライブラリ中のCDECLのシンボルの先頭にマイクロソフト互換の
「_」文字をつけるスイッチで、ここでは必ずつけてください。
python22imp.libができたら、
Pythonのインストールディレクトリの下にある、Libsにでも置いときましょう。
この場所はビルド時に指定できるので、どこでもよいです。
最後はMakefileです。テキストファイルで下のように直書きします。
PYROOT = C:\Wintools\Python22 CC = bcc32 SHLD = bcc32 -tWD # ↑bcc32.cfg、ilink32.cfgの設定をし、コンパイラにPATHを通しておきます。 CFLAGS = -I"$(PYROOT)\include" LIBS = $(PYROOT)\libs\python22imp.lib # ↑インポートライブラリはどこでもいいので置いてください(無責任) .c.obj: $(CC) $(CFLAGS) -e$@ -c $*.c escaper.dll: escaper.obj $(SHLD) -e$@ $? $(LIBS) |
出来上がるDLLの名前は、モジュール名+「module.dll」または
モジュール名+「.dll」のどちらかです。つまり、
ここでは escaper.dll でも構いません。
| (*1) ソースプログラムの名前を「escapermodule.c」の代わりに「escaper.c」 とすることも可能です。ただしこの場合はビルドするDLLの名前は必ず「escaper.dll」 にしなければならず、「escapermodule.dll」としてビルドしても認識されません。 なぜそのようになるのかは、あまりよく分かっていません。 |
> c:\borland\bcc55\bin\make -f Makefile |
出来たDLLはPythonのインストールディレクトリの下、
DLLsディレクトリに置くのが普通ですが、
勿論、PYTHONPATHを通した場所に置いてもOKです。
Pythonスクリプトからのインポート方法、使い方はUNIX環境の場合と全く同じです。
import escaper
a = escaper.escape('\x09\x10\x13Hello,\x76\x77\x90', 16)
print a
|
ちなみに、あるバージョンのPython環境でビルドしたDLLを他のバージョンのPython
インタープリタで使うことはできません。
例えば、バージョン2.2でビルドしたescapermodule.dllを2.1で使おうとすると、
Fatal Python error: Interpreter not initialized (version mismatch?)というエラーメッセージが表示されます。 |
というわけで、UNIX環境に比べてちょっとややこしいですが、 MS-Windows環境でも無償配布の BC++コンパイラを使ってPythonモジュールを作ることができます。 どうしてもC言語で書きたいルーチンをPythonに追加することで、 Pythonを使う幅がますます広がるはずです。
セクションのサブメニューに戻る
(first uploaded 2001/01/27 last updated 2002/07/20)