ぬの部屋(仮)
nu-no-he-ya
  •      12
    3456789
    10111213141516
    17181920212223
    24252627282930
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    30      
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
     123456
    78910111213
    14151617181920
    21222324252627
    282930    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
         12
    3456789
    10111213141516
    17181920212223
    2425262728  
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    30      
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
       1234
    567891011
    12131415161718
    19202122232425
    26272829   
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
      12345
    6789101112
    13141516171819
    20212223242526
    27282930   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
       1234
    567891011
    12131415161718
    19202122232425
    2627282930  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728     
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
     123456
    78910111213
    14151617181920
    21222324252627
    282930    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
       1234
    567891011
    12131415161718
    19202122232425
    2627282930  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
      12345
    6789101112
    13141516171819
    20212223242526
    27282930   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
     123456
    78910111213
    14151617181920
    21222324252627
    28      
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
      12345
    6789101112
    13141516171819
    20212223242526
    27282930   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
     123456
    78910111213
    14151617181920
    21222324252627
    282930    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
       1234
    567891011
    12131415161718
    19202122232425
    2627282930  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
    1234567
    891011121314
    15161718192021
    22232425262728
           
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    30      
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
     123456
    78910111213
    14151617181920
    21222324252627
    282930    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
      12345
    6789101112
    13141516171819
    20212223242526
    27282930   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
         12
    3456789
    10111213141516
    17181920212223
    242526272829 
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    30      
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
        123
    45678910
    11121314151617
    18192021222324
    25262728   
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
       1234
    567891011
    12131415161718
    19202122232425
    2627282930  
           
    1234567
    15161718192021
    293031    
           
         12
    3456789
    10111213141516
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    30      
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
      12345
    6789101112
    13141516171819
    20212223242526
    27282930   
           
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
       1234
    567891011
    12131415161718
    19202122232425
    2627282930  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728     
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
     123456
    78910111213
    14151617181920
    21222324252627
    282930    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
     123456
    78910111213
    14151617181920
    21222324252627
    282930    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
      12345
    6789101112
    13141516171819
    20212223242526
    27282930   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
          1
    2345678
    9101112131415
    16171819202122
    232425262728 
           
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
  • 矩形をxmlで再帰的に分割

    UIを簡易的に定義したいので、自作xmlとそれに基づいて領域を計算するコードを書く。

    XML

    まずXMLを定義。

    nameは識別子だが一意である必要はない。arrangementは子要素の配置方法。ratioは親要素に対するその要素の割合。

    <?xml version="1.0" encoding="utf8" ?>
    <box name="main" ratio="1.0" arrangement="horizonal">
      <box name="red" ratio="0.3" arrangement="vertical">
         <box name="area1"></box>
         <box name="area2"></box>
         <box name="area3" ratio="0.5"></box>
      </box>
        
      <box name="blue"  ratio="0.6" arrangement="vertical" >
         <box name="area1" ratio="0.2"></box>
         <box name="area2" ratio="0.8"></box>
      </box>
    
      <box name="green" ratio="0.1" arrangement="vertical" >
         <box name="area1" ratio="0.3"></box>
         <box name="area2" ratio="0.3"></box>
         <box name="area3" ratio="0.3"></box>
         
         <box name="area4" ratio="0.1"  arrangement="horizonal">
           <box name="target1" ratio="0.3"></box>
           <box name="target2" ></box>
         </box>
      </box>
    </box>
    

    コード

    #include <windows.h>
    
    #include <iostream>
    #include <string>
    #include <vector>
    #include <pugixml.hpp>
    #include <functional>
    
    // 子要素の配置方法
    enum class Arrangement {
        Horizonal,
        Vertical,
    };
    
    // 各情報を元に計算される実際のサイズ。sx,sy, ex,eyは矩形の最小、最大を表すピクセル座標
    struct Rect {
        int sx, sy;
        int ex, ey;
    };
    
    struct BOX {
        Arrangement type;          // 子要素の配置方法
        Rect rect;                 // この領域の矩形
        std::string name;          // この領域の名前
        double ratio;              // この領域が親要素のどれだけの割合を占めるか
        std::vector<BOX> children; // 子要素
    };
    
    
    // xmlファイルの内容を解析してノード作成
    void parseNode(const pugi::xml_node& xmlNode, BOX& node) {
    
        // BOXの名前を取得
        node.name = xmlNode.attribute("name").as_string("");
        // BOXの親要素に対する割合を取得。指定がない場合はNaNにしておいて、自動計算する
        node.ratio = xmlNode.attribute("ratio").as_double( std::numeric_limits<double>::quiet_NaN()   );
    
        // 配置方法を取得。デフォルトをverticalとしておく。
        std::string arrangement = xmlNode.attribute("arrangement").as_string("vertical");
        node.type = (arrangement == "horizonal") ? Arrangement::Horizonal : Arrangement::Vertical;
    
        // 子要素を再帰的に解析
        for (pugi::xml_node child = xmlNode.first_child(); child; child = child.next_sibling()) {
            BOX childNode;
            parseNode(child, childNode);
            node.children.push_back(childNode);
        }
    }
    
          
          
    // xmlファイルからレイアウト情報を読み込む
    BOX LoadLayout(const char* xmlfilename) {
        pugi::xml_document doc;
        pugi::xml_parse_result result = doc.load_file(xmlfilename);
    
        // ファイルが読み込めなかったら例外を投げる
        if (!result) {
            throw std::runtime_error("xmlファイルが読み込めませんでした");
        }
    
        BOX rootNode;
        pugi::xml_node root = doc.child("box");
        parseNode(root, rootNode);
    
        return rootNode;
    }
    
          
          
    // 読み込んだxmlファイルから領域を計算
    void UpdateLayout(BOX& node, const Rect& parent) {
        node.rect = parent;
        int totalWidth = parent.ex - parent.sx;
        int totalHeight = parent.ey - parent.sy;
    
        /////////////////////////////////
        // 親要素を占める割合の計算
        // ratioを省略可能としている
        // ratioが省略されたときは、その領域内の残りのratioを省略されたbox数で割る
    
        double remainingRatio = 1.0; // 1.0 = 100% parent全体の割合を1.0とする
        int undefinedRatioCount = 0; // ratioが定義されていないboxの個数
    
        for (auto& child : node.children) {
    
            if (std::isnan(child.ratio) == true) {
    
                // ratioが定義されていないノードの個数を知りたいのでここで確認する
                undefinedRatioCount++;
    
            }
            else {
                // ratioが定義されているノードの割合を引き、
                // ratio未定義のboxが使用可能な割合を計算する
                remainingRatio -= child.ratio;
            }
    
        }
    
        // ratioが定義されていないノードがある場合は、使用可能な割合を等分する
        if (undefinedRatioCount > 0) {
            // ratioが定義されていないノードは、使用可能な量を等分する
            double defaultRatio = remainingRatio / undefinedRatioCount;
    
            // ratio未定義のノードにratioを与える
            for (auto& child : node.children) {
                if (std::isnan(child.ratio) == true) {
                    child.ratio = defaultRatio;
                }
            }
        }
    
        // 子要素の矩形を計算する
        double accumRatio = 0.0;
        for (auto& child : node.children) {
            if (node.type == Arrangement::Horizonal) {
                child.rect.sx = parent.sx + totalWidth * accumRatio;
                child.rect.ex = child.rect.sx + totalWidth * child.ratio;
                child.rect.sy = parent.sy;
                child.rect.ey = parent.ey;
            }
            else {
                child.rect.sy = parent.sy + totalHeight * accumRatio;
                child.rect.ey = child.rect.sy + totalHeight * child.ratio;
                child.rect.sx = parent.sx;
                child.rect.ex = parent.ex;
            }
            accumRatio += child.ratio;
    
        }
    
        // 子要素の子要素の矩形を計算する
        for (auto& child : node.children) {
            UpdateLayout(child, child.rect);
        }
    }
    

    テストコード

    // 領域を描画
    void drawRects(HDC hdc, const BOX& node) {
    
        
        RECT rect = { node.rect.sx, node.rect.sy, node.rect.ex, node.rect.ey };
        
        // ランダムな色を作成
        int r = rand() % 256;
        int g = rand() % 256;
        int b = rand() % 256;
    
    
        HPEN hPen = CreatePen(PS_SOLID, 1, RGB(r, g, b)); // 1ピクセル幅のペンを作成
        HGDIOBJ oldPen = SelectObject(hdc, hPen); // ペンを選択
    
        // 矩形を描画
        Rectangle(hdc, rect.left, rect.top, rect.right, rect.bottom);
    
        SelectObject(hdc, oldPen); // 元のペンに戻す
        DeleteObject(hPen); // ペンを削除
    
    
    
        for (const BOX& child : node.children) {
            drawRects(hdc, child);
        }
    }
    
    
    BOX rootArea;
    
    
    LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
        HDC hdc;
        PAINTSTRUCT ps;
        RECT rect;
    
        switch (msg) {
        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;
        case WM_PAINT:
            hdc = BeginPaint(hwnd, &ps);
    
            GetClientRect(hwnd, &rect);
            FillRect(hdc, &rect, (HBRUSH)GetStockObject(WHITE_BRUSH));
    
            drawRects(hdc, rootArea);// テスト用 矩形描画
    
            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-WINDOW");
    
        if (!RegisterClass(&winc)) return 0;
    
    
        Rect rootRect{ 0, 0, 800, 300 };
        rootArea = LoadLayout(R"(layout2.xml)");// レイアウト読み込み
        UpdateLayout(rootArea, rootRect);// 矩形を計算
    
    
        hwnd = CreateWindow(
            TEXT("SZL-WINDOW"), TEXT("test window"),
            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;
    }
    

    Rustでスレッドを使ってみる

    Rustでスレッドを作成するにはspawnを使用する。よくあるように、一度作成したスレッドはハンドルを取得し、join()で待機する。スレッドとして走らせた関数の戻り値はjoin()の戻り値に入っているが、これはエラーなども扱うResultとして入っているのでunwrapして戻り値を取り出す。

    1.引数のない関数を呼び出す

    spawnには原則として引数なしの関数しか渡せない。

    // スレッドとして実行する関数
    // 引数をとることはできないが、任意の型の戻り値を返すことはできる
    fn my_sum()->i32 {
        let mut sum = 0;
        for i in 0..50 {
            println!("{}", i);
            sum += i;
        }
        return sum;
    }
    
    fn main(){
    
        // 関数が引数を一つも取らない場合、spawnに関数名を渡すだけでスレッドが作成される
        let thread_handle1 = std::thread::spawn(my_sum);
        let thread_handle2 = std::thread::spawn(my_sum);
    
        // my_sumは引数をとることはできないが、任意の型の戻り値を返すことはできる
        let result1:i32 = thread_handle1.join().unwrap();
        let result2:i32 = thread_handle2.join().unwrap();
    
        println!("results are {} , {} ", result1,result2);
    }
    

    2.引数を与えて呼び出す(マジックナンバー)

    引数を与える場合、直接spawnに与えることができないので、クロージャ(ラムダ関数のようなもの)を与える。Rustのクロージャは頭に || をつける。

    以下の例では、「myprintに'a' 'b' 50 というマジックナンバーを与えて呼び出す」という処理を行う引数なしの関数をspawnに渡している。

    // 値を表示する関数
    fn myprint(mark : char,count:i32) {
        for i in 0..count{
            println!("{} -> {}",mark,i);
        }
    }

    fn
    main() { // spawnにはクロージャ(≒ラムダ関数)を渡す。 || はクロージャを表す。 // 即ち、「myprintを引数付きで渡す」という処理を行う、引数なしの関数を渡している。 let thread_handle1 = std::thread::spawn(|| myprint( 'a',50)); let thread_handle2 = std::thread::spawn(|| myprint( 'b',50)); // 終了まで待機 thread_handle1.join().unwrap(); thread_handle2.join().unwrap(); }

    3.メインスレッドのデータを引数渡しで処理する

    メインスレッドのデータをスレッドの中で処理したい場合、メインスレッド側でデータが消えたりするとまずいので、そのまま参照を渡すことができない。

    そこでデータを参照カウンタ付きのスマートポインタArcで渡すことで、メインスレッドを含め各スレッドが処理している最中にデータが消える危険性を排除する。

    // vecの不変参照を受け取り、表示する関数
    fn myprint(input: &Vec<i32>) {
        for i in input.iter(){
            println!("i=={}",i);
        }
    }
    
    // テスト用のVecのデータを作成する関数
    fn create_data()->Vec<i32>{
        let mut data = vec![];
        for k in 0..100{
            data.push(k);
        }
    
        return data;
    }
    
    
    fn main() {
    
        // スレッドに渡すためにはArcでラップする必要がある
        let data1 = std::sync::Arc::new(create_data());
        let data2 = std::sync::Arc::new(create_data());
    
        // spawnmoveキーワードによりスレッドに変数の所有権が移ってしまうので
        // clone()で参照のコピーを作成する。Arccloneは参照カウンタが増えるだけなので負荷はほぼない
        let d1_clone = data1.clone();
        let d2_clone = data2.clone();
        let thread_handle1 = std::thread::spawn(move || myprint( &d1_clone)); // d1_cloneの所有権が移動
        let thread_handle2 = std::thread::spawn(move || myprint( &d2_clone)); // d2_cloneの所有権が移動
    
        // 終了まで待機
        thread_handle1.join().unwrap();
        thread_handle2.join().unwrap();
    
        // データの所有権は移行していないので、ここで表示できる
        println!("data: {:?}", data1);
        println!("data: {:?}", data2);
    
    
    }
    

    4.スレッドに可変な変数を渡して編集する

    スレッド内で変数を編集したい場合、さらにMutexでラップする。

    // 配列を受け取り、二倍にする関数
    fn double(input: &std::sync::Mutex<Vec<i32>>) {
        let mut vecdata = input.lock().unwrap();
        for i in vecdata.iter_mut() {
            *i *= 2;
        }
    }

    fn main() {
    
        let data1 = std::sync::Arc::new(std::sync::Mutex::new(vec![1,2,3,4,5]));
        let data2 = std::sync::Arc::new(std::sync::Mutex::new(vec![10,20,30,40,50]));
    
        let mut d1_clone = data1.clone();
        let mut d2_clone = data2.clone();
        let thread_handle1 = std::thread::spawn(move || double(&d1_clone));
        let thread_handle2 = std::thread::spawn(move || double(&d2_clone));
    
        thread_handle1.join().unwrap();
        thread_handle2.join().unwrap();
    
    
        println!("data1: {:?}", &data1);
        println!("data2: {:?}", &data2);
    
    
    }
    

    Rustでタイマー

    C言語のタイマーと違い、精度の高いタイマーが標準で使用できる。ただし精度は環境依存なので、比較的新しい環境だとマイクロ秒,ナノ秒単位で取得できるが、そうでない場合も考えられる。

    fn main() {
    
        // 時間計測開始
        let mytimer = std::time::Instant::now();
    
        use_rand();
    
        // 時間計測終了
        let elapsed = mytimer.elapsed();
    
        // 経過時間を表示
        println!("{:?}",elapsed);
    
    
    }

    fn use_rand(){
        use rand::Rng; // 乱数を使うためにrand::Rngを使用
    
        let mut rng = rand::thread_rng();
    
        let min:i32 = 0;
        let max:i32 = 20;
        
        (0..5000).for_each(|_|{
    
            let randint:i32 = rng.gen_range(min..max); // 最大・最小を指定して乱数を生成
    
            println!("{}",randint);
    
        });
    
    }
    

    Sleep時間も計測する

    CのclockなどはCPU時間なので、Sleep時間がタイマーに反映されないが、Rustのstd::timeはちゃんとSleep時間も含んだ結果が得られる。

    fn main() {
    
        // 時間計測開始
        let mytimer = std::time::Instant::now();
    
        // 5秒間スリープ
        std::thread::sleep(std::time::Duration::from_secs(5));
    
        // 時間計測終了
        let elapsed = mytimer.elapsed();
    
        // 経過時間を表示
        println!("{:?}",elapsed);
    
    
    }
    

    結果

    5.0009648s

    値の取得

    表示だけならDuration型に対して{:?}すればよいが、値を取り出す場合は以下のようにする。

        // 経過時間を表示
        println!("second i32 {}",elapsed.as_secs());
        println!("second f32 {}",elapsed.as_secs_f32());
        println!("micro second {}",elapsed.as_micros());
        println!("nano second {}",elapsed.as_nanos());
    

    結果

    second i32 5
    second f32 5.000559
    micro second 5000558
    nano second 5000558900
    

    Rustでクイックソートを書いてみる

    Rustのスライスを使ってみたくて書いた。

    クイックソートを書いたのはたぶん15年ぶりくらい。正直てこずった。

    fn my_qsort_slice(data: &mut [i32]){
    
        if data.len() <= 1{
            return;
        }
    
    
        // ピボットを求める
        let pivot =data[data.len()/2];
    
        let idmin:i32 = 0;
        let idmax:i32 = (data.len()-1) as i32;
    
        //////////////////////////////////////////////////
        // ピボットより小さいものを左に、大きいものを右に分ける
        let mut l =idmin; // 左側の探索位置。pivotより大きい値を探す
        let mut r =idmax; // 右側の探索位置。pivot未満の値を探す
    
        loop{
    
    
            // 左側の交換すべき値を探す
            while data[l as usize] < pivot {
                l += 1;
            }
    
            // 右側の交換すべき値を探す
            while data[r as usize] > pivot {
                r -= 1;
            }
    
            if l >= r{
                break;
            }
    
            // 交換
            let tmp = data[l as usize];
            data[l as usize] = data[r as usize];
            data[r as usize] = tmp;
    
    l += 1;
    r -= 1; } { // 左側のスライスを作成 let mut left = &mut data[0..l as usize]; // 左側のスライスを再帰的にソート my_qsort_slice(&mut left); } { // 右側のスライスを作成 let mut right = &mut data[ (r+1) as usize ..]; // 右側のスライスを再帰的にソート my_qsort_slice(&mut right); } }

    fn my_qsort(data : &mut Vec<i32>){
    
        my_qsort_slice(&mut data[..]);
    
    }
    

    fn main() {
    
    
        let mut data:Vec<i32> = Vec::new();
    
        data = [8,1,9,2,4,6].to_vec();
    
        my_qsort(&mut data);
    
        for i in 0..data.len(){
            println!("{}",data[i]);
        }
    
        println!("Hello, world!");
    }
    

    Rustで乱数

    Rustの乱数の使い方。

    サンプルコード

    Cargo.toml

    乱数を使用するためにはrandクレートを使用する。

    [dependencies]
    # 乱数を使用 https://crates.io/crates/rand
    rand = "0.8.5"

    最も基本的なコード

    fn main() {
    
        use rand::Rng; // 乱数を使うためにrand::Rngを使用
    
        let mut rng = rand::thread_rng();
    
        let min:i32 = 0;
        let max:i32 = 20;
        
        // 50回ループ
        (0..50).for_each(|_|{
    
            let randint:i32 = rng.gen_range(min..max); // 最大・最小を指定して乱数を生成
    
            println!("{}",randint);
    
        });
    
    }
    

    分布を指定して乱数生成

    rand_distrクレートを使用すると分布を変えられる。rand_distr::Normalは正規分布。

     

    [dependencies]
    # 乱数を使用 https://crates.io/crates/rand
    rand = "0.8.5"

    # 乱数の分布を指定 https://crates.io/crates/rand_distr
    rand_distr = "0.4.3"

     

    fn main() {
        use rand::prelude::{Distribution, thread_rng};    
        use rand_distr::Normal;
    
        let mut rng = rand::thread_rng();
    
        let mean:f32 = 10.0;
        let stddev:f32 = 8.0;
        
        // 平均値と標準偏差を用いた乱数生成器を作成
        let normal = Normal::new(
            mean,  // 平均        
            stddev // 標準偏差
       ).unwrap();
    
    
        // 50回ループ
        (0..100).for_each(|_|{
    
            let randint:i32 = normal.sample(&mut rng) as i32; // 乱数を生成
    
            println!("{}",randint);
    
        });
    
    }
    

    win32apiで二次元にランダムな点をプロット

    これだけではさすがにつまらないので、win32apiを使ってSetPixelで点を散布してみる。

    以前作ったメモリデバイスコンテキストのコードで乱数のプロットを表示する。

    Rust+windows-rsでDIBを使う

    winmain.rs

    #[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
    static mydib:Lazy<Mutex<Win32DIBCanvas>> = 
            Lazy::new(|| 
                Mutex::new(
                    Win32DIBCanvas::new()
                )
            );
    
    ///////////////////////////////////////////////
    /// MemDCの背景をクリア  //////////////////////
    fn clear_mydib_background(rmdib : &mut Win32DIBCanvas){
        // super::super::Foundation::COLORREF
        // let refmydib = &mut mydib.lock().unwrap();
        let width:i32 = rmdib.bitmapInfo.bmiHeader.biWidth;
        let height:i32 = -rmdib.bitmapInfo.bmiHeader.biHeight;
    
        let rect:RECT = RECT{
            left:0,
            top:0,
            right:width,
            bottom:height,
        };
    
        unsafe {
            
            let whitebrush = CreateSolidBrush(COLORREF(0x00FFFFFF));
            FillRect(rmdib.hMemDC,&rect,whitebrush);        
            DeleteObject(whitebrush);
            
        }
    
    }
    
    
    ///////////////////////////////////////////////
    /// count個の乱数を生成
    fn  get_random(count:i32, min:i32,max:i32) -> Vec<i32>{
    
        use rand::Rng; // 乱数を使うためにrand::Rngを使用
    
        let mut rng = rand::thread_rng();
    
    
        let mut rlist:Vec<i32> = Vec::new();
    
        for i in 0..count{
    
            let randint:i32 = rng.gen_range(min..max);
    
            rlist.push(randint);
    
        }
    
        return rlist;
    
    }
    

    ///////////////////////////////////////////////
    // ■ HWND ... windows::Win32::Foundation::HWND // ■ WPARAM ... windows::Win32::Foundation::WPARAM // ■ LPARAM ... windows::Win32::Foundation::LPARAM // ■ LRESULT ... windows::Win32::Foundation::LRESULT extern "system" fn wndproc(window: HWND, message: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT { unsafe { // ■ RECT ... windows::Win32::Foundation::RECT let rectnullptr: Option<*const RECT> = None; match message as u32 { WM_CREATE => { // mydib.lock().unwrap() への可変な参照をrefmydibに束縛 let refmydib = &mut mydib.lock().unwrap(); refmydib.create(640,480); clear_mydib_background(refmydib);
                    ///////////////////////////////////////
                    /// 乱数で座標を作成 //////////////////
                    let count = 50000;
                    let xlist:Vec<i32> = get_random(count,0,640);
                    let ylist:Vec<i32> = get_random(count,0,480);
    
                    // 点をプロット
                    for pindex in 0..count as usize{
                        SetPixel(
                            refmydib.hMemDC,
                            xlist[pindex],
                            ylist[pindex],
                            COLORREF(0x00000000));
                    }
                    ///////////////////////////////////////
    
                    // ■ LRESULT ... windows::Win32::Foundation::LRESULT
                    LRESULT(0)
                }
                WM_PAINT => {
    
    
                    // ■ PAINTSTRUCT ... windows::Win32::Graphics::Gdi::PAINTSTRUCT
                    // ■ BeginPaint  ... windows::Win32::Graphics::Gdi::BeginPaint
                    let mut ps = PAINTSTRUCT::default();
                    let hdc = BeginPaint(window, &mut ps);
                    let refmydib = &mut mydib.lock().unwrap();
    
    
                    //////////////////////////////////////////////////////////
                    // 背景をクリア ///////////////////////////////////////////
                    let mut clientrect:RECT = RECT::default();
                    GetClientRect(window, &mut clientrect);
                    let whitebrush = CreateSolidBrush(COLORREF(0x00FFFFFF));
                    FillRect(hdc,&clientrect,whitebrush);
                    DeleteObject(whitebrush);
                    //////////////////////////////////////////////////////////
    
    
                    // ■ BitBlt  ... windows::Win32::Graphics::Gdi
                    // ■ SRCCOPY ... windows::Win32::Graphics::Gdi::SRCCOPY
                    BitBlt(
                        hdc,
                        0,
                        0,
                        640,
                        480,
                        refmydib.hMemDC,
                        0,
                        0,
                        SRCCOPY
                    );
    
                    // ■ EndPaint ... windows::Win32::Graphics::Gdi::EndPaint
                    EndPaint(window, &ps);
    
                    // ■ LRESULT ... windows::Win32::Foundation::LRESULT
                    LRESULT(0)
                }
                WM_DESTROY => {
                    println!("WM_DESTROY");
    
                    // ■ windows::Win32::UI::WindowsAndMessaging::PostQuitMessage
                    PostQuitMessage(0);
    
                    // ■ LRESULT ... windows::Win32::Foundation::LRESULT
                    LRESULT(0)
                }
                // ■ windows::Win32::UI::WindowsAndMessaging::DefWindowProcA
                _ => DefWindowProcA(window, message, wparam, lparam),
            }
        }
    }
    
    
    
    // ■ HINSTANCE ... windows::Win32::Foundation::HINSTANCE
    /// 自作 WinMain
    pub fn WinMain(
            hInstance : HINSTANCE, 
            hPrevInstance : HINSTANCE, 
            lpCmdLine:Vec<String>, 
            nCmdShow:i32
        )->i32{
    
        unsafe{
    
            // ■ PCSTR ... windows::core::PCSTR
            // ■ windows::Win32::UI::WindowsAndMessaging::LoadCursorW
            // ■ windows::Win32::UI::WindowsAndMessaging::IDC_ARROW
            // ■ windows::Win32::UI::WindowsAndMessaging::CS_HREDRAW
            // ■ windows::Win32::UI::WindowsAndMessaging::CS_VREDRAW
            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
            // ■ WNDCLASSA ... windows::Win32::UI::WindowsAndMessaging::WNDCLASSA
            let wc=WNDCLASSA{
                hCursor :_hCursor,
                hInstance: _hInstance,
                lpszClassName: _lpszClassName,
                style: _style,
                lpfnWndProc: Some(wndproc),
                ..Default::default()
            };
    
            // ■ windows::Win32::UI::WindowsAndMessaging::RegisterClassA
            let atom = RegisterClassA(&wc);
            debug_assert!(atom != 0);
    
    
            let nullptr: Option<*const ::core::ffi::c_void> = None;
    
            // ■ windows::Win32::UI::WindowsAndMessaging::CreateWindowExA
            // ■ PCSTR ... windows::core::PCSTR
            // ■ windows::Win32::UI::WindowsAndMessaging::WS_OVERLAPPEDWINDOW
            // ■ windows::Win32::UI::WindowsAndMessaging::WS_VISIBLE
            // ■ windows::Win32::UI::WindowsAndMessaging::CW_USEDEFAULT
            CreateWindowExA(
                Default::default(),
                _lpszClassName,
                PCSTR::from_raw("This is a sample window\0".as_ptr()),
                WS_OVERLAPPEDWINDOW | WS_VISIBLE,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                700,
                600,
                None,
                None,
                _hInstance,
                nullptr,
            );
    
            // ■ windows::Win32::UI::WindowsAndMessaging::MSG
            let mut message = MSG::default();
    
            // ■ windows::Win32::UI::WindowsAndMessaging::GetMessageA
            // ■ windows::Win32::UI::WindowsAndMessaging::DispatchMessageA
            // ■ HWND ... windows::Win32::Foundation::HWND
            while GetMessageA(&mut message, HWND(0), 0, 0).into() {
                DispatchMessageA(&mut message);
            }
        }
        return 0;
    
    
    }
    

    正規分布の乱数

    先のプログラムのget_randomを以下のように変える(minは使わないことにした)と、乱数の分布が変わったのがわかる。

    ///////////////////////////////////////////////
    /// 正規分布の乱数を生成する ///////////////////
    fn get_random(count:i32, max:i32) -> Vec<i32>{
        
        use rand::prelude::{Distribution, thread_rng};    
        use rand_distr::Normal;
    
    
        let mut rng = rand::thread_rng();
    
        // 平均値と標準偏差を用いた乱数生成器を作成
        let normal = Normal::new(
             (max as f32) / 2.0,  // 平均
             (max as f32) / 8.0  // 標準偏差
        ).unwrap();
    
        let mut rlist:Vec<i32> = Vec::new();
    
        for i in 0..count{
    
            let randint:i32 = normal.sample(&mut rng) as i32;// 平均値と標準偏差で乱数生成
    
            rlist.push(randint);
    
        }
    
        return rlist;
    
    }
    

    シードを指定して乱数生成

    シードを指定する場合、以下のように[u8;32]の配列をシードとして与える。シードだけで32byteも与えなければならない。なお[0;32]の配列指定で要素の型がu8に定まるのは、文脈的にseedがfrom_seedで与えられていることからデータ型を推論しているかららしい。

    fn main() {
    
        use rand::Rng; // 乱数を使うためにrand::Rngを使用
        use rand::SeedableRng; // シードの指定に必要
        use rand::rngs::StdRng; // シードの指定に必要
    
        let seed = [0; 32];// 全要素0、要素数32,u8型の配列を作成
        let mut rng:StdRng = SeedableRng::from_seed(seed); // シードを固定した乱数生成器を初期化
        //let mut rng = rand::thread_rng(); // シード未指定の場合
    
    
    
        let min:i32 = 0;
        let max:i32 = 20;
        
        // 50回ループ
        (0..50).for_each(|_|{
    
            let randint:i32 = rng.gen_range(min..max);// 最大・最小を指定して乱数を生成
    
            println!("{}",randint);
    
        });
    }
    

    Blender 3.4とか4.0でGeometry NodeからDistribute Points on Faces

    しばらく使っていなかったらGeometry nodeでオブジェクト周辺に点群を生成する機能の使い方が随分と変わっていたので更新しておきたい。初期のころはPoint Distributeとか呼ばれていたもののこと。

    なお各ノードの追加はすべて[Shift+A]から検索している。

    過去記事:

    変数形式で入力しなければいけなかったところがノード接続になって好感が持てる。

    検索して手間取ったのが生成するインスタンスを指定する方法が、Point InstanceからObject Info->Geometry to Instance とより抽象化されたところ。

    C++のconceptについて超初歩的まとめ

    (誤解を恐れずに言うなら)conceptはテンプレートで渡す際のclass TのTに条件を付けるための機能。

    以下のように、

    template<class T>
    concept 条件名 = requires(T val){ ... }

    という形で定義する。

    型Tで与えられた変数valの使い方を定義すると考えると読みやすくなる。

    #include <iostream>
    
    
    // T型の変数valは、print()と++を持たなければならない
    template
    <class T> concept CallableFunction = requires (T val) { val.print(); // print()関数があることを要求 val.operator++(); // ++演算子があることを要求 };

    // CallableFunctionの制約がついている所で使用できるクラス
    class MyValidClass {
        int val;
    public:
        MyValidClass() {
            val = 5;
        }
    
        void print() {// print()関数を定義
    		std::cout << "val == " << val << std::endl;
    	}
    
        MyValidClass& operator++() {// ++演算子を要求
            ++val;
    		return *this;
    	}
    
    };
    

    // CallableFunctionの制約がある場合は指定できない。
    // operator++() がない。
    class MyInvalid {
        int val;
    public:
        MyInvalid() {
            val = 5;
        }
    
        void print() {
            std::cout << "val == " << val << std::endl;
        }
    };
    

    // print()関数と++演算子を持つクラスを引数に取る関数
    template <CallableFunction Type>
    void func(Type val) {
        val.print();
    }
    

    int main()
    {
        MyValidClass my;
    
        func(my);
    }
    

    式が有効でなければならないという要求を定義

    {  } 内に式を書くと、その式を書くことができるような型だけを要求することができる。

    #include <iostream>
    
    #include <algorithm>
    #include <vector>
    
    
    template <class T>
    concept Sortable = requires (T& val) {
    
        // { } 内の式が有効であることを要求する書き方ができる。
        // 例えば、以下は T 型の val はソート可能であることを要求する
        {
            std::sort(val.begin(), val.end())
        };
    
    };
    

    // print()関数と++演算子を持つクラスを引数に取る関数
    template <Sortable Type>
    void func(Type& val) {
    
        std::sort(val.begin(), val.end());
    
    }
    

    int main()
    {
        std::vector<int> my{ 5,3,8 };
    
        func(my);
    
    
        for (auto& i : my) {
            std::cout << i << std::endl;
        }
    }
    

    戻り値の要求

    { 式 } で、式が有効であることを要求できるが、式の結果のデータ型を要求するには

    { 式 } -> std::same_as<型>;

    とする。

    この仕組みを応用すれば、関数の戻り値を要求できる。

    #include <iostream>
    
    #include <algorithm>
    #include <vector>
    
    
    template <class T>
    concept Sizable = requires (T & val) {
    
        {
            val.size() // size()関数を持つ
    
        }->std::same_as<size_t>; // size()関数の戻り値はsize_t型
        // std::same_asはconcept。式の戻り値の型を指定する。
    };
    

    class MyArray {
        std::vector<int>vec;   
    public: 
        void push_back(int i) { vec.push_back(i); }
        int size() { return vec.size(); }
    };
    

    // この関数は、戻り値がsize_t型のsize()関数を持つ型しか受け付けない
    template <Sizable Type>
    void func(Type& val) {
    
        std::cout<<val.size()<<std::endl;
    
    }
    

    int main()
    {
        std::vector<int> my;
        // MyArray my;
    
        my.push_back(3);
        my.push_back(1);
        my.push_back(8);
    
        func(my);
    
    }
    

    wxWidgetsをstatic link libraryとしてビルドする

    wxWidgetsを使用した場合の問題の一つが、配布時に同梱するDLLの数が増えることで、これを解決するためにwxWidgetsをStatic Link Libraryとしてビルドしてexeとくっつけてしまうことで、DLLの同梱を不要にする。

    CMake

    ダウンロード

    まずソースコードをダウンロードするが、zipでは依存関係が多くて面倒なので、git clone --recursiveで依存関係も含め一括でダウンロードする。

    git clone --recursive https://github.com/wxWidgets/wxWidgets.git

    cmake

    CMakeでは、wxBUILD_SHAREDを検索し、このチェックを外す

    ALL_BUILDをビルドし、INSTALLする。

    使用方法

    基本的な使い方はShared Libraryの時と同じだが、何点か注意点がある。

    VC++でwxWidgetsを試す(1)

    注意点

    ・WXUSINGDLL を指定しない

    プリプロセッサにWXUSINGDLLを指定しない。指定してしまうとLNK2001が大量発生する

    ・windowsのlibファイルのリンクを追加

    comctl32.lib , rpcrt4.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")
    
    // wxWidgets
    #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")
    
    
    /////////////////////////////////////
    /////////////////////////////////////
    /////////////////////////////////////
    
    // ウィンドウ作成
    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);
    

    win32api タスクトレイアイコンをクリックしたときにメニューを表示

    なんでもタスクトレイという言い方は正しくなく、通知領域、Notification Areaが正しいらしい。知ったことではない。

    タスクトレイにアイコンを表示する方法を以前調べた。

    タスクトレイにアイコンを表示する

    クリックしたときにメニューを表示する場合、GetCursorPosでマウスカーソルの位置を取得して、TrackPopupMenuでメニューを表示する。

    #pragma warning(disable:4996)
    
    #include <windows.h>
    
    #include <shellapi.h>
    
    static const int TasktrayIconID = 100;
    static const int WM_MY_NOTIFYICON = WM_USER + TasktrayIconID;
    
    const int ID_MENU_MSG = 100;
    const int ID_MENU_EXIT = 101;
    
    HMENU myCreateMenu();
    void intoTasktray(HWND hwnd, const int IconID);
    void fromTasktray(HWND hwnd, const int IconID);
    void menuPopup(HWND hwnd, HMENU menu);
    
    /////////////////////////////////
    /////////////////////////////////
    /////////////////////////////////
    /////////////////////////////////
    
    
    LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
    
    
      static HMENU hMenu = nullptr;
    
      switch (msg) {
      case WM_CREATE:
        hMenu = myCreateMenu();//メニュー作成
        break;
      case WM_LBUTTONDOWN://ウィンドウをクリックしたらアイコン表示開始
      {
        intoTasktray(hwnd, TasktrayIconID);
        break;
      }
      case WM_MY_NOTIFYICON:
        if (lp == WM_LBUTTONDOWN && wp == TasktrayIconID) {
          // アイコンを左クリックしたらウィンドウを表示
          fromTasktray(hwnd, TasktrayIconID);
        }
        else if (lp == WM_RBUTTONUP) {
          // アイコンを右クリックしたらポップアップメニューを表示
          menuPopup(hwnd, hMenu);
        }
        break;
      case WM_COMMAND:
        switch (wp)
        {
        case ID_MENU_MSG:  // "Open"がクリックされた場合
          MessageBoxA(hwnd, "MessageBox", "Info", MB_OK);
          break;
        case ID_MENU_EXIT:  // "Exit"がクリックされた場合
          DestroyWindow(hwnd);
          break;
        }
        break;
    
      case WM_DESTROY:
        DestroyWindow(hwnd);
        PostQuitMessage(0);
        return 0;
      }
      return DefWindowProc(hwnd, msg, wp, lp);
    }
    
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    	PSTR lpCmdLine, int nCmdShow) {
    	HWND hwnd;
    	MSG msg;
    	WNDCLASS winc;
    
    	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-WINDOW");
    
    	if (!RegisterClass(&winc)) return -1;
    
    	hwnd = CreateWindow(
    		TEXT("SZL-WINDOW"), TEXT("test"),
    		WS_OVERLAPPEDWINDOW | WS_VISIBLE,
    		CW_USEDEFAULT, CW_USEDEFAULT,
    		300, 300,
    		NULL, NULL, hInstance, NULL
    	);
    
    	if (hwnd == NULL) return -1;
    
    	while (GetMessage(&msg, NULL, 0, 0)) DispatchMessage(&msg);
    	return msg.wParam;
    }
    
    
    
    // メニューの作成
    HMENU myCreateMenu() {
      HMENU menu = CreatePopupMenu();
      AppendMenuA(menu, MF_STRING, ID_MENU_MSG, "Open");
      AppendMenuA(menu, MF_STRING, ID_MENU_EXIT, "Exit");
    
      return menu;
    }
    

    // ウィンドウを消し、アイコンをタスクトレイへ格納
    void intoTasktray(HWND hwnd, const int IconID) {
    
      NOTIFYICONDATA nid;
      nid.cbSize = sizeof(nid);
      nid.hWnd = hwnd;
      nid.uID = IconID;//複数のアイコンを表示したときの識別ID。なんでもいい。
      nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
      nid.uCallbackMessage = WM_MY_NOTIFYICON; //WM_USER以降の定数。
      nid.hIcon = (HICON)LoadIcon(NULL, IDI_APPLICATION);
      wcscpy(nid.szTip, L"タスクトレイで表示する文字列");
      int ret = (int)Shell_NotifyIcon(NIM_ADD, &nid);
      ShowWindow(hwnd, SW_HIDE);
    
    }
    

    // タスクトレイからアイコンを削除し、ウィンドウを表示
    void fromTasktray(HWND hwnd, const int IconID) {
      ShowWindow(hwnd, SW_SHOW);
    
      //タスクトレイアイコンを削除
      NOTIFYICONDATA nid;
      nid.cbSize = sizeof(nid);
      nid.hWnd = hwnd;
      nid.uID = IconID;
      nid.uFlags = 0;
      Shell_NotifyIcon(NIM_DELETE, &nid);
    
    }
    

    // ポップアップメニューを表示
    void menuPopup(HWND hwnd, HMENU menu) {
      POINT pt;
      GetCursorPos(&pt);
      SetForegroundWindow(hwnd);
      TrackPopupMenu(menu, TPM_RIGHTALIGN, pt.x, pt.y, 0, hwnd, NULL);
    
    }
    

    Rust+windows-rsでDIBを使う

    CreateDIBSectionを使ってDIBを作り、CreateCompatibleDCを使って描画してみる。

    各関数・データ型・定数がwindows-rsのどこにあるかさえわかれば、あとはそこまで難しくはない。

    • win32dib.rs ... DIB関連の変数を一括で管理する構造体+CreateDIBSection,CreateCompatibleDCを呼び出す関数
    • main.rs ... エントリポイント
    • winmain.rs ... ウィンドウを表示して描画を行う

    一番厄介なのはwinmain.rs内でmydibのインスタンスであるグローバル変数で、これはstatic + Lazy + Mutexで実現しないといけない。

    win32dib.rs

    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.rs

    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(())
    
    }
    

    winmain.rs

    #[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;
    
    
    }