- Windows SDK
-
- Win32 API
-
コンソールアプリならば D言語の標準ライブラリだけでも組むことが出来ますが、GUIアプリの場合は簡単なものでも Win32 API を使う必要があります。
API というのは Application Programming Interface ということで、OS に限らず Application の機能を使うためのインターフェイスで、その機能名(関数名)やパラメーターなどはその API 独自のものになるのか普通です。
プログラム言語の標準ライブラリーもある意味 API で、その手順はほとんど同じで、必要な関数の場所と引数などがコンパイラにわかるようにしてやれば良いだけです。
Win32 API が難解なのはその量が膨大なため欲しい機能の使い方を探すのが難解で、しかもその機能を使う場合には初期化だの何だのといろいろな準備が必要なことが多く、特に GUI は単にウィンドウに hello world を表示するだけでも難解です。
さらに異常に古いものが互換性のためか残されていていたり、継承されていたり、混在していたり・・・とさらに難解です。
また、解説やサンプルは C言語ですから、最低限の知識も必要になります。
C言語以外の解説もありますし、結構古いものでも参考になりますが、正確に使うためには msdn で確認した方がよいでしょう。
- SDK
-
API を使うということは、公開された関数を呼び出すことが基本になりますから、関数名やその引数などを知る必要がありますが、Windowsの場合 SDK(ソフトウェア開発キット)が無料公開されてます。
Platform SDK と呼ばれていたものが、.net とかの API と統合されて Windows SDK となったようです。
msdn で公開されてる情報を見ることで何とかなる部分もありますが、少なくとも最新の .lib ファイルといくつかのツールは必要になるでしょう。
- Microsoft Windows SDK for Windows 7
-
D言語で Windows の GUIアプリを作る場合のアプローチはいくつかあるようですが、DMD に標準で添付されるものは、ライブラリなども不十分なので SDK のインストールが必要になります。
最新(2011年1月27日現在 version : 7.1)のものはMicrosoft の Download Center から
のどちらかをダウンロードしますが、iso ファイルの場合は開発環境に合ったものが必要です。
- winsdk_web.exe
- 環境にあったものを自動的にダウンロード、インストールしてくれる。
- GRMSDK_EN_DVD.iso
- 開発環境が 32ビット Windows なら。
- GRMSDKX_EN_DVD.iso
- 64ビット Windows なら。
iso ファイルの場合ダウンロードしたものをメディアに焼くか、仮想ドライブにマウントしてからインストール。 - インストール
-
筆者の環境では iso ファイル を使わないとうまくインストール出来ないことがありましたが、winsdk_web ならダウンロードしたファイルを実行するだけで簡単です。
また、どちらでインストールしても手順はほとんど同じです。
大事なことが書いてあるのでしょうが、さらっと I Agree を選んで Next >
- Tools
- Samples
- サンプルコードのインストール先。
- Foldef for Tools
-
デフォルトだと
"C:\Program Files\Microsoft SDKs"なんかにインストールされるはずです。- ..\Windows\v7.1\Bin
-
ほとんど何するツールなのかわからないものばかりですが、リソースコンパイラ(
rc.exe)などいくつか必要なツーが入ってます。 - ..\Windows\v7.1\Include
-
.hファイル。 - ..\Windows\v7.1\Lib
-
.libファイル。dmd にも標準添付されていますが完全ではありませんから、ここのある
.libファイルが必要になる場合があります。そのままでは使えませんから、coffimplib で変換します。
- Folder for Samples
-
..\Windows\v7.1\Samplesサンプルプログラム。
最新 API の基本的な使い方は無料の情報源が少なく、このサンプルから学ぶ必要があることが多い。
- Debugging Tools for Windows
- ポーティング( 移植 )
-
- MSDN
-
D では C の関数を直接呼び出すことが出来ますから、MSDN のリファレンスを見ながら宣言して呼び出すことも出来ます。
例えば、
RegisterClassExを呼ぶ場合、MSDN ではATOM WINAPI RegisterClassEx( __in CONST WNDCLASSEX *lpwcx );
となっていますから
extern( Windows ){ ATOM RegisterClassEx( WNDCLASSEX* ); }
と宣言して
User32.libをリンクすれば使えるわけですが、その前にATOMやWNDCLASSEXなんかの宣言も必要になります。WNDCLASSEXはRegisterClassExのところにも リンク があってtypedef struct tagWNDCLASSEX { UINT cbSize; UINT style; WNDPROC lpfnWndProc; int cbClsExtra; int cbWndExtra; HINSTANCE hInstance; HICON hIcon; HCURSOR hCursor; HBRUSH hbrBackground; LPCTSTR lpszMenuName; LPCTSTR lpszClassName; HICON hIconSm; } WNDCLASSEX, *PWNDCLASSEX;
なんてのは D でもほとんど同じで、
struct WNDCLASSEX { UINT cbSize; UINT style; WNDPROC lpfnWndProc; int cbClsExtra; int cbWndExtra; HINSTANCE hInstance; HICON hIcon; HCURSOR hCursor; HBRUSH hbrBackground; LPCTSTR lpszMenuName; LPCTSTR lpszClassName; HICON hIconSm; } alias WNDCLASSEX* PWNDCLASSEX;
と定義すれば OK ですが、
UINT、WNDPROC、HINSTANCE・・・ と、なんかとっても面倒です。また、これは ANSI の定義ですから、Unicode の場合、
LPCTSTRをLPCWSTRにする必要があったりもします。MSDN の情報は重要ですが、定義・宣言をするための情報ではないようです。 - .h ファイル
-
- Windows Headers
-
RegisterClassExは WinUser.h にWINUSERAPI ATOM WINAPI RegisterClassA( __in CONST WNDCLASSA *lpWndClass); WINUSERAPI ATOM WINAPI RegisterClassW( __in CONST WNDCLASSW *lpWndClass); #ifdef UNICODE #define RegisterClass RegisterClassW #else #define RegisterClass RegisterClassA #endif // !UNICODE
WNDCLASSEXも WinUser.h にtypedef struct tagWNDCLASSEXA { UINT cbSize; /* Win 3.x */ UINT style; WNDPROC lpfnWndProc; int cbClsExtra; int cbWndExtra; HINSTANCE hInstance; HICON hIcon; HCURSOR hCursor; HBRUSH hbrBackground; LPCSTR lpszMenuName; LPCSTR lpszClassName; /* Win 4.0 */ HICON hIconSm; } WNDCLASSEXA, *PWNDCLASSEXA, NEAR *NPWNDCLASSEXA, FAR *LPWNDCLASSEXA; typedef struct tagWNDCLASSEXW { UINT cbSize; /* Win 3.x */ UINT style; WNDPROC lpfnWndProc; int cbClsExtra; int cbWndExtra; HINSTANCE hInstance; HICON hIcon; HCURSOR hCursor; HBRUSH hbrBackground; LPCWSTR lpszMenuName; LPCWSTR lpszClassName; /* Win 4.0 */ HICON hIconSm; } WNDCLASSEXW, *PWNDCLASSEXW, NEAR *NPWNDCLASSEXW, FAR *LPWNDCLASSEXW; #ifdef UNICODE typedef WNDCLASSEXW WNDCLASSEX; typedef PWNDCLASSEXW PWNDCLASSEX; typedef NPWNDCLASSEXW NPWNDCLASSEX; typedef LPWNDCLASSEXW LPWNDCLASSEX; #else typedef WNDCLASSEXA WNDCLASSEX; typedef PWNDCLASSEXA PWNDCLASSEX; typedef NPWNDCLASSEXA NPWNDCLASSEX; typedef LPWNDCLASSEXA LPWNDCLASSEX; #endif // UNICODE
で、もちろん C言語 ですから D言語 への翻訳が必要です。
ほとんど手作業で移植するのが基本ですが、WinUser.h だけでも 13千行 ほどありますし、必要なファイルもまだまだまだまだあります。
-
標準
windows.di -
最小限の定義・宣言は組み込みのやつにありますから、それで足りる場合
std.c.windows.windowsをimportすれば OK。「定義・宣言が最小限」と言うこと以外の問題はほとんどありません。
- Bindings for the Windows API
-
.hファイルのポーティング・プロジェクトです。download a snapshot of latest revision in SVN (.zip)
をダウンロードして使えます。
「D で Windows API 扱うならこれ」ちゅう感じで、筆者もお世話になりましたし、組み込みのやつより作りが丁寧で、移植の良いお手本にもなります。最新( Windows7 )への対応がされていれば( 2011年8月11日現在まだ )うれしかったのですが、
- 自前ヘッダー
-
自動変換プロジェクトですが、変換済みサンプルを使えば OK。
2011年8月11日現在、最新対応で不足も最少のはず・・・
- .lib ファイル
-
- リンカー・エラー
-
.lib の問題はリンカー・エラーになりますが、慣れないと原因の特定が困難です。
OPTLINK (R) for Win32 Release 8.00.12 Copyright (C) Digital Mars 1989-2010 All rights reserved. http://www.digitalmars.com/ctg/optlink.html
こんなメッセージ以下はリンカー・エラーです。
- File Not Found
-
最初の
win7.lib Warning 2: File Not Found win7.lib
は読み込み指定されてる .lib がないと出ます。
この場合の
win7.libは、構成が変わって不要になった .lib ですから指定を削除すれば良いのですが、Window API の .lib のファイルの場合 SDK にあるファイルを変換 してやる必要があります。 - Symbol Undefined
-
未定義シンボルとか言われても、リンカーのメッセージは記号付きで使った覚えのないものに化けているのが普通です。
hellow.obj(hellow) Error 42: Symbol Undefined _CoInitialize@4 hellow.obj(hellow) Error 42: Symbol Undefined _CoUninitialize@0 hellow.obj(hellow) Error 42: Symbol Undefined _CoCreateInstance@20
まずは、こんな
@付きの奴らこの手は外部参照シンボルが解決できないと出るらしく、大抵は Windows API の Function です。
例えば
_CoInitialize@4はCoInitializeが未定義シンボルで、この手はネット検索で CoInitialize Function なんかにたどり着けば解決の糸口がつかめます。hellow.obj(hellow) Error 42: Symbol Undefined _D5win328uiribbon17IUICommandHandler11__InterfaceZ hellow.obj(hellow) Error 42: Symbol Undefined _D5win328uiribbon14IUIApplication11__InterfaceZ
ちょっと暗号な奴らは以外とわかりやすくて
_D5win328uiribbon17IUICommandHandler11__InterfaceZはwin32.uiribbon.IUICommandHandlerのことで、interfaceやstructなんかの定義がコンパイルされていないとエラーが出ます。これは
win32.uiribbonをコンパイル・リンクすることで解決しますが、hellow.obj(hellow) Error 42: Symbol Undefined _CLSID_UIRibbonFramework hellow.obj(hellow) Error 42: Symbol Undefined _IID_IUIFramework hellow.obj(hellow) Error 42: Symbol Undefined _IID_IUIRibbon hellow.obj(hellow) Error 42: Symbol Undefined _IID_IUICommandHandler hellow.obj(hellow) Error 42: Symbol Undefined _IID_IUIApplication
これはも
importしたモジュールのコンパイルが必要です。それぞれ頭の
'_'以下のシンボルが対象で、モジュールを特定する材料はありませんが、 - coffimplib( COFFIMPLIB )
-
Windows API の library file は COFF format で、D
では OMF format ということですから、変換が必要になります。
ftp://ftp.digitalmars.com/coffimplib.zip からダウンロード。
coffimplib.exeを適当な場所にインストール。Windows SDK の必要な .lib ファイルを
coffimplib ole32.lib .\new\ole32.lib
- ヘッダーファイルのビルド
-
組み込みの
std.c.windows.windows以外の移植ファイルを使う場合、その.dのコンパイルとリンクが必要になります。ポーティング・ファイルに限らず、インポート先の定数以外を参照する場合にはそのインポート先ファイルのビルドは必至です。
ビルド時にそのファイルをいっしょに指定してしまっても何とかなりますが、その場合はimportとは違いそのファイルのパス指定が必要になり面倒です。必要な ポーティング・ファイル は全てコンパイル(
.lib化 )して、リンカーが検索できる場所に置いておくのが基本です。
- Recommend
-
ただでさえ Windows API は難解で試行錯誤が必要ですから、最新 OS の API を試すにも準備ばかりに手間をかけるのは勉強にはなりますがうんざりするほどですし、ビルドでつまずいたりするとD言語がいやになったりするかも?
単純で覚えやすい構成で再構築やメンテナンスがしやすく、トラブルフリーな環境を作ってやれば OK。
- 自前ヘッダー
-
.\HtoD以下.\win32( Unicode ).\win32a( ANSI ).\win64( 64bit )
に .h の変換済み .d 及び .di ファイルが、
.\libsに各ビルド済み .lib ファイルがあります。.d 及び .di ファイルのモジュール名には、各フォルダと同名のパッケージ名が付けられていますから、フォルダごとコピーするか、同名フォルダを作って .di をコピーします。 -
最新
.lib -
何しろ Windows SDK の
.libは大量にありますから、必要なやつと、必要になるだろうやつをピックアップ・・・ とかは面倒です。要は、最新 SDK の
.libファイルを 全部変換 して、リンカーには 最初 にそのファイル群を検索してもらえば良いわけです。適当なフォルダ作って
convlib.dとかでimport std.stdio, std.file, std.path, std.process, std.string; void main( in string[] args ){ // デフォルト値 string indir = r".\lib", outdir = r".\winlib"; int i = args.length - 1; // 変換先フォルダ switch( i ){ case 0: break; case 2: outdir = std.string.format( r".\%s", args[ i ] ); i--; goto case; case 1: indir = std.string.format( r".\%s", args[ i ] ); break; default: assert( 0 ); } // フォルダ確認 if( !std.file.exists( indir ) ){ assert( 0 ); } if( !std.file.exists( outdir ) ){ std.file.mkdir( outdir ); } //---------------------------- // // coffimplib.exe で変換 void coff( in string s ){ writeln( s ); std.process.system( std.string.format( r"coffimplib %s\%s %s\%s", indir, s, outdir, s ) ); } // indir の .lib ファイルを列挙する foreach( string s; std.file.dirEntries( indir, SpanMode.shallow ) ){ // 大文字 "LIB" とかも考慮 if( !std.string.icmp( std.path.getExt( s ), "lib" ) ){ coff( std.path.basename( s ) ); } } }
こんなんをコンパイルして convlib.exe をビルド
- 初期設定ファイル
-
変換した .di ファイルを
.\dmd2\\src\druntime\importに、.lib ファイルを.\dmd2\\windows\libにコピーしても Ok ですが、D コンパイラ の 初期設定ファイル( sc.ini ) を書き換えることで、メンテナンスしやすい任意の場所に置くことも出来ます。- HOME 環境変数
-
コンパイラは、通常
.\dmd2\windows\binの sc.ini を読み込みますから、その sc.ini を書き換えることでもカスタマイズできますが、コンパイラのアップデート時に上書きされてしまいます。sc.ini に限らず、
.\dmd2の中は一切いじらずに済む様にした方が良いでしょう。コンパイラは、
- 実行時のカレントディレクトリー
- 環境変数
HOMEで指定されたディレクトリー - dmd.exe のあるディレクトリー
の順で sc.ini を検索しますから、環境変数
HOME使って任意の初期設定ファイルを読み込ませることが出来ます。 -
%@P% -
初期設定ファイルでは
%@P%という変数を使えます。%@P%は読み込んだ sc.ini のあるディレクトリーのフルパスに置き換えられます。.\dmd2\windows\binにある素の sc.ini では[Version] version=7.51 Build 020 [Environment] LIB="%@P%\..\lib";\dm\lib DFLAGS="-I%@P%\..\..\src\phobos" "-I%@P%\..\..\src\druntime\import" LINKCMD=%@P%\link.exe
%@P%は.\dmd2\windows\bin( フルパス )に置き換えられます。dmd2 と 最新 SDK の変換済み .lib と .di 群と sc.ini を1つのパッケージにして、そこを
%HOME%にしてやれば OK。 -
%HOME%パッケージ -
筆者の環境では
D:\Program Files\Dにインストールされていますから、%HOME%ディレクトリーにimportフォルダを作って HtoD のwin32、win32a、win64をフォルダごとコピー。winlibフォルダを作って、HtoD の.\libの.libと、変換済み Windows SDK の最新.libを全部コピー。 - sc.ini 編集
-
sc.ini[Version] version=7.51 Build 020 [Environment] LIB="%@P%\winlib";"%@P%\dmd2\windows\lib"; DFLAGS="-I%@P%\dmd2\src\phobos" "-I%@P%\dmd2\src\druntime\import" "-I%@P%\import" LINKCMD=%@P%\dmd2\windows\bin\link.exe
- Recommend+
-
module windows; enum : string{ ShlObj = "shlobj", WinCodec = "wincodec", D2D1 = "d2d1", D3D10 = "d3d10_1", UiRibbon = "uiribbon", Richedit = "richedit" } /***************************** * * 文字列合成 Template * */ template ImpJoin( string TOP, string S, T ... ){ const: static if( T.length > 1 ){ string ImpJoin = ImpJoin!( TOP ~ S ~ T[ 0 ] ~ ", ", S, T[ 1 .. $ ] ); } else{ string ImpJoin = TOP ~ S ~ T[ 0 ] ~ ";"; } } template ImpJoinBy( string S, T ... ){ const string ImpJoinBy = ImpJoin!( "public import ", S, T ); } /************************************************************* * * * */ template ImpWinAPIOf( bool isPrg, T ... ){ version( ANSI ){ static if( isPrg ){ pragma( lib, "win32a.lib" ); } mixin( ImpJoinBy!( "win32a.", T ) ); } else version( Win64 ){ static if( isPrg ){ pragma( lib, "win64.lib" ); } mixin( ImpJoinBy!( "win64.", T ) ); } else{ static if( isPrg ){ pragma( lib, "win32.lib" ); } mixin( ImpJoinBy!( "win32.", T ) ); } } template ImpWinAPI( T ... ){ mixin ImpWinAPIOf!( true, "windows", T ); } template ImpWinAPIEx( T ... ){ mixin ImpWinAPIOf!( false, T ); }
2010年6月23日










