HWNDを必要としない入力コントールをDirect2Dで作成しました。C++で作成されるアプリはやがてこの方式へ移行すると思われます。
従来、Windowsコントロール(button,textbox,listbox,...)は以下のようにParent Windowのchild windowとして作成しなければ、なりませんでした。


// hParentWnd: parent window handle
HWND hBtnWnd = ::CreateWindow( L"button",L"btn1", WS_CHILD|WS_VISIBLE, 0,0,100,30,hParentWnd,NULL,hInst,0 );

ボタンのHWNDが作成されます。これを以下のようにします。


// parent : D2DWindow 唯一HWNDをもつWindowクラス
// cs : D2DControls 親コントロール
D2DButton* btn = new D2DButton();
btn->CreateWindow( parent, cs, rc, VISIBLE|BORDER,L"btn1" );
ボタンのHWNDは作成されません。ボタンは親コントロール同じWM_PAINTで描画します。これら一連の動作はD2DButton::WndProcに記述します。

LRESULT D2DControls:WndProc(D2DWindow* d,UINT messsage, WPARAM wParam, LPARAM lParam )
{
// 親Controls
switch( message )
{
case WM_PAINT:
for( auto& it : constrols_ )
it->WndProc( d, WM_PAINT,wParam,lParam ); // buttonのWndProcへWM_PAINTを投げる
break;
....
}
return 0;
}

LRESULT D2DButton::WndProc(D2DWindow* d,UINT messsage, WPARAM wParam, LPARAM lParam )
{
switch( message )
{
case WM_PAINT:
// ボタンの絵
break;
case WM_LBUTTONDOWN:
// クリックした場合
break;
...
}
return 0;
}

この方式の利点は、簡単にコントロール類を入れ子で多重化ができることです。例えばリストボックスの中にリストボックスをいれるとか。
XP時代にこれに挑戦しても、できないわけではありませんが動作がぎこちなく、またIMEは勝手に変換Windowをかぶせてきたりして意味あるものになりませんでした。
一方現在は、Direct2Dでは描画がアニメが作れるほど(60fps)高速化され、IMEはTFSとなり状況が大きく変化しました。対象がWindowsだけと限らなくなったという点もあります。

ソース一式はここです。

なるべく素直にプログラムできるようにまた、ありがちなC++の独りよがりなクラスにならないように、従来のプログラミングをなるべく継承して実装しました。
とは言っても、クロージャーやstd::functionはのような新しい?機能は時々利用してます。
また、面食らわないように、必要最小限の構成にソースを整理してあります。
しかし、拡張に必要な仕組みはすべてはいっています。

VisualStudio2013でビルドできます。

Direct2Dの本当の意味はこれに尽きるはずです。

前のBinaryクラスで簡単なサンプルを作成。PNGファイルを暗号->複合。
非常にあっさりとしたプログラムになる。

int _tmain(int argc, _TCHAR* argv[])
{
_tsetlocale ( LC_ALL, L"" );
_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_DELAY_FREE_MEM_DF|_CRTDBG_LEAK_CHECK_DF);

// AES256でPNGファイルを暗号化
{
Binary png, dst;
BinaryRead( L"E:\\work\\sample.PNG", png );

std::wstring pwd = L"東京都特許許可局"; // パスワード:実在しない団体

Binary bipwd = utf8(pwd); // 漢字が含まれるのでutf8
Binary pwd2 = hash(bipwd,2); // sha256化(32byte)

std::wstring temp = base64(pwd2);
std::wcout << L"pasword is '" << temp << L"'\n"; // 表示してみる

aes256( true, pwd2, png, dst ); // 暗号化 png->dst

BinaryWrite( L"E:\\work\\enc.bin", dst ); // 書き込み
}

// 暗号化されたファイルを複合化
{
std::wstring pwd = L"東京都特許許可局";

Binary png, dst;
BinaryRead( L"E:\\work\\enc.bin", dst );

Binary bipwd = utf8(pwd);
bipwd = hash(bipwd,2);

aes256( false, bipwd, dst, png ); // 複合化 dst->png

BinaryWrite( L"E:\\work\\sample2.PNG", png ); // 書き込み
}


{
Binary png1, png2;
BinaryRead( L"E:\\work\\sample.PNG", png1 ); // 元のファイル
BinaryRead( L"E:\\work\\sample2.PNG", png2 ); //変換されたファイル

std::wstring s1 = hex(hash(png1,2));
std::wstring s2 = hex(hash(png2,2));

_ASSERT( s1 == s2 ); // 同じならtrueになるはず

}

std::wcout << L"success";

return 0;
}


前のBinaryクラスを使用して文字列(unicode)とバイナリデータ(ansi文字列を含む)のややこしいやり取りを極限まで簡素化してみた。



// utf8とunicodeの変換
Binary utf8( std::wstring& s );
std::wstring utf8( const Binary& b );

// バイナリーデータのbase64変換
Binary base64( std::wstring& s );
std::wstring base64( const Binary& b );

// バイナリのハッシュ関数, typ is 0:md5, 1:sha1, 2:sha2
Binary hash( const Binary& b, int typ );

// ハッシュ関数結果の文字列化
std::wstring hex( const Binary& b );

// 共通キーによる暗号、復号
bool aes256( bool IsEncrypt, const Binary& password, const Binary& src, Binary& dst );

// ファイルへ書き出し、読み込み
bool BinaryRead( LPCWSTR filename, Binary& b );
bool BinaryWrite( LPCWSTR filename, Binary& b );




Binary utf8( std::wstring& s )
{
int cchMultiByte = ::WideCharToMultiByte(CP_UTF8, 0, s.c_str(),s.length(), NULL, 0, NULL, NULL);
Binary r(0,cchMultiByte);
int result = ::WideCharToMultiByte(CP_UTF8, 0, s.c_str(), s.length(), (LPSTR)r.get(), cchMultiByte, NULL, NULL);
_ASSERT( result == cchMultiByte );

return r;
}
std::wstring utf8( const Binary& b )
{
std::wstring r;
int charcnt = ::MultiByteToWideChar(CP_UTF8, 0, (LPSTR)b.get(), b.length(), NULL, 0);
r.resize( charcnt );
int result = ::MultiByteToWideChar(CP_UTF8, 0, (LPSTR)b.get(), b.length(), (LPWSTR)r.c_str(), charcnt);
_ASSERT( result == charcnt );
return r;
}

Binary base64( std::wstring& s )
{
DWORD len;
int srclen = s.length();

BOOL bl = CryptStringToBinary(s.c_str(), srclen,CRYPT_STRING_BASE64, NULL, &len, NULL, NULL);
_ASSERT(bl);
Binary ret(0,len);
bl = CryptStringToBinary(s.c_str(), srclen,CRYPT_STRING_BASE64, ret.get(), &len, NULL, NULL);

_ASSERT(bl);

return ret;
}
std::wstring base64( const Binary& b )
{
std::wstring r;

DWORD len;

BOOL bl = CryptBinaryToString((const BYTE*)b.get(), b.length(),CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, NULL, &len );
_ASSERT(bl);
r.resize(len);
bl = CryptBinaryToString((const BYTE*)b.get(), b.length(),CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, (LPWSTR)r.c_str(), &len );
_ASSERT(bl);
return r;
}

#pragma comment(lib, "crypt32.lib")


Binary hash( const Binary& b, int typ )
{
Binary ret;
HCRYPTPROV hprov;

ALG_ID hash_type[] = { CALG_MD5,CALG_SHA1,CALG_SHA_256 };
DWORD len[] = { 16,20,32 };

_ASSERT(0 < b.length());
_ASSERT(typ==0|| typ==1|| typ==2);

if ( CryptAcquireContext(&hprov, NULL, MS_ENH_RSA_AES_PROV_W, PROV_RSA_AES, CRYPT_VERIFYCONTEXT ))
{
HCRYPTHASH hHash;
if ( CryptCreateHash(hprov, hash_type[typ],0,0,&hHash))
{
if ( CryptHashData(hHash,(BYTE*)b.get(), b.length(),0))
{
DWORD dwHashLen=len[typ];
Binary r(0,dwHashLen);
if ( CryptGetHashParam(hHash,HP_HASHVAL,r.get(),&dwHashLen,0))
{
ret = r;
}
}
CryptDestroyHash(hHash);
}
CryptReleaseContext(hprov, 0);
}
return ret;
}


std::wstring hex( const Binary& b )
{
_ASSERT(b.length());

DWORD len=0;
CryptBinaryToString((const BYTE*)b.get(), b.length(),CRYPT_STRING_HEXRAW|CRYPT_STRING_NOCRLF, NULL, &len );
std::wstring r1;
r1.resize(len);
CryptBinaryToString((const BYTE*)b.get(), b.length(),CRYPT_STRING_HEXRAW|CRYPT_STRING_NOCRLF, (LPWSTR)r1.c_str(), &len );
return r1;
}

bool aes256( bool IsEncrypt, const Binary& pwd, const Binary& src, Binary& dst )
{
bool ret = false;
HCRYPTPROV hProv;

_ASSERT( pwd.length() == 32 ); // 32:AES256 , 16:AES128

struct AesKeyBlob
{
BLOBHEADER hdr;
DWORD keySize;
BYTE bytes[32];
};

AesKeyBlob blob;
memset( &blob, 0, sizeof(blob));
blob.hdr.bType = PLAINTEXTKEYBLOB;
blob.hdr.bVersion = CUR_BLOB_VERSION;
blob.hdr.aiKeyAlg = CALG_AES_256;
blob.keySize = pwd.length();
memcpy(blob.bytes, pwd.get(), pwd.length());


if ( CryptAcquireContext(&hProv, NULL, MS_ENH_RSA_AES_PROV_W, PROV_RSA_AES, CRYPT_VERIFYCONTEXT ))
{
HCRYPTHASH hHash;
HCRYPTKEY hKey;

if ( CryptCreateHash(hProv,CALG_SHA_256,0,0,&hHash ))
{
if (CryptHashData(hHash, pwd.get(),pwd.length(),0 ))
{
if (CryptImportKey(hProv, (BYTE*)&blob, sizeof(AesKeyBlob), NULL, 0, &hKey))
{
DWORD mode = CRYPT_MODE_CBC; // 暗号モード
DWORD padding_mode = PKCS5_PADDING; // パディングモード

BOOL bl = CryptSetKeyParam(hKey, KP_PADDING, (BYTE*)&padding_mode, 0);
_ASSERT(bl);
bl = CryptSetKeyParam(hKey, KP_MODE, (BYTE*)&mode, 0);
_ASSERT(bl);


// IV
BYTE iv[ENCRYPT_BLOCK_SIZE] = {0};
memcpy( iv, pwd.get(), min(pwd.length(), ENCRYPT_BLOCK_SIZE) );
bl = CryptSetKeyParam( hKey, KP_IV, iv, 0 );
_ASSERT(bl);

DWORD dwLen = src.length();

if ( IsEncrypt )
{
DWORD dwNewLen = ((dwLen-1)/ENCRYPT_BLOCK_SIZE + 1) * ENCRYPT_BLOCK_SIZE;
Binary xdst(0,dwNewLen);
memcpy( xdst.get(), src.get(), dwLen );
if (CryptEncrypt(hKey,0,TRUE,0, xdst.get() ,&dwLen, dwNewLen ))
{
dst = xdst;
ret = true;
}
}
else
{
Binary x = src.clone();
if (CryptDecrypt(hKey,0,TRUE,0, x.get(),&dwLen))
{
dst = x.shrink(dwLen);
ret = true;
}
}
CryptDestroyKey(hKey);
}
CryptDestroyHash(hHash);
}
CryptReleaseContext(hProv, 0);
}
}
return ret;
}



bool BinaryRead( LPCWSTR filename, Binary& b )
{
bool ret = false;
HANDLE pf = CreateFile( filename, GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL );
if ( INVALID_HANDLE_VALUE != pf )
{
DWORD len = GetFileSize( pf, NULL );
DWORD lx=0;

Binary x(0,len);
if (::ReadFile( pf, x.get(), len, &lx,0) && lx > 0 )
{
b = x;
ret = true;
}
CloseHandle(pf);
}
return ret;
}

bool BinaryWrite( LPCWSTR filename, const Binary& b )
{
bool ret = false;
HANDLE pf = CreateFile( filename, GENERIC_WRITE,0,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL );
if ( INVALID_HANDLE_VALUE != pf )
{
DWORD len = b.length();
DWORD lx=0;

if (::WriteFile( pf, b.get(), len, &lx,0) && lx > 0 )
{
ret = true;
}
CloseHandle(pf);
}
return ret;
}


バイナリデータを扱う上で必ず必要になる情報はバイナリデータの長さである。
これらをまとめて、かつ参照カウンタで寿命を管理する簡単便利なクラスを作ってみた。
参照カウンタの加減算にロックはかけていないので、スレッドには未対応である。



////////////////////////////////////////////////////////////////////////////////////////////////

// bin_ = 参照カウンタ(DWORD)+長さ(DWORD)+バイナリデータ(bi_length byte)

class Binary
{
public :
Binary():bin_(0){}
Binary( const BYTE* bi, DWORD bi_length )
{
DWORD alclen = 4 + bi_length;
bin_ = new BYTE[ alclen+sizeof(DWORD)*2];

DWORD cnt = 1;
memcpy( bin_, &cnt, sizeof(DWORD) );
memcpy( bin_+sizeof(DWORD), &bi_length, sizeof(DWORD) );

if ( bi )
memcpy( bin_+sizeof(DWORD)*2, bi, bi_length );
else
memset( bin_+sizeof(DWORD)*2 , 0, bi_length );// fill zero.
}
Binary( const Binary& s ):bin_(0)
{
this->operator=(s);
}

~Binary(){ clear(); }

// バイナリデータ
BYTE* get() const
{
return bin_ + sizeof(DWORD)*2;
}
// 長さ
DWORD length() const{ return *(DWORD*)(bin_ + sizeof(DWORD)); }

void clear()
{
if ( bin_ )
{
DWORD& cnt = *(DWORD*)bin_;
cnt--;

if ( cnt == 0 )
delete [] bin_;

bin_ = NULL;
}
}

Binary& operator=(const Binary& src)
{
if ( this == &src ) return *this;

clear();

DWORD& cnt = *(DWORD*)src.bin_;
cnt++;

bin_ = src.bin_;

return *this;
}

Binary clone() const
{
return Binary(get(), len());
}

Binary cut(DWORD pos, DWORD xlen) const
{
_ASSERT( pos + xlen <= len());
_ASSERT( 0 < xlen );
return Binary(get()+pos, xlen);
}
Binary& shrink(DWORD xlen )
{
_ASSERT( xlen < length());
memcpy( bin_+sizeof(DWORD), &xlen, sizeof(DWORD) );
return *this;
}
bool isnull() const { return (bin_ == nullptr); }
private :
BYTE* bin_;
};


HWNDはウインドウハンドルだが、OOPではちょっと邪魔な存在である。
子ウインドウは親ウインドウのWM_PAINTとは別系統でシステムが描画するからである。
コントロールの描画をWM_PAINTだけで統一できれば、色々なことができるはずだが、20年以上前のメモリ4Mbyte、C++もない時代の仕様なので仕方ない。
そこで、HWNDを外枠のMainFrameだけに限定して、Direct2Dで内部のコントロールをすべて作成してみた。

以下のコントロールをDirect2Dで作成できれば、あとはその変形版で何とかなるはずである。


1、Button コントロール
2、Combobox コントロール
3、Textbox コントロール

ボタンは簡単である。絵を書いてWM_LBUTTONDOWN,WM_LBUTTONUPを処理すればいい。
コンボボックスは少し難しい。作って気づく部分があった。
テキストボックスは、相当難しい。日本語変換をTFSで実装しなければ、HWNDが文字変換で登場してしまう。
しかし、TFSが長年謎の存在で手がつけられなかった。あれAPIとしては失格だと思う。

誤魔化すというより、Window内にDirect2DのWindowシステムを作成!!。

また、画面の高精細化(96DPIから約200DPIへの対応)という別の問題が現在持ち上がっていて(多分ね)、どう対応すべきか悩んでいるはずである。
MSはC#のWPFを推進したいらしいが(それしか方法がないかのように)、WPFは大失敗とみているので個人的にはもう関わりたくない。

理由は、WEBシステムと同じセキュリティ上の理由で、いろんな制限がかかりすぎている。
APIとしては抽象度が高すぎる。動作がもっさりしている。画面がXML化され冗長である。
Silverlight,WindowsPhone,Windows8 と関わりをもったものはみな失敗。

一方、Direct2Dの問題はC++が難解すぎて人材を確保できないという点だと思う。(長年、.NETばかり注力したせいで、、)

そのうちD2DWindowとしてgitで公開するつもりである。


Direct2DでイメージファイルであるPNGを表示する方法。



ID2D1RenderTarget* cxt = ...

CComPtr bmp;
D2DResource::LoadImage( cxt, L"e:\test\images\abc.png", &bmp );
// auto image_size = bmp->GetPixelSize(); イメージの大きさ

//リソースから読み込む場合
//CComPtr bmp;
//D2DResource::LoadImage( cxt, L"png", IDB_PNG1, &bmp );

D2D1_RECT_U rc = ...

cxt->DrawBitmap( bmp, rc );


/////////////////////////////////////////////////////////////////////////////////////////////////////
class D2DResource
{
public :
static bool LoadImage ( ID2D1RenderTarget* target, LPCWSTR filenm, ID2D1Bitmap** bmp );
static bool LoadImage ( ID2D1RenderTarget* target, LPCWSTR resource_section, int resource_id, ID2D1Bitmap** bmp );
static bool LoadImage2( ID2D1RenderTarget* target, IWICImagingFactory* pWICFactory, IWICStream* pStream, ID2D1Bitmap** bmp );
};


bool D2DResource::LoadImage( ID2D1RenderTarget* target, LPCWSTR filenm, ID2D1Bitmap** bmp )
{
HRESULT hr;
CComPtr pStream;
CComPtr pWICFactory;

hr = CoCreateInstance( CLSID_WICImagingFactory, NULL, CLSCTX_ALL, __uuidof(IWICImagingFactory), (void**)&pWICFactory );
if ( hr != S_OK ) return false;
hr = pWICFactory->CreateStream( &pStream );
if ( hr != S_OK ) return false;
hr = pStream->InitializeFromFilename( filenm, GENERIC_READ); // bmp,gif,jpg,png OK
if ( hr != S_OK ) return false;

return LoadImage2( target,pWICFactory, pStream, bmp );
}

bool D2DResource::LoadImage( ID2D1RenderTarget* target, LPCWSTR resource_section, int resource_id, ID2D1Bitmap** bmp )
{
HRESULT hr;
CComPtr pWICFactory;
CComPtr pStream;

hr= CoCreateInstance( CLSID_WICImagingFactory, NULL, CLSCTX_ALL, __uuidof(IWICImagingFactory), (void**)&pWICFactory );
if ( hr != S_OK ) return false;

// load png from .rc
HMODULE hmodule = ::GetModuleHandle(NULL);
HRSRC hsrc = FindResource( hmodule, MAKEINTRESOURCE(resource_id), resource_section ); // resource_section:png
if ( hsrc == NULL ) return false;

DWORD len = SizeofResource(hmodule,hsrc);
HGLOBAL hg = LoadResource( hmodule, hsrc );
byte* d = (byte*)LockResource( hg );

hr = pWICFactory->CreateStream( &pStream );
if ( hr != S_OK ) return false;
hr = pStream->InitializeFromMemory( d, len ); // bmp,gif,jpg,png OK
if ( hr != S_OK ) return false;

return LoadImage2( target,pWICFactory, pStream, bmp );
}


bool D2DResource::LoadImage2( ID2D1RenderTarget* target,IWICImagingFactory* pWICFactory,IWICStream* pStream, ID2D1Bitmap** bmp )
{
_ASSERT( pWICFactory );

HRESULT hr;

CComPtr pDecoder;
CComPtr pSource;
CComPtr pConverter;

hr = pWICFactory->CreateDecoderFromStream( pStream, 0, WICDecodeMetadataCacheOnLoad, &pDecoder ); // jpeg,png:OK, bmp:88982f50のエラーになる, iconもエラー
if ( hr != S_OK ) return false;
hr = pDecoder->GetFrame(0, &pSource);
if ( hr != S_OK ) return false;

// Convert the image format to 32bppPBGRA
// (DXGI_FORMAT_B8G8R8A8_UNORM + D2D1_ALPHA_MODE_PREMULTIPLIED).
hr = pWICFactory->CreateFormatConverter(&pConverter);
if ( hr != S_OK ) return false;
hr = pConverter->Initialize( pSource, GUID_WICPixelFormat32bppPBGRA,WICBitmapDitherTypeNone,NULL,0.f,WICBitmapPaletteTypeMedianCut);
if ( hr != S_OK ) return false;
hr = target->CreateBitmapFromWicBitmap(pConverter,NULL,bmp );

return ( hr == S_OK );
}


下のMainFrameをDirect2Dで今風(Windows8 Desktop)に作成してみました。Caption右上のボタンは手作りです。




// Win32Project2.cpp : アプリケーションのエントリ ポイントを定義します。
//

#include "stdafx.h"
#include "Win32Project2.h" // VSが作成したデフォルトテンプレート名
#include
#include
#include
#include

#define MAX_LOADSTRING 100

// グローバル変数:
HINSTANCE hInst; // 現在のインターフェイス
TCHAR szTitle[MAX_LOADSTRING]; // タイトル バーのテキスト
TCHAR szWindowClass[MAX_LOADSTRING]; // メイン ウィンドウ クラス名

// このコード モジュールに含まれる関数の宣言を転送します:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

#define CAPTION_HEIGHT 30

using namespace D2D1;

#pragma comment(lib,"dwrite.lib")
#pragma comment(lib,"d2d1.lib")

#define CLIENT_SPCCX 6
#define CLIENT_SPCCY 6

HWND hChildWnd;

#define D2RGBA(r,g,b,a) ColorF((r/255.0f), (g/255.0f),(b/255.0f),(a/255.0f) )

class FRectF : public D2D1_RECT_F
{
public :
FRectF(){ left=right=bottom=top=0; }
FRectF( const RECT& rc ){ left = (float)rc.left;top=(float)rc.top;bottom=(float)rc.bottom;right=(float)rc.right;}
FRectF(float l,float t, float r, float b ){ SetRect(l,t,r,b); }

void SetRect(float l,float t, float r, float b )
{
left=l;top=t;right=r;bottom=b;
}
void Offset(float cx, float cy )
{
left += cx; right += cx;
top += cy; bottom += cy;
}
bool PtInRect( POINT pt )
{
return ( left <= pt.x && pt.x <= right && top <= pt.y && pt.y <= bottom );
}
};


struct D2DMainFrame
{
CComPtr wrfactory;
CComPtr factory;
CComPtr textformat;
CComPtr cxt;

CComPtr br[3];
CComPtr black, white;

FRectF btn[3];
UINT btnStat;
LPCWSTR title;

enum COLORS{ MOUSE_FLOAT,CLOSEBTN,ACTIVECAPTION };
};

static D2DMainFrame st;

void D2DInitial(HWND hWnd1)
{
HRESULT hr;
D2D1_FACTORY_OPTIONS options;
options.debugLevel = D2D1_DEBUG_LEVEL_NONE;
hr = D2D1CreateFactory( D2D1_FACTORY_TYPE_SINGLE_THREADED,__uuidof(ID2D1Factory),&options,(void**)&st.factory );
_ASSERT(hr==S_OK);

hr = DWriteCreateFactory( DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast(&st.wrfactory));
_ASSERT(hr==S_OK);

hr = st.wrfactory->CreateTextFormat( L"Arial",0,DWRITE_FONT_WEIGHT_NORMAL,DWRITE_FONT_STYLE_NORMAL,DWRITE_FONT_STRETCH_NORMAL,14,L"",&st.textformat);
_ASSERT(hr==S_OK);

hr = st.factory->CreateHwndRenderTarget(D2D1::RenderTargetProperties(), D2D1::HwndRenderTargetProperties(hWnd1, D2D1::SizeU(1,1), D2D1_PRESENT_OPTIONS_NONE), &st.cxt);
_ASSERT(hr==S_OK);

st.cxt->CreateSolidColorBrush( ColorF(ColorF::Black), &st.black );
st.cxt->CreateSolidColorBrush( ColorF(ColorF::White), &st.white );
st.cxt->CreateSolidColorBrush( D2RGBA(54,101,179,255), &st.br[D2DMainFrame::MOUSE_FLOAT] );
st.cxt->CreateSolidColorBrush( D2RGBA(199,80,80,255), &st.br[D2DMainFrame::CLOSEBTN] );
st.cxt->CreateSolidColorBrush( D2RGBA(144,169,184,255), &st.br[D2DMainFrame::ACTIVECAPTION] );

st.btn[0] = FRectF(0,0,26,20); // MINI BUTTON
st.btn[1] = FRectF(0,0,27,20); // MAX BUTTON
st.btn[2] = FRectF(0,0,45,20); // CLOSE BUTTON

st.btnStat = 0;

st.title = L"D2DMainFrame example";

}


int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPTSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);

// TODO: ここにコードを挿入してください。
MSG msg;
HACCEL hAccelTable;

CoInitialize(0);
_tsetlocale ( 0, _T("japanese") );
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );

// グローバル文字列を初期化しています。
LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadString(hInstance, IDC_WIN32PROJECT2, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);

// アプリケーションの初期化を実行します:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}

hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WIN32PROJECT2));

// メイン メッセージ ループ:
while (GetMessage(&msg, NULL, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}

return (int) msg.wParam;
}
//
// 関数: MyRegisterClass()
//
// 目的: ウィンドウ クラスを登録します。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEX wcex;

wcex.cbSize = sizeof(WNDCLASSEX);

wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WIN32PROJECT2));
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = NULL; // (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

return RegisterClassEx(&wcex);
}

//
// 関数: InitInstance(HINSTANCE, int)
//
// 目的: インスタンス ハンドルを保存して、メイン ウィンドウを作成します。
//
// コメント:
//
// この関数で、グローバル変数でインスタンス ハンドルを保存し、
// メイン プログラム ウィンドウを作成および表示します。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd;

hInst = hInstance; // グローバル変数にインスタンス処理を格納します。

hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW|WS_CLIPCHILDREN,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

if (!hWnd)
{
return FALSE;
}

ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);

return TRUE;
}
//
// 関数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// 目的: メイン ウィンドウのメッセージを処理します。
//
// WM_COMMAND - アプリケーション メニューの処理
// WM_PAINT - メイン ウィンドウの描画
// WM_DESTROY - 中止メッセージを表示して戻る
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;

switch (message)
{
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// 選択されたメニューの解析:
switch (wmId)
{
case IDM_ABOUT:
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_PAINT:
{
hdc = BeginPaint(hWnd, &ps);
st.cxt->BeginDraw();
D2D1_MATRIX_3X2_F mat = Matrix3x2F::Identity();
st.cxt->SetTransform(mat);
st.cxt->Clear(D2RGBA(110,129,141,255));// 枠線

RECT rc;
GetClientRect(hWnd,&rc);

const RECT rcClient = rc;

FRectF rcf(rc);
FRectF rcCaption(rcf);


FRectF rcwaku(rcf);
rcwaku.left++; rcwaku.right--;
rcwaku.top++; rcwaku.bottom--;

st.cxt->FillRectangle( &rcwaku, st.br[D2DMainFrame::ACTIVECAPTION] );


rcCaption = rcwaku;

rcCaption.bottom=rcCaption.top+CAPTION_HEIGHT;
st.cxt->FillRectangle( rcCaption, st.br[D2DMainFrame::ACTIVECAPTION] );

FRectF rctext(1,1,rcwaku.right,CAPTION_HEIGHT);

st.cxt->DrawText( st.title, lstrlen(st.title), st.textformat, &rctext, st.black );


mat._31 = (float)( rcClient.right - (26+27+45)); // X ZeroPoint
mat._32 = 1; // Y ZeroPoint

for( int i = 0; i < 3; i++ )
{
FRectF rc;
st.cxt->SetTransform(mat);

if ( i == 0 )
{
// Draw Minimize button.
rc.SetRect(10,12,17,14);
auto br = ( st.btnStat == 0 ? st.black : st.white );
if ( st.btnStat == 1 )
{
st.cxt->FillRectangle( st.btn[i], st.br[D2DMainFrame::MOUSE_FLOAT] );
st.cxt->FillRectangle( rc, st.white );
}
else
st.cxt->FillRectangle( rc, st.black );
}
else if ( i == 1 )
{
// Draw Maxmize button.
// when floatting
if ( st.btnStat == 2 )
{
FRectF rck = st.btn[i];
rck.left++;rck.right--;
st.cxt->FillRectangle( rck, st.br[D2DMainFrame::MOUSE_FLOAT] );
}
auto br = ( st.btnStat == 2 ? st.white : st.black );
rc.SetRect(9,6,10,14); st.cxt->FillRectangle( rc, br );
rc.SetRect(9,13,19,14); st.cxt->FillRectangle( rc, br );
rc.SetRect(18,6,19,14); st.cxt->FillRectangle( rc, br );
rc.SetRect(9,6,19,8); st.cxt->FillRectangle( rc, br );
}
else if ( i == 2 )
{
// Draw close button.
st.cxt->FillRectangle( st.btn[i], st.br[D2DMainFrame::CLOSEBTN]);
FRectF rc1,rc2;
rc1.SetRect(19,6,21,7);
rc2.SetRect(25,6,27,7);
for( int ii = 0; ii < 7; ii++ )
{
st.cxt->FillRectangle( rc1, st.white );
rc1.Offset(1,1);
st.cxt->FillRectangle( rc2, st.white );
rc2.Offset(-1,1);
}
}

mat._31 += st.btn[i].right;
}
st.cxt->EndDraw();
EndPaint(hWnd, &ps);
}
break;
case WM_SIZE :
{
UINT cx = LOWORD(lParam);
UINT cy = HIWORD(lParam);

st.cxt->Resize( D2D1::SizeU(cx,cy));

::MoveWindow(hChildWnd,CLIENT_SPCCX,CAPTION_HEIGHT,cx-CLIENT_SPCCX*2,cy-CAPTION_HEIGHT-CLIENT_SPCCY,TRUE);

return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;

case WM_CREATE:
{
D2DInitial(hWnd);

RECT rc;
GetClientRect(hWnd,&rc);

// 簡単なchild windowを作成
hChildWnd = CreateWindow( L"EDIT", L"SAMPLE",WS_CHILD|WS_VISIBLE|ES_MULTILINE,CLIENT_SPCCX,CAPTION_HEIGHT,rc.right-CLIENT_SPCCX*2,rc.bottom-CAPTION_HEIGHT,hWnd,NULL,hInst,0);

return DefWindowProc(hWnd, message, wParam, lParam);
}
break;

case WM_NCCALCSIZE:
if ( wParam == FALSE )
{
RECT* rc = (RECT*)lParam;
return 0;
}
else
{
NCCALCSIZE_PARAMS* pm = (NCCALCSIZE_PARAMS*)lParam;

WINDOWPLACEMENT info;
info.length = sizeof(info);
GetWindowPlacement(hWnd, &info);

if ( info.showCmd == SW_SHOWMAXIMIZED )
{
RECT r1;
SystemParametersInfo(SPI_GETWORKAREA,0,&r1,0);
pm->rgrc[0] = r1;


pm->lppos->x=pm->rgrc[0].left;
pm->lppos->y=pm->rgrc[0].top;
pm->lppos->cx=pm->rgrc[0].right;
pm->lppos->cy=pm->rgrc[0].bottom;

return WVR_ALIGNTOP|WVR_ALIGNLEFT|WVR_ALIGNBOTTOM|WVR_ALIGNRIGHT;
}
}
return 0;
break;
case WM_NCHITTEST:
{
RECT rc;
GetClientRect(hWnd,&rc);

POINT pt;
pt.x = LOWORD(lParam);
pt.y = HIWORD(lParam);

ScreenToClient( hWnd, &pt );

POINT ptbtn = pt;

ptbtn.x -= (rc.right - (26+27+45));
ptbtn.y -= 1;
FRectF rcbtn(0,0,(26+27+45),20);
UINT prv = st.btnStat;

UINT nstat = 0;
if ( rcbtn.PtInRect( ptbtn ) )
{
if ( ptbtn.x < 26 )
nstat =1;
else if ( ptbtn.x < (26+27) )
nstat =2;
else
nstat =3;
}

if ( nstat != prv )
{
st.btnStat = nstat;
InvalidateRect(hWnd,NULL,FALSE); // redraw
}

int w = 5;
int width = rc.right;
int height = rc.bottom;
if ( !rcbtn.PtInRect( ptbtn ) )
{
if ( pt.y < w )
{
if ( pt.x < w )
return HTTOPLEFT;
else if ( width - pt.x < w)
return HTTOPRIGHT;
else
return HTTOP;
}
else if ( height-pt.y < w )
{
if ( pt.x < w )
return HTBOTTOMLEFT;
else if ( width - pt.x < w)
return HTBOTTOMRIGHT;
else
return HTBOTTOM;
}
else if ( pt.y < CAPTION_HEIGHT )
return HTCAPTION;
else if ( pt.x < w )
return HTLEFT;
else if ( width - pt.x < w )
return HTRIGHT;
}
return HTCLIENT;
}
break;
case WM_LBUTTONUP:
{
// OnCLicked

if ( st.btnStat == 1 )
ShowWindow( hWnd, SW_MINIMIZE );
else if ( st.btnStat == 2 )
ShowWindow( hWnd, SW_MAXIMIZE );
else if ( st.btnStat == 3 )
DestroyWindow(hWnd);
}
break;
case WM_WINDOWPOSCHANGED:
{
InvalidateRect(hWnd,NULL,FALSE); // redraw
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_ERASEBKGND:
return 1;
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}