./index.html ../index.html

Project: windows.h for D

This page is written by Japanease and encoded UTF-8.

I'm trying to make full "windows.h" D version to replace windows.d in Phobos.

D付属のPhobosに含まれるwindows.dは、結構丁寧で、人の手で書いたとしか思えないのですが、それでは数多のWin32 APIを網羅することなど望めません。 D用のWin32 APIヘッダーは他にも幾つかあるのですが、文字コードやIME関係が抜けてたり、W版APIが含まれていなかったりと抜けが多いため…その抜けが、使わないようなのなら別に構わないのですが、僕は使うので…自分で変換を試みてみました。

単純に好みで#deinfe UNICODE状態で作ってます。 ニュースグループを機にANSIバージョンも一応試みてますが…いいじゃん、9xなんて存在しないOSで。(ごめんなさいごめんなさいごめんなさい)

I welcome the feed back.

フィードバック歓迎。

Download / ダウンロード

windows.h for D (2004-07-09) *1
COM for D (2004-01-30)
converter script (2004-07-09)

Policy / 作業指針

Perlで変換したのをそのまま使えるのが理想。 そのため#defineの定数化、関数化なんかも全部win32sdk2d.pl中に含めています。

定数やヘッダ内で定義する関数などの外部リンケージは可能な限りデフォルトのextern(D)にする…さもないとPhobosのwindows.dの同名のものがextern(Windows)で宣言されているのでリンクエラーに。

どうでもいいといえばどうでもいいけど、整数定数はconstよりも無名のenumで宣言した方が.objに埋めこまれない分お得感があります。

COM関係は、実はIDLから変換した方が賢いんじゃないかと思いつつも、シンタックスをC++とあわせるために.hから手書き変換。 コンパイル通すのに必要な分だけ…。

interfaceはDではポインタになるため、インターフェースを引数に取る関数は、IUnknown*LPUNKNOWNといった感じで置き換えてあります。

Progress / 進行状況

headerstateWAremark
windows.h ok OO
winnt.h ok OO
guiddef workingOOhandwriting
windef.h ok OOincluding basetsd.h
winbase.h ok OO
winerror.hok OO
wingdi.h ok OO
winuser.h ok OO
winnls.h ok OO
wincon.h ok OO
winver.h ok OO
winreg.h ok OOincluding reason.h
dde.h ok OO
ddeml.h ok OO
dlgs.h ok OO
mmsystem.hok OO
shellapi.hok OO
winspool.hok OO
commdlg.h ok OOincluding cderr.h
imm.h ok OO
commctrl.hok OOincluding prsht.h
winsock.h never OXnot imported by windows.d
winsock2.hnever XX
ole2.h never XXnot imported by windows.d
lzexpand.hnever XXnot imported by windows.d
nb30.h never XXnot imported by windows.d
rpc.h never XXnot imported by windows.d
winnetwk.hnever XXnot imported by windows.d
winperf.h never XXnot imported by windows.d
stralign.hnever XXnot imported by windows.d
tvout.h never XXnot imported by winuser.d
basetsd.h - included windef.d
reason.h - included winreg.d
cderr.h - included commdlg.d
prsht.h - included commctrl.d
pshpack1.h- replaced by align(1):
pshpack2.h- replaced by align(2):
pshpack4.h- replaced by align(4):
pshpack8.h- replaced by align(8):
poppack.h - replaced by align:

winsock.hはwindows.hから#includeされているのに、winsock2.hがされていないので、両方とも外しました。

headerstateWAremark
cguid.h workingOXhandwriting
objbase.h okOX
objidl.h workingOXhandwriting
propidl.h workingOXhandwriting
unknwn.h workingOXhandwriting
urlmon.h workingOXhandwriting
wtypes.h workingOXhandwriting

Build / ビルド

Compile all *.d files and create library. Use lib.exe of Digital Mars C++, NOT lib.exe of Microsoft.

const => enum 化によって、最終的な.libがかなり小さくなりましたので、一緒にアーカイブしてますが、自前でビルドしたい場合は、各ファイルをコンパイルしたら、こんな感じでまとめます。 libはDigital Mars C++付属のもの(OMF用)を使ってください。 試してないけどBorlandのでも可能なはず。 MSの(COFF用)を使わないように注意。

lib win32.lib +guiddef.obj +winbase.obj ... +winuser.obj, win32.lst,

Create a sub directory named "win32", and copy *.d to there.

sc.iniでDFLAGSに-Iとして渡しているディレクトリのどれかに、win32というサブディレクトリを作り、ソースをそこへ放り込めば使えるようになるはずです。 リンク時にwin32.libを渡してください。

Digital Mars C++ doesn't have several import libraries. If you need them, please ready yourself with implib or coff2omf.

DMC++にはSDKにはあるはずの幾つかのインポートライブラリが用意されていません。 implibで普通に.dllから生成した場合、名前の後ろの「@数字」が付いてくれません。 この数字は引数のサイズなので.dllから知る方法が無いのです。 方法としては、.defファイルを書いてimplibに渡せばOKです。

LIBRARY "shell32.dll"
EXPORTS
    _ShellExecuteA@24              @369 ; ShellExecuteA

こんな方法もありますが、最近のMSのlib.exe(実際に処理するのはlink.exe)は、/CONVERTオプションを打ち切ってやがります。 旧バージョンのlink.exeを持っていたら、大切にしましょう。 DirectX9からBorlandの.libが付いて来なくなったり、最近のMicrosoftは他社の開発環境を締め出そうとしているのですか?

Sample / 動作確認

private import std.utf;
version(ANSI){
  private import win32.ansi.windows;
  char[] toT(char[] t){
    wchar[] s = toUTF16(t);
    char[] result;
    result.length = WideCharToMultiByte(0, 0, s, s.length, null, 0, null, null);
    WideCharToMultiByte(0, 0, s, s.length, result, result.length, null, null);
    return t; 
  }
}else{
  private import win32.windows;
  alias toUTF16 toT;
}

const int IDC_BTNCLICK = 101;
const int IDC_BTNDONTCLICK = 102;

extern(Windows) LRESULT WindowProc(HWND hWnd, uint uMsg, WPARAM wParam, LPARAM lParam)
{
  switch(uMsg){
  case WM_COMMAND:
    switch (LOWORD(wParam)){
    case IDC_BTNCLICK:
      if(HIWORD(wParam) == BN_CLICKED)
        MessageBox(hWnd, "Hello, world!", "Greeting", MB_OK | MB_ICONINFORMATION);
      break;
    case IDC_BTNDONTCLICK:
      if(HIWORD(wParam) == BN_CLICKED){
        MessageBox(hWnd, "You've been warned...", 
          "Prepare to GP fault", MB_OK | MB_ICONEXCLAMATION);
        *(cast(int*) null) = 666;
      }
      break;
    }
    break;
  case WM_PAINT:
    {
      static TCHAR[] text = "D Does Windows";
      PAINTSTRUCT ps;
      HDC dc = BeginPaint(hWnd, &ps);
      RECT r;
      GetClientRect(hWnd, &r);
      HFONT font = CreateFont(80, 0, 0, 0, FW_EXTRABOLD, FALSE, FALSE,
        FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
        DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, "Arial");
      HGDIOBJ old = SelectObject(dc, cast(HGDIOBJ)font);
      SetTextAlign(dc, TA_CENTER | TA_BASELINE);
      TextOut(dc, r.right / 2, r.bottom / 2, text, text.length);
      SelectObject(dc, old);
      EndPaint(hWnd, &ps);
    } 
    break;
  case WM_DESTROY:
    PostQuitMessage(0);
    break;
  default:
    break;
  }
  return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

int doit()
{
  HINSTANCE hInst = GetModuleHandle(NULL);
  WNDCLASS wc;
  wc.lpszClassName = "DWndClass";
  wc.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
  wc.lpfnWndProc = &WindowProc;
  wc.hInstance = hInst;
  wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  wc.hCursor = LoadCursor(NULL, IDC_CROSS);
  wc.hbrBackground = cast(HBRUSH)(COLOR_BTNFACE);
  wc.lpszMenuName = NULL;
  wc.cbClsExtra = wc.cbWndExtra = 0;
  ATOM r = RegisterClass(&wc);
  assert(r);
  
  HWND hWnd, btnClick, btnDontClick;
  hWnd = CreateWindowEx(0/*WS_EX_WINDOWEDGE*/, "DWndClass", "Just a window", 
    /*WS_THICKFRAME | */ WS_DLGFRAME | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SYSMENU | WS_VISIBLE,
    CW_USEDEFAULT, CW_USEDEFAULT, 400, 300, HWND_DESKTOP,
    NULL, hInst, NULL);
  assert(hWnd);
  
  btnClick = CreateWindow("BUTTON", "Click Me", WS_CHILD | WS_VISIBLE,
    0, 0, 100, 25, hWnd, cast(HMENU)IDC_BTNCLICK, hInst, NULL);

  btnDontClick = CreateWindow("BUTTON", "DON'T CLICK!", WS_CHILD | WS_VISIBLE,
    110, 0, 100, 25, hWnd, cast(HMENU)IDC_BTNDONTCLICK, hInst, NULL);

  MSG msg;
  while(GetMessage(&msg, NULL, 0, 0))
  {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }
  return msg.wParam;
}

extern(C){
  void gc_init();
  void gc_term();
  void _minit();
  void _moduleCtor();
  void _moduleUnitTests();
}

extern(Windows) int WinMain(
  HINSTANCE hInstance, 
  HINSTANCE hPrevInstance, 
  LPSTR lpCmdLine, 
  int nCmdShow)
{
  int result;
  gc_init();
  _minit();
  try{
    _moduleCtor();
    _moduleUnitTests();
    result = doit();
  }catch (Object o){
    TCHAR[] msg = toT(o.toString() ~ "\0");
    MessageBox(NULL, cast(TCHAR*)msg, "Error", MB_OK | MB_ICONEXCLAMATION);
    result = 1;
  }
  gc_term();
  return result;
}

Another porting libraries / 他のポーティングライブラリ

DedicateD FreePascal付属のwindows.pasからのポーティング
ということでC言語の無茶な部分が見られず奇麗ですが、古いです
Dig これに含まれるwindows.dはPhobosのwindows.dと大差無いです。
ただしXPのテーマAPIも付属しています。
Atari-Soldiers DIDEのサイト。
各種ポーティングライブラリ*2が収録されています。
D Win32 COM libraries
現在位置
COMも含めたインポートライブラリがあります。
構成から、全部手書きとしか思えないのですが…。(すげえ)

Tips / ちょっとしたこと

We should use "private import" instead of "(public) import" for platform modules.

windows.dはこのように乱立状態です。 無論僕としてはこいつがデファクトスタンダードになってくれるとそれなりには嬉しいのですが、とりあえず、ポーティングライブラリをimportするときは、private importにしてください。 Dのimportはprivateにしない限り#includeよろしく連鎖するので、ポーティングライブラリをインポートしたソースをインポートしたソースが、ポーティングライブラリの選択という実装を強制的に見せられてしまうのは、単に好ましくないだけに留まらず、識別子の衝突やらで色々迷惑です。


*1 7-zip archiver

*2 「これ」も収録されてたり…。