重厚長大なモジュラー型警察ミステリ。 エジンバラの難民問題をストーリーに絡ませ社会派ミステリとしての一面も見せてくれる。 時には軽口もたたくリーバス警部は魅力的。 一見、何のつながりもないと思われていた事件が絡み合うプロットは見事。
だけど、短編集でリーバス物に入門した私には、ストーリーが長くて読むのに少し疲れた。
これで今年は翻訳4冊、国内4冊で五分と五分。
突然、自分の目の前に現れた魅力的な女性。 彼女が話す言葉や仕草は、死んだはずのかつての恋人のものとしか思えなくなってくる。 死んだはずの恋人が生まれ変わったのか、それともこれは巧妙に仕組まれた罠なのか?
ミステリには魅力的な謎が欠かせません。 いくら謎解きがすごくても、発端の謎の見せ方がしょぼいと駄目なのです。 その点、この作品の謎はとびっきり魅力的です。 謎があまりにも魅力的なので、真相が逆にしょぼいと嫌だなあと思いながら読んだのですが、 この結末にはいい意味でも悪い意味でも裏切られました。
まあ、作者も書いている通り、常軌を逸した謎解きなしのサスペンスです。 謎解きを期待するとがっかりするでしょうが、ぐいぐい読ませる変なサスペンスとして楽しめます。
井上夢人とかダン・シモンズとかが好きならこの作品も楽しめるかも知れません。
わかりやすく暗号技術を解説している本。 例え話を使ったり、理解度確認のための問題を普通の本なら「問題」とするところをクイズとしたり、 アリスやマロリーやボブといった登場人物を使うなど、わかりやすくしようとする工夫が見られる。 暗号技術の世界の敷居を下げてくれる本。
奇妙な感じのかなり癖のあるミステリ風味の短編集。 SF色の強い短編もあり、サイコ・サスペンス風味の短編もあり、なんだか良くわからないような短編もある。 「君微笑めば」のサイコ風な逆転劇にぞくりとさせられ、 「マエストロを殺せ」の意外な展開と緊迫感にドキドキさせられ、 「ルウェリンの犯罪」の皮肉な結末に何とも言えない情けないような切ないような気にさせられた。 1900年代半ばの作品とは思えない。サイコ・サスペンスの最先端ではないかと思ってしまう。
読んで良かった、これは傑作かも知れないと思わせた「ルウェリンの犯罪」が好み。
思うところがあって、J2SE 1.4、J2SE 5.0、Eclipse 3.1.2 をインストールした。
ctypes モジュールのサイトをちょっと見た範囲では、 やはり、Ruby の dl拡張ライブラリと同じようなものらしい。
ソースに含まれているように見えません...。
どうやら、ctypes.com は、comtypesモジュールに置き変えられたらしい。
ドキュメントもサンプルも無いのはつらいなあ。
0.9.6 には ctypes.com モジュールが含まれていた。
インストールした。
新しいプロジェクトで、クラスライブラリを選択する。 プロジェクト名を MyComSvrCSLib と指定する。 Class1.csをMyComSvr.csに変更する。
public メソッド sum を追加する。
namespace MyComSvrCSLib
{
public class MyComSvr
{
public int sum(int x, int y)
{
return x + y;
}
}
}
「プロジェクト」メニューから「MyComSvrCSLibのプロパティ」を選択する。 アセンブリ情報ボタンを押し、アセンブリをCOM参照可能にする。 ビルドタブを選択し、COM相互運用機能の登録にチェックを入れる。 (これらをチェックするとビルド時にMyComSvrの情報がCOMサーバーとしてレジストリに書き込まれます。 チェックを外してリビルドすれば、レジストリの情報は削除されるようです。)
コマンドプロンプトで、次のように実行する。
ruby -rwin32ole -e 'x = WIN32OLE.new("MyComSvrCSLib.MyComSvr"); p x.sum(10, 20)'
結論。Visual C# 2005 Express Edition なら簡単にCOMサーバ(?)を作ることができる。 (と言えなくもないんじゃないかと思う。)
COMと.NETの相互運用については、 COM and .NET Interoperability に解説があるかも知れない。
ソースをぼんやりと眺めてみたんだけど、COMサーバーのカラクリがわからず。 Pythonのソースの読み方をわかってないからかも知れない。
Rubyのスクリプトと、COMサーバーをどう結びつけるかというところで、 Ruby のスクリプトを解釈するインタプリタ搭載の dll がCOMサーバーになる方法しか思い浮かばない。 (が、インタプリタを組み込むのは、RubyWinで一度やったことがあるので、今一つ気分が乗らない。)
pythoncom.dllがCOMサーバーっぽいんだけど、Pythonも同じアプローチなのかどうなのか確信が持てず。
ctypesモジュールは python.exeそのものをCOMサーバーとして登録している。 こっちのソースも見てみるか。 ctypes って、もしかしたら、Ruby の dl拡張ライブラリみたいなもの?
超適当いい加減COMプログラミングの続き。
私はCOMプログラミングの素人なので、間違いがあると思います。 間違いは指摘してもらえると嬉しいです。
Win32OLEは、CMyComSvr::GetIDsOfNames を呼び出して 「sumメソッドってありますか? あったら呼び出し番号教えて。」と質問します。
呼び出し番号を返すようにしてあげます。 sumメソッドについて聞かれたときだけ、呼び出し番号を返してあげるようにします。 こんな感じです。 メソッドは大文字小文字を無視して比較することにします。 「sumメソッド」でも「SUMメソッド」でも「Sumメソッド」でも大丈夫です。 VBなんかと同じですね。
STDMETHODIMP CMyComSvr::GetIDsOfNames(
const IID & iid,
LPOLESTR *pszNames,
UINT cNames,
LCID lcid,
DISPID *pDispID) {
for(UINT i = 0; i < cNames; i++) {
pDispID[i] = DISPID_UNKNOWN;
if(lstrcmpiW(pszNames[i], L"sum") == 0) {
pDispID[i] = 1;
}
else {
return DISP_E_MEMBERNOTFOUND;
}
}
return S_OK;
}
nmake して mycomcl.rb を実行すると
OLE error code:0
とエラーになります。
sum メソッドの番号を教えてもらったWin32OLEは、その番号で、 CMyComSvr::Invoke を呼び出します。 今は、まだ、 CMyComSvr::Invoke では、常にエラーを返すようにしているので、エラーになる訳です。 そこで、idが1番のとき(sumメソッドが呼ばれたとき)には、それなりの処理をしてあげるようにします。
STDMETHODIMP CMyComSvr::Invoke(
DISPID id,
const IID & iid,
LCID lcid,
WORD wFlags,
DISPPARAMS *pDispParams,
VARIANT *pVarResult,
EXCEPINFO *pExcepInfo,
UINT *pUArgError
) {
int cArgs = pDispParams->cArgs;
VARIANTARG *rgvarg = pDispParams->rgvarg;
INT v1 = 0, v2 = 0, r = 0;
HRESULT hr;
switch(id) {
case 1:
if (cArgs != 2) {
return DISP_E_BADPARAMCOUNT;
}
if (V_VT(&rgvarg[0]) != VT_I4 || V_VT(&rgvarg[1]) != VT_I4) {
return DISP_E_BADVARTYPE;
}
v2 = V_I4(&rgvarg[0]);
v1 = V_I4(&rgvarg[1]);
r = 0;
hr = sum(v1, v2, &r);
if (pVarResult) {
V_I4(pVarResult) = r;
V_VT(pVarResult) = VT_I4;
}
return hr;
break;
default:
return DISPID_UNKNOWN;
}
return DISPID_UNKNOWN;
}
引数の数のチェックと引数の型のチェックを最低しておきます。 VariantChangeTypeなどを使って、違う型だったとしてもそれなりに変換してあげるのが筋かも知れませんが、ここは手抜きです。
nmake してから、mycomsvr.rb を実行してみると晴れて期待通りの結果が得られるようになります。
実は、VBScript や VBAから実行するとエラーになってしまいます。 VBScript や VBA では、整数値は、VT_I2 に変換して渡してくるので、 VT_I4のチェックしかしてないCMyComSvr::Invokeではエラー扱いになってしまうのです。
VBScriptや VBAで使えるようにするなら、VT_I4 のところ とか V_I4のところを全部 VT_I2 や V_I2 に置き変えればいいのですが 今度はWin32OLEからはエラーになってしまいます。 どちらでも使えるようにするには、それなりに工夫が必要です。 ですが、超適当いい加減COMプログラミングの範囲を越えそうなので、この辺りで一旦やめます。 (気が向いたらやるかも知れませんが...。)
Win32OLEからでも、VBAからでも、VBScriptからでも使えるようなCOMサーバーを実装するには、COMサーバー側でそれなりの工夫が必要です。 逆に、どんなCOMサーバー相手でも、Win32OLEから使えるようにするには、Win32OLE側にもそれなりの工夫が必要です。 今回の例のように、COMサーバーは、それなりに適当に作れちゃう訳で、型チェックなんかはCOMサーバー次第ですから。
ここまでのソース 一揃いを圧縮した mycomsvr05.zipを置いておきます。 信用できない超適当いい加減COMプログラミングなので、勇気のある方は自己責任で利用してください。 煮るなり焼くなり好きにしてください。
結論。 VC2005 EE と Platform SDK を使っても、ものすごく頑張れば、 COMサーバープログラミングもできなくはない。 (と言ってもいいんじゃないかと思う。)
超適当いい加減COMプログラミングの続き。
mycomsvr.idl に sum メソッドを追加します。
interface IMyComSvr:IDispatch
{
import "oaidl.idl";
HRESULT sum([in] INT v1, [in] INT v2, [out, retval] INT *result);
};
メソッドはHRESULT型です。 引数は3つです。v1 と v2が入力される引数で、resultにはv1とv2を足した結果が入ります。 ここで、最後の引数の前に
[out, retval]
とあります。 これは、Win32OLEから呼び出したときには戻り値になることを意味しています。
ここで、nmakeするとエラーになりますので、mycomsvr.cppに CMyComSvr::sumメソッドを定義します。
class CMyComSvr : public IMyComSvr
{
...
STDMETHODIMP Invoke(DISPID,REFIID,LCID,WORD,DISPPARAMS *,VARIANT *,EXCEPINFO *,UINT *);
//IComMySvr
STDMETHODIMP sum(INT, INT, INT *);
};
...
STDMETHODIMP CMyComSvr::sum(
INT v1,
INT v2,
INT *result) {
*result = v1 + v2;
return S_OK;
}
では、ここで、mycomcl.rb から呼び出すようにしてみます。
require "win32ole"
obj = WIN32OLE.new("MyComSvrLib.MyComSvr")
p obj
p obj.sum(10, 20)
mycomcl.rbを実行すると残念ながら
メンバが見つかりません
とエラーになります。
実は、Win32OLEは、CMyComSvr::sum メソッドを直接呼び出さないのです。 Win32OLEは、CMyComSvr::GetIDsOfNames と CMyComSvr::Invoke を経由してsumメソッドを実行することになります。 ですから、 GetIDsOfNames と Invoke をそれっぽくしてやる必要があります。
次回は、 GetIDsOfNames の実装からです。
超適当いい加減COMプログラミングの続き。 CMyComSvrFactoryクラスの実装は、ほとんど決まり文句になります。 まあ、適当な実装なので、決まり文句だけで十分ではないかと...。
CMyComSvrFactory::CMyComSvrFactory()
{
m_cRef = 0L;
g_cRefThisDll++;
}
CMyComSvrFactory::~CMyComSvrFactory()
{
g_cRefThisDll--;
}
STDMETHODIMP CMyComSvrFactory::QueryInterface(REFIID riid, void** ppv)
{
*ppv = NULL;
if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IClassFactory))
{
*ppv = (IClassFactory*)this;
AddRef();
return S_OK;
}
return E_NOINTERFACE;
}
STDMETHODIMP_(ULONG) CMyComSvrFactory::AddRef()
{
return ++m_cRef;
}
STDMETHODIMP_(ULONG) CMyComSvrFactory::Release()
{
if (--m_cRef)
return m_cRef;
delete this;
return 0L;
}
STDMETHODIMP CMyComSvrFactory::CreateInstance(IUnknown* pUnkOuter, REFIID riid, void **ppvObj)
{
*ppvObj = NULL;
if (pUnkOuter)
return CLASS_E_NOAGGREGATION;
CMyComSvr *pObj = new CMyComSvr();
if (NULL == pObj)
return E_OUTOFMEMORY;
return pObj->QueryInterface(riid, ppvObj);
}
STDMETHODIMP CMyComSvrFactory::LockServer(BOOL fLock)
{
return S_OK;
}
最大のポイントは CMyComSvrFactory::CreateInstance で、CMyComSvrオブジェクトを生成して返しているところでしょうか。 今度は、nmakeすると、CMyComSvrの実体がないためにリンクでエラーになってしまいます。
CMyComSvrもかなり適当に実装してしまいましょう。
CMyComSvr::CMyComSvr() {
m_cRef = 0;
}
CMyComSvr::~CMyComSvr() {
}
STDMETHODIMP_(ULONG) CMyComSvr::AddRef() {
m_cRef++;
return m_cRef;
}
STDMETHODIMP_(ULONG) CMyComSvr::Release()
{
if (--m_cRef)
return m_cRef;
delete this;
return 0L;
}
STDMETHODIMP CMyComSvr::QueryInterface(REFIID riid, void** ppv)
{
*ppv = NULL;
if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IDispatch))
{
*ppv = (IDispatch*)this;
AddRef();
return S_OK;
}
return E_NOINTERFACE;
}
STDMETHODIMP CMyComSvr::GetTypeInfoCount(UINT * pUint) {
return E_NOTIMPL;
}
STDMETHODIMP CMyComSvr::GetTypeInfo(UINT, LCID, ITypeInfo **) {
return E_NOTIMPL;
}
STDMETHODIMP CMyComSvr::GetIDsOfNames(
const IID & iid,
LPOLESTR *pszNames,
UINT cNames,
LCID lcid,
DISPID *pDispID) {
for(UINT i = 0; i < cNames; i++) {
pDispID[i] = DISPID_UNKNOWN;
return DISP_E_MEMBERNOTFOUND;
}
return S_OK;
}
STDMETHODIMP CMyComSvr::Invoke(
DISPID id,
const IID & iid,
LCID lcid,
WORD wFlags,
DISPPARAMS *pDispParams,
VARIANT *pVarResult,
EXCEPINFO *pExcepInfo,
UINT *pUArgError
) {
int cArgs = pDispParams->cArgs;
VARIANTARG *rgvarg = pDispParams->rgvarg;
return DISPID_UNKNOWN;
}
とりあえず手抜きで、 GetTypeInfoCount と GetTypeInfo は E_NOTIMPL を返すことにします。 (Win32OLEにとって)肝心なのは、 GetIDsOfNames と Invoke なのですが、 今のところはほとんど何もやらず、それぞれ、DISP_E_MEMBERNOTFOUND、DISPID_UNKNOWN を返すぐらいにとどめておきます。
では、nmake してみましょう。やっと無事に dll ができました。 例によって、cssc04.rbを使って
ruby cssc04.rb MyComSvr
と実行すれば、dllを生成するために必要なファイル一式ができます。
さて、気分を変えて、Rubyプログラミングを少々。 次のスクリプトmycomcl.rbを用意します。
require 'win32ole'
obj = WIN32OLE.new("MyComSvrLib.MyComSvr")
p obj
mycomcl.rbを実行してみましょう。
クラス文字列が無効です。
とエラーになります。
ここで、
regsvr32 mycomsvr.dll
と実行します。これによってレジストリに情報が書き込まれます。
もう一度 mycomcl.rb を実行します。今度は次のようにエラーもなく終了しました。
#<WIN32OLE:0x2ac53c8>
ようやく、WIN32OLEオブジェクトを生成するところまでたどり着きました。
ちなみに、regsvr32 mycomsvr.dll でレジストリに登録した情報は
regsvr32 /u mycomsvr.dll
で削除できます。
今のところWIN32OLEオブジェクトを生成できるだけで、そこから先は何もできません。 次回からは、いよいよメソッドを実装してWin32OLEからも呼び出せるようにします。
一応、まとまったひな型ができたので、ソース一揃いを圧縮したmycomsvr04.zipを置いておきます。
超適当いい加減COMプログラミングの続き。 断っておきますが、私は、COMプログラミングに関して、ずぶの素人です。 間違いがあったら指摘していただけると助かります。
断ってませんでしたが、今回はCOMサーバーDLLを作ります。
DLLのCOMサーバーでは、DllCanUnloadNow、DllGetClassObject、DllRegisterServer、DllUnregisterServer の4つの関数を用意してエクスポートしなくてはなりません。 あと、DLLなので、DllMain関数も用意しましょう。
UINT g_cRefThisDll = 0;
HINSTANCE g_hmodThisDll = NULL;
BOOL SetRegKeyValue(LPTSTR pszKey, LPTSTR pszSubkey, LPTSTR pszName, LPTSTR pszValue)
{
BOOL bOk = FALSE;
LONG ec;
HKEY hKey;
TCHAR szKey[255];
lstrcpy(szKey, pszKey);
if (NULL != pszSubkey)
{
lstrcat(szKey, TEXT("\\"));
lstrcat(szKey, pszSubkey);
}
ec = RegCreateKeyEx(HKEY_CLASSES_ROOT, szKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, NULL);
if (ERROR_SUCCESS == ec) {
if (NULL != pszValue) {
ec = RegSetValueEx(hKey, pszName, 0, REG_SZ, (BYTE *)pszValue, (lstrlen(pszValue)+1)*sizeof(TCHAR));
}
if (ERROR_SUCCESS == ec)
bOk = TRUE;
RegCloseKey(hKey);
}
return bOk;
}
extern "C" int APIENTRY
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
g_hmodThisDll = hInstance;
DisableThreadLibraryCalls(hInstance);
}
else if (dwReason == DLL_PROCESS_DETACH)
{
}
return 1;
}
STDAPI DllCanUnloadNow(void)
{
return (g_cRefThisDll == 0 ? S_OK : S_FALSE);
}
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppvOut)
{
*ppvOut = NULL;
if (IsEqualIID(rclsid, CLSID_MyComSvr))
{
CMyComSvrFactory *pcf = new CMyComSvrFactory;
return pcf->QueryInterface(riid, ppvOut);
}
return CLASS_E_CLASSNOTAVAILABLE;
}
STDAPI DllRegisterServer(void)
{
TCHAR szModulePath[MAX_PATH];
GetModuleFileName(g_hmodThisDll, szModulePath, sizeof(szModulePath)/sizeof(TCHAR));
SetRegKeyValue(TEXT("MyComSvrLib.MyComSvr"), NULL, NULL, TEXT("MyComSvrLib"));
SetRegKeyValue(TEXT("MyComSvrLib.MyComSvr"), TEXT("CLSID"), NULL, TEXT("{8DA1EE08-1ED0-40B6-9B1A-0E7A189D0254}"));
SetRegKeyValue(TEXT("CLSID\\{8DA1EE08-1ED0-40B6-9B1A-0E7A189D0254}"), NULL, NULL, TEXT("MyComSvrLib"));
SetRegKeyValue(TEXT("CLSID\\{8DA1EE08-1ED0-40B6-9B1A-0E7A189D0254}"), TEXT("InprocServer32"), NULL, szModulePath);
SetRegKeyValue(TEXT("CLSID\\{8DA1EE08-1ED0-40B6-9B1A-0E7A189D0254}\\InprocServer32"), NULL, TEXT("ThreadingModel"), TEXT("Apartment"));
SetRegKeyValue(TEXT("CLSID\\{8DA1EE08-1ED0-40B6-9B1A-0E7A189D0254}"), TEXT("ProgID"), NULL, TEXT("MyComSvrLib.MyComSvr"));
return S_OK;
}
STDAPI DllUnregisterServer(void)
{
RegDeleteKey(HKEY_CLASSES_ROOT, TEXT("MyComSvrLib.MyComSvr\\CLSID"));
RegDeleteKey(HKEY_CLASSES_ROOT, TEXT("MyComSvrLib.MyComSvr"));
RegDeleteKey(HKEY_CLASSES_ROOT, TEXT("CLSID\\{8DA1EE08-1ED0-40B6-9B1A-0E7A189D0254}\\InprocServer32"));
RegDeleteKey(HKEY_CLASSES_ROOT, TEXT("CLSID\\{8DA1EE08-1ED0-40B6-9B1A-0E7A189D0254}\\ProgID"));
RegDeleteKey(HKEY_CLASSES_ROOT, TEXT("CLSID\\{8DA1EE08-1ED0-40B6-9B1A-0E7A189D0254}"));
return S_OK;
}
DllRegisterServerでは、ProgIDの情報だとかなんだとかCOMで必要な情報をレジストリに登録します。 (この情報は、Win32OLEが後からしっかり利用することになります。) DllUnregisterServerでは、レジストリの情報を削除するようにしておきます。
DllGetClassObject で、CMyComSvrFactoryオブジェクトを生成してQueryInterfaceで要求されたCOMオブジェクトを返すようにします。
ついでに、mycomsvr.def ファイルも作っておきましょう。
LIBRARY MyComSvr
EXPORTS
DllCanUnloadNow private
DllGetClassObject private
DllRegisterServer private
DllUnregisterServer private
この際なので、かなりいい加減なMakefileも作っておきます。
mycomsvr.dll: iid_mycomsvr.obj mycomsvr.obj
link /DLL /OUT:mycomsvr.dll /DEF:mycomsvr.def mycomsvr.obj iid_mycomsvr.obj user32.lib ole32.lib oleaut32.lib advapi32.lib
.c.obj:
cl -c $<
iid_mycomsvr.c:mycomsvr.idl
midl /proxy nul /iid iid_mycomsvr.c /h mycomsvr.h /tlb mycomsvr.tlb mycomsvr.idl
cssc03.rbを使えば、
ruby cssc03.rb MyComSvr
と実行すれば、ここまでのファイルがカレントディレクトリにできます。 (Rubyは、1.9の開発版が必要です。)
準備ができましたので、試しに nmake を実行してみます。 予想通り link でエラーになります。 CMyComSvrFactory と CMyComSvrクラスは、宣言しただけで実装してませんからね。
次回は、CMyComSvrFactoryをいい加減に実装することにします。
ついに結城さんがRubyにも手を出した!
超適当いい加減COMプログラミングの続き。
そろそろC++のソースも書きましょう。まずは、classの宣言だけ書いた mycomsvr.cppを用意します。 IClassFactoryを継承したCMyComSvrFactoryと IMyComSvrを継承したCMyComSvrを用意します。 CMyComSvrクラスが mycomsvr.idl で定義した coclass MyComSvrに対応します。
#include "mycomsvr.h"
class CMyComSvrFactory : public IClassFactory
{
protected:
ULONG m_cRef;
public:
CMyComSvrFactory();
~CMyComSvrFactory();
//IUnknown...
STDMETHODIMP QueryInterface(REFIID, void**);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
//IClassFactory...
STDMETHODIMP CreateInstance(IUnknown*, REFIID, void**);
STDMETHODIMP LockServer(BOOL);
};
class CMyComSvr : public IMyComSvr
{
protected:
ULONG m_cRef;
public:
CMyComSvr();
~CMyComSvr();
//IUnknown...
STDMETHODIMP QueryInterface(REFIID, void**);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
//IDispatch
STDMETHODIMP GetTypeInfoCount(UINT *);
STDMETHODIMP GetTypeInfo(UINT, LCID, ITypeInfo **);
STDMETHODIMP GetIDsOfNames(REFIID,OLECHAR**,UINT,LCID,DISPID *);
STDMETHODIMP Invoke(DISPID,REFIID,LCID,WORD,DISPPARAMS *,VARIANT *,EXCEPINFO *,UINT *);
};
ここで、試しに
cl -c mycomsvr.cpp
と実行すれば、mycomsvr.obj ができます。 class の実体が無いので、リンクはできませんが、objファイルはできます。
ここで、cssc02.rbを使えば
ruby cssc02.rb MyComSvr
とすれば、mycomsvr.idlとmycomsvr.cppが作られます。 mycomsvr.cppの中身は上のものと一緒です。
以下、超適当いい加減COMプログラミング。
RubyのWin32OLEから利用できるように、IDispatchインターフェースを持つCOMサーバーを作ります。 今回は、IDLファイルの作成。 次のmycomsvr.idlを作成します。
[
object,
uuid(8F730BC2-4923-4623-9344-D699C73F9EFE),
pointer_default(unique),
dual
]
interface IMyComSvr:IDispatch
{
import "oaidl.idl";
};
[
uuid(33E054DD-ED0E-4E15-AEB8-A5BF0946AB65),
helpstring("MyComSvr Type Library"),
version(1.0)
]
library MyComSvrlib
{
importlib("stdole32.tlb");
[
uuid(8DA1EE08-1ED0-40B6-9B1A-0E7A189D0254),
helpstring("MyComSvr class")
]
coclass MyComSvr
{
[default] interface IMyComSvr;
};
};
Win32OLEから利用できるようにdualなIDispatchインターフェースを派生させたIMyComSvrインターフェースを作ります。 IMyComSvrインターフェースには、まだメソッドを定義していませんがこの後定義していきます。
uuid(XXXXX...)
の部分は、Platform SDKのuuidgenを使って
uuidgen -i
と実行すれば、ユニークなGUIDを生成してくれます。
Ruby 1.9 のCVS最新版であれば、
ruby -rwin32ole -e 'puts WIN32OLE.create_guid'
でGUIDができます。
IMyComSvrをデフォルトのインターフェースとしてもつMyComSvrクラスを定義しておきます。
ここで、cssc01.rbを使えば
ruby cssc01.rb MyComSvr
とすれば、mycomsvr.idlが作られます。中身は上のものと一緒です。
ここで、Platform SDK の midlを使って
midl /proxy nul /iid iid_mycomsvr.c /h mycomsvr.h /tlb mycomsvr.tlb mycomsvr.idl
とすれば、iid_mycomsvr.c、mycomsvr.h、mycomsvr.tlb などが作られます。 とりあえず、C++で実装していきますので、今後、使うのは、iid_mycomsvr.c と mycomsvr.hです。
Platform SDK附属の OLE-COM Object Viewerを使って、mycomsvr.tlbを開けば、なんかそれっぽい情報を見ることができます。 mycomsvr.tlbを開くには、OLE-COM Object Viewer で、[File] メニューの [View TypeLib...]でmycomsvr.tlbを選択します。
北京オリンピックの代表選考会を兼ねた福岡国際マラソン。 42.195キロの間に参加ランナー、先導の白バイ警官、コーチ、マネージャーなどの思惑が複雑に絡み合うドラマ。 そして最後に栄光のゴールテープを切るのは?
長距離ミステリと言えば、ピーター・ラヴゼイの『死の競歩』が有名ですが(←本当か?)、こちらはマラソン。
一応、ミステリなので、殺人事件が起こって解決されます。 でもそれは、この小説のエピソードの一つにしか過ぎません。 マラソンのレースの行方がこの小説の読み所なのです。
何と言っても構成が見事。 レース開始直後の地点からゴールまで、1キロ単位に章を変え、語り手を変えて話は展開します。 このゴールにだんだん近付いていく臨場感がたまりません。 走っているランナー達の刻々と変化する心理状態、ゴールまでの駆引き。 それこそ、この小説の読みどころ。
そして、ラストには参りました。 まさか、そういう展開が待っていたとは。 完全にやられました。 一読の価値あり。
COMサーバーを作れそうなことが確認できた。 RubyのWin32OLEからも呼び出せる。
そうは言っても、かなりしょぼい。 作ったのは、それはそれは適当なCOMサーバーである。
Invokeなんて引数チェックもなしで
return S_OK;
してるだけ。
それでも、やろうと思えば、VC2005 EE と Platform SDK で COMサーバーが作れそうだ。
WIN32OLE.create_guid というメソッドを作者の個人的な都合で追加したいと思い始める。 Win32OLEテスト用のCOMサーバーのC++のソースコードをRubyで自動生成するときに少し楽ができそう。
高校生の菜加には妙なものを拾ってくる癖がある。犬や猫やアルマジロ...。 そんな菜加が拾ってきたのは、母親に置き去りにされた子供だった。
子供を家に返そうとするたびに、邪魔が入ったり、すれ違いがあったりして、なかなか返せない。 前半は、ドタバタユーモアサスペンスという感じ。 面白く読ませてくれる。 後半、明らかになってくる事件の全貌は重いものがある。 そのわりには、主役の高校生たちのキャラクターが明るいせいか、読んでいて重い気分になることはない。 携帯電話を使った駆引きは、息詰まるサスペンスを見ているようだった。 終盤の読みどころ。 青春ミステリというより、サスペンスだよ。やっぱり。
1.9に追加した。
おととしはミステリで100冊突破、去年は意図的に読書量を減らす、という目標(?)を達成してきた。 今年は、国内ミステリは控えて翻訳ミステリを中心に読むという目標に最近決めた。
それにしても翻訳ミステリの文庫本って高いなあ。 『よい子はみんな天国へ』は1344円。 『狼の帝国』は1050円。 『魔力の女』は1140円。 ジェフリー・ディーヴァーの『獣たちの庭園』 『クリスマス・プレゼント』でようやく950円。
結局、手に取ったのが『夜明けのフロスト』。
[ruby-list:41810]の件で、VT_VARIANT|VT_ARRAY をやめて、配列の一番目の要素に合わせて、 VT_XXX|VT_ARRAYにすれば良いかと一瞬考えた。 でも、文字列と数値が交ざったような配列だったりすると逆に困る気がする。
require 'win32ole'
application = WIN32OLE.new('Excel.Application')
application.visible = TRUE
workbook = application.Workbooks.Add();
worksheet = workbook.Worksheets(1);
worksheet.Range("A1:B2").value = [["String1", 100], ["String2", 200]];
worksheet.Range("A3:B3").value = ["String3", 300]
最初にVT_VARIANT|VT_ARRAYでやってみて駄目なら配列の一番目の要素に合わせて VT_XXX|VT_ARRAY に 変換してやってみるというパターンを使うか、Perlの方法を積極的に採用するか。
Perlの方法は1.9に一部取り込んだんだけど、まだ、配列までは対応しきれてないままの気がする。
VimがCOMサーバーになっていて、しかもATLを使ってないみたいだ。 Win32OLEテスト用COMサーバを作るときの参考になりそう。
短篇集。 「拳銃よりも強い武器」「画商の女」が好み。 でも、何と言ってもやはり表題作の「最後の一壜」が一番。 トリックといい、最後のピリっと辛い捻りのきいたオチといい見事です。 さすが短編の名手。
以前、ダウンロードしていた vc.isoからインストールした。 インストール先は、Linux 上の VMWare の Windows 2000。
なので、先に、Linux上で
mkdir cdrom mount -t iso9660 -o loop vc.iso cdrom
としておいて、cdrom ディレクトリをWindows 2000から samba で見えるようにしてインストールした。
SQLServerは今のところ使わないので、インストール対象から除外。
(私の環境は、C:の空きが少くなっているので、基本的にすべてD:にインストールしてます。 C:にインストールされた人は、以下の記述で、D:とある部分は C:と読み替えてください。)
まず、何はともあれ、Visual C++ 2005 Express Editionが無ければお話にならないので、インストール。
次は、Windows Platform SDKのインストール。 Windows Server 2003 SP1 Platform SDK Web Installのサイトから PSDK-x86.exe をダウンロードしてインストール。 今のところ IA64やAMD64を必要としていないので、Core SDK の x86だけ選択。
CVSから直接取ってきてRuby1.9をコンパイルするときには、bison と sed が必要になる。 Ruby 1.8だとbyaccが必要になる。 今回は、GNU Win32で配布されている sed と byacc とbison をダウンロードしてインストール。 byaccをインストールしても、yacc.exe しかないので、 yacc.exe をコピーして byacc.exeを作っておく。
いきなりRubyのコンパイルに進んでもいいんだけど、念のため、次の簡単なプログラムが コンパイルできるかどうか確認。
/*
* winapp.c
*
* Test program for Visual C++ 2005 Express Edition with Platform SDK
*/
#include <windows.h>
int main(int argc, char **argv) {
MessageBox(NULL, "Hello", "Hello", MB_OK);
return 0;
}
コマンドプロンプトを起動。 コマンドプロンプトで
D:\...>"D:\Program Files\Microsoft Visual Studio 8\VC\vcvarsall.bat" Setting environment for using Microsoft Visual Studio 2005 x86 tools. D:\...>"D:\Program Files\Microsoft Platform SDK\SetEnv.Cmd" /2000 Attempting to detect a Microsoft Visual Studio installation Targeting Windows 2000 and IE 5.0 DEBUG
(ここで、SetEnv.Cmd のオプションに /2000 を指定しているのは、環境がWindows 2000だから。 32Bit Windows XPの環境の人は、/XP32 と指定します。 SetEnv.cmd /? でオプションの指定方法が表示されます。 あと、先にSetEnv.cmdを実行してから、vcvarsall.batを実行する方がいいかも知れません。)
では、winapp.cをコンパイル。
D:\...>cl winapp.c user32.lib
winapp.exeができ、実行できることを確認。
Rubyのコンパイルをする前に、bison と byacc と sed が使えるように PATHを通しておく。
D:\...>set PATH="D:\Program Files\GnuWin32\bin";%PATH%
いよいよRubyのコンパイル。
D:\...\ruby>win32\configure D:\...\ruby>nmake D:\...\ruby>nmake test
ここまでは、問題なかったが、
D:\...\ruby>nmake test-all
が unknown software exceptionで落ちた。 うーむ。 私の環境のせいかも知れない。 まあ、今日のところは、nmake testが通るのでよしということにしておく。
Visual C++ 2005 Express Edition でコンパイルしたRubyは、MSVCR80.dllが必要。
念のため断っておきますが、一応、Visual C++ 2005 Express Edition の起動もしてます。 Win32 Consoleアプリケーションのプロジェクトを作ってコンパイルしました。 (「Visual C++ 2005 Express Edition と Microsoft Platform SDK を一緒に使う」 に書かれているWin32アプリケーションを作るための設定はしてないけど。)
Visual C++ 2005 Express Edition には含まれていない。 MFCは特に今必要としていないので構わない。 ATLが無いのがちょっと残念かも。 あれば、Win32OLEのテスト用COMサーバーを簡単に作れそうな気がするから。 Visual Studio 2005 Standard Edition には含まれていて 30000円ぐらい。 Platform SDK と Visual C++ 2005 Express Edition でどこまで頑張れるか。
midl.exeやuuidgen.exeは使えるので、 「COMプログラミングの基本(中)」 に書いてあることを試すことができた。 ちょっとだけ修正が必要だったけど、サンプルに含まれるサーバー側のVCのソースはコンパイルできた。
いろいろ調べていたら、「COM サーバを ctypes で実装する」 というのを見つけた。 Pythonすごいなあ。
Visual Studio 6.0 からアップグレードできるので、20000円ぐらいで済む。 うーむ。 というか、素直に6.0をインストールすればATL使えるじゃないか。
真相には驚かされるのだが、驚かされるだけ。 伏線はちゃんと張ってあるのかどうか良くわからなかったのは、読み方が悪いせい???
立ち読みで斜め読み。
興味をもった点は、Java SE 6では、JavaScriptを実行できるらしいこと。 JavaScriptはサポートするらしいが別にJavaScriptに限定はせず、規格に従ってインターフェースを実装すれば、 RubyもJavaから実行できるようになるらしい。
Trailsの記事で、RoRが取り上げられている。 最近、雑誌やWebでRubyが取り上げられるときに特別な説明なしにわりと普通に登場するようになったんではないかと感じる。
久々に活動した気がする。
MATLABについて調べて ruby-list と ruby-talk にメールを出して、Microsoft Word を動かした。 Rubyのコードは、ほとんど書いてないし、何一つ解決していないんだけど。
本当は、活動する気でいたのだが、 その後、風邪をひいたらしく、喉の調子が悪くておとなしくしていた。 今週に入って喉の痛みは消えたんだけど、痰がひどく絡む。 これさえ無くなれば、普通なんだけど。
DisableAutoMacros って一体何者? Microsoft Word 2000のオブジェクトブラウザで探しても出てこない。 WordでVBE開いて、"WordBasic" と入力した後で、 "." 入れても何も出てこないし。
試しに
require 'win32ole'
wApp = WIN32OLE.new('Word.Application')
wApp.visible = true
p wApp.wordbasic.ole_obj_help
としたら
インターフェースがサポートされていません。
と言われてしまう。