Excelとかで呼び出すWINAPI系DLLをclangで作る

clangで作ったDLLをExcelから呼べなくてはまったので解決法をメモ。

Excelから外部DLLを呼ぶ場合

  • DLL側関数は「WINAPI」をつける必要がある*1
  • つまり「MS stdcall」って呼び出し規約になる模様

clangでWINAPIをつけた関数

  • 「ExportedFunction@20」みたいに「@」付きでexportされる
  • clangというよりは、MinGWのldがそういう動作をしている模様
  • 「@」関数しか無い状態だと「エントリ"ExportedFunction"がDLLファイル"WinapiCallingConvention-clang.dll"内に見つかりません。」的なエラーメッセージが出てDLL関数呼び出しができない

解決法

  • リンカオプションに「--add-stdcall-alias」を追加する

オプションをつけると「@」が付かないタイプの関数名がエイリアスとして作られるようだ。

objdumpとかでエクスポートテーブルを確認するとこんな感じになる。(必要箇所を抜粋)

C:\clang-excel-dll-test>objdump -x WinapiCallingConvention-clang.dll
[ 0] ExportedFunction
[ 1] ExportedFunction@20

makefile

例えばmakefileはこんな感じにする。

CFLAGS  = -Wall -W -fPIC -fvisibility=hidden
LFLAGS  = -shared -static -Wl,--add-stdcall-alias
INCS    = 
LIBS    = 
CC      = clang
TARGET  = WinapiCallingConvention-clang.dll
SRCS    =							\
			main.c					\

OBJS    = $(SRCS:.c=.o)

.PHONY: all debug clean delete_objs delete_target

all: CFLAGS += -O2
all: $(TARGET)

debug: CFLAGS += -g
debug: LFLAGS += -g
debug: $(TARGET)

clean: delete_objs delete_target

delete_objs:
	del /f /q $(OBJS)

delete_target:
	del /f /q $(TARGET)



.SUFFIXES: .c .o

.c.o:
	$(CC) $(INCS) -c $(CFLAGS) $<

$(TARGET): $(OBJS)
	$(CC) -o $@ $(OBJS) $(LFLAGS) $(LIBS)

余談

今回も無駄にはまってしまった。

  • MinGW stdcall remove @
  • Excel エントリ DLLファイル 内に見つかりません」
  • Excel 自作DLL 関数が見つからない」

などのキーワードでググってもdlltoolとかdefファイルばかり引っかかるし。
それらを試したり内容を読んだりしたりで時間がかかった。

最終的にふと「そういえば、DLL作ってるのはリンカだよね」と思いだして、MinGWのldについて調べたり「ld --help」を打ってようやくそれっぽいオプションを見つけて試してみてようやくうまく動いた。

なんというか、目の前の現象も大事だけどツールとかがどういう仕組みで動いているかを想像して問題個所を絞り込めないと回り道しちゃうね。
特にclangはバックエンドで仕事しているツールたちを意識しないと原因にたどり着けないねぇ。

*1:というか、「__stdcall」をつける