2011/6/12 - 2012/4/5
Pen Jr.D言語なんてやってみそ自作ツールH to D
<<・・H to D
概要
ポーティング・プロジェクト
D言語で Windows API を使うには、ヘッダーファイルの移植が必要になります。

最小限のものは付属でありますが、本当に最小限で使えません。

Bindings for the Windows API などのポーティング・プロジェクトがありますし、それなりに使えますが、有志のプロジェクトでマメなメンテナンスは望めない様子だから、最新コンパイルでは修正が必要だったり、最新 OS の API を試したい場合は自力で何とかする必要があります。

とりあえず必要なものだけ手作業で追加して、誰かがまとものを作ってくれるのを待つ。

というのが基本ですが、

ポーティング・ツール
異種言語間のソースコードの完全移植は、互換性がない限り手作業でも無理でしょうから、変換ツールは補助的なもの以外ないのが普通ですが、

API のヘッダーファイルに特化すれば、完全移植は必要ありませんから、

  • 変換が不可能、または、困難な場合スルーして OK
  • 変換ファイルが特定できるので、対処療法的な補完が可能
Windows Headers 専用ツール
最新 OS に対応するには、最新ポーティングが必要になりますが、

一度まともな変換が出来れば次にツールが必要になるのは、Windows SDK のバージョンアップかコンパイラの仕様変更に対応が必要になった時で、それも誰かが変換したファイルを公開すればそれで済みます。

仕様
ファイル指定
コマンドラインから、任意のファイル指定は出来ません。
プリプロセッサ
#if#ifdef
versionstatic if への置き換えは一切行わず、出来るだけ C の仕様通りにに展開。

もし、コンパイル時に version 指定で切り替えたければ、

例えば、

version( Unicode ){
  pragma( lib, "win32.lib" );
  import win32.windows;
}
else{
  pragma( lib, "win32a.lib" );
  import win32a.windows;
}

とかじゃダメなの?

どちらにしても、筆者は version などへの変換は断念しました。
#define
文字列置換に登録するかデータで判定。

文字列置換に登録しない場合、マクロは関数テンプレートに変換。

値の定義は enum、キーワードが1対1の場合は alias に変換し、それ以外は文字列置換に登録する。

例えば

#define HOGE 100
#define FOO HOGE
#define FOOS FOOS_FOO
#define HOGE_MACRO( a, b ) a + b

を文字列置換に登録しなければ、

enum{
  HOGE = 100,
  FOO = HOGE
}

alias FOOS_FOO FOOS;

auto ref HOGE_MACRO( T0, T1 )( T0 a, T1 b ){
  return a + b;
}
struct
ネスト
Conflict
typedef union _Foo{
  struct {
      DWORD A;
      DWORD B;
      BYTE r[ 8 ];
  } AA;
  struct {
      DWORD   A;
      DWORD   B;
      BYTE r[ 8 ];
  } BB;
  struct {
      DWORD   A;
      DWORD   B;
      DWORD   C;
      BYTE r[ 4 ];
  } CC;
} Foo;

union _FOO{
  struct {
      DWORD A;
      DWORD B;
      BYTE r[ 8 ];
  }
  struct CC_a{
      DWORD   A;
      DWORD   B;
      DWORD   C;
      BYTE r[ 4 ];
  }
  CC_a CC;
}
alias _Foo Foo;
親が union で衝突数とメンバー数が同じ場合、同じ struct と判断して削除する。

「同じではない」場合もあるかも知れないが、union なら問題にならない?

継承
typedef struct Hoge : public Foo{
  int x;
};

は、

struct Hoge{
  Foo _par_Hoge;
  alias _par_Hoge this;
  int x;
}
ビットフィールド
std.bitmanip.bitfields に置き換え。
enum
そのまま変換しても、ヘッダー内で使われているメンバーに enum 名を付けてやれば問題ありませんが、メンバー名は重複していませんから、D で使う際も enum 名は省略出来ます。
メンバーを alias 宣言 ( -ea
enum FOO{
  A,
  B
}

は、

enum FOO{
  A,
  B
}
alias FOO.A A;
alias FOO.B B;
かなり冗長なコードになるけど、自動ツールなら無問題。

ただし

alias FOO.A A;
alias A HOGE;

は NG だったりするからいまいち。

enum 名を typedef で宣言 ( -et
enum FOO{
  A,
  B
}

は、

typedef typeof( 0 ) FOO;
enum : FOO{
  A,
  B
}
alias 宣言するより問題がなく、Cenum に最も近いと思うのだけど、typedef が本家リファレンスから削除されてるのがちょっと問題?
&
C++ にある &( 参照値 )は D では ”no equivalent” ですが、*( ポインター )扱いにするのが基本?

引数の & は、参照渡し( ref )にすればいいんでない?

参照渡しがデフォルトですが、コマンドラインスイッチ( -tp )で出切り換え出来ます。
CONST
interface
変数
C++ では変数をポインタにしてるから、 interface の場合 * を一つ減らす。
インライン・メンバー関数
final で実装。

&* にしていても、インライン・メンバー関数の &ref になる。

IID
EXTERN_C const IID IID_IUnknown;
extern "C++"
{
    MIDL_INTERFACE("00000000-0000-0000-C000-000000000046")
    IUnknown
    {

extern( C ){
  const:
  IID IID_IUnknown = {
    0x00000000, 0x0000, 0x0000,
    [ 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 ]
  };
未定義
未定義 structinterface などが引数にあったりします。

どこかで定義されているのか、意味がいまいちわからないですし、そもそも使いようがない関数になるわけですから変換の必要がないのだと思いますが、Undefined の連鎖を嫌って、空の struct などを定義しています。

コンバーター
インストール
HtoD.zip のダウンロード

変換済みファイル

Win32.zip のダウンロード

ソースコード
.\HtoD
ソースコードと実行ファイル。
.\HtoD\my
ソースコード
サンプル

作者の好みに不満がなければ、変換済みファイルで問題ないはずですから、ツールを使う必要はありません。

.\HtoD\win32
デフォルト( Unicode )
whtod win32
.\HtoD\win32a
ANSI
whtod win32a -a
.\HtoD\win64
WIN64
whtod win64 -w64
.\HtoD\libs
ビルド済み各 .lib ファイル
.\HtoD\basic
引数の inenumtypedef をしていない、標準変換ファイル

※ 検証は一切していません。

.\HtoD\windows.d
更新
2012年4月5日 6.42( 1149 )
  • ラムダ式など試してみた
  • dwmapi 追加
2012年2月20日 6.41( 1146 )
D 2.058 対応
  • cast を不要にするため interface**const( hoge )* にした
2012年2月19日 6.40( 1140 )
D 2.058 対応
  • interface 継承の public とか消した
2012年1月10日 6.39( 1134 )
  • D 2.057 対応
6.38
  • データ修正
  • oledb, oledlgole
  • iaccessole から
2011年11月6日 6.37( 1109 )
  • 正規表現周りの修正、ちょっと高速化
  • データ見直し
  • class 登録
  • wsman 追加
2011年11月4日 6.36( 1090 )
  • Macro チェックが機能していなかった
  • 複数行 Macro 処理を修正
  • #undef などの矛盾を回避できないスレッドを統合( deve.d, ana.d )
  • 簡単な template 処理
  • struct 処理を修正
  • class 処理?
  • fwcommon, wuapi, werapi, tspi, tapi3, searchapi, msp, encdec 追加
2011年10月29日 6.35( 1033 )
  • D 2.056 対応
  • typedef が使えなくされたから、デフォルトで enumalias にした
  • Macro 文字列置換処理の修正
  • データの見直し
  • wrap 処理の追加、修正
  • #defineGUID 変換は必要最小限に
  • DEFINEGUID 処理を修正
  • sapiddk, sti, iiscnfg, clusapi, indexsrv, ntquery, dbgeng, winternl, photoacquire、wab 追加
2011年10月21日 6.34( 0966 )
  • 数値の接尾辞などの処理を修正・統一した
  • d3d9, mfapi, oledb, wininet, mapi, lm 修正
  • iaguid, capi, cryptuiapi, mileffects, cryptdlg, softpub, audiopolicy 追加
2011年10月20日 6.33( 0951 )
  • GC の問題解決!!!、メモリ馬鹿食い解決!!
  • ks.d, mapi.d 修正
  • wrap 処理
  • audioclient, mfapi, d3d9, dxva, tdh 追加
2011年10月16日 6.32( 0920 )
  • 意味ない? public importprivate
  • patchapi, dinputd, xinput, wlanihv, uiautomationclient
  • netmon,winsatcominterfacei, hliface, inetsdk
  • shdeprecated, winfax, tuner, vidcap, ks 追加
  • IID,CLSIDextern は処理しないようにした
  • 複数文字 char リテラルの処理
  • std.string.wrap がダメだから、wrap 処理してない・・・
2011年10月6日 6.31( 0864 )
  • Bindings for the Windows API にある .d ( 7.1 に .h がある )はほとんど追加した
  • 壊れてた enum の型決定を修正
  • {} のある複数行 Macro に対応
  • const GUID
  • #define での GUID
  • Function 解析などを修正
  • .lib を分割した( コンパイラが落ちるから )
  • 未使用 Macro をチェック
  • #undef の処理を変えた
  • intsafe.hwinbase.d に読み込んだ ( INT_MAX などはそのまま enum に )
  • その他、追加ファイルで表面化したバグ修正や内部機能の追加など・・・
2011年9月19日 6.30( 0694 )
  • inline Function の解析・整形を統合した
  • lmat.hlm.d
  • raserror.h, mprapi.hras.d
  • mapi.d, usp10.d, winldap.d 追加
2011年9月16日 6.29( 0677 )
  • uxtheme.d, mmc.d, mmcobj.d, ras.d, rasdlg.d, raserror.d 追加
  • 文字列置換後の文字列置換を禁止( 無限ループ防止 )
  • enum 処理の template 化などの修正
2011年9月13日 6.28( 0674 )
  • 型登録を改善( 基本、省略はしない )
  • cast 判定を改善
  • -u スイッチ追加
2011年9月5日 6.27( 0635 )
  • typedef でポインターが列挙されると、'*' が貯まっていたのを修正
  • 型登録をまともにして、Conflict チェックも出来るようにした( ちょいオソ )
  • 適当すぎな cast 判定をいくらかまともにした
  • データ構造を少し変更、ファイル挿入指定でフリーズすることがあったのを修正
  • shlobj.dmshtml.h を読み込んだ
  • winsock2.dIPV6STRICT を define ( wsipv6ok.h の読み込み )
  • http.d, oledlg.d, lm.d, drt.d, richole.d, textserv.d, p2p.d 追加
2011年9月2日 6.26( 0574 )
  • strsafe.d 追加
  • http.dwinsock2.d 追加
  • シンボル管理の見直し(ほとんど連想配列にまかせた)
  • コード補完の修正・追加
シンボル管理は大改造したが、既存変換ファイルの変更は微小。

無駄な検索が減ったから、若干速くなった・・・若干

2011年8月27日 6.25( 0468 )
  • .\basic のメンテナンス
  • -en での Undefined を修正
2011年8月27日 6.24( 0462 )
  • -cc スイッチ追加
2011年8月25日 6.23( 0447 )
  • typedef& の処理をしていなかったのを修正
  • struct メンバーで interface の処理をしていなかったのを修正
2011年8月24日 6.22( 0434 )
  • charbyte への置換をやめた
  • SNDMSG を呼ぶ Macro は template 関数へ置き換えるようにした
2011年8月23日 6.21( 0420 )

※ 変換ファイルに大きな変更があります。

  • dwrite.hdcommon.hd2d1.d に統合
  • & の扱いを変更( REFIID などは文字列置換で処理され、alias 宣言はなくした )
  • -tr,-tp スイッチ追加
  • interface のインライン・メンバー関数を実装!!!
  • 6.12 の変更でファイル挿入が壊れてたのを修正
  • enumstruct をちゃんと区別して登録
  • structinterface の別名宣言をちゃんと登録!
2011年8月17日 6.20( 0367 )
  • winnt.h と、winbase.h 以外での関数オーバーロード
  • interface のインライン・メンバー関数は挫折
2011年8月16日 6.19( 0343 )
  • conflicts になるのを修正
2011年8月16日 6.18( 0337 )
  • dwrite.d の追加
  • 読み込みだった dcommon.d にした
  • いくつかの private importpublic にした
  • その他修正
2011年8月11日 6.17( 0324 )
  • -o スイッチを追加、.lib.di とは別フォルダに作成できるようにした
  • いつの間にか、エラーメッセージの行数表示が 0 になっていた( 行数の更新 )
  • 無名 Nest Struct のメンバー名衝突処理を修正。
2011年8月10日 6.16( 0304 )
  • -d-i に変更、修正
  • pragma( lib, ) 挿入周りの修正
2011年8月8日 6.15( 0292 )
  • -d を修正
  • -p,-l スイッチの追加
  • 関数宣言に export を付けるようにした
  • 同名2重 extern になっていたところを修正
2011年8月6日 6.14( 0283 )
2011年8月5日 6.13( 0281 )
  • -v スイッチの追加
  • 挿入コメント変更
  • メンテナンスを考慮した修正
2011年8月3日 6.12( 0277 )
  • .h ファイルの指定を修正
  • Nested ClassTemplate Class を出来るだけ使わないよう改造
  • core.thread.Thread.join を使わないようにスレッドを改造
  • GC.enable を挿入( メモリ食いは性分 )

GC がらみで落ちるのは解決できないが、大改造が少しは報われて、スレッドが走ってる時だけ GC.disable で正常終了はするようにはなった。

2011年7月31日 6.11( 0267 )
  • 実行ファイルを whtod に改名
  • データ参照を変更
  • -o スイッチ削除
  • -s-d に変更
  • -f,-z,-ndel スイッチの追加
2011年7月31日 6.10( 0261 )
  • -m,-nmod スイッチの追加
  • std.regex の置換問題を修正
2011年7月30日 6.09( 0253 )
  • .h ファイルの PATH 指定でコメント表示が変だったのを修正
2011年7月30日 6.08( 0251 )
  • -h スイッチ( .h ファイルの PATH 指定 )追加
  • スレッド周りで意味不明なコードを削除
2011年7月27日 6.07( 0248 )
  • -w64,-s,-ndi,nlib スイッチの追加
  • -w64 変換をサンプルに追加
64bit 版は dmd の方がまだだから、今まで通りのコンパイルが通るのを確認しただけ、

これで 64bit アプリが素直に動けば苦労が報われるわけだけど・・・

2011年7月25日 6.06( 0215 )
  • enum 名を付けられるようにした
  • -en スイッチの追加
GC がらみの対策で Thread をいじってみたが、挫折。

元に戻した。

2011年7月23日 6.05( 0187 )
  • 変換ファイルのコマンドライン指定はなしにした(固定)
  • -a,-ea,-cn スイッチの追加
  • ANSI 変換をサンプルに追加
2011年7月19日 6.04( 0157 )
  • .di ファイルと .lib ファイルの自動作成を追加
  • サンプルライブラリを win32.lib に統合
2011年7月14日 6.03( 0154 )
  • D 2.054 対応
2011年7月7日 6.02( 0151 )
  • 進捗表示の変更など
変換実行
コマンドライン
whtod 出力フォルダ -Switch ...

出力フォルダの指定は必至( -m 指定がなければ、モジュール・パッケージ名、-p 指定がなければ、.lib ファイル名 )、.h ファイルのある場所で実行するか、.h ファイルの PATH-h で指定します。

Windows SDK がデフォルトでインストール済みなら指定は -h だけで OK。

Switch
-a
ANSI

デフォルトでは UNICODEdefine

-w32
デフォルト
-w64
_M_AMD64, _WIN64
-v7
デフォルト
-vVista
-vXP
-v2K
-cin
デフォルト

const 引数は in にする。

結局、castconstimmutable を外すことになるのなら、いっしょ。

煩わしくない分便利。

-cc
引数の constconst

別に in とかせんでも、普通に const でも

-cn
引数の const は無視する。
本家の指示に従うなら・・・
-tr
デフォルト

引数の & を 記憶クラス ref にする。

-tp

引数の & は ポインター * にする。

-et
デフォルト

enum 名を typedef で宣言して、名無しにする。

-ea
enum メンバーを enum 名を省略して使えるように、alias 宣言する。
-en
enum はそのまんま、何もしない。
あえて enum 名を使うメリットはないと思うが・・・
-ndi
コンパイル禁止
-nlib
コンパイル禁止
-nmod
module 宣言をしない。

※ 自動コンパイルは全てキャンセルされる。

-p
自動コンパイル時の .lib 名を指定。

デフォルトは出力フォルダ名。

無名の場合、コンパイルがキャンセルされる。

-o
自動コンパイル時の .lib の出力フォルダ指定。

デフォルトは libs

-ndel
テンポラリーファイルを削除しない。
-i
.di ファイルのコピー先を指定。

指定 PATH にモジュール・パッケージ名を追加した PATH に移植した .di または .d ファイルを全てコピーする。

指定がなければコピーはしない。

-l
.lib ファイルのコピー先を指定。

指定がなければコピーはしない。

-h
ヘッダーファイル( .h )の PATH 指定。

PATH 指定がなければ Windows SDK のデフォルト

"%Program Files%\Microsoft SDKs\Windows\v7.1\Include"

が適用される。

-m
モジュール・パッケージ名を指定。

指定がない( -m )場合、モジュール・パッケージ名が省略される。

自動コンパイルは全てキャンセルされる。

-f
, 区切りで .d のタブ、インデックス、最大幅の指定。

-f4,8,120 タブ:4、インデックス:8、最大幅:120

-f,,120 最大幅:120

※ デフォルトは タブ:2、インデックス:4、最大幅:96

-z
作業フォルダ( .\tmp )を削除する。
-u
template 関数に変換した Macro の cast Undefined Symbol が無いかチェックする

-debug でコンパイルされていると常に on

変換ファイル
windows
Include Files : sdkddkver, winnls, wincon, winver, winreg, reason, winnetwk, wnnc, cderr, dde, ddeml, dlgs, lzexpand, mmsystem, nb30, winperf, winsock, inaddr, wincrypt, bcrypt, ncrypt, winefs, winscard, winioctl, winsmcrd, winspool, prsht, commdlg, stralign, mcx, imm, ime_cmodes
winbase
Include Files : windef, winnt, basetsd, guiddef, cguid, winerror
wingdi
なし
winuser
なし
shellapi
なし
ole2
Include Files : rpc, rpcasync, objbase, rpcndr, rpcnsip, rpcsal, wtypes, unknwn, objidl, urlmon, oleidl, servprov, msxml, oaidl, propidl, oleauto
regstr
なし
shlobj
Include Files : shlguid, isguids, exdisp, ocidl, docobj, shldisp, knownfolders, shobjidl, propsys, propkeydef
commctrl
なし
wincodec
なし
richedit
なし
d2d1
Include Files : dcommon, d2derr, d2dbasetypes, dwrite
dxgiformat
なし
d3d10_1
Include Files : d3d10, dxgi, dxgitype, d3d10sdklayers, d3d10misc, d3d10shader, d3d10effect, d3d10_1shader
uiribbon
Include Files : uiribbonkeydef
http
Include Files : ws2tcpip, ws2ipdef, in6addr
winsock2
Include Files : ws2def, qos
strsafe
strsafe.lib が変換できない。

そもそも、文字列関係だから不要?

既知の問題
マルチスレッドと GC がらみで落ちる
マルチスレッドでの処理中 GC が動くと落ちる。
マルチスレッドで処理する時だけ GC.disable で正常終了する。
#pragma intrinsic
64bit では処理の必要がありそうなのだけど、基本無視して、コンパイルの通らないところだけ対処してる。
オーバーロードの禁止
関数は引数情報を持たないため、単に重複と判断してる。

export を優先してるから、致命的ではないが・・・・

pragma( lib, ) の不足
ちゃんとデータを作っていない・・・・・
検証
作者のオンリーワン環境( Windows7 64bit、AMD 6コア、8GBメモリ )でのみ動作確認。

その他の環境でどうなるかは予想も出来ない。

変換ファイルの検証も、簡単なプログラムでしか検証できていない。
E-Mail : open@pen-jr.org

ご意見やご指摘は大歓迎ですが、質問など返信は期待しないでください

pen jr.
D言語研究
わかったつもりになるD言語
D言語友の会
News&Days
はじめてのブログ選び