簡単に手持ちのC,C++の処理に進捗バーを実装する方法

 C言語やC++で何か処理を書ける人は多いですが、Windowプログラムとなると実装方法が難しいです。VisualStudioのプロジェクトの雛形にボタンを配置し、そこから手持ちのコードを起動すれば動きそうです。

しかし、その方法では「未応答」状態になったり、ウィンドウの操作すらできなくなります。もちろん処理が終われば正常に動きはしますが、見た目は悪いです。

進捗バーを表示しようと配置しても、当然それを動かすためのコードも必要です。

メッセージループを処理しながら、自前のコードを動かしたり、「状態機械」にコードを移植した後でメッセージループに組み込むなど方法はありますが、非常に可読性が悪くなります。

今回は、スレッドを一本発行することで手持ちのコードを動作させます。完全に無改造では動かないですが、手持ちの関数に2つの処理を埋め込むだけで対応できます。

1つ目は進捗バーに状態を知らせるためのグローバル変数を更新することです。処理に応じて増分させた数値を定期的に書き込んでください。

2つ目は強制終了フラグを監視し、検出したら処理ループを安全に停止させる処理を行うことです。外部からスレッドを停止させる方法は、処理中のファイルやリソースのクローズがされないので問題があります。 強制終了フラグを検知したら、区切りの良い場所まで処理して、その後クロージング処理などが出来ます。

このように簡単に手持ちのC,C++コードを今風に作り上げることが出来ます。個人的に、仕事で変換プログラムなどを持っている人はリニューアルしてみてください。

AmazonでC++関連の書籍を買います

  1. /*******************************************************
  2. G++ .\progress2.cpp -mwindows -lvfw32 -municode -o progress2 でコンパイルしてください。
  3. 手持ちのC言語やC++で作成した時間がかかる変換プログラムも簡単にウィンドウアプリに出来ます。
  4. Tiktok @pistalove 好きなことをやらない人生は最高 も御覧ください。
  5. ********************************************************/
  6. #include <windows.h>
  7. #include <commctrl.h>
  8. HWND hWnd; //メインウィンドウハンドル
  9. HWND hProgress; //進捗表示用ハンドル
  10. HWND hButton; //ボタン表示用ハンドル
  11. HWND hText; //文字表示用ハンドル
  12. int iProgress = 0; //進捗状態
  13. int bAbort = false; //強制終了フラグ
  14. /******************************************
  15. ここに時間がかかる手持ちのコードを書きます。仕様は
  16. ・bAbortがtrueになったら親ウィンドウの停止ボタンが押されています。安全に処理を終了してください。
  17. ・iProgressに進捗状況をパーセントで書き込んでください。進捗バーが動きます。
  18. ・処理が完全に終了したら SendMessageで親ウィンドウを閉じてください。
  19. ・手持ちのGUIに対応していないプログラムをコピペすれば進捗バーと停止ボタンが付いて見た目が良いです。
  20. ********************************************/
  21. DWORD WINAPI MyThreadFunction( LPVOID lpParam ){
  22.      for (int i = 0; i <= 100; ++i) {
  23.             
  24.                 if(bAbort) break; //強制終了指示が出たら終了する
  25.                 iProgress = i;
  26.                  Sleep(500); //試験的に待っています。
  27.             }
  28.  
  29.     SendMessage(hWnd, WM_DESTROY, 0, 0);
  30.     return 0;
  31. }
  32. //以下のコードは基本的にそのままで動きます。表示される文字やウィンドウの体裁だけ整えてください。
  33. //ウィンドウプロシージャ
  34. LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
  35.     switch (message) {
  36.     case WM_CREATE:
  37.         // プログレスバーの作成
  38.         hProgress = CreateWindowEx(0, PROGRESS_CLASS, NULL,WS_CHILD | WS_VISIBLE | PBS_SMOOTH,10,40,280,10,hWnd,NULL,NULL,NULL);
  39.         SendMessage(hProgress, PBM_SETRANGE, 0, MAKELPARAM(0, 100));
  40.         SendMessage(hProgress, PBM_SETSTEP, 1, 0);
  41.         hText = CreateWindowEx(0,L"STATIC",
  42.                 L"Tiktok @pistalove", // 表示するテキスト
  43.                 WS_VISIBLE | WS_CHILD | SS_CENTER,10, 10,280, 20,hWnd,NULL,NULL,NULL);
  44.     hButton = CreateWindow(L"BUTTON",L"停止",WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,230,55,60,30,hWnd,(HMENU)1,NULL,NULL);
  45.     
  46.     SetTimer(hWnd, 1, 100, NULL);//進捗画面更新用タイマー設定100ミリ秒に一回
  47.         CreateThread(NULL, 0, MyThreadFunction, NULL, 0, NULL); //手持ちのコードの関数が実行されます。
  48.         return 0;
  49.     case WM_COMMAND:
  50.         if (LOWORD(wParam) == 1) { //ボタンクリック
  51.             bAbort = true; //停止フラグを設定
  52.         }
  53.         return 0;
  54.     case WM_TIMER:
  55.     SendMessage(hProgress, PBM_SETPOS, iProgress, 0);
  56.     
  57.     return 0;
  58.     
  59.     case WM_CLOSE:
  60.      return 0;
  61.     
  62.     case WM_DESTROY:
  63.      KillTimer(hWnd, 1);
  64.         PostQuitMessage(0);
  65.         return 0;
  66.     default:
  67.         return DefWindowProc(hWnd, message, wParam, lParam);
  68.     }
  69. }
  70. int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hinstPrev, LPWSTR lpCmdLine, int nCmdShow){
  71.     WNDCLASS wc = {};
  72.     wc.lpfnWndProc = WindowProc;
  73.     wc.hInstance = hInstance;
  74.     wc.lpszClassName = L"ProgressDemo";
  75.     
  76.     RegisterClass(&wc);
  77.     
  78.     hWnd = CreateWindowEx(0, L"ProgressDemo", L"Tiktok @Pistalove",    WS_OVERLAPPEDWINDOW ^ (WS_THICKFRAME|WS_MAXIMIZEBOX) | WS_VISIBLE,CW_USEDEFAULT, CW_USEDEFAULT, 320, 200, NULL,NULL,hInstance, NULL);
  79.     
  80.     MSG msg;
  81.     while (GetMessage(&msg, NULL, 0, 0)) {
  82.         TranslateMessage(&msg);
  83.         DispatchMessage(&msg);
  84.     }
  85.     return 0;
  86. }

CSVで作成したログを動画に重ねる方法

 


データロガーや波形測定機、サイクルコンピュータなどで取得した情報を観察した動画に重ねたいときに便利なツールを作成しました。測定器からCSVファイルに変換できれば後は変換するだけです。

使いかた

  1. csvファイルを作成します。一行目が項目名(動画の温度とか)、二行目が単位(%)などです。三行目からがデータになります。データはそのまま動画に書き込まれますので、各自フォーマット整形をお願いします。カンマ区切りなのでカンマは使えません。また、エスケープも出来ません。どうしても使いたい場合は全角カンマを使ってください。文字はUnicodeに対応しています。大体の文字は対応できると考えます。
  2. data2avi.exe を実行します。問題なければvideo.aviという動画ファイルが出来ます。
  3. この動画ファイルを動画編集ソフトで希望の動画に重ねます。動画は1秒周期で出来ます。細かいサンプリングが希望なら、多めのCSVレコードを作成して、再生速度を早めれば対応可能だと考えます。
  4. アルファチャンネルが含まれますので、設定を有効にすれば背面が透けます。スッキリと合成できると考えます。
  5. 限度はありますが、CSVのフィールドを増やせば多くの項目を合成できます。
  6. 項目名、単位名で表示したくない場合は全角スペースで埋めることをおすすめします。
  7. 大体のエラーは対処方法を日本語で表示していますので対応可能だと思われます。

実行ファイルは以下からダウンロードしてください。ネットアクセスやdata.csvへの読み込み、video.aviへの出力以外の処理は行っていないので安心してください。

http://tool.toyokawa.jp/software/data2avi.zip


#warning MSACM.H: Defining _UNICODE because application defined UNICODE

 GPPでビルドするとエラーではないが

#warning MSACM.H: Defining _UNICODE because application defined UNICODE

という警告が出る場合があります。この場合、Unicodeを使っているがその宣言をしていないためだと考えます。この場合には

#define _UNICODE //ワイド文字列 (Unicode) 対応

の定義を行えば警告が出ません。是非試してください。プログラム初心者にはわかりにくいですが、プログラム中の文字列の格納方法は

  • Ascii文字(8ビットの英数字、昔からある形式)
  • マルチバイト文字列 (SJIS)  #define _MBCS
  • ワイド文字列 (Unicode) 対応 #define _UNICODE

があります。別に自前で全てを8ビット文字列として扱えば良いと思われますが、既存の関数を使いたい場合は、文字列の違いはこの仕組みで吸収されます。ちょっと苦しいですが覚えておくと良いと考えます。

C++でアルファチャンネル付きAVIを書き出す方法 サイコン情報書き込みに役立ちそうです


サイコン(サイクルコンピュータ)から走行情報を一秒単位のCSVファイルに変換して、それをAVIに書き込めば高価なソフト不要で走行動画に斜度や心拍情報などを表示できます。とりあえずは、文字の部分は透けずに、背景の緑の部分だけをある程度透けるようなAVIファイルを作成しています。

とりあえずはアルファチャンネル付きのAVIが出力されていますので、ある程度のプログラミングができる人であれば動画に重ねる情報を作るソフトなどを作成できそうです。

手持ちのテキスト情報を時系列に変換したり、気温データなどを動画に重ねて表示するなどの応用例が考えられます。とりあえずは単に数字をカウントアップするだけのサンプルファイルです。

  1. #include <windows.h>
  2. #include <vfw.h>
  3. #pragma comment (lib, "vfw32.lib")
  4. const int cWidth = 300;
  5. const int cHeight = 200;
  6. //実行すると100までカウントアップするアルファチャンネル付きAVI動画を作成します。複数レイヤで正確なマスクを作成します。
  7. //g++ .\outCntAviA2.cpp -mwindows -lvfw32
  8. //Bチャンネルの情報をアルファチャンネルとして独自バッファへ保存
  9. void makeAlpha(LPDWORD lpPixel,int width,int height,unsigned char *buf){
  10.     
  11.         unsigned char alphaValue;
  12.         
  13.         for(int y=0;y<height;y++){
  14.     for(int x=0;x<width;x++){
  15.     int memadr = x+y*width;
  16.                 unsigned char *data = (unsigned char *)&lpPixel[memadr];
  17.     buf[memadr] = *data;
  18.             }
  19.         }
  20. }
  21. //独自バッファへ保存されたアルファチャンネルを実際のアルファチャンネルへコピー
  22. void loadAlpha(LPDWORD lpPixel,int width,int height,unsigned char *buf){
  23.     
  24.         
  25.         unsigned char alphaValue;
  26.         
  27.         for(int y=0;y<height;y++){
  28.     for(int x=0;x<width;x++){
  29.     int memadr = x+y*width;
  30.                 unsigned char *pTest=(unsigned char *)&lpPixel[memadr];
  31.    pTest +=3; //アドレスを3つ増分(アルファチャンネルにアクセス)
  32.                 *pTest = buf[memadr]; //本物のアルファチャンネルに独自アルファチャンネルをコピー
  33.                 }
  34.         }
  35. }
  36. int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
  37. {
  38.     PAVIFILE pfile;
  39.     PAVISTREAM pavi;
  40.     AVISTREAMINFO si;
  41.     LPAVISTREAMINFO lpsi = &si;
  42.     HDC hdcMem;
  43.     HBITMAP hbmpMem, hbmpMemPrev;
  44.     DWORD i;
  45.     TCHAR szBuf[32];
  46.     LPVOID lpBits;
  47.     BITMAPV5HEADER bmiHeader;    
  48.     
  49.     
  50.     unsigned char * savealpha; //アルファチャンネル保存用
  51.     
  52.     //作業用描画領域
  53.     HDC hdcWork,hdcMask;
  54.     HBITMAP hdcWorkBitmap,hdcMaskBitmap,hdcWorkBitmapPrev,hdcMaskBitmapPrev;
  55.     
  56.     AVIFileInit();
  57.     if (AVIFileOpen(&pfile, TEXT("video.avi"), OF_CREATE | OF_WRITE, NULL) != 0) {
  58.         MessageBox(NULL, TEXT("Fail file open."), TEXT("OK"), MB_OK);
  59.         AVIFileExit();
  60.         return 0;
  61.     }
  62.     ZeroMemory(&si, sizeof(AVISTREAMINFO));
  63.     si.fccType = streamtypeVIDEO;
  64.     si.fccHandler = comptypeDIB;
  65.     si.dwScale = 1;
  66.     si.dwRate = 2;
  67.     si.dwLength = 0;
  68.     si.dwQuality = (DWORD)-1;
  69.     SetRect(&si.rcFrame, 0, 0, cWidth,cHeight);
  70.     if (AVIFileCreateStream(pfile, &pavi, &si) != 0) {
  71.         MessageBox(NULL, TEXT("Fail open stream."), TEXT("OK"), MB_OK);
  72.         AVIFileRelease(pfile);
  73.         AVIFileExit();
  74.         return 0;
  75.     }
  76.     ZeroMemory(&bmiHeader, sizeof(BITMAPINFOHEADER));
  77.     bmiHeader.bV5Size = sizeof(BITMAPINFOHEADER);
  78.     bmiHeader.bV5Width = lpsi->rcFrame.right;
  79.     bmiHeader.bV5Height = lpsi->rcFrame.bottom;
  80.     bmiHeader.bV5Planes = 1;
  81.     bmiHeader.bV5BitCount = 32;
  82.     bmiHeader.bV5Compression = BI_RGB;
  83.     bmiHeader.bV5SizeImage = bmiHeader.bV5Height * bmiHeader.bV5Width * 4;
  84.     if (AVIStreamSetFormat(pavi, 0, &bmiHeader, sizeof(BITMAPINFOHEADER)) != 0){
  85.         MessageBox(NULL, TEXT("Fail Init BMI Header."), TEXT("OK"), MB_OK);
  86.     }
  87.     savealpha = new unsigned char[cWidth*cHeight];
  88.     
  89.     hdcMem = CreateCompatibleDC(NULL);
  90.     hbmpMem = CreateDIBSection(NULL, (LPBITMAPINFO)&bmiHeader, DIB_RGB_COLORS, &lpBits, NULL, 0);
  91.     hbmpMemPrev = (HBITMAP)SelectObject(hdcMem, hbmpMem);
  92.     
  93.     //作業用領域確保
  94.     hdcWork = CreateCompatibleDC(hdcMem);
  95.     hdcWorkBitmap = CreateCompatibleBitmap(hdcMem,cWidth,cHeight);
  96.     hdcWorkBitmapPrev = (HBITMAP)SelectObject(hdcWork,hdcWorkBitmap);
  97.     
  98.     hdcMask =CreateCompatibleDC(NULL);
  99.     hdcMaskBitmap = CreateCompatibleBitmap(hdcMem,cWidth,cHeight);
  100.     hdcMaskBitmapPrev = (HBITMAP)SelectObject(hdcMask,hdcMaskBitmap);
  101.     
  102.     //バックグラウンドモードを透過に設定
  103.     SetBkMode(hdcWork, TRANSPARENT);
  104.     SetBkMode(hdcMask, TRANSPARENT);
  105.     //テキスト描画カラーを設定
  106.     SetTextColor(hdcWork, RGB(255, 0, 0));    //純粋にこの色が描画される
  107.     SetTextColor(hdcMask, RGB(0, 0, 255));    //アルファチャンネル用として描画される(青チャンネルをアルファとして利用)
  108.     
  109.     HGDIOBJ hFont = CreateFontW(100, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_OUTLINE_PRECIS,
  110.         CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, DEFAULT_PITCH | FF_DONTCARE, L"UI Gothic");
  111.     //仮領域とマスク領域にフォントを設定+初期設定されたフォントを保管
  112.     HGDIOBJ hWorkFontOld = SelectObject(hdcWork, hFont);
  113.     HGDIOBJ hMaskFontOld = SelectObject(hdcMask, hFont);
  114.     
  115.     
  116.     HBRUSH hBrush = CreateSolidBrush(RGB(30, 255, 30));    //背景色描画用
  117.     HBRUSH hBrushMask = CreateSolidBrush(RGB(0, 0, 150));//アルファチャネル用背景
  118.     for (int fCnt = 0; fCnt < 1000; fCnt++) {
  119.         wsprintf(szBuf, TEXT("%d"), fCnt);
  120.         FillRect(hdcWork, &lpsi->rcFrame, hBrush); //仮領域へ描画
  121.         FillRect(hdcMask, &lpsi->rcFrame, hBrushMask); //マスク領域へ描画
  122.         DrawText(hdcWork, szBuf, -1, &lpsi->rcFrame, DT_CENTER | DT_VCENTER | DT_SINGLELINE); //仮領域へ文字出力
  123.         DrawText(hdcMask, szBuf, -1, &lpsi->rcFrame, DT_CENTER | DT_VCENTER | DT_SINGLELINE); //マスク領域へ文字出力
  124.         
  125.         BitBlt(hdcMem,0,0,cWidth,cHeight,hdcMask,0,0,SRCCOPY); //とりあえずマスク領域をメイン領域へコピー
  126.         makeAlpha((LPDWORD)lpBits,cWidth,cHeight,savealpha); //メイン領域の描画情報を独自のアルファチャンネルへコピー
  127.         
  128.         BitBlt(hdcMem,0,0,cWidth,cHeight,hdcWork,0,0,SRCCOPY); //裏描画領域をメイン領域へコピー
  129.         loadAlpha((LPDWORD)lpBits,cWidth,cHeight,savealpha);    //独自のアルファチャンネルからメイン領域のアルファチャンネルへコピー
  130.         AVIStreamWrite(pavi, fCnt, 1, lpBits, bmiHeader.bV5SizeImage, AVIIF_KEYFRAME, NULL, NULL);    //AVIファイルに一コマ書き込み
  131.     }
  132.     
  133.     delete[] savealpha;    //アルファチャンネル保存領域を開放
  134.     
  135.     //hdcで掴んでいるフォント手放して、それぞれ元のフォントを掴む
  136.     SelectObject(hdcWork, hWorkFontOld);
  137.     SelectObject(hdcMask, hMaskFontOld);
  138.     
  139.     //フォントを削除
  140.     DeleteObject(hFont);
  141.     //ブラシの削除    
  142.     DeleteObject(hBrushMask);
  143.     DeleteObject(hBrush);
  144.     
  145.     SelectObject(hdcWork, hdcWorkBitmapPrev);
  146.     DeleteObject(hdcWorkBitmap);
  147.     DeleteDC(hdcWork);
  148.     
  149.     SelectObject(hdcMask, hdcWorkBitmapPrev);
  150.     DeleteObject(hdcMaskBitmap);
  151.     DeleteDC(hdcMask);
  152.     
  153.     SelectObject(hdcMem, hbmpMemPrev);
  154.     DeleteObject(hbmpMem);
  155.     DeleteDC(hdcMem);
  156.     AVIStreamRelease(pavi);
  157.     AVIFileRelease(pfile);
  158.     AVIFileExit();
  159.     MessageBox(NULL, TEXT("Done."), TEXT("OK"), MB_OK);
  160.     
  161.     return 0;
  162. }

C++でアルファチャンネル付きのAVIを出力する方法

 とりあえず、1ピクセル32ビットのアルファチャンネルを出力できるC++のコードを作成できました。コンパイルして実行すればAVIファイルができるので、それを動画編集ソフトに取り込めば背景が透過します。

バグなどの問題もあるので、手持ちの数値データを動画にしたいなどの個人的なツールとして利用するための参考にしてみてください。

  1. #include <windows.h>
  2. #include <vfw.h>
  3. #pragma comment (lib, "vfw32.lib")
  4. //実行すると100までカウントアップするアルファチャンネル付きAVI動画を作成します。。
  5. //g++ .\outCntAviA.cpp -mwindows -lvfw32
  6. void paintAlpha(LPDWORD lpPixel,const unsigned int alpha,const RECT *fill,const RECT *canbas){
  7.     
  8.         unsigned char *pTest;
  9.         
  10.         for(int y=fill->top;y<fill->bottom;y++){
  11.     for(int x=fill->left;x<fill->right;x++){
  12.     
  13.     int memadr = x+(canbas->bottom - y)*canbas->right;
  14.   
  15.     
  16.    
  17.    pTest=(unsigned char *)&lpPixel[memadr];
  18.    pTest++; //B
  19.    pTest++; //G
  20.    pTest++; //R
  21.     *pTest = alpha; //A
  22.     
  23.     }
  24.         }
  25.     
  26.     
  27. }
  28. int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
  29. {
  30.     PAVIFILE pfile;
  31.     PAVISTREAM pavi;
  32.     AVISTREAMINFO si;
  33.     LPAVISTREAMINFO lpsi = &si;
  34.     HDC hdcMem;
  35.     HBITMAP hbmpMem, hbmpMemPrev;
  36.     DWORD i;
  37.     TCHAR szBuf[32];
  38.     LPVOID lpBits;
  39.     BITMAPV5HEADER bmiHeader;    
  40.     
  41.     AVIFileInit();
  42.     if (AVIFileOpen(&pfile, TEXT("video.avi"), OF_CREATE | OF_WRITE, NULL) != 0) {
  43.         MessageBox(NULL, TEXT("Fail file open."), TEXT("OK"), MB_OK);
  44.         AVIFileExit();
  45.         return 0;
  46.     }
  47.     ZeroMemory(&si, sizeof(AVISTREAMINFO));
  48.     si.fccType = streamtypeVIDEO;
  49.     si.fccHandler = comptypeDIB;
  50.     si.dwScale = 1;
  51.     si.dwRate = 2;
  52.     si.dwLength = 0;
  53.     si.dwQuality = (DWORD)-1;
  54.     SetRect(&si.rcFrame, 0, 0, 300,200);
  55.     if (AVIFileCreateStream(pfile, &pavi, &si) != 0) {
  56.         MessageBox(NULL, TEXT("Fail open stream."), TEXT("OK"), MB_OK);
  57.         AVIFileRelease(pfile);
  58.         AVIFileExit();
  59.         return 0;
  60.     }
  61.     ZeroMemory(&bmiHeader, sizeof(BITMAPINFOHEADER));
  62.     bmiHeader.bV5Size = sizeof(BITMAPINFOHEADER);
  63.     bmiHeader.bV5Width = lpsi->rcFrame.right;
  64.     bmiHeader.bV5Height = lpsi->rcFrame.bottom;
  65.     bmiHeader.bV5Planes = 1;
  66.     bmiHeader.bV5BitCount = 32;
  67.     bmiHeader.bV5Compression = BI_RGB;
  68.     bmiHeader.bV5SizeImage = bmiHeader.bV5Height * bmiHeader.bV5Width * 4;
  69.     if (AVIStreamSetFormat(pavi, 0, &bmiHeader, sizeof(BITMAPINFOHEADER)) != 0){
  70.         MessageBox(NULL, TEXT("Fail Init BMI Header."), TEXT("OK"), MB_OK);
  71.     }
  72.     hdcMem = CreateCompatibleDC(NULL);
  73.     hbmpMem = CreateDIBSection(NULL, (LPBITMAPINFO)&bmiHeader, DIB_RGB_COLORS, &lpBits, NULL, 0);
  74.     hbmpMemPrev = (HBITMAP)SelectObject(hdcMem, hbmpMem);
  75.     SetBkMode(hdcMem, TRANSPARENT);
  76.     SetTextColor(hdcMem, RGB(255, 255, 255));
  77.     HGDIOBJ hFont = CreateFontW(100, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_OUTLINE_PRECIS,
  78.         CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, DEFAULT_PITCH | FF_DONTCARE, L"UI Gothic");
  79.     HGDIOBJ hFontOld = SelectObject(hdcMem, hFont);    
  80.     
  81.     HBRUSH hBrush = CreateSolidBrush(RGB(0, 0, 0));
  82.     for (int fCnt = 0; fCnt < 1000; fCnt++) {
  83.         wsprintf(szBuf, TEXT("%d"), fCnt);
  84.         FillRect(hdcMem, &lpsi->rcFrame, hBrush);
  85.         //アルファチャンネル塗りつぶし
  86.         paintAlpha((LPDWORD)lpBits,0x90,&si.rcFrame,&si.rcFrame);
  87.         
  88.         DrawText(hdcMem, szBuf, -1, &lpsi->rcFrame, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
  89.         AVIStreamWrite(pavi, fCnt, 1, lpBits, bmiHeader.bV5SizeImage, AVIIF_KEYFRAME, NULL, NULL);
  90.     }
  91.     
  92.     SelectObject(hdcMem, hFontOld);
  93.     DeleteObject(hFont);
  94.     
  95.     DeleteObject(hBrush);
  96.     
  97.     SelectObject(hdcMem, hbmpMemPrev);
  98.     DeleteObject(hbmpMem);
  99.     DeleteDC(hdcMem);
  100.     
  101.     MessageBox(NULL, TEXT("Done."), TEXT("OK"), MB_OK);
  102.     AVIStreamRelease(pavi);
  103.     AVIFileRelease(pfile);
  104.     AVIFileExit();
  105.     return 0;
  106. }

C++でAVIファイルを作成するサンプルコード

 C++でAVIの動画ファイルを出力する方法です。以下のソースコードをG++でビルドすることで0から100までのカウントアップ動画を出力します。GDIに描画する方法を知っていればその内容をアニメーションにして出力できます。また、CSVのデータを読み込み動画に合わせて出力する(サイコンからのデータを動画にする)などの応用が可能です。

出力サンプル


ソースコード

  1. #include <windows.h>
  2. #include <vfw.h>
  3. #pragma comment (lib, "vfw32.lib")
  4. //実行すると100までカウントアップするAVI動画を作成します。以下のコンパイルオプションでビルドしてください。
  5. //g++ .\outCntAvi.cpp -mwindows -lvfw32
  6. int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
  7. {
  8.     PAVIFILE pfile;
  9.     PAVISTREAM pavi;
  10.     AVISTREAMINFO si;
  11.     LPAVISTREAMINFO lpsi = &si;
  12.     HDC hdcMem;
  13.     HBITMAP hbmpMem, hbmpMemPrev;
  14.     DWORD i;
  15.     TCHAR szBuf[32];
  16.     LPVOID lpBits;
  17.     BITMAPINFOHEADER bmiHeader;    
  18.     
  19.     AVIFileInit();
  20.     if (AVIFileOpen(&pfile, TEXT("video.avi"), OF_CREATE | OF_WRITE, NULL) != 0) {
  21.         MessageBox(NULL, TEXT("Fail file open."), TEXT("OK"), MB_OK);
  22.         AVIFileExit();
  23.         return 0;
  24.     }
  25.     ZeroMemory(&si, sizeof(AVISTREAMINFO));
  26.     si.fccType = streamtypeVIDEO;
  27.     si.fccHandler = comptypeDIB;
  28.     si.dwScale = 1;
  29.     si.dwRate = 2;
  30.     si.dwLength = 0;
  31.     si.dwQuality = (DWORD)-1;
  32.     SetRect(&si.rcFrame, 0, 0, 320, 240);
  33.     if (AVIFileCreateStream(pfile, &pavi, &si) != 0) {
  34.         MessageBox(NULL, TEXT("Fail open stream."), TEXT("OK"), MB_OK);
  35.         AVIFileRelease(pfile);
  36.         AVIFileExit();
  37.         return 0;
  38.     }
  39.     ZeroMemory(&bmiHeader, sizeof(BITMAPINFOHEADER));
  40.     bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  41.     bmiHeader.biWidth = lpsi->rcFrame.right;
  42.     bmiHeader.biHeight = lpsi->rcFrame.bottom;
  43.     bmiHeader.biPlanes = 1;
  44.     bmiHeader.biBitCount = 24;
  45.     bmiHeader.biCompression = BI_RGB;
  46.     bmiHeader.biSizeImage = bmiHeader.biHeight * ((3 * bmiHeader.biWidth + 3) / 4) * 4;
  47.     AVIStreamSetFormat(pavi, 0, &bmiHeader, sizeof(BITMAPINFOHEADER));
  48.     hdcMem = CreateCompatibleDC(NULL);
  49.     hbmpMem = CreateDIBSection(NULL, (LPBITMAPINFO)&bmiHeader, DIB_RGB_COLORS, &lpBits, NULL, 0);
  50.     hbmpMemPrev = (HBITMAP)SelectObject(hdcMem, hbmpMem);
  51.     SetBkMode(hdcMem, TRANSPARENT);
  52.     SetTextColor(hdcMem, RGB(255, 255, 255));
  53.     HGDIOBJ hFont = CreateFontW(100, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_OUTLINE_PRECIS,
  54.         CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, DEFAULT_PITCH | FF_DONTCARE, L"UI Gothic");
  55.     HGDIOBJ hFontOld = SelectObject(hdcMem, hFont);    
  56.     for (int fCnt = 0; fCnt < 100; fCnt++) {
  57.         wsprintf(szBuf, TEXT("%d"), fCnt);
  58.         FillRect(hdcMem, &lpsi->rcFrame, (HBRUSH)GetStockObject(BLACK_BRUSH));
  59.         DrawText(hdcMem, szBuf, -1, &lpsi->rcFrame, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
  60.         AVIStreamWrite(pavi, fCnt, 1, lpBits, bmiHeader.biSizeImage, AVIIF_KEYFRAME, NULL, NULL);
  61.     }
  62.     
  63.     SelectObject(hdcMem, hFontOld);
  64.     DeleteObject(hFont);
  65.     
  66.     SelectObject(hdcMem, hbmpMemPrev);
  67.     DeleteObject(hbmpMem);
  68.     DeleteDC(hdcMem);
  69.     
  70.     MessageBox(NULL, TEXT("Done."), TEXT("OK"), MB_OK);
  71.     AVIStreamRelease(pavi);
  72.     AVIFileRelease(pfile);
  73.     AVIFileExit();
  74.     return 0;
  75. }

右クリックでクリップボード内の画像をファイル保存する設定

 スクリーンショットやプログラムから画像をコピーしてもそのあとにファイルにしたい場合は画像編集ソフトを起動する必要があり面倒です。ここで Custom Context Menu と PowerShell を使った方法を説明します。 Custom Context Menu はあらかじめストアからダウンロードしてください(有料アプリですが、永遠の試用期間があります)。

Custom Context MenuをインストールしたらWindowsを再起動して有効にしてください。何も警告や案内が出ないので再起動を忘れますが、それでは動きません。

次に以下のPowerShellスクリプトとを作成して適当なファイル名で保存します。今回は「SaveClipboardImageAsJpeg.ps1」で保存しています。エンコードはSJISにしてください。

  1. param ([parameter(mandatory=$true)][String]$Arg1)
  2. Add-Type -AssemblyName System.Windows.Forms
  3. Add-Type -AssemblyName System.Drawing
  4. function Save-ClipboardImageAsJpeg {
  5.     [CmdletBinding()]
  6.     param ()
  7.     # 現時刻を取得し、ファイル名を生成
  8.     $timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
  9.     $outputPath = Join-Path -Path "$Arg1" -ChildPath "img$timestamp.jpg"
  10.     # クリップボードから画像を取得
  11.     $clipboardImage = [System.Windows.Forms.Clipboard]::GetImage()
  12.     if ($clipboardImage -ne $null) {
  13.         # 画像をJPEG形式で保存
  14.         $jpegEncoder = [System.Drawing.Imaging.ImageCodecInfo]::GetImageEncoders() | Where-Object { $_.MimeType -eq "image/jpeg" }
  15.         $encoderParameters = New-Object System.Drawing.Imaging.EncoderParameters(1)
  16.         $encoderParameters.Param[0] = New-Object System.Drawing.Imaging.EncoderParameter([System.Drawing.Imaging.Encoder]::Quality, 100L)
  17.         $clipboardImage.Save($outputPath, $jpegEncoder, $encoderParameters)
  18.         Write-Output "画像が保存されました: $outputPath"
  19.      } else {
  20.         # ポップアップ表示
  21.         [System.Windows.Forms.MessageBox]::Show("クリップボードに画像がありません。", "エラー", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Error)
  22.     }
  23. }
  24. # コマンドレットを実行
  25. Save-ClipboardImageAsJpeg

次に Custom Context MenuのMenu Configで以下を設定します。

Exe 「"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe"」

-Param 「 -ExecutionPolicy Bypass -File "C:\data\powershell\SaveClipboardImageAsJpeg.ps1" {path}」



です。 メニュー名は任意で決めてください。 これで、何か画像をコピーして、適当なフォルダを右クリックすると、タイムスタンプのファイル名でJPEGファイルが保存されます。

この設定は便利ですので是非行ってみて下さい。


簡単に手持ちのC,C++の処理に進捗バーを実装する方法

 C言語やC++で何か処理を書ける人は多いですが、Windowプログラムとなると実装方法が難しいです。VisualStudioのプロジェクトの雛形にボタンを配置し、そこから手持ちのコードを起動すれば動きそうです。 しかし、その方法では「未応答」状態になったり、ウィンドウの操作すら...