[PR]テレビ番組表
今夜の番組チェック


実践Python拡張モジュールづくり (MS-Windows編)

 さて、今度はMS-Windows環境でC言語を使って、 Pythonの拡張モジュールを作る方法を手順を追って紹介してみます。 使うコンパイラはボーランドのBorland C/C++ Compiler 5.5 (BC++)、 Python 2.2で確認しています。 PythonのWin32バイナリ配布には、マイクロソフトのVisual C++ 6.0 用のプロジェクトファイルやライブラリがついてきますが、 それだと使える環境にある人はかなり限られてしまいます。 このページでは、 ときどき開発系の雑誌に付録CD-ROMでついてくるBC++で試してみました。 ただし、 ここで取り上げる方法は、 Pythonのマニュアルにはドキュメントされていない方法です。 これで確実にビルドできるわけではないのでご了承ください。

ソースプログラムを書く
 このページで使うサンプルも、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」としてビルドしても認識されません。 なぜそのようになるのかは、あまりよく分かっていません。

 escapermodule.c、python22imp.lib、それに Makefile ができたら準備はOKです。 さっそくビルドしてみます。

> 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)