Bitmap: Dで手軽にビットマップを扱うクラス

こちらは未完成です。
バグを見つけた方はご指摘下さると嬉しいです。


リソースやファイルからビットマップを読み込んで、
DIBSectionを作り出します。
バイスコンテキストを持っているので、
GDIで描画できます。
ファイル書き出し機能もおまけで付けました。

module bitmap;

//------------------------------------------------------------------------------------------//

import win32.windows;
import std.c.string;
import std.stream;
import std.utf;

//------------------------------------------------------------------------------------------//

pragma(lib, "gdi32.lib");

static if (WINVER < 0x0500)
{
    const DWORD
        NOMIRRORBITMAP = 0x80000000,
        CAPTUREBLT     = 0x40000000;
}

//------------------------------------------------------------------------------------------//

const uint RASTER = SRCCOPY | CAPTUREBLT;

//------------------------------------------------------------------------------------------//

class BitmapException : Exception
{
    this(string message)
    {
        super(message);
    }
}

//------------------------------------------------------------------------------------------//

interface Image
{
// Methods
public:
    void Create(int w, int h, ushort bitCount=32, uint clrUsed=0);
    void Create(BITMAPINFO* bi);
    void Create(Image image);
    void Load(ushort rsrcID);
    void Load(string filename);
    void Save(string filename);
    void Dispose();
    void Draw(HDC hDC, int left=0, int top=0, uint dwRaster=RASTER);
    void Draw(HDC hDC, int left=0, int top=0, int width=0, int height=0,
              int sx=0, int sy=0, int sw=0, int sh=0, uint dwRaster=RASTER);
    
// Properties
public:
    int         Width();
    int         Height();
    ushort      BitCount();
    uint        ColorUsed();
    uint        Size();
    HDC         hDC();
    BITMAPINFO* BmpInfo();
    HANDLE      pBits();
}

//------------------------------------------------------------------------------------------//

class Bitmap : Image
{
// Attributes
protected:
    int         m_width      = 0;
    int         m_height     = 0;
    ushort      m_bitCount   = 0;
    uint        m_clrUsed    = 0;
    int         m_lineLength = 0;
    HDC         m_hDC        = null;
    HBITMAP     m_hBitmap    = null;
    HPALETTE    m_hPalette   = null;    
    BITMAPINFO* m_lpBI       = null;
    HANDLE      m_pbits      = null;
    
// Methods
public:
    // コンストラクタ
    this()
    {
    }
    
    this(int width, int height, ushort bitCount=32, uint clrUsed=0)
    {
        Create(width, height, bitCount, clrUsed);
    }
    
    this(BITMAPINFO* bi)
    {
        Create(bi);
    }
    
    this(Image image)
    {
        Create(image);
    }
    
    this(ushort rsrcID)
    {
        Load(rsrcID);
    }
    
    this(string filename)
    {
        Load(filename);
    }
    
    // デストラクタ
    ~this()
    {
        Dispose();
    }
    
    // ビットマップを解放する
    void Dispose()
    {
        m_width      = 0;
        m_height     = 0;
        m_bitCount   = 0;
        m_clrUsed    = 0;
        m_lineLength = 0;
        DeleteDC(m_hDC);          m_hDC      = null; /// DCの解放がオブジェクトの解放より先らしい
        DeleteObject(m_hBitmap);  m_hBitmap  = null; /// m_pbitsはここで解放される
        DeleteObject(m_hPalette); m_hPalette = null;
        m_lpBI       = null;
        m_pbits      = null;
    }
    
    // ビットマップを描画する
    void Draw(HDC hDC, int left = 0, int top = 0, uint dwRaster = RASTER)
    {
        Draw(hDC, left, top, 0, 0, 0, 0, 0, 0, dwRaster);
    }
    
    // ビットマップを描画する
    void Draw(HDC hDC, int left = 0, int top = 0, int width = 0, int height = 0,
               int sx = 0, int sy = 0, int sw = 0, int sh = 0, uint dwRaster = RASTER)
    {
        // パレットの選択
        HPALETTE hOldPalette = null;
        if (m_hPalette)
        {
            hOldPalette = SelectPalette(hDC, m_hPalette, false);
            if (hOldPalette is null)
            {
                throw new BitmapException(r"パレット選択エラー");
            }
            
            RealizePalette(hDC);
        }
        
        if (width == 0 || height == 0)
        {
            // 描画
            BitBlt(hDC, left, top, m_width, m_height, m_hDC, 0, 0, dwRaster);
        }
        else
        {
            // 描画元画像サイズのデフォルトを指定
            if (sw == 0) sw = m_width;
            if (sh == 0) sh = m_height;
            
            // ビットマップ伸縮モードの設定
            auto oldSthMode = SetStretchBltMode(hDC, COLORONCOLOR); /// 縮小・拡大ともこちらのモードが最適
            if (oldSthMode == 0)
            {
                throw new BitmapException(r"ビットマップ伸縮モード設定エラー");
            }
            
            // 描画
            StretchDIBits(hDC, left, top, width, height, sx, sy, sw, sh,
                          m_pbits, m_lpBI, DIB_RGB_COLORS, dwRaster);
            
            // ビットマップ伸縮モードの設定解除
            SetStretchBltMode(hDC, oldSthMode);
        }
        
        // パレットの選択解除
        if (hOldPalette)
        {
            SelectPalette(hDC, hOldPalette, false);
        }
    }
    
    // 空のビットマップを作成する
    void Create(int width=1, int height=1, ushort bitCount=32, uint clrUsed=0)
    {
        try
        {
            Dispose();
            
            // データをメンバ変数に記憶
            /// ここで m_width, m_height, m_bitCount, m_clrUsed, m_lineLength が初期化される
            StoreData(width, height, bitCount, clrUsed);
            
            // ヘッダ情報の作成
            size_t bmpInfoSize = BITMAPINFOHEADER.sizeof + RGBQUAD.sizeof * m_clrUsed;
            m_lpBI = cast(BITMAPINFO*)new ubyte[bmpInfoSize];
            
            m_lpBI.bmiHeader.biSize          = BITMAPINFOHEADER.sizeof;
            m_lpBI.bmiHeader.biWidth         = m_width;
            m_lpBI.bmiHeader.biHeight        = m_height;
            m_lpBI.bmiHeader.biPlanes        = 1;
            m_lpBI.bmiHeader.biBitCount      = m_bitCount;
            m_lpBI.bmiHeader.biCompression   = BI_RGB;
            m_lpBI.bmiHeader.biSizeImage     = this.Size;
            m_lpBI.bmiHeader.biXPelsPerMeter = 0;
            m_lpBI.bmiHeader.biYPelsPerMeter = 0;
            m_lpBI.bmiHeader.biClrUsed       = m_clrUsed;
            m_lpBI.bmiHeader.biClrImportant  = 0;
            
            // 対応している形式かどうか
            Check();
            
            // パレットデータの作成
            CreatePalette();
            
            // 互換DCの作成
            CreateCompatibleDC();
            
            // DIBSectionの生成
            CreateDIBSection();
            
        }
        catch (Object o)
        {
            Dispose();
            throw o;
        }
    }
    
    // ヘッダ情報からビットマップを作成する
    void Create(BITMAPINFO* bmi)
    {
        try
        {
            // 空のビットマップを作成
            /// ここで m_lpBI が初期化される
            Create(bmi.bmiHeader.biWidth, bmi.bmiHeader.biHeight,
                        bmi.bmiHeader.biBitCount, bmi.bmiHeader.biClrUsed);
            
            // ヘッダ情報をコピー
            m_lpBI.bmiHeader.biXPelsPerMeter = bmi.bmiHeader.biXPelsPerMeter;
            m_lpBI.bmiHeader.biYPelsPerMeter = bmi.bmiHeader.biYPelsPerMeter;
            m_lpBI.bmiHeader.biClrImportant  = bmi.bmiHeader.biClrImportant;
            
            // パレットデータをコピー
            memcpy(m_lpBI + BITMAPINFOHEADER.sizeof, bmi + bmi.bmiHeader.biSize, RGBQUAD.sizeof * m_clrUsed);
            
        }
        catch (Object o)
        {
            Dispose();
            throw o;
        }
    }
    
    // イメージオブジェクトからビットマップを作成する
    void Create(Image image)
    {
        try
        {
            Dispose();
            
            // ヘッダ情報からビットマップを作成
            Create(image.BmpInfo);
            
            // ピクセルデータのコピー
            image.Draw(this.hDC);
            
        }
        catch (Object o)
        {
            Dispose();
            throw o;
        }
    }
    
    // リソースから読み込む
    void Load(ushort rsrcID)
    {
        try
        {
            Dispose();
            
            // リソースの読み込み
            auto hRsrc = FindResource(GetModuleHandle(null), MAKEINTRESOURCE(rsrcID), RT_BITMAP);
            if(hRsrc is null)
            {
                throw new BitmapException(r"リソースが見つかりませんでした");
            }
            auto hBmp = LoadResource(GetModuleHandle(null), hRsrc);
            if(hBmp is null)
            {
                throw new BitmapException(r"リソースの読み込みに失敗しました");
            }
            
            // ヘッダ情報およびパレット情報の読み込み
            /// 仮にヘッダのバージョンがV4形式やV5形式でもこれで対応できます。
            m_lpBI = cast(BITMAPINFO*)LockResource(hBmp);
            if(m_lpBI is null)
            {
                throw new BitmapException(r"ヘッダ情報の読み込みに失敗しました");
            }
            
            // ヘッダ情報のうちよく使うデータをメンバ変数に記憶
            StoreData(m_lpBI.bmiHeader.biWidth, m_lpBI.bmiHeader.biHeight,
                        m_lpBI.bmiHeader.biBitCount, m_lpBI.bmiHeader.biClrUsed);
            
            // 対応している形式かどうか
            Check();
            
            // パレットデータの作成
            CreatePalette();
            
            // 互換DCの作成
            CreateCompatibleDC();
            
            // DIBSectionの生成
            CreateDIBSection();
            
            // ピクセルデータの読み込み
            void* source = m_lpBI + m_lpBI.bmiHeader.biSize + RGBQUAD.sizeof * m_clrUsed;
            memcpy(m_pbits, source, this.Size);
        }
        catch (Object o)
        {
            Dispose();
            throw o;
        }
        finally
        {
            /// リソースやリソースロックの解放はしなくてよいらしい
            /// http://www.microsoft.com/japan/msdn/library/ja/jpwinui/html/Toppage_Resource.asp
        }
    }
    
    // ファイルから読み込む
    void Load(string filename)
    {
        File file;
        try
        {
            Dispose();
            
            file = new File(filename, FileMode.In);
            
            // ファイルヘッダの読み込み
            BITMAPFILEHEADER bmpfh;
            file.readExact(&bmpfh, bmpfh.sizeof);
            if (bmpfh.bfType != 0x4D42)
            {
                throw new BitmapException(r"ビットマップちゃうやん");
            }
            
            // ヘッダ情報およびパレット情報の読み込み
            /// 仮にヘッダのバージョンがV4形式やV5形式でもこれで対応できます。
            size_t size = bmpfh.bfOffBits - bmpfh.sizeof;
            m_lpBI = cast(BITMAPINFO*)new ubyte[size];
            file.readExact(m_lpBI, size);
            if(m_lpBI is null)
            {
                throw new BitmapException(r"ヘッダ情報の読み込みに失敗しました");
            }
            
            // ヘッダ情報のうちよく使うデータをメンバ変数に記憶
            StoreData(m_lpBI.bmiHeader.biWidth, m_lpBI.bmiHeader.biHeight,
                        m_lpBI.bmiHeader.biBitCount, m_lpBI.bmiHeader.biClrUsed);
            
            // 対応している形式かどうか
            Check();
            
            // パレットデータの作成
            CreatePalette();
            
            // 互換DCの作成
            CreateCompatibleDC();
            
            // DIBSectionの生成
            CreateDIBSection();
            
            // ピクセルデータの読み込み
            file.readExact(m_pbits, this.Size);
        }
        catch (Object o)
        {
            Dispose();
            throw o;
        }
        finally
        {
            if ( file )    file.close();
        }
    }
    
    // ファイルに書き出す
    void Save(string filename)
    {
        File file;
        try
        {
            file = new std.stream.File(filename, FileMode.OutNew);
            
            // ファイルヘッダの書き出し
            BITMAPFILEHEADER bmpfh;
            bmpfh.bfType      = 0x4d42; /// `BM`
            bmpfh.bfOffBits   = bmpfh.sizeof + m_lpBI.bmiHeader.biSize + RGBQUAD.sizeof * m_clrUsed;
            bmpfh.bfReserved1 = 0;
            bmpfh.bfReserved2 = 0;
            bmpfh.bfSize      = bmpfh.bfOffBits + m_lpBI.bmiHeader.biSizeImage;
            file.writeExact(&bmpfh, bmpfh.sizeof);
            
            // ヘッダ情報およびパレット情報の書き出し
            file.writeExact(m_lpBI, m_lpBI.bmiHeader.biSize + RGBQUAD.sizeof * m_clrUsed);
            
            // ピクセルデータの書き出し
            file.writeExact(m_pbits, this.Size);
        }
        catch (Object o)
        {
            throw o;
        }
        finally
        {
            if ( file ) file.close();
        }
    }
    
protected:
    // データをメンバ変数に記憶
    void StoreData(int width, int height, ushort bitCount, uint clrUsed)
    {
        m_width      = width;
        m_height     = height;
        m_bitCount   = bitCount; /// 1, 4, 8, (16,) 24, 32, ...
        m_clrUsed    = (bitCount > 8) ? 0 : (clrUsed > 0) ? clrUsed : (1 << bitCount);
        m_lineLength = (((width * bitCount) + 31) & ~31) / 8; /// 4バイト境界に揃える
    }
    
    // 対応している形式かどうか調べる
    void Check()
    {
        if ( m_lpBI.bmiHeader.biSize != BITMAPINFOHEADER.sizeof &&
            m_lpBI.bmiHeader.biSize != BITMAPV4HEADER.sizeof &&
            m_lpBI.bmiHeader.biSize != BITMAPV5HEADER.sizeof )
        {
            throw new BitmapException(r"対応していないヘッダ形式です");
        }
        
        if ( m_lpBI.bmiHeader.biCompression == BI_RGB )
        {
            switch ( m_bitCount )
            {
            case 1:
            case 4:
            case 8:
            case 16:
            case 24:
            case 32:
                return;
            default:
                throw new BitmapException(r"対応していないビット深度です");
            }
        }
        else if ( m_lpBI.bmiHeader.biCompression == BI_BITFIELDS )
        {
            switch ( m_bitCount )
            {
            case 16:
            case 32:
                return;
            default:
                throw new BitmapException(r"対応していないビット深度です");
            }
        }
        else
        {
            throw new BitmapException(r"圧縮ビットマップには非対応です");
        }
        
    }
    
    // パレットデータを作成する
    void CreatePalette()
    {
        if ( m_bitCount > 8 )
        {
            return; /// フルカラーの場合は必要なし
        }
        
        try
        {
            // 色数に応じて必要なメモリを確保
            auto lpLogPal = cast(LOGPALETTE*)new ubyte[LOGPALETTE.sizeof + PALETTEENTRY.sizeof * m_clrUsed];
            lpLogPal.palVersion = 0x300;
            lpLogPal.palNumEntries = cast(ushort)m_clrUsed;
            
            for ( size_t i = 0; i < m_clrUsed; i++ )
            {
                lpLogPal.palPalEntry[i].peRed   = m_lpBI.bmiColors[i].rgbRed;
                lpLogPal.palPalEntry[i].peGreen = m_lpBI.bmiColors[i].rgbGreen;
                lpLogPal.palPalEntry[i].peBlue  = m_lpBI.bmiColors[i].rgbBlue;
            }
            
            m_hPalette = .CreatePalette(lpLogPal);
            if (m_hPalette is null)
            {
                throw new BitmapException(r"パレットデータの作成に失敗しました");
            }
        }
        catch (Object o)
        {
            DeleteObject(m_hPalette);
            m_hPalette = null;
            throw o;
        }
    }
    
    // デスクトップと互換性のあるデバイスコンテキストを作成する
    void CreateCompatibleDC()
    {
        HDC hDesktopDC;
        try
        {
            hDesktopDC = GetDC(null);
            m_hDC = .CreateCompatibleDC(hDesktopDC);
            if (m_hDC is null)
            {
                throw new BitmapException(r"デバイスコンテキストが作成できませんでした");
            }
        }
        catch (Object o)
        {
            DeleteDC(m_hDC);
            m_hDC = null;
            throw o;
        }
        finally
        {
            ReleaseDC(null, hDesktopDC);
        }
    }
    
    // DIBSectionを生成する
    void CreateDIBSection()
    {
        try
        {
            // DIBSectionの作成
            m_hBitmap = .CreateDIBSection(m_hDC, m_lpBI, DIB_RGB_COLORS, cast(void**)&m_pbits, null, 0);
            if (m_hBitmap is null)
            {
                throw new BitmapException(r"DIBSectionの生成に失敗しました");
            }
            
            // ビットマップオブジェクトをデバイスコンテキストにセット
            HGDIOBJ hOldBmp = SelectObject(m_hDC, m_hBitmap);
            DeleteObject(hOldBmp);
        }
        catch (Object o)
        {
            DeleteObject(m_hBitmap);
            m_hBitmap = null;
            throw o;
        }
    }
    
// Properties
public:
    int         Width()     { return m_width; }
    int         Height()    { return m_height; }
    ushort      BitCount()  { return m_bitCount; }
    uint        ColorUsed() { return m_clrUsed; }
    uint        Size()      { return m_lineLength * m_height; }
    HDC         hDC()       { return m_hDC; }
    HPALETTE    hPalette()  { return m_hPalette; }
    HBITMAP     hBitmap()   { return m_hBitmap; }
    BITMAPINFO* BmpInfo()   { return m_lpBI; }
    HANDLE      pBits()     { return m_pbits; }
}
  • ライセンスは暫定的にNYSLで。
  • ご質問・ご指摘等 歓迎します

DWnd: Dで手軽にウィンドウを生成するクラス

こっそり復帰。
何年も前に作ったものだけど、こなれてきたので晒してみるテスト。
もちろん動作は無保証ですので悪しからず。

元ネタは
http://mkmqwerty.hp.infoseek.co.jp/programing/tips/tips_window03.html
…だと思います。
5年以上前に参考にしたので、大元のページじゃないかも知れません。。。
SetProp() で thisポインタを埋め込む、という基本的な考え方は同じです。

module dwnd;

//------------------------------------------------------------------------------------------//

import win32.windows;

//------------------------------------------------------------------------------------------//

enum
{
    WM_ATTACH = 0xBFFE, WM_DETACH = 0xBFFF /// WM_APP (0x8000) 〜 0xBFFF
}

//------------------------------------------------------------------------------------------//

version (Unicode)
{
    import std.utf: toUTF16z;
    
    LPCTSTR TEXT(string s)  { return toUTF16z(s); }
    LPCTSTR TEXT(wstring s) { return (s ~ "\0").ptr; }
}
else
{
    import std.utf: toUTF8;
    import std.windows.charset: toMBSz;
    
    LPCTSTR TEXT(string s)  { return toMBSz(s); }
    LPCTSTR TEXT(wstring s) { return toMBSz(toUTF8(s)); }
}

//------------------------------------------------------------------------------------------//

// 基底ウィンドウクラス
class DWndBase
{
// Attributes
protected:
    static ATOM m_atom = 0;
    
    HWND    m_hWnd       = null;
    HWND    m_hParentWnd = null;
    WNDPROC m_oldWndProc = null;
    bool    m_isDialog   = false;
    int     m_left       = 0;
    int     m_top        = 0;
    int     m_width      = 0;
    int     m_height     = 0;
    DWORD   m_style      = 0;
    DWORD   m_styleEx    = 0;
    LPCTSTR m_text       = null;
    
// Methods
public:
    // ウィンドウを生成する
    HWND Create(HWND hParentWnd = null)
    {
        // 親ウィンドウのハンドルをメンバ変数に記憶
        m_hParentWnd = hParentWnd;
        
        // 親ウィンドウを持つ場合はウィンドウスタイルにWS_CHILDを追加
        m_style |= m_hParentWnd ? WS_CHILD : 0;
        
        // ウィンドウを生成
        m_hWnd = CreateWindowEx(m_styleEx, cast(LPCTSTR)m_atom, m_text, m_style,
                                m_left, m_top, m_width, m_height,
                                m_hParentWnd, null, cast(HINSTANCE)GetModuleHandle(null),
                                cast(void*)this);
        if (!m_hWnd)
        {
            throw new Exception("Falied to create a new window.");
        }
        
        // ウィンドウを表示
        UpdateWindow(m_hWnd);
        
        return m_hWnd;
    }
    
    // ウィンドウを破棄する
    void Destroy()
    {
        Detach();
        DestroyWindow(m_hWnd);
    }
    
    // ウィンドウハンドルとDWndオブジェクトを結びつける
    final bool Attach(HWND hWnd)
    {
        if (!hWnd)
        {
            return false;
        }
        
        // 前のが残っていたら切り離し
        Detach();
        
        // ダイアログかウィンドウかを判定する
        m_isDialog = (GetWindowLongPtr(hWnd, DWL_DLGPROC) != 0);
        int nIndex = (m_isDialog ? DWL_DLGPROC : GWLP_WNDPROC); /// GWLP_WNDPROC = -4です
        
        // ウィンドウのプロパティリストにDWndへのポインタを記憶
        SetProp(hWnd, TEXT("DWnd"c), cast(HANDLE)this);
        
        // 既存のウィンドウをサブクラス化する場合は、
        // ウィンドウ(ダイアログ)プロシージャをWindowMapProcに置き換える
        if (GetWindowLongPtr(hWnd, nIndex) != cast(LONG_PTR)&WndMapProc)
        {
            m_oldWndProc = cast(WNDPROC)SetWindowLongPtr(hWnd, nIndex, cast(LONG_PTR)&WndMapProc);
            
            if (!m_oldWndProc)
            {
                throw new Exception("Failed to subclass.");
            }
        }
        
        // ウィンドウハンドルを記憶
        m_hWnd = hWnd;
        
        // ウィンドウメッセージを発行
        PostMessage(hWnd, WM_ATTACH, 0, 0);
        
        return true;
    }
    
    // ウィンドウハンドルをDWndオブジェクトから切り離す
    final bool Detach()
    {
        if (!m_hWnd)
        {
            return false;
        }
        
        // ウィンドウがサブクラス化されている場合は、
        // ウィンドウ(ダイアログ)プロシージャを元に戻す
        if (m_oldWndProc)
        {
            SetWindowLongPtr(m_hWnd, (m_isDialog ? DWL_DLGPROC : GWLP_WNDPROC),
                             cast(LONG_PTR)m_oldWndProc);
        }
        
        // ウィンドウハンドルをDWndオブジェクトから切り離し
        RemoveProp(m_hWnd, TEXT("DWnd"c));
        
        // ウィンドウメッセージを発行
        PostMessage(hWnd, WM_DETACH, 0, 0);
        
        return true;
    }
    
    // ウィンドウプロシージャ
    LRESULT WndProc(HWND hWnd, uint uMsg, WPARAM wp, LPARAM lp)
    {
        if (m_oldWndProc) /// サブクラス化している場合
        {
            return CallWindowProc(m_oldWndProc, hWnd, uMsg, wp, lp);
        }
        else
        {
            return DefWindowProc(hWnd, uMsg, wp, lp);
        }
    }
    
protected:
    // 適切なウィンドウプロシージャを呼び出す
    extern(Windows)
    final static LRESULT WndMapProc(HWND hWnd, uint uMsg, WPARAM wp, LPARAM lp)
    {
        // ウィンドウのプロパティリストからDWndへのポインタの取得を試みる
        DWnd TargetWnd = cast(DWnd)GetProp(hWnd, TEXT("DWnd"c));
        
        if (!TargetWnd) /// 登録がまだだったら
        {
            // ウィンドウが出来たときに
            if ( (uMsg == WM_CREATE) || (uMsg == WM_NCCREATE) )
            {
                // CreateWindowExの第12引数から取得して
                TargetWnd = cast(DWnd)((cast(CREATESTRUCT*)lp).lpCreateParams);
                
                // ウィンドウハンドルとDWndオブジェクトを結びつけ
                if (TargetWnd)
                {
                    TargetWnd.Attach(hWnd);
                }
            }
        }
        
        if (TargetWnd) /// 無事取得出来たら
        {
            // メンバ変数に保存
            if (uMsg == WM_MOVE)
            {
                scope RECT rc;
                GetWindowRect(hWnd, &rc);
                
                TargetWnd.m_left = rc.left; /// ウィンドウx座標
                TargetWnd.m_top  = rc.top;  /// ウィンドウy座標
            }
            if (uMsg == WM_SETTEXT)
            {
                TargetWnd.m_text = cast(LPCTSTR)lp; /// テキスト文字列
            }
            if (uMsg == WM_SIZE)
            {
                scope RECT rc;
                GetClientRect(hWnd, &rc);
                
                TargetWnd.m_width  = rc.right - rc.left; /// ウィンドウ幅
                TargetWnd.m_height = rc.bottom - rc.top; /// ウィンドウ高
            }
            
            // 派生クラスのウィンドウプロシージャを呼び出す
            LRESULT lResult = TargetWnd.WndProc(hWnd, uMsg, wp, lp);
            
            // ウィンドウが破棄されたときに
            if (uMsg == WM_DESTROY)
            {
                // ウィンドウハンドルをハンドルマップから切り離し
                TargetWnd.Detach();
            }
            
            return lResult;
        }
        else /// ウィンドウがまだ出来ていなかったら
        {
            return DefWindowProc(hWnd, uMsg, wp, lp);
        }
    }    
    
// Properties
public:
    ATOM    Atom()                 { return m_atom; }
    HWND    hWnd()                 { return m_hWnd; }
    HWND    hParentWnd()           { return m_hParentWnd; }
    WNDPROC OldWndProc()           { return m_oldWndProc; }
    bool    IsDialog()             { return m_isDialog; }
    int     Left()                 { return m_left; }
    int     Top()                  { return m_top; }
    int     Width()                { return m_width; }
    int     Height()               { return m_height; }
    LPCTSTR Text()                 { return m_text; }
    void    Text(LPCTSTR text)     { return SetWindowText(m_hWnd, text); }
    HFONT   Font()                 { return cast(HFONT)SendMessage(m_hWnd, WM_GETFONT, cast(WPARAM)0, cast(LPARAM)0); }
    void    Font(HFONT hFont)      { SendMessage(m_hWnd, WM_SETFONT, cast(WPARAM)hFont, cast(LPARAM)true); }
    uint    Style()                { return m_style; }
    void    Style(DWORD style)     { m_style = style; SetWindowLongPtr(m_hWnd, GWL_STYLE, cast(LONG_PTR)&m_style); }
    uint    StyleEx()              { return m_styleEx; }
    void    StyleEx(DWORD styleEx) { m_styleEx = styleEx; SetWindowLongPtr(m_hWnd, GWL_EXSTYLE, cast(LONG_PTR)&m_styleEx); }
    bool    IsVisible()            { return cast(bool)IsWindowVisible(m_hWnd); }
}

//------------------------------------------------------------------------------------------//

// 基本ウィンドウクラス
class DWnd : DWndBase
{
// Methods
public:
    // コンストラクタ
    this()
    {
        // ウィンドウクラスを登録
        if (m_atom == 0)
        {
            WNDCLASSEX wc;
            wc.cbSize        = WNDCLASSEX.sizeof;
            wc.style         = 0;
            wc.lpfnWndProc   = cast(WNDPROC)&WndMapProc;
            wc.cbClsExtra    = 0;
            wc.cbWndExtra    = 0;
            wc.hInstance     = cast(HINSTANCE)GetModuleHandle(null);
            wc.hIcon         = null;
            wc.hCursor       = LoadCursor(null, IDC_ARROW);
            wc.hbrBackground = null;
            wc.lpszMenuName  = null;
            wc.lpszClassName = TEXT("DWnd"c);
            wc.hIconSm       = null;
            
            m_atom = RegisterClassEx(&wc);
            if (m_atom == 0)
            {
                throw new Exception("Failed to register the windows class.");
            }
        }
        
        // メンバ変数を初期化
        m_left   = CW_USEDEFAULT;
        m_top    = CW_USEDEFAULT;
        m_width  = CW_USEDEFAULT;
        m_height = CW_USEDEFAULT;
    }
    
    // コンストラクタ
    this(DWORD style = 0, DWORD styleEx = 0, LPCTSTR text = null)
    {
        this();
        
        // メンバ変数を初期化
        m_style   = style;
        m_styleEx = styleEx;
        m_text    = text;
    }
    
    // デストラクタ
    ~this()
    {
        Destroy();
    }
    
    // ウィンドウプロシージャ
    LRESULT WndProc(HWND hWnd, uint uMsg, WPARAM wp, LPARAM lp)
    {
        switch (uMsg)
        {
        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;
        default:
            return super.WndProc(hWnd, uMsg, wp, lp);
        }
    }
    
    // ウィンドウを再描画する
    bool InvalidateRect(RECT* lpRect = null, bool bErace = false)
    {
        return cast(bool).InvalidateRect(m_hWnd, lpRect, bErace);
    }
    
    // ウィンドウを移動する
    bool Move(int x, int y)
    {
        return cast(bool)SetWindowPos(m_hWnd, null, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
    }
    
    // ウィンドウの移動とウィンドウサイズの変更を行う
    bool Move(int x, int y, int w, int h)
    {
        return cast(bool)SetWindowPos(m_hWnd, null, x, y, w, h, SWP_NOZORDER | SWP_NOACTIVATE);
    }
    
    // クライアントサイズを変更する
    bool Resize(int w, int h)
    {
        RECT rcWindow, rcClient;
        GetWindowRect(m_hWnd, &rcWindow);
        GetClientRect(m_hWnd, &rcClient);
        w = w + (rcWindow.right - rcWindow.left) - (rcClient.right - rcClient.left);
        h = h + (rcWindow.bottom - rcWindow.top) - (rcClient.bottom - rcClient.top);
        
        return cast(bool)SetWindowPos(m_hWnd, null, 0, 0, w, h, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
    }
    
    // ウィンドウの可視状態を変える
    bool Hide()
    {
        return cast(bool)ShowWindow(m_hWnd, SW_HIDE);
    }
    
    // ウィンドウの可視状態を変える
    bool Show(int nCmdShow = SW_SHOW)
    {
        return cast(bool)ShowWindow(m_hWnd, nCmdShow);
    }
    
}

使用例:

module mywnd;

import dwnd;

class MyWnd : DWnd
{
    // コンストラクタ
    this()
    {
        // ウィンドウクラスを登録
        if (m_atom == 0)
        {
            WNDCLASSEX wc;
            wc.cbSize        = WNDCLASSEX.sizeof;
            wc.style         = 0;
            wc.lpfnWndProc   = cast(WNDPROC)&WndMapProc;
            wc.cbClsExtra    = 0;
            wc.cbWndExtra    = 0;
            wc.hInstance     = cast(HINSTANCE)GetModuleHandle(null);
            wc.hIcon         = null;
            wc.hCursor       = LoadCursor(null, IDC_ARROW);
            wc.hbrBackground = cast(HBRUSH)GetStockObject(WHITE_BRUSH);
            wc.lpszMenuName  = null;
            wc.lpszClassName = TEXT("MyWnd"c);
            wc.hIconSm       = null;
            
            m_atom = RegisterClassEx(&wc);
            if (m_atom == 0)
            {
                throw new Exception("Failed to register the windows class.");
            }
        }
        
        // メンバ変数を初期化
        m_left    = 100;
        m_top     = 100;
        m_width   = 640;
        m_height  = 480;
        m_style   = WS_OVERLAPPEDWINDOW;
        m_styleEx = 0;
        m_text    = TEXT("My Window Class"c);
    }
    
    // ウィンドウプロシージャ
    override LRESULT WndProc(HWND hWnd, uint uMsg, WPARAM wp, LPARAM lp)
    {
        switch (uMsg)
        {
        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;
        default:
            return super.WndProc(hWnd, uMsg, wp, lp);
        }
    }
    
}
import mywnd;

auto wnd = new MyWnd();
wnd.Create();
wnd.Show();
  • DWndBaseは基底クラスです。こいつから直接派生クラスを作ってもいいけど、

 いくつか便利機能を埋め込んだ、DWndクラスから派生させた方が使いやすいと思います。

  • 派生させて使うときは、コンストラクタでRegisterClass(Ex)でウィンドウクラスを登録し、

 overrideしたWndProc()を実装してください。

  • オブジェクトの生成とウィンドウの生成が分離しているのは、

 ウィンドウクラスを派生クラスごとに登録する仕様にしたからです。
 気に入らない方は改造しちゃってください。

  • Bindings for the Windows API を使ってます。

 持っていない方は
 http://www.dsource.org/projects/bindings/wiki/WindowsApi
 からダウンロードして使ってください。

  • ライセンスはよくわからないので暫定的にNYSLで。
  • ご質問・ご指摘等 歓迎します

[D言語][Regnessem][無能くん] iniファイルの読み書き

 さて、そろそろファイルの読み書きをする部分を作ります。

module inifile;

private import std.path;
private import std.string;

(略)
//---------------------------------------------------------------------------//
// グローバル変数の宣言
public:
char[256] modulepath; // char[]だとなんかエラーになる
char[] foldername;	   // iniファイルのあるフォルダ(フルパス)
char[] filename;     // iniファイル名(フルパス)

//---------------------------------------------------------------------------//
public:
// iniファイルの読み込み
int Load()
{
  GetModuleFileName(g_hInstance, modulepath, 1024); // 256 * 8 = 1024

  foldername = getDirName(modulepath)~r"\"~MODULENAME~r"\";
  filename   = foldername~MODULENAME~".ini";

  isMunokunOn = (bit)GetPrivateProfileInt("General", "isMunokunOn", false, filename);

  return 1;
}

// iniファイルの書き出し
int Save()
{
  CreateDirectory(foldername, NULL);

  HANDLE hFile = CreateFile(filename, GENERIC_WRITE, 0, NULL,
                            CREATE_NEW,  FILE_ATTRIBUTE_NORMAL, NULL);
  if(hFile)
    CloseHandle(hFile);

  hFile = NULL;

  WritePrivateProfileString("General", "isMunokunOn", toString(isMunokunOn), filename);

  return 1;
}
(以下略)

 最初標準ライブラリだけ使ってやろうとしたんだけど、結局WinAPI使っちゃった
 こう見るとまだまだ標準ライブラリって充実してないのかな。
 拡張子を付け替える関数はあったけど、拡張子を取り除く関数がない…
 . が付いたままやっちゅうねん。


 ちなみにiniファイルを開く部分は最初、std.stream を使って書いてたのね。

  File inifile = new File(filename);


class File : Stream
ファイルシステムのストリームを扱う派生クラスです。

this()
this(char filename)
this(char
filename, FileMode mode)
1:ファイルを開かず、2:読み書き両用でファイルを開いて、 3:モードを明示指定してファイルを開いて、ストリームを作成します。 mode は、FileMode.In (ファイルを読み込めることを示す) と FileMode.Out (ファイルへ書き込めることを示す) の組み合わせです。
ファイルが存在しなかった場合は、ファイルが作られます。

 ウソつくなよ。作られないよ? きっちり「ファイルが見つかりませんでした」
 ってエラーになるよ。かといって

  File inifile = new File;
  try
  {
    inifile.open(filename);
  }

 はダメだし、

  File inifile = new File;
  try
  {
    inifile.create(filename);
  }

 ってすると、ファイルがあった場合に前のが消されちゃう。(;_;)
 使い方がよくわからん…



 さて、辞書ファイルの方はフォルダ内のファイルを列挙するところまで出来てるので、
 次は中身の読み込みです。はあ。

[D言語][Regnessem][無能くん] 文字列の検索

 やっぱり正規表現はムリでした。(x_x)
 wchar[]型は使えないみたい。対応してくれないかなあ…
 find() もムリだったので、Phobosのランタイムライブラリを使うのは
 諦める。そこで wcsstr() を使うことにした。
 と言っても wchar.h がなかったので

extern (C) wchar* wcsstr(wchar*, wchar*);

 を書き加えないとコンパイルが通らない。うぐぅ

(略)
  wchar[] msgBody;
  PMessageInfo pMI = (PMessageInfo)lParam;

  if ( !wcsstr(pMI.lpBody, toWCSz("こんにち")) )
    return 0;

  msgBody.length = lstrlenW(toWCSz("こんにちは!"));
  lstrcpyW(msgBody, toWCSz("こんにちは!"));
(以下略)

 これでメッセージに「こんにちは」か「こんにちわ」
 が含まれていれば「こんにちは!」と返事を返すことが出来るようになる。
 「こんにちでは…」とか言われても返すけどね。( ´,_ゝ`)
 てメッセでそんな硬い文章喋るひといないか。


 うーん、でも正規表現使えないと苦しいなあ。
 一旦 toMBS() でエンコードしてから比較して toWCSz() で戻す…
 これぢゃ外国語扱えないぢゃん。
 う゛ーん。なんか wchar型 が冷遇されてるなあ。



 ちなみに正規表現でこんなコードを書くと100%落ちます。

RegExp reg = new RegExp("*","");

 参考にならねえ… おいらこんなのばっかり見つけるの得意ですな。。。

[D言語][Regnessem] GetInfoサービスのラップ

 無能くんは相手や状況によって辞書ファイルを自動的に切り替えることを
 目標に開発されてるので、コネクションやセッションの情報を取得しないといけません。
 でこんなの書いてみた。

module session;
(略)
public:
// System/Session/GetInfoサービスをラップする関数
int GetInfo(HNsmSession hSession, int nInfoKey, char[] string)
{
  if ( !hGetInfo )
    hGetInfo = GetService(NMS_SYSTEM_SESSION_GETINFO);
	
  if ( hGetInfo )
  {
    TNsmInfo nsmInfo;
    nsmInfo.nType       = NMIT_STRING;
    nsmInfo.lpBuffer    = (LPBYTE)string;
    nsmInfo.nBufferSize = DEFAULT_BUFFERSIZE;
		
    TNsmSessionInfo sessionInfo;
    sessionInfo.cbSize   = sessionInfo.size;
    sessionInfo.nInfoKey = nInfoKey;
    sessionInfo.lpInfo   = &nsmInfo;
		
    return (int)CallService(hGetInfo, (WPARAM)hSession, (LPARAM)&sessionInfo);
            // 正常終了
  }
	
  return 0; // 異常終了
}

int GetInfo(HNsmSession hSession, int nInfoKey, wchar[] wstring)
{
  if ( !hGetInfo )
    hGetInfo = GetService(NMS_SYSTEM_SESSION_GETINFO);
	
  if ( hGetInfo )
  {
    TNsmInfo nsmInfo;
    nsmInfo.nType       = NMIT_WIDESTRING;
    nsmInfo.lpBuffer    = (LPBYTE)wstring;
    nsmInfo.nBufferSize = DEFAULT_BUFFERSIZE;
		
    TNsmSessionInfo sessionInfo;
    sessionInfo.cbSize   = sessionInfo.size;
    sessionInfo.nInfoKey = nInfoKey;
    sessionInfo.lpInfo   = &nsmInfo;
		
    return (int)CallService(hGetInfo, (WPARAM)hSession, (LPARAM)&sessionInfo);
            // 正常終了
  }
	
  return 0; // 異常終了
}

int GetInfo(HNsmSession hSession, int nInfoKey, out int integer)
{
  if ( !hGetInfo )
    hGetInfo = GetService(NMS_SYSTEM_SESSION_GETINFO);
	
  if ( hGetInfo )
  {
    TNsmInfo nsmInfo;
    nsmInfo.nType       = NMIT_INTEGER;
    nsmInfo.lpBuffer    = (LPBYTE)&integer;
    nsmInfo.nBufferSize = integer.size;
		
    TNsmSessionInfo sessionInfo;
    sessionInfo.cbSize   = sessionInfo.size;
    sessionInfo.nInfoKey = nInfoKey;
    sessionInfo.lpInfo   = &nsmInfo;
		
    return (int)CallService(hGetInfo, (WPARAM)hSession, (LPARAM)&sessionInfo);
            // 正常終了
  }
	
  return 0; // 異常終了
}

(以下略)

 オーバーロードの使い方ってこれでいいんだろうか…(^^;
 これで、セッションのプロトコル名を取得するときは

char[64] protocol; // 64は てきとう
session.GetInfo(hSession, NMSI_PROTOCOL, protocol);

 コネクションハンドルを取得するときは

int hConnection;
session.GetInfo(hSession, NMSI_CONNECTION, hConnection);

 でいけます。intのときはout修飾子をつけて、文字列でもコネクションハンドルでも
 ポインタを意識せずに同じ書き方で取得できるようにしてみた。
 でも本来HNsmConnection型をしているはずのものがint型なのが気に入らない。
 最初は

int GetInfo(HNsmSession hSession, int nInfoKey, out HNsmConnection hConnection)

 も作ってたんだけど、
 「char* と void* を区別できません」ってコンパイルエラーが出て(そりゃそうだ)、
 プロトコル名の取得に支障が出ちゃいました。
 char[]は暗黙にchar*にキャストされます… でしたね。
 ただ nBufferSize = integer.size としてるから、
 void* が64bitになったときに通用しなくなるかもね。うひょ。



 いや、おいらもちゃんとここは読んでるんですよ。
 おいらよりよっぽどよいラップの仕方をしていると思います…
 もっとDelphiわかるようにならんとダメだね。。。


 ちなみにconnection.GetInfo()もおんなじノリで作ってみた。
 ソースはほぼ同じ。

[D言語][Regnessem][無能くん] 受け取ったメッセージのコピー

 やっと文字列をコピーできました。3日もかかったよ orz

extern (Windows)
int OnReceiveMessage(WPARAM wParam, LPARAM lParam)
{
  if ( !isMunokunOn )
    return 0;
	
  wchar[] msgBody;
  PMessageInfo pMI = (PMessageInfo)lParam;
	
  msgBody.length = lstrlenW(pMI.lpBody);
  // ↑これを忘れるとmsgBody.length = 0 のままなのでコピー不能!
  lstrcpyW(msgBody, pMI.lpBody);
	
  TTextAttributeInfo txtAttInfo;
  txtAttInfo.cbSize     = txtAttInfo.size;
  txtAttInfo.lpFontName = toWCSz(r"MS UI Gothic");
  txtAttInfo.nCharSet   = pMI.lpTextAttribute.nCharSet;
  txtAttInfo.nFontSize  = 10;
  txtAttInfo.nFontColor = 0x000000;
  txtAttInfo.nBgColor   = 0xFFFFFF;
  txtAttInfo.nStyles    = 0;

  TMessageInfo msgInfo;
  msgInfo.cbSize          = msgInfo.size;
  msgInfo.lpFrom          = pMI.lpFrom;
  msgInfo.lpBody          = msgBody;
  msgInfo.lpTextAttribute = &txtAttInfo;
  msgInfo.nFlags          = 0;
	
  CallService(GetService(NMS_SYSTEM_SESSION_SENDMESSAGE), wParam, (LPARAM)&msgInfo);
	
  return 1;
}

 見たまんまです。コメントの付いてる行を書き忘れたために
 いつまでたってもコピーできませんでした。まぬけ…


 これもまだオウム返しするだけのプログラムです。
 msgBodyにコピーできたので、これを使って文字列の検索を…
 と思ったら、find()もstrstr()もchar型しか使えないぢゃん。
 あちゃー、std.regexpもそうだよ。wchar
版はないの?(;_;)
 うむぅ、regexp.dを読むとtchar[]型になってますな。ええっと、いけるのかなあ…

[研究] いのち

 動物室にてかえるちゃんの干物がハケーンされる。
 おそらく水替えの時に逃げ出して、誰にも気づかれず
 そのままになっていたんだろう。あわれ。


 ウチの研究室は常時20〜50匹のウシガエルを飼ってるけど、
 こんなことははぢめて。
 窓は全開だったけど、物陰に隠れる性質があるから
 部屋から抜け出せなかったんだろうね。
 まあ、仮に窓からダイブしたとしても6階だけど…


 ウチのは実験室で養殖したやつぢゃなくて、
 名人の方に拉致ってきてもらった天然物なのね。
 だからかそれぞれに個性があって、
 エサを大人しく食べるやつもいれば、
 近づくだけで耳をつんざかんばかりの叫び声を上げるやつもいるんです。
 こいつら可愛くないなあ、と思って毎日世話してきた*1けど、
 こういう死に方されるとねえ。


 ウチは網膜・視細胞が研究材料で、
 故にかえるちゃんの眼しか使いません。
 ぢつは動物室には失明してるやつもいるんだけど、
 だからといって「要らない」で殺処分する気になれなくて、
 もう3ヶ月以上飼ってるんだよね。
 (おそろしく元気なので、野に放っても生きていけそうな感じだけど。)
 その傍らで死に急ぐやつもいて、
 でもほっといても順番でお迎えが来る。


 動物実験講習で はぢめて知ったんだけど、
 両生類は実験動物として認識されていないんです。
 もっぱら哺乳類、鳥類、爬虫類のことを言うんですね。
 差別だなあと思うんだけど。
 動物アイゴー団体の人達って真剣に動物愛護考えてないなあ、
 と思い知った瞬間でした。


 ま、こんなこと言ってても研究進まないんだけどね。( ̄ー ̄)
 暗い話おわり。



*1:こう思ってるのはおいらだけみたいで、大概のひとは「気持ち悪い」だそうです。。。