CreateDIBSectionを使ってDIBを作り、CreateCompatibleDCを使って描画してみる。
各関数・データ型・定数がwindows-rsのどこにあるかさえわかれば、あとはそこまで難しくはない。
一番厄介なのはwinmain.rs内でmydibのインスタンスであるグローバル変数で、これはstatic + Lazy + Mutexで実現しないといけない。
use windows::Win32::Graphics::Gdi::*; use windows::core::*; use windows::Win32::Foundation::*; // ■ BITMAPINFO ... windows::Win32::Graphics::Gdi::BITMAPINFO // ■ BITMAPINFOHEADER ... windows::Win32::Graphics::Gdi::BITMAPINFOHEADER // ■ BI_RGB ... windows::Win32::Graphics::Gdi::BI_RGB // ■ CreatedHDC ... windows::Win32::Graphics::Gdi::CreatedHDC // ■ CreateDCA ... windows::Win32::Graphics::Gdi::CreateDCA // ■ CreateCompatibleDC ... windows::Win32::Graphics::Gdi::CreateCompatibleDC // ■ CreateDIBSection ... windows::Win32::Graphics::Gdi::CreateDIBSection // ■ DIB_RGB_COLORS ... windows::Win32::Graphics::Gdi::DIB_RGB_COLORS // ■ DeleteDC ... windows::Win32::Graphics::Gdi::DeleteDC // ■ HBITMAP ... windows::Win32::Graphics::Gdi::HBITMAP // ■ HBITMAP ... windows::Win32::Graphics::Gdi::HBITMAP // ■ SelectObject ... windows::Win32::Graphics::Gdi::SelectObject // ■ HANDLE ... windows::Win32::Foundation::HANDLE // ■ PCSTR ... windows::core::PCSTR pub struct Win32DIBCanvas{ pub bitmap :HBITMAP, pub bitmapInfo :BITMAPINFO, pub ppixel :*mut std::ffi::c_void, pub hMemDC : CreatedHDC, } // ppixel:*mut std::ffi::c_void, があるので、Send,Syncを実装しなければmyDIBをLazy<Mutex<myDIB>>に入れられない unsafe impl Send for Win32DIBCanvas {} unsafe impl Sync for Win32DIBCanvas {} impl Win32DIBCanvas{ pub fn new()->Win32DIBCanvas{ Win32DIBCanvas{ bitmap :HBITMAP(0), bitmapInfo:Default::default(), ppixel :std::ptr::null_mut(), hMemDC :CreatedHDC(0), } } pub fn create(&mut self,width:i32,height:i32){ unsafe{ let mut hdca = CreateDCA( PCSTR::from_raw("DISPLAY\0".as_ptr()), None, None, None ); self.bitmapInfo.bmiHeader.biSize = std::mem::size_of::<BITMAPINFOHEADER>() as u32; self.bitmapInfo.bmiHeader.biWidth = width; self.bitmapInfo.bmiHeader.biHeight = -height; self.bitmapInfo.bmiHeader.biPlanes = 1; self.bitmapInfo.bmiHeader.biBitCount = 3*8; self.bitmapInfo.bmiHeader.biCompression = BI_RGB; let hBitmap = CreateDIBSection( hdca, &self.bitmapInfo, DIB_RGB_COLORS, &mut self.ppixel, HANDLE(0), 0 ); self.hMemDC = CreateCompatibleDC(hdca); if let Ok(hbmp) = hBitmap{ SelectObject(self.hMemDC,hbmp); } DeleteDC(hdca); } } }
main関数。CではWinMain開始だが、Rustではwindowsプログラムもmainから始める。
気分的にWinMainが欲しいので、main関数からWinMainを呼ぶようにしている。
mod winmain; use winmain::WinMain; use windows::Win32::Foundation::*; use windows::Win32::System::LibraryLoader::*; use windows::Win32::System::Threading::*; // ■ HINSTANCE ... windows::Win32::Foundation::HINSTANCE // ■ GetModuleHandleA ... windows::Win32::System::LibraryLoader::GetModuleHandleA // ■ STARTUPINFOW ... windows::Win32::System::Threading::STARTUPINFOW // ■ GetStartupInfoW ... windows::Win32::System::Threading::GetStartupInfoW // Rustのwindows-rsからウィンドウズアプリケーションをビルドする時には、 // WinWain ではなくmainをエントリポイントとする。 fn main()-> windows::core::Result<()> { let hInstance:HINSTANCE; unsafe{ // https://github.com/microsoft/windows-samples-rs/blob/master/create_window/src/main.rs hInstance = GetModuleHandleA(None).unwrap(); } // https://doc.rust-jp.rs/book-ja/ch12-01-accepting-command-line-arguments.html let args: Vec<String> = std::env::args().collect(); // https://stackoverflow.com/questions/68322072/how-to-get-args-from-winmain-or-wwinmain-in-rust let mut si = STARTUPINFOW { cb: std::mem::size_of::<STARTUPINFOW>() as u32, ..Default::default() }; unsafe { GetStartupInfoW(&mut si) }; let cmd_show = si.wShowWindow as i32; //////////////////////////////////////////////////////////////////////////////////// // 自作WinMainを呼び出す WinMain( hInstance, HINSTANCE(0), args, cmd_show ); Ok(()) }
#[path="./win32dib.rs"] mod win32dib; use win32dib::Win32DIBCanvas; use windows::{ core::*, Win32::Foundation::*, Win32::Graphics::Gdi::*, Win32::UI::WindowsAndMessaging::*, }; use once_cell::sync::Lazy; use std::sync::Mutex; // ■ Mutex ... std::sync::Mutex // ■ Lazy ... once_cell::sync::Lazy // DIBを管理する構造体のグローバル変数 static mydib:Lazy<Mutex<Win32DIBCanvas>> = Lazy::new(|| Mutex::new( Win32DIBCanvas::new() ) ); // ■ COLORREF ... windows::Win32::Foundation::COLORREF // ■ HINSTANCE ... windows::Win32::Foundation::HINSTANCE // ■ HWND ... windows::Win32::Foundation::HWND // ■ LPARAM ... windows::Win32::Foundation::LPARAM // ■ LRESULT ... windows::Win32::Foundation::LRESULT // ■ POINT ... windows::Win32::Foundation::POINT // ■ RECT ... windows::Win32::Foundation::RECT // ■ WPARAM ... windows::Win32::Foundation::WPARAM // ■ BeginPaint ... windows::Win32::Graphics::Gdi::BeginPaint // ■ BitBlt ... windows::Win32::Graphics::Gdi::BitBlt // ■ CreatePen ... windows::Win32::Graphics::Gdi::CreatePen // ■ EndPaint ... windows::Win32::Graphics::Gdi::EndPaint // ■ LineTo ... windows::Win32::Graphics::Gdi::LineTo // ■ MoveToEx ... windows::Win32::Graphics::Gdi::MoveToEx // ■ PS_SOLID ... windows::Win32::Graphics::Gdi::PS_SOLID // ■ PAINTSTRUCT ... windows::Win32::Graphics::Gdi::PAINTSTRUCT // ■ SelectObject ... windows::Win32::Graphics::Gdi::SelectObject // ■ SRCCOPY ... windows::Win32::Graphics::Gdi::SRCCOPY // ■ CreateWindowExA ... windows::Win32::UI::WindowsAndMessaging::CreateWindowExA // ■ CS_HREDRAW ... windows::Win32::UI::WindowsAndMessaging::CS_HREDRAW // ■ CS_VREDRAW ... windows::Win32::UI::WindowsAndMessaging::CS_VREDRAW // ■ CW_USEDEFAULT ... windows::Win32::UI::WindowsAndMessaging::CW_USEDEFAULT // ■ DefWindowProcA ... windows::Win32::UI::WindowsAndMessaging::DefWindowProcA // ■ DispatchMessageA ... windows::Win32::UI::WindowsAndMessaging::DispatchMessageA // ■ GetMessageA ... windows::Win32::UI::WindowsAndMessaging::GetMessageA // ■ LoadCursorW ... windows::Win32::UI::WindowsAndMessaging::LoadCursorW // ■ IDC_ARROW ... windows::Win32::UI::WindowsAndMessaging::IDC_ARROW // ■ MSG ... windows::Win32::UI::WindowsAndMessaging::MSG // ■ PostQuitMessage ... windows::Win32::UI::WindowsAndMessaging::PostQuitMessage // ■ RegisterClassA ... windows::Win32::UI::WindowsAndMessaging::RegisterClassA // ■ WNDCLASSA ... windows::Win32::UI::WindowsAndMessaging::WNDCLASSA // ■ WS_OVERLAPPEDWINDOW ... windows::Win32::UI::WindowsAndMessaging::WS_OVERLAPPEDWINDOW // ■ WS_VISIBLE ... windows::Win32::UI::WindowsAndMessaging::WS_VISIBLE // ■ PCSTR ... windows::core::PCSTR extern "system" fn wndproc(window: HWND, message: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT { unsafe { let rectnullptr: Option<*const RECT> = None; match message as u32 { WM_CREATE => { let refmydib = &mut mydib.lock().unwrap(); refmydib.create(640,480); let hPen = CreatePen( PS_SOLID, 1, COLORREF(0x000000FF) ); // 赤いペンを作成 if hPen.is_invalid() == false{ // ペンが作成できていたら線を引く let oldPen = SelectObject( refmydib.hMemDC, hPen ); // refmydib.hMemDC の 50,50 ~ 100,100 に線を引く MoveToEx( refmydib.hMemDC, 50, 50, None as Option<*mut POINT> ); LineTo( refmydib.hMemDC, 100, 100 ); SelectObject( refmydib.hMemDC, oldPen ); // ペンを削除 DeleteObject(hPen); } LRESULT(0) } WM_PAINT => { let mut ps = PAINTSTRUCT::default(); let hdc = BeginPaint(window, &mut ps); let refmydib = &mut mydib.lock().unwrap(); BitBlt( hdc, 0, 0, 640, 480, refmydib.hMemDC, 0, 0, SRCCOPY ); EndPaint(window, &ps); LRESULT(0) } WM_DESTROY => { PostQuitMessage(0); LRESULT(0) } _ => DefWindowProcA(window, message, wparam, lparam), } } } /// 自作 WinMain pub fn WinMain( hInstance : HINSTANCE, hPrevInstance : HINSTANCE, lpCmdLine:Vec<String>, nCmdShow:i32 )->i32{ unsafe{ let _hCursor = LoadCursorW(None,IDC_ARROW).unwrap(); let _hInstance = hInstance; let _lpszClassName = PCSTR::from_raw("MY_NEW_WINDOW\0".as_ptr()); let _style = CS_HREDRAW | CS_VREDRAW; // https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/UI/WindowsAndMessaging/struct.WNDCLASSA.html let wc=WNDCLASSA{ hCursor :_hCursor, hInstance: _hInstance, lpszClassName: _lpszClassName, style: _style, lpfnWndProc: Some(wndproc), ..Default::default() }; let atom = RegisterClassA(&wc); debug_assert!(atom != 0); let nullptr: Option<*const ::core::ffi::c_void> = None; CreateWindowExA( Default::default(), _lpszClassName, PCSTR::from_raw("This is a sample window\0".as_ptr()), WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, None, None, _hInstance, nullptr, ); let mut message = MSG::default(); while GetMessageA(&mut message, HWND(0), 0, 0).into() { DispatchMessageA(&mut message); } } return 0; }
Rustのグローバル変数(可変)について調べた話。正直自信がない。
use once_cell::sync::Lazy; // 1.3.1 use std::sync::Mutex;
struct Foo { a: i32, b: i32, } impl Foo{ fn new() -> Foo { Foo { a: 1, b: 1, } } }
// グローバル変数 static myGlobal: Lazy<Mutex<Foo>> = Lazy::new(|| Mutex::new( Foo::new() ) );
fn call_1(){ let mut myg: std::sync::MutexGuard<'_, Foo> = myGlobal.lock().unwrap(); myg.a += 1; }
fn call_2(){ let mut myg: std::sync::MutexGuard<'_, Foo> = myGlobal.lock().unwrap(); myg.a += 1; }
fn main() { call_1(); call_2(); // myGlobal のミューテックスガードを取得 let mut myg: std::sync::MutexGuard<'_, Foo> = myGlobal.lock().unwrap(); myg.a += 1; println!("myg.a= {} , myg.b= {} ", myg.a,myg.b ); }
[package]
name = "rs-static"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
once_cell = "1.3.1"
グローバル変数はstaticをつけなければならない。
しかし、staticで定義できるのは定数のみ。従ってstaticだけではかなり限定的な使い方しかできない。
Mutexをつけると、
1.スレッドセーフになるのでunsafeをつけなくてもよくなる
2.内部可変性パターンにより、変数をmutableにできる
これにより、staticをつけながら可変な変数を定義できる。
つまり、可変なグローバル変数はMutexが必要。
Lazyは変数を遅延初期化するための機能。遅延初期化は、変数へ最初にアクセスしたときに初期化が発生するようにする。
初期化のタイミングをコントロールするためには、Lazyが必要。
正直、なくても動かすことはできる。
少なくとも、今回の小規模なプログラムに関しては。
ただ、初期化のタイミングがコンパイル時になる。さらにMutex::new()に与える引数が定数でなければならなくなるため、わざわざconst fnをimplしないといけなくなる。
lazyの || は無名関数を定義するためのもので、以下のように使用する。引数、戻り値型は省略可。処理を括る{}も省略できる。
fn main() { // let func = | 引数 | -> 戻り値型{処理}; let func = | x:i32 , y:i32 | -> f32 { ( x + y ) as f32 }; let a = func(1,2); println!("a = {} ", a ); }
// use once_cell::sync::Lazy; //Lazyは必須ではない use std::sync::Mutex; struct Foo { a: i32, b: i32, } impl Foo{ fn new() -> Foo { Foo { a: 1, b: 1, } } const fn new_const() -> Foo {// constをつけて定数のFooを返せる Foo { a: 1, b: 1, } } } // グローバル変数 // Lazyがないときは new_constにしなければいけない static myGlobal: Mutex<Foo> = Mutex::new( Foo::new_const() ); // 以下同じ
use once_cell::sync::Lazy; use std::sync::Mutex; // グローバル変数 static myGlobal: Lazy<Mutex<Vec<i32> > >= Lazy::new(|| Mutex::new( vec![] )); fn call_1(){ let mut myg:std::sync::MutexGuard<'_, Vec<i32>> = myGlobal.lock().unwrap(); myg.push(1); }
use once_cell::sync::Lazy; use std::sync::Mutex;
#[derive(Debug)] struct Foo { a: i32, b: i32, } impl Foo{ fn new() -> Foo { Foo { a: 1, b: 1, } } }
// グローバル変数 static myGlobal: Lazy<Mutex<Box<Foo> > >= Lazy::new(|| Mutex::new( Box::new(Foo::new()) ));
fn call_1(){ let mut myg:std::sync::MutexGuard<'_, Box<Foo>> = myGlobal.lock().unwrap(); myg.a += 1; }
wxWidgetsでアイコンを表示したい。
HICONを wxIcon::CreateFromHICON関数でwxWidgetsのwxIconへ変換。これをさらにwxBitmapに変換する。
// https://docs.wxwidgets.org/3.0/overview_helloworld.html // プリプロセッサに以下二つを追加 // __WXMSW__ // WXUSINGDLL // サブシステムをWindowsに設定(WinMainで呼び出すので) // Windows (/SUBSYSTEM:WINDOWS) #ifndef WX_PRECOMP #include <wx/wx.h> #endif #include <wx/frame.h> // wxFrameに必要 // デバッグとリリースでライブラリを分ける #ifdef _DEBUG #pragma comment(lib,"wxbase32ud.lib") #pragma comment(lib,"wxbase32ud_net.lib") #pragma comment(lib,"wxbase32ud_xml.lib") #pragma comment(lib,"wxmsw32ud_adv.lib") #pragma comment(lib,"wxmsw32ud_aui.lib") #pragma comment(lib,"wxmsw32ud_core.lib") #pragma comment(lib,"wxmsw32ud_gl.lib") #pragma comment(lib,"wxmsw32ud_html.lib") #pragma comment(lib,"wxmsw32ud_media.lib") #pragma comment(lib,"wxmsw32ud_propgrid.lib") #pragma comment(lib,"wxmsw32ud_qa.lib") #pragma comment(lib,"wxmsw32ud_ribbon.lib") #pragma comment(lib,"wxmsw32ud_richtext.lib") #pragma comment(lib,"wxmsw32ud_stc.lib") #pragma comment(lib,"wxmsw32ud_webview.lib") #pragma comment(lib,"wxmsw32ud_xrc.lib") #else #pragma comment(lib,"wxbase32u.lib") #pragma comment(lib,"wxbase32u_net.lib") #pragma comment(lib,"wxbase32u_xml.lib") #pragma comment(lib,"wxmsw32u_adv.lib") #pragma comment(lib,"wxmsw32u_aui.lib") #pragma comment(lib,"wxmsw32u_core.lib") #pragma comment(lib,"wxmsw32u_gl.lib") #pragma comment(lib,"wxmsw32u_html.lib") #pragma comment(lib,"wxmsw32u_media.lib") #pragma comment(lib,"wxmsw32u_propgrid.lib") #pragma comment(lib,"wxmsw32u_qa.lib") #pragma comment(lib,"wxmsw32u_ribbon.lib") #pragma comment(lib,"wxmsw32u_richtext.lib") #pragma comment(lib,"wxmsw32u_stc.lib") #pragma comment(lib,"wxmsw32u_webview.lib") #pragma comment(lib,"wxmsw32u_xrc.lib") #endif #include <commctrl.h> #pragma comment(lib, "comctl32.lib") // IID_IImageList に必要 #include <commoncontrols.h>
// アイコンを取得 wxBitmap my_get_icon(const wchar_t* path) { wxBitmap ret; // システムイメージリストの取得 HIMAGELIST hImageList; HRESULT hr = SHGetImageList(SHIL_JUMBO, IID_IImageList, (void**)&hImageList); // SHIL_JUMBO 256x256 if (SUCCEEDED(hr)) { SHFILEINFO shfi; memset(&shfi, 0, sizeof(SHFILEINFO)); // path からアイコンの「イメージリスト上のインデクス」を取得 // ※ アイコンハンドルではない SHGetFileInfo(path, 0, &shfi, sizeof(SHFILEINFO), SHGFI_SYSICONINDEX); HICON hicon = ImageList_GetIcon(hImageList, shfi.iIcon, ILD_NORMAL); // wxWidgetsのwxIconに変換 wxIcon computerIcon; computerIcon.CreateFromHICON((WXHICON)hicon); ret = wxBitmap(computerIcon); // wxBitmapに変換してからDestryIconする DestroyIcon(hicon); } return ret; }
///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// // ウィンドウ作成 class MyFrame : public wxFrame { wxBitmap mybmp_drive; wxBitmap mybmp_file; wxBitmap mybmp_doc; public: MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size) : wxFrame(NULL, wxID_ANY, title, pos, size) { SetBackgroundColour(*wxWHITE); mybmp_drive = my_get_icon( L"C:\\"); mybmp_file = my_get_icon(L"C:\\Windows\\System32\\notepad.exe"); mybmp_doc = my_get_icon(L"C:\\Users\\Public\\Documents\\desktop.ini"); // PAINTイベントをバインド Bind(wxEVT_PAINT, &MyFrame::OnPaint, this); } // PAINTイベントのハンドラ void OnPaint(wxPaintEvent& event) { wxPaintDC dc(this); if (mybmp_drive.IsOk()) { dc.DrawBitmap(mybmp_drive, 5+0, 5, false); } if (mybmp_file.IsOk()) { dc.DrawBitmap(mybmp_file, 5 + 256*1, 5, false); } if (mybmp_doc.IsOk()) { dc.DrawBitmap(mybmp_doc, 5 + 256*2, 5, false); } } }; ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// // wxWidgetsのアプリケーション作成 class MyApp : public wxApp { public: virtual bool OnInit() { MyFrame* frame = new MyFrame("Hello World", wxPoint(50, 50), wxSize(1000, 340)); frame->Show(true); return true; } }; ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// // WinMainをマクロで定義 wxIMPLEMENT_APP(MyApp);
#include<windows.h> #include<tchar.h> // HIMAGELIST // ImageList_GetIcon #include <commctrl.h> #pragma comment(lib, "comctl32.lib") // IID_IImageList に必要 #include <commoncontrols.h> #include <cstdio> #pragma warning(disable:4996) // アイコンを取得 HICON my_get_icon(const wchar_t* path) { // システムイメージリストの取得 HIMAGELIST hImageList; HRESULT hr = SHGetImageList(SHIL_JUMBO , IID_IImageList, (void**)&hImageList); // SHIL_JUMBO 256x256 // SHIL_EXTRALARGE 48x48 // SHIL_LARGE 32x32 // SHIL_SMALL 16x16 // hrからファイルのアイコンを取得 if (SUCCEEDED(hr)) { SHFILEINFO shfi; memset(&shfi, 0, sizeof(SHFILEINFO)); // path からアイコンの「イメージリスト上のインデクス」を取得 // ※ アイコンハンドルではない SHGetFileInfo(path, 0, &shfi, sizeof(SHFILEINFO), SHGFI_SYSICONINDEX); // SHGetImageListで取得したイメージリストから // SHGetFileInfoで取得したインデクスを指定してアイコンハンドルを取得 HICON hicon = ImageList_GetIcon(hImageList, shfi.iIcon, ILD_NORMAL); // ※ hiconは使わなくなったら DestroyIcon で破棄すること return hicon; } }
#include<windows.h> #include<tchar.h> // HIMAGELIST // ImageList_GetIcon // 表示用に ImageList_Draw も使える #include <commctrl.h> #pragma comment(lib, "comctl32.lib") // SHGetSpecialFolderLocation // SHGetFolderLocation #include <ShlObj.h> #include <cstdio> #pragma warning(disable:4996)
// アイコンを取得 HICON my_get_special_icon() { #if 0 // 注: SHGetSpecialFolderLocation は非推奨 // https://learn.microsoft.com/ja-jp/windows/win32/api/shlobj_core/nf-shlobj_core-shgetspecialfolderlocation PIDLIST_ABSOLUTE pidl; HRESULT hr = SHGetSpecialFolderLocation(NULL, CSIDL_FONTS, &pidl); #else LPITEMIDLIST pidl; // マイコンピュータのPIDLを取得 HRESULT hr = SHGetFolderLocation(NULL, CSIDL_DRIVES, NULL, 0, &pidl); // CSIDL_DRIVES マイコンピュータアイコン // CSIDL_FONTS フォントアイコン // CSIDL_DESKTOP デスクトップアイコン // ... etc #endif SHFILEINFO shfi; memset(&shfi, 0, sizeof(SHFILEINFO)); DWORD_PTR ret = SHGetFileInfoW(// イメージリストとその中のpidlで指定したアイコンのidを取得 (LPCTSTR)pidl, 0, &shfi, sizeof(SHFILEINFO), SHGFI_PIDL | SHGFI_SYSICONINDEX | SHGFI_LARGEICON); HIMAGELIST imagelist = (HIMAGELIST)ret; // アイコンリストが取得できたかどうかを表す if (imagelist != nullptr) { // imagelistからアイコンを取得 // ImageList_GetIconはアイコンのコピーを渡すので、後でDestroyIconが必要 HICON hicon = ImageList_GetIcon(imagelist, shfi.iIcon, ILD_NORMAL); // SHGFI_SYSICONINDEX を指定した場合、hIconは無効 //DestroyIcon(shfi.hIcon); return hicon;// コピーしたアイコンを返す } return NULL; }
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) { PAINTSTRUCT ps; HDC hdc; // アイコン static HICON myicon; switch (msg) { case WM_CREATE: // コピーしたアイコンを取得 myicon = my_get_special_icon(); return 0; case WM_DESTROY: // コピーしたアイコンを破棄 DestroyIcon(myicon); PostQuitMessage(0); return 0; case WM_PAINT: hdc = BeginPaint(hwnd, &ps); // アイコンを描画 DrawIconEx(hdc, 0 + 10, 10, myicon, 0, 0, 0, NULL, DI_NORMAL | DI_COMPAT); EndPaint(hwnd, &ps); return 0; } return DefWindowProc(hwnd, msg, wp, lp); } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, int nCmdShow) { HWND hwnd; WNDCLASS winc; MSG msg; winc.style = CS_HREDRAW | CS_VREDRAW; winc.lpfnWndProc = WndProc; winc.cbClsExtra = winc.cbWndExtra = 0; winc.hInstance = hInstance; winc.hIcon = LoadIcon(NULL, IDI_APPLICATION); winc.hCursor = LoadCursor(NULL, IDC_ARROW); winc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); winc.lpszMenuName = NULL; winc.lpszClassName = TEXT("SZL-WND"); if (!RegisterClass(&winc)) return 0; hwnd = CreateWindow( TEXT("SZL-WND"), TEXT("icon test"), WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 200, 200, NULL, NULL, hInstance, NULL ); if (hwnd == NULL) return 0; while (GetMessage(&msg, NULL, 0, 0)) DispatchMessage(&msg); return msg.wParam; }
WindowsでSHGetFileInfoでアイコンを取得する方法。
SHGFI_SYSICONINDEXを指定した場合、同じアイコンはiIconが同じになるので、テーブルを作っておけばメモリを節約できる。
ちなみに下で、アイコンの描画はDrawIconを使っているが、これはアイコン本来のサイズで描画してくれないので、動作確認のためにはDrawIconExを使ったほうが良い。
DrawIconExに、第五、第六、第七引数に0を指定し、且つ DI_NORMAL | DI_COMPAT をつけて描画すると、アイコンの本来のサイズで描画できる。
DrawIconEx(hdc, 0 + 10, 10, icondrive, 0, 0, 0, NULL, DI_NORMAL | DI_COMPAT); DrawIconEx(hdc, 32 + 10, 10, iconfolder, 0, 0, 0, NULL, DI_NORMAL | DI_COMPAT); DrawIconEx(hdc, 64 + 10, 10, iconfile, 0, 0, 0, NULL, DI_NORMAL | DI_COMPAT); DrawIconEx(hdc, 96 + 10, 10, iconexe, 0, 0, 0, NULL, DI_NORMAL | DI_COMPAT);
本来ならCopyIconしないでそのまま返して、WM_DESTROYのところでDestroyIconすればいいが、「SHGFI_ICONで取得したアイコンはDestroyIconが必要」ということを強調するためにあえて行っている。
#include<windows.h> #include<tchar.h> #include <cstdio> #pragma warning(disable:4996)
// アイコンを取得 HICON my_get_icon(const wchar_t* path) { SHFILEINFO shfi; memset(&shfi, 0, sizeof(SHFILEINFO)); DWORD_PTR ret = SHGetFileInfoW(// アイコンを取得 path, 0, &shfi, sizeof(SHFILEINFO), SHGFI_ICON | /* SHGFI_SYSICONINDEX | */ SHGFI_LARGEICON); // retはアイコンが取得できたかどうかを表す if (SUCCEEDED(ret)) { // アイコンをコピー HICON hicon = CopyIcon(shfi.hIcon); // SHGFI_ICON を指定しているので取得したのはアイコン // なのでDestroyIconで破棄する DestroyIcon(shfi.hIcon); return hicon;// コピーしたアイコンを返す } return NULL; }
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) { PAINTSTRUCT ps; HDC hdc; // アイコン一覧 static HICON icondrive; static HICON iconfolder; static HICON iconfile; static HICON iconexe; switch (msg) { case WM_CREATE: // コピーしたアイコンを取得 icondrive = my_get_icon(L"C:\\"); iconfolder = my_get_icon(L"C:\\Windows"); iconfile = my_get_icon(L"C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\MSVC\\14.35.32215\\atlmfc\\include\\afx.h"); iconexe = my_get_icon(L"C:\\Windows\\notepad.exe"); return 0; case WM_DESTROY: // コピーしたアイコンを破棄 DestroyIcon(icondrive); DestroyIcon(iconfolder); DestroyIcon(iconfile); DestroyIcon(iconexe); PostQuitMessage(0); return 0; case WM_PAINT: hdc = BeginPaint(hwnd, &ps); // アイコンを描画 DrawIcon(hdc, 0+10, 10, icondrive); DrawIcon(hdc, 32+10, 10, iconfolder); DrawIcon(hdc, 64+10, 10, iconfile); DrawIcon(hdc, 96+10, 10, iconexe); EndPaint(hwnd, &ps); return 0; } return DefWindowProc(hwnd, msg, wp, lp); }
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, int nCmdShow) { HWND hwnd; WNDCLASS winc; MSG msg; winc.style = CS_HREDRAW | CS_VREDRAW; winc.lpfnWndProc = WndProc; winc.cbClsExtra = winc.cbWndExtra = 0; winc.hInstance = hInstance; winc.hIcon = LoadIcon(NULL, IDI_APPLICATION); winc.hCursor = LoadCursor(NULL, IDC_ARROW); winc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); winc.lpszMenuName = NULL; winc.lpszClassName = TEXT("SZL-WND"); if (!RegisterClass(&winc)) return 0; hwnd = CreateWindow( TEXT("SZL-WND"), TEXT("icon test"), WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 300, 100, NULL, NULL, hInstance, NULL ); if (hwnd == NULL) return 0; while (GetMessage(&msg, NULL, 0, 0)) DispatchMessage(&msg); return msg.wParam; }
よく考えたらImageList_GetIconの時もDestroyIconが必要なので上と合わせるべきなのだが間違っているわけではないというかむしろこちらが正攻法だと思うのでこのままにしておく。
#include<windows.h> #include<tchar.h> // HIMAGELIST // ImageList_GetIcon // 表示用に ImageList_Draw も使える #include <commctrl.h> #pragma comment(lib, "comctl32.lib") #include <cstdio> #pragma warning(disable:4996) // アイコンを取得 HICON my_get_icon(const wchar_t* path) { SHFILEINFO shfi; memset(&shfi, 0, sizeof(SHFILEINFO)); DWORD_PTR ret = SHGetFileInfoW( path, 0, &shfi, sizeof(SHFILEINFO), /*SHGFI_ICON |*/ SHGFI_SYSICONINDEX | SHGFI_LARGEICON); HIMAGELIST imagelist = (HIMAGELIST)ret; // アイコンリストが取得できたかどうかを表す if (imagelist != nullptr) { // imagelistからアイコンを取得 // ImageList_GetIconはアイコンのコピーを渡すので、後でDestroyIconが必要 HICON hicon = ImageList_GetIcon(imagelist, shfi.iIcon, ILD_NORMAL); // SHGFI_SYSICONINDEX を指定した場合、hIconは無効 //DestroyIcon(shfi.hIcon); return hicon;// コピーしたアイコンを返す } return NULL; } /* 上と同じ */
wxGLCanvasでglewを使用するとき、glewInit()の呼び出しに失敗する場合がある。
例えば以下のようなコードを書くと、エラーメッセージを取得できる。
GLenum err = glewInit(); if (err != GLEW_OK) { std::string msg = (const char*)glewGetErrorString(err); std::cerr << "Error: " << msg << std::endl; }
これはOpenGLのコンテキストがまだ作成されていないのにglewInit()を呼んでしまったからである。
これを回避する確実な方法は、glewInit()の呼び出しをwxEVT_PAINTの処理時にすることらしい。
つまり描画の最初の一回目でglewInit()する。
BindでwxEVT_PAINT時にRender関数が呼ばれるように設定する。
MyGLCanvas::MyGLCanvas(wxFrame* parent) : wxGLCanvas(parent, wxID_ANY), m_context(new wxGLContext(this)) { Bind(wxEVT_PAINT, &MyGLCanvas::Render, this); /* ... */ }
描画関数。SetCurrentした後でglewInit()する。
void MyGLCanvas::Render(wxPaintEvent& evt) { if (!IsShown()) return; wxGLCanvas::SetCurrent(*m_context); if (glew_inistialized == false) {// 初期化は一回だけでいいのでフラグで制御 GLenum err = glewInit(); if (err != GLEW_OK) {
// エラーの場合はエラー内容を表示
std::string msg = (const char*)glewGetErrorString(err); std::cerr << "Error: " << msg << std::endl; } else { glew_inistialized = true;// 二回呼び出されないようにフラグを立てる } } wxPaintDC(this); int w, h; GetClientSize(&w, &h); glViewport(0, 0, w, h); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); /* 略 */ glFlush(); SwapBuffers(); }
wxWidgetsを使用するときに、.libファイルを大量にリンクしなければならない。この問題を解決するために、無数にある.libファイルを一つのファイルにまとめてしまうことができる。
まず、以下のようにmulti-lib/ フォルダ内にwxWidgetsの.libファイルが入っているとする。
D:.
└─multi-lib/
wxbase33u.lib
wxbase33u_net.lib
wxbase33u_xml.lib
wxexpat.lib
wxjpeg.lib
wxlexilla.lib
wxmsw33u_adv.lib
wxmsw33u_aui.lib
wxmsw33u_core.lib
wxmsw33u_gl.lib
wxmsw33u_html.lib
wxmsw33u_media.lib
wxmsw33u_propgrid.lib
wxmsw33u_qa.lib
wxmsw33u_ribbon.lib
wxmsw33u_richtext.lib
wxmsw33u_stc.lib
wxmsw33u_webview.lib
wxmsw33u_xrc.lib
wxpng.lib
wxregexu.lib
wxscintilla.lib
wxtiff.lib
wxzlib.lib
複数のライブラリを一つにまとめるには、lib.exeを使用する。
lib.exe /OUT:纏めたファイル名.lib ライブラリ1.lib ライブラリ2.lib
まず、 x64 Native Tools Command Prompt for VS 2022 を開いて、Visual Studioのツールを使えるようにする。
そして、以下のように入力してcombined_wx.libを作成する。カレントディレクトリに出力されるので、この例だとmulti-lib/combined_wx.libが作成される。
特に注意するところはない。普通に使用できる。
// プリプロセッサに以下を追加 // __WXMSW__ // static link library の場合、以下は指定しない // WXUSINGDLL // サブシステムをWindowsに設定(WinMainで呼び出すので) // Windows (/SUBSYSTEM:WINDOWS) #ifndef WX_PRECOMP #include <wx/wx.h> #endif #include <wx/gdicmn.h> // wxPointに必要 #include <wx/frame.h> // wxFrameに必要 // Win32 API #pragma comment(lib, "comctl32.lib") #pragma comment(lib, "rpcrt4.lib") #if 1 // 一つに纏めた.libファイル群 #pragma comment(lib,"combined_wx.lib") #else // 本来使用する.libファイル群 #pragma comment(lib,"wxmsw33u_core.lib") #pragma comment(lib,"wxbase33u.lib") #pragma comment(lib,"wxbase33u_net.lib") #pragma comment(lib,"wxbase33u_xml.lib") #pragma comment(lib,"wxexpat.lib") #pragma comment(lib,"wxjpeg.lib") #pragma comment(lib,"wxlexilla.lib") #pragma comment(lib,"wxmsw33u_adv.lib") #pragma comment(lib,"wxmsw33u_aui.lib") #pragma comment(lib,"wxmsw33u_gl.lib") #pragma comment(lib,"wxmsw33u_html.lib") #pragma comment(lib,"wxmsw33u_media.lib") #pragma comment(lib,"wxmsw33u_propgrid.lib") #pragma comment(lib,"wxmsw33u_qa.lib") #pragma comment(lib,"wxmsw33u_ribbon.lib") #pragma comment(lib,"wxmsw33u_richtext.lib") #pragma comment(lib,"wxmsw33u_stc.lib") #pragma comment(lib,"wxmsw33u_webview.lib") #pragma comment(lib,"wxmsw33u_xrc.lib") #pragma comment(lib,"wxpng.lib") #pragma comment(lib,"wxregexu.lib") #pragma comment(lib,"wxscintilla.lib") #pragma comment(lib,"wxtiff.lib") #pragma comment(lib,"wxzlib.lib") #endif ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// // ウィンドウ作成 class MyFrame : public wxFrame { public: MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size) : wxFrame(NULL, wxID_ANY, title, pos, size) { } private: // イベント処理しないときはこれを入れない // wxDECLARE_EVENT_TABLE(); }; ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// // wxWidgetsのアプリケーション作成 class MyApp : public wxApp { public: virtual bool OnInit() { MyFrame* frame = new MyFrame("Hello World", wxPoint(50, 50), wxSize(450, 340)); frame->Show(true); return true; } }; ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// // WinMainをマクロで定義 wxIMPLEMENT_APP(MyApp);
比較的最近のGLSLでは、gl_ModelViewMatrix、gl_ProjectionMatrixという名前の変数を定義するとエラーになる。正確には、「先頭に gl_ がついている変数名はコンパイルエラーになる」。
glTranslatedなどが使用できないとか以前に、「変数名によってエラーになる」ことが問題。例えば以下のようなコードを書くと、コンパイルエラーが発生する。
#version 330 core layout (location = 0) in vec3 aPos; layout (location = 1) in vec3 incolor; out vec4 vertexColor; uniform mat4 gl_ModelViewMatrix; uniform mat4 gl_ProjectionMatrix; void main() { gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * vec4(aPos, 1.0); vertexColor = vec4(incolor, 1.0); }
ただし、上の場合、gl_Positionは自分で定義しているわけではないのでエラーにならない。
gl_が頭についていない変数を使えばよい。ところで、gl_ModelViewMatrixやgl_ProjectionMatrixは、glMatrixMode→glTranslatef等で渡された行列の可能性がある。これが使えなくなるので、行列は自分で計算しなければいけない。面倒なのでglmを使用することになる。
#version 330 core layout (location = 0) in vec3 aPos; layout (location = 1) in vec3 incolor; out vec4 vertexColor; uniform mat4 MyModelViewMatrix; uniform mat4 MyProjectionMatrix; void main() { gl_Position = MyProjectionMatrix * MyModelViewMatrix * vec4(aPos, 1.0); vertexColor = vec4(incolor, 1.0); }
glUseProgram(ProgramID); // これの代わりに glGetUniformLocation + glUniformMatrix4fv // //glMatrixMode(GL_MODELVIEW); //glLoadMatrixf(glm::value_ptr(projectionMatrix)); //glMatrixMode(GL_MODELVIEW); //glLoadMatrixf(glm::value_ptr(modelViewMatrix)); glm::mat4 modelViewMatrix; // モデルビュー行列の計算 static int theta = 0.0f; modelViewMatrix = glm::rotate( glm::mat4(1.0), glm::radians((float)theta), glm::vec3(0.0, 0.0, 1.0) ); theta += 5; glm::mat4 projectionMatrix = glm::mat4(1.0); // 投影行列の計算 GLint modelViewMatrixID = glGetUniformLocation(ProgramID, "MyModelViewMatrix"); GLint projectionMatrixID = glGetUniformLocation(ProgramID, "MyProjectionMatrix"); glUniformMatrix4fv(modelViewMatrixID, 1, GL_FALSE, glm::value_ptr(modelViewMatrix)); glUniformMatrix4fv(projectionMatrixID, 1, GL_FALSE, glm::value_ptr(projectionMatrix));
なんか前にも書いた気がするのだが、日本語の検索結果があまり出てこなかったのでここにまとめておく。
meson.build作成したうえで、以下のように実行する
meson setup 出力ディレクトリ --buildtype=モード --backend=vs
例:
releaseとdebugを同時に指定できない。release用.slnとdebug用.slnを別々に作る必要がある。
meson setup release_build --buildtype=release --backend=vs
meson setup debug_build --buildtype=debug --backend=vs
#include<windows.h> LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) { HDC hdc; switch (msg) { case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, msg, wp, lp); } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, int nCmdShow) { HWND hwnd; WNDCLASS winc; MSG msg; winc.style = CS_HREDRAW | CS_VREDRAW; winc.lpfnWndProc = WndProc; winc.cbClsExtra = winc.cbWndExtra = 0; winc.hInstance = hInstance; winc.hIcon = LoadIcon(NULL, IDI_APPLICATION); winc.hCursor = LoadCursor(NULL, IDC_ARROW); winc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); winc.lpszMenuName = NULL; winc.lpszClassName = TEXT("SZL-WIN"); if (!RegisterClass(&winc)) return 0; hwnd = CreateWindow( TEXT("SZL-WIN"), TEXT("test"), WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL ); if (hwnd == NULL) return 0; while (GetMessage(&msg, NULL, 0, 0)) DispatchMessage(&msg); return msg.wParam; }
WinMainを使う場合、/SUBSYSTEM:WINDOWSを指定する。
問題は、mesonが勝手に/SUBSYSTEM:CONSOLEをつけてしまうので、エントリポイントがmainなのかWinMainなのかわからなくなりエラーが出る。
これを回避するために、 gui_app: true を指定する。
project('myapp', 'cpp') cpp = meson.get_compiler('cpp') executable('myapp', 'main.cpp', gui_app: true, # WinMainを使うときはこれが必要 cpp_args: [ '/D_WINDOWS' ], link_args: [ '/SUBSYSTEM:WINDOWS' # WinMainを使うときはこれが必要 ] )
meson setup build
meson compile -C build
の順で実行する。
(mesonenv) C:\Users\myuser\source\repos\win32>meson setup build Directory already configured. Just run your build command (e.g. ninja) and Meson will regenerate as necessary. If ninja fails, run "ninja reconfigure" or "meson setup --reconfigure" to force Meson to regenerate. If build failures persist, run "meson setup --wipe" to rebuild from scratch using the same options as passed when configuring the build. To change option values, run "meson configure" instead. (mesonenv) C:\Users\myuser\source\repos\win32>meson compile -C build Activating VS 17.5.4 INFO: automatically activated MSVC compiler environment INFO: autodetecting backend as ninja INFO: calculating backend command to run: C:\Users\myuser\anaconda3\envs\mesonenv\Library\bin\ninja.EXE -C C:/Users/szl/source/repos/win32/build ninja: Entering directory `C:/Users/myuser/source/repos/win32/build' [0/1] Regenerating build files. The Meson build system Version: 1.0.1 Source dir: C:\Users\myuser\source\repos\win32 Build dir: C:\Users\myuser\source\repos\win32\build Build type: native build Project name: myapp Project version: undefined C++ compiler for the host machine: cl (msvc 19.35.32217.1 "Microsoft(R) C/C++ Optimizing Compiler Version 19.35.32217.1 for x64") C++ linker for the host machine: link link 14.35.32217.1 Host machine cpu family: x86_64 Host machine cpu: x86_64 Build targets in project: 1 Found ninja-1.10.2 at C:\Users\myuser\anaconda3\envs\mesonenv\Library\bin\ninja.EXE Cleaning... 0 files. [1/1] Linking target myapp.exe
#include<windows.h> LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) { HDC hdc; switch (msg) { case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, msg, wp, lp); } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, int nCmdShow) { HWND hwnd; WNDCLASS winc; MSG msg; winc.style = CS_HREDRAW | CS_VREDRAW; winc.lpfnWndProc = WndProc; winc.cbClsExtra = winc.cbWndExtra = 0; winc.hInstance = hInstance; winc.hIcon = LoadIcon(NULL, IDI_APPLICATION); winc.hCursor = LoadCursor(NULL, IDC_ARROW); winc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); winc.lpszMenuName = NULL; winc.lpszClassName = TEXT("SZL-WIN"); if (!RegisterClass(&winc)) return 0; hwnd = CreateWindow( TEXT("SZL-WIN"), TEXT("test"), WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL ); if (hwnd == NULL) return 0; while (GetMessage(&msg, NULL, 0, 0)) DispatchMessage(&msg); return msg.wParam; }