- まずは簡単に
-
自前の Window は表示するだけでちょっとたいへんですが、
MessageBoxを使えばコンソールアプリ並に簡単です。以下 自前ヘッダー をインストール前提です。
import win32.windows; void main(){ .MessageBox( null, "Hellow World", "Hellow", MB_OK ); }
※ この文字列リテラルは0終端になるとりあえず、これでOK
適当な作業フォルダに
Hellow.dとかで保存して、コマンドプロンプトをそこで開きます。ビルドも簡単に
dmd -L/exet:nt/su:windows:4.0 hellow.d
出来た
Hellow.exeを起動すれば
日本語は
import win32.windows; void main(){ .MessageBox( null, "こんにちは日本", "Hellow", MB_OK ); }
Unicodeだとこれで OK 。 - 基本
-
MessageBoxだけで GUI アプリちゅうわけにはいきませんから、やることは C や C++ とほとんど同じですが、本家の指示に従うと
import core.runtime; import win32.windows; extern( Windows ){ int MyWndProc( HWND hw, UINT msg, WPARAM wp, LPARAM lp ){ switch( msg ){ case WM_DESTROY: .PostQuitMessage( 0 ); break; default: } return .DefWindowProc( hw, msg, wp, lp ); } int WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow ){ int ret; void exc( Throwable e ){ throw e; } try{ Runtime.initialize( &exc ); ret = myMain( hInstance ); Runtime.terminate( &exc ); } catch( Throwable o ){ .MessageBox( null, cast( LPWSTR )o.toString, "Hellow", MB_OK ); } return ret; } }
MyWndProcはWM_DESTROYを拾いたいから(PostQuitMessageは必須 )myMainはint myMain( HINSTANCE hinst ){ WNDCLASSEX wc; with( wc ){ cbSize = WNDCLASSEX.sizeof; hInstance = hinst; style = CS_HREDRAW | CS_VREDRAW; lpszClassName = "MyHellow"; // これもちゃんと 0 終端になる lpfnWndProc = &MyWndProc; hCursor = .LoadCursor( null, IDC_ARROW ); hbrBackground = cast( HBRUSH )( COLOR_WINDOW + 1 ); } .CreateWindowEx( 0, cast( LPWSTR ).RegisterClassEx( &wc ), "Hellow", WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 300, 200, null, null, hinst, null ); for( MSG msg; .GetMessage( &msg, null, 0, 0 ) > 0; ){ .TranslateMessage( &msg ); .DispatchMessage( &msg ); } return 0; }
ビルドをすると
D:\Pen-Jr\My Programs\Hellow win32>dmd -L/exet:nt/su:windows:4.0 hellow.d OPTLINK (R) for Win32 Release 8.00.12 Copyright (C) Digital Mars 1989-2010 All rights reserved. http://www.digitalmars.com/ctg/optlink.html hellow.obj(hellow) Error 42: Symbol Undefined _D5win327winuser14tagWNDCLASSEXW6__initZ --- errorlevel 1
と、エラーになります
自前の ヘッダー(
win32.windows) をimportしていますが、そのヘッダーのコンパイルとリンクが必要です。この場合
winuser.dだけをコンパイルしても良いのですが、libsフォルダ以下にあるwin32.libでビルド済みですから、これを同じフォルダに持ってきてdmd -L/exet:nt/su:windows:4.0 hellow.d win32.lib
win32.libを追加すれば OK ですが、最初の
import win32.windows;をimport windows; mixin ImpWinAPI;
とすれば OK。
できたHellow.exeを起動すれば
こんなウィンドウが表示されるはずですが
バグがあったりすると、プロセスが終了しなかったりして面倒です。
- WinDbg
-
GUI アプリは安全のため WinDbg などから実行した方がよいでしょう。
インストールされていれば、スタートメニューの Debugging Tools for Windows (x64) なんかに居るはずです。
起動すると
File->Open Executableで実行ファイルを開きます。Debug->GoまたはF5暴走とかエラーは止まりますが、フリーズしたら
Break次のビルドをする前に
stop debuggingしておかないと、実行ファイルの書き換えが出来ず、リンカーでエラーになります。高機能なデバッガーらしくいろいろ出来るのでしょうが、これだけでも十分です。 - リソース
-
Windows の GUI アプリでな何かと使いますから、リソースコンパイラ(
rc.exe)が必要。Windows SDK の Tools に入っていますから、インストールして PASS を通してあれば使えます。
試しに、
D:\Pen-Jr\My Programs\Hellow win32>rc /? Microsoft (R) Windows (R) Resource Compiler Version 6.1.7600.16385 Copyright (C) Microsoft Corporation. All rights reserved. Usage: rc [options] .RC input file Switches: /r Emit .RES file (optional) /v Verbose (print progress messages) /d Define a symbol /u Undefine a symbol /fo Rename .RES file /l Default language ID in hex /i Add a path for INCLUDE searches /x Ignore INCLUDE environment variable /c Define a code page used by NLS conversion /w Warn on Invalid codepage in .rc (default is an error) /y Don't warn if there are duplicate control ID's /n Append null's to all strings in the string tables /fm Localizable resource only dll file name /q RC Configuration file for the resource only DLL /g Specify the ultimate fallback language ID in hex /g1 Specify if version only MUI file can be created /g2 Specify the custom file version for checksum in MUI creation /nologo Suppress startup logo Flags may be either upper or lower case
アイコンなんかは基本ですから、自作するなり適当なのを拾ってきて
テキストファイルでそのアイコンを指定します
101 ICON "penguin.ico" // 101 は任意の ID
こんなのを
Hellow.rcで保存したらD:\Pen-Jr\My Programs\Hellow win32>rc hellow.rc Microsoft (R) Windows (R) Resource Compiler Version 6.1.7600.16385 Copyright (C) Microsoft Corporation. All rights reserved. D:\Pen-Jr\My Programs\Hellow win32>
と、無言で終了すれば
hellow.resが出来ますから、ビルドに追加しますdmd -L/exet:nt/su:windows:4.0 hellow.d hellow.res
ビルドしたファイルにはちゃんとアイコンが表示されます。
これだけでは、実行時にウィンドウやタスクバーにアイコンは表示されませんから
Hellow.dのmyMainにリソースで指定した id を追加します。WNDCLASSEX wc; with( wc ){ cbSize = WNDCLASSEX.sizeof; hInstance = hinst; style = CS_HREDRAW | CS_VREDRAW; lpszClassName = "MyHellow"; lpfnWndProc = &MyWndProc; // 以下を追加 hIcon = .LoadIcon( hinst, .MAKEINTRESOURCE( 101 ) ); // hCursor = .LoadCursor( null, IDC_ARROW ); hbrBackground = cast( HBRUSH )( COLOR_WINDOW + 1 ); } // 以下省略
ビルドして実行すれば
ちゃんとアイコンが表示されます。
D言語で基本的な Windows GUI アプリを作る準備は完了です。 - ちょっとモダンに
-
この程度のプログラムに
classだの何だのは必要ありませんが、GUI アプリを自力で作るとコンパクトなものにはなりませんから、汎用性とか拡張性とか考えてみます。ここまでの様な単純なプログラムでは、誰が書いても同じようなコードになると思いますが、この先はアプローチからして個人差が出ますからあくまでも参考に
汎用モジュールのつもりで追加
module app; public import core.runtime, std.utf; import windows; // win32.windows を public import して pragma( lib ) を追加 mixin windows.ImpWinAPI; class CsMain{ private{ // 書き換え禁止 const{ HINSTANCE hMainInst; LPTSTR lpCmdLine; } } public: this( in HINSTANCE hi, in LPTSTR cmd ){ hMainInst = hi; lpCmdLine = cmd; } ATOM registClass( in string nam, in int icon, HBRUSH hbr, WNDPROC proc ){ WNDCLASSEX wc; with( wc ){ cbSize = WNDCLASSEX.sizeof; hInstance = cast( HINSTANCE )hMainInst; style = CS_HREDRAW | CS_VREDRAW; lpszClassName = toUTF16z( nam ); // UTF8 -> UTF16 ( 0 終端追加 ) std.utf lpfnWndProc = proc; hIcon = .LoadIcon( cast( HINSTANCE )hMainInst, .MAKEINTRESOURCE( icon ) ); hCursor = .LoadCursor( null, IDC_ARROW ); hbrBackground = hbr; } return .RegisterClassEx( &wc ); } HWND createWindow( in LPWSTR cnam, in string wnam, in DWORD style, in DWORD exstyle, in int x, in int y, in int cx, in int cy ){ return .CreateWindowEx( exstyle, cnam, toUTF16z( wnam ), style, x, y, cx, cy, null, null, cast( HINSTANCE )hMainInst, null ); } int opCall(){ MSG msg; while( .GetMessage( &msg, null, 0, 0 ) > 0 ){ .TranslateMessage( &msg ); .DispatchMessage( &msg ); } return msg.wParam; } } // // WinMain のテンプレート template TpWinMain( Tc : CsMain, string Tappnam ){ extern( Windows ){ int WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow ){ int ret; void exc( Throwable e ){ throw e; } try{ Runtime.initialize( &exc ); ret = ( new Tc( hInstance, lpCmdLine ) )(); Runtime.terminate( &exc ); } catch( Throwable o ){ ret = .MessageBox( null, toUTF16z( o.toString ), toUTF16z( Tappnam ), MB_OK | MB_ICONERROR | MB_TOPMOST ); } return ret; } } }
これを
app.dで保存Hellow.dはimport app; extern( Windows ){ int MyWndProc( HWND hw, UINT msg, WPARAM wp, LPARAM lp ){ switch( msg ){ case WM_DESTROY: .PostQuitMessage( 0 ); break; default: } return .DefWindowProc( hw, msg, wp, lp ); } } class CsMyMain : CsMain{ this( in HINSTANCE hi, in LPTSTR cmd ){ super( hi, cmd ); } override{ int opCall(){ createWindow( cast( LPWSTR )registClass( "MyMainClass", 101, cast( HBRUSH )( COLOR_WINDOW + 1 ), &MyWndProc ), "Hellow", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 0, CW_USEDEFAULT, CW_USEDEFAULT, 300, 200 ); return super.opCall(); } } } mixin TpWinMain!( CsMyMain, "Hellow" );
こう書き換えて
ビルドに
app.dを追加>dmd -L/exet:nt/su:windows:4.0 hellow.d app.d hellow.res
これを実行しても結果は同じですが、まあ - make
-
一行のコマンドですが、コマンドプロンプトを起動し直すたびに毎回打ち込むのは面倒です。
batファイルを使っても良いのですが、makeを使えば何かと便利です。all:hellow.exe hellow.exe:hellow.d app.d hellow.res dmd -L/exet:nt/su:windows:4.0 hellow.d app.d hellow.res hellow.res:hellow.rc rc hellow.rc clean: del hellow.exe del hellow.res
これを拡張しなしの
makefileで保存すればD:\Pen-Jr\My Programs\Hellow win32>make dmd -L/exet:nt/su:windows:4.0 hellow.d app.d hellow.res D:\Pen-Jr\My Programs\Hellow win32>
- wstring
-
メッセージを表示するには
Hellow.dにWM_PAINTの処理を追加します。enum string MyMsg = "ようこそD言語の世界へ"; extern( Windows ){ int MyWndProc( HWND hw, UINT msg, WPARAM wp, LPARAM lp ){ switch( msg ){ case WM_DESTROY: .PostQuitMessage( 0 ); break; case WM_PAINT: PAINTSTRUCT ps; RECT rc; HDC dc = .BeginPaint( hw, &ps ); .GetClientRect( hw, &rc ); .DrawText( dc, toUTF16z( MyMsg ), MyMsg.length, &rc, DT_SINGLELINE | DT_VCENTER | DT_CENTER ); .EndPaint( hw, &ps ); return 0; default: } return .DefWindowProc( hw, msg, wp, lp ); } }
DrawTextでは0終端ではなく文字数で処理されますが、UTFー8の.lengthを渡すと、
文字数にはならず、うまくいきません。
対応策はいろいろ考えられますが、
// UTF16 にする enum wstring MyMsg = "ようこそD言語の世界へ"; void drawText( HDC dc, ref RECT rc, in wstring s ){ .DrawText( dc, s.ptr, s.length, &rc, DT_SINGLELINE | DT_VCENTER | DT_CENTER ); } extern( Windows ){ int MyWndProc( HWND hw, UINT msg, WPARAM wp, LPARAM lp ){ switch( msg ){ case WM_DESTROY: .PostQuitMessage( 0 ); break; case WM_PAINT: PAINTSTRUCT ps; RECT rc; HDC dc = .BeginPaint( hw, &ps ); .GetClientRect( hw, &rc ); .DrawText( // ptr と length を渡せば OK dc, MyMsg.ptr, MyMsg.length, &rc, DT_SINGLELINE | DT_VCENTER | DT_CENTER ); .EndPaint( hw, &ps ); return 0; default: } return .DefWindowProc( hw, msg, wp, lp ); } }

D言語の標準ライブラリーでもほとんど
wstringに対応しているようですから、wstringを使った方が悩みは減るかも知れません。
2010年7月6日





