Windows Dev. Site

Haskell from Excel

HaskellのモジュールをExcelから呼び出す方法がかかれたBlogをみつけたので、実際に自分の環境でやってみました。

Calling Haskell from Excel

http://flxldn.tumblr.com/post/163596541/calling-haskell-from-excel

しかし環境が違うためか、残念ながらできませんでした。せっかくなので覚えとして書きたいと思います。
(いろいろやってみてXLLアドインの勉強にはありましたが・・)

C++からHaskellの呼び出し、ExcelからのC++(XLL)呼び出しはできますが、つなげると以下のようなアラートがでました。
xllError
XLLは、xlw(A Wrapper for the Excel API)を使う方法と、Excel 2010 XLL SDKを使う方法と、二通りやりました。

参考)
xlw :

http://xlw.sourceforge.net/

http://d.hatena.ne.jp/teramonagi/20110124/1295866787

xll sdk:

http://www.microsoft.com/en-us/download/details.aspx?id=20199

テスト環境 Excel 2010 + xlw ver5, Visual Studio 2010 / Windows 7

まずHaskellソースからDLLを作成します。
addr.hs

module Adder where 

adder :: Int -> Int -> IO Int --gratuitous use of IO 
adder x y = return (x+y) 

foreign export stdcall adder :: Int -> Int -> IO Int

adder.h

#ifdef __cplusplus
extern “C”
{
#endif
__declspec(dllexport) void __stdcall HsBegin(void);
__declspec(dllexport) void __stdcall HsEnd(void);
__declspec(dllexport) long __stdcall adder(long x, long y);
#ifdef __cplusplus
}
#endif

adder.def

LIBRARY Adder
EXPORTS
adder@8=_adder
HsBegin@0=_HsBegin
HsEnd@0=_HsEnd

dllMain.c

static char* args[] = {"ghcDll" , NULL };

BOOL STDCALL DllMain( HANDLE hModule, DWORD reason, void* reserved)
{  return TRUE;}
__stdcall void HsBegin()
{
startupHaskell(1, args, __stginit_Adder);
}
__stdcall void HsEnd()
{
hs_exit();
}

以下コンパイル手順です。(VisualStudio2010コマンドプロンプト)

ghc -static -c adder.hs -fglasgow-exts
ghc -static -c dllMain.c
ghc -static -shared -o adder.dll adder.o adder_stub.o dllMain.o
lib /MACHINE:x86 /DEF:adder.def /OUT:adder.lib /NOLOGO /SUBSYSTEM:WINDOWS

出力されたadder.dllはSystem32フォルダにコピーします。

次に、XLLファイルを作成します。
xlwのインストールディレクトリの中にあるxlwTemplateExtractor.exe実行すると、XLL_Projectというフォルダがマイドキュメントに作成されるので、ここにあるソリューションを開きます。
デフォルトのEchoShortの下に追加して以下のようになります。

#include<cppinterface.h>

#ifdef __cplusplus
extern "C"
{
#endif
__declspec(dllexport) void __stdcall HsBegin(void);
__declspec(dllexport) void __stdcall HsEnd(void);
__declspec(dllexport) long __stdcall adder(long x, long y);
#ifdef __cplusplus
}
#endif

#pragma warning (disable : 4996)

short // echoes a short
EchoShort(short x // number to be echoed
           )
{
    return x;
}

short funcAdd(short d1, short d2){
 return adder(d1, d2);
//return d1 + d2;
}

cppinterface.hにはfuncAddのプロトタイプを追加します。
xlwWrapper.cppというソースは自動で生成されるようです。
adder.libを参照するようにします。

まずコメントしてあるd1+d2で成功したことを確認してから、adderのテストしました。
すると上記アラートがでます。
ここで、HsBegin、HsEndを使い方を元のブログを見てみると、ライブラリ自体のmakeが必要のようだったのであきらめ、次にXLL SDKをテストしました。(ライブラリを変更してもたぶん結果は同じという気もしましたし)

XLL SDKをインストールしたフォルダ内にある、EXAMPLEプロジェクトを使用します。
他の関数を参考にして、


#define rgFuncsRows 30

static LPWSTR rgFuncs[rgFuncsRows][7] = {

....
	{L"CalcCircum",					L"BB", L"CalcCircum"},
	{L"funcAdd",					L"III", L"funcAdd"}
};

....
__declspec(dllexport) double WINAPI CalcCircum(double pdRadius)
{
	return pdRadius * 6.283185308;
}
__declspec(dllexport) short WINAPI funcAdd(short x, short y)
{
	return (short)adder((long)x,(long)y);
	//return x + y;
}

このソースのxAutoOpen()、xAutoClose()にそれぞれHsBegin()、HsEnd()を追加。これらとadderの宣言部もxlwと同様に追加。

これもreturn x+yで動作確認後、Haskell呼び出し部をテストしました。それで上記アラートが出ました。
Excel-DNAてadder.dllを、とも思いましたが、あれはマネージDLLでした。
成果が何もないのもなんなので、cppコンソールアプリでHaskell呼び出しをしてみました。(adder.dllはexeと同じところに置く)

#include "stdafx.h"

#ifdef __cplusplus
extern "C"
{
#endif
__declspec(dllexport) void __stdcall HsBegin(void);
__declspec(dllexport) void __stdcall HsEnd(void);
__declspec(dllexport) long __stdcall adder(long x, long y);
#ifdef __cplusplus
}
#endif

int _tmain(int argc, _TCHAR* argv[])
{
	HsBegin();
	printf("adder : %d\n", adder(1,2));	
	HsEnd();	
	getchar();
	return 0;
}

3 を表示します。

また解決したらつづきを書きたいと思います。