2017年9月11日月曜日

windowCap ver1.01




■ダウンロード

file: windowCap101.zip
size: 138KB




■概要
    PrintScreenが押されると、ウィンドウのスクリーンショットを撮って画像ファイルで出力します。
    対応出力形式:BMP、PNG
    ShiftやCtrl、Altキーの同時押しによる、キャプチャ範囲の個別設定が可能。(3つまで)
    音とバルーン表示でわかりやすく出力確認。
    出力ファイル名に日付を入れるといった、ちょっとだけ柔軟な名前設定もできます。



■動作環境
    Windows 8.1
    CPU 1GHz以上
    メモリ 1GB以上



■アンインストール
    レジストリは触っておりません。
    削除の際はフォルダごとゴミ箱へ。



■使い方
    ●キー設定
        スクリーンショットの撮り方と記録キーを3つまで設定できます。

        ○記録するウィンドウに関する設定
        ・なし
            キーが押されても何もしません。
        ・デスクトップ全体
            メインのデスクトップを記録します
        ・アクティブウィンドウ
            現在アクティブなウィンドウを記録します
        ・アクティブウィンドウのクライアント領域
            現在アクティブなウィンドウのクライアント領域を記録します
        ・カーソル下のウィンドウ
            マウスカーソルの下にあるウィンドウを記録します
        ・カーソル下のウィンドウのクライアント領域
            マウスカーソルの下にあるウィンドウのクライアント領域を記録します
        ・複数のデスクトップ全体
            複数デスクトップそれぞれを別個に記録します
            ファイル出力の場合、複数ファイルに出力され、
                ファイル名には(1),(2)なと末尾に数値が追記されます。
            クリップボード出力だとプライマリのデスクトップのみ記録されます。
        ・複数のデスクトップ全体を1枚に
            複数のデスクトップをまとめて1枚の画像で記録します
        ・カーソル下のデスクトップ全体
            現在カーソルがあるデスクトップを記録します

        ○記録キーの同時押しの設定
            PrintScreenキー と同時に Shift、Ctrl、Alt キーを同時に押させるかを設定できます。

    ●キャプチャ設定
        マウスカーソルを含める
            チェックをいれると、画像にカーソルを含むことができます。
        出力先
            ファイルかクリップボードから選択できます。

    ●通知
        バルーン
            記録に成功したらバルーン表示でお知らせします。
        音を鳴らす
            記録に成功したら音を鳴らします。
            実行ファイルと同フォルダにある sound.wav を置き換えて好きな音に変更してください

    ●ファイル設定
        ファイル形式
            ビットマップかPNGで画像ファイルを保存できます
        上書き時に確認ウィンドウを出す
            画像ファイル出力先に出力するファイル名が既に存在していたら、確認ウィンドウを出すようにします。
            チェックが入っていなければ、自動的に上書きします。
        出力先フォルダ
            画像ファイルが出力されるフォルダを設定できます。
            指定したフォルダが存在していないと出力されませんのでご注意ください。
        ...ボタン
            フォルダ選択ウィンドウを出します。出力したいフォルダを選択してください。
        出力先フォルダを開く
            設定したフォルダを開きます。
            指定フォルダが存在してないと、グレー表示になり押すことができなくなります。
        ファイル名
            画像ファイル名を設定できます。
            ファイル名には「\」「/」?」「*」「"」「>」「<」「|」の文字は設定できません。
            また、以下の指定文字を使用すると、自動的に数値に置き換えられます。
                 「%y」年4桁
                 「%m」月
                 「%d」日
                 「%h」時
                 「%i」分
                 「%s」秒
                 「%l」ミリ秒
                 「%Y」年(下2桁。2015年であれば15と出力されます)
                 「%M」月(2桁。1月であれば01と出力されます)
                 「%D」日(2桁)
                 「%H」時(2桁)
                 「%I」分(2桁)
                 「%S」秒(2桁)
                 「%L」ミリ秒(3桁)
                 「%n」連番値
                 「%%」文字の%
            例
                [%y%m%d]cap%n → [2015213]cap0001
                [%Y%M%d]cap%n → [150213]cap0001
                [%Y-%M-%d]cap%n → [15-02-13]cap0001
                [%y%M%d_%H%I%S]cap%n → [20150213_010101]cap0001

            拡張子はファイル形式で設定されたファイルの拡張子が自動的につきます。

        連番値
            画像が出力されるごとに+1されていく数値です。ファイル名%nにて使用することできます。
        連番桁数
            ファイル名で%nを使用したときに、連番値が設定した桁数になるよう調整されます。
            4桁であれば1 → 0001、7桁であれば1 → 0000001 とファイル名に設定されるようになります。



■そのほか
    このソフトウェアを使用し、何らかの障害が発生しても責任を取りかねますのでご了承ください。

    zlibライブラリを使用しています。
        Copyright (C) 1995-2005 Jean-loup Gailly and Mark Adler
        http://www.winimage.com/zLibDll/

    libpngライブラリを使用しています。
        Copyright (c) 1998-2007 Glenn Randers-Pehrson
        http://www.libpng.org/



■更新履歴
    2017-09-11 ver 1.01
        複数デスクトップを1枚の画像に保存できる機能を追加
    2016-12-18 ver 1.00b
        バルーンクリック時にファイル選択状態で出力先フォルダを開くよう修正
    2015-02-15 ver 1.00a
        終了確認メッセージウィンドウが出ているときにキャプチャすると強制終了していたのを修正
    2015-02-14 ver 1.00

2017年9月9日土曜日

setDragXY ver1.00




■ダウンロード

file: setDragXY100.zip
size: 44.1KB




■概要
    ドラッグ操作の開始とはみなされない範囲の設定を行います。
    Windows内のドラッグ操作にかかわる値を変更することで、
    クリック操作がドラッグに化けてしまうのを防ぐことができます。



■動作環境
    Windows 10
    CPU 1.4GHz以上
    メモリ 4GB以上



■アンインストール
    レジストリは触っておりません。
    削除の際はフォルダごとゴミ箱へ。



■使い方
    XYの値を変更するとすぐに反映されます。
    値が大きいほどドラッグが開始されるまでの距離が長くなります。
    
    ●起動時の初期値
        ・初期値に戻す
            変更した値をこのソフトが起動したときの値に戻したいときにボタンを押してください

        ・終了時に初期値に戻してから終了する
            チェックを入れておくと、ソフト終了時に元の値に戻すようにします
            このソフトが起動中のみ値を変更しておく動作にしたい場合に使ってください


    ・起動後に範囲設定をしたらすぐに終了する
        ソフトを起動したら設定してある値を反映後すぐにソフトを終了するようにします
        スタートアップにソフトを登録しておき、ウィンドウズ起動時に値を変更させてすぐソフト終了といったことができます
        ctrlを押したままソフトを起動すると終了処理をキャンセルできます
        またこのチェックがついているときは「終了時に初期値に戻してから終了する」は無効になります



■そのほか
    このソフトウェアを使用し、何らかの障害が発生しても責任を取りかねますのでご了承ください。

    このソフトはWINAPIのSystemParametersInfo()を使用しSPI_SETDRAGWIDTH, SPI_SETDRAGHEIGHTの値を変更しています
    作者環境では初期値はX=4, Y=4でした。
    範囲設定の値は、Windowsが再起動されるとWindowsの初期値にもどされます。
    よくわからなくなったら再起動してみてください。



■更新履歴
    2017-09-17 ver 1.00
        作成

2017年5月29日月曜日

noneScreenSave ver1.20b


■ダウンロード

file: noneScreenSave120b.zip
size: 42.0KB






■概要
    アプリ起動中、スクリーンセーバー、モニタの電源オフを一時的に無効にします。



■動作環境
    Windows 8, 10
    CPU 800MHz以上
    メモリ 256MB以上



■アンインストール
    レジストリは触っておりません。
    削除の際はフォルダごとゴミ箱へ。



■使い方
    起動するとスクリーンセーバーとモニタの電源オフが無効化されます。
    「閉じる」ボタンをクリックすると終了します。



■そのほか
    このソフトウェアを使用し、何らかの障害が発生しても責任を取りかねますのでご了承ください。



■更新履歴
    2017-05-28 ver 1.20b
        非アクティブ時に機能してなかったので、SetThreadExecutionState()で抑制する方法に変更
    2017-05-27 ver 1.20a
        Windows10にて、スクリーンセーバーオフ、指定時間後モニタ電源オフ設定のときに機能してないのを修正
    2015-04-18 ver 1.20
        うまく動いていなかったので、タイマーにてカーソルを0dot動かすことで阻止するよう修正
    2015-04-16 ver 1.10
        キー押しだとヘルプチップが表示されないなどの問題があるとわかったので、
        DLLによるグローバルフックにてスクリーンセーバーを封じるよう修正
    2015-04-12 ver 1.0b
        アクティブでないときに機能してなかったのを修正。適当に使ってないキーを押す処理を追加。
    2013-08-09 ver 1.0

2017年5月27日土曜日

noneScreenSave ver1.20a


■ダウンロード

file: noneScreenSave120a.zip
size: 42.1KB





■概要
    アプリ起動中、スクリーンセーバー、モニタの電源オフを一時的に無効にします。



■動作環境
    Windows XP, 8
    CPU 800MHz以上
    メモリ 256MB以上



■アンインストール
    レジストリは触っておりません。
    削除の際はフォルダごとゴミ箱へ。



■使い方
    起動するとスクリーンセーバーとモニタの電源オフが無効化されます。
    「閉じる」ボタンをクリックすると終了します。



■そのほか
    このソフトウェアを使用し、何らかの障害が発生しても責任を取りかねますのでご了承ください。



■更新履歴
    2017-05-27 ver 1.20a
        Windows10にて、スクリーンセーバーオフ、指定時間後モニタ電源オフ設定のときに機能してないのを修正
    2015-04-18 ver 1.20
        うまく動いていなかったので、タイマーにてカーソルを0dot動かすことで阻止するよう修正
    2015-04-16 ver 1.10
        キー押しだとヘルプチップが表示されないなどの問題があるとわかったので、
        DLLによるグローバルフックにてスクリーンセーバーを封じるよう修正
    2015-04-12 ver 1.0b
        アクティブでないときに機能してなかったのを修正。適当に使ってないキーを押す処理を追加。
    2013-08-09 ver 1.0

2017年2月24日金曜日

pointClick ver1.30b





■ダウンロード

file: pointClick130b.zip
size: 64KB




■概要
    クリックしたときに、マウスがちょっと動いてしまってドラッグになってしまうのを防ぎます。



■動作環境
    Windows 8.1
    CPU 1GHz以上
    メモリ 1GB以上



■アンインストール
    レジストリは触っておりません。
    削除の際はフォルダごとゴミ箱へ。



■使い方
    ○ on/off
        位置を補正する機能のオンオフを行います

    ○左クリック、ホイールクリック、右クリック
        位置補正機能を有効にするマウスのボタンを選択できます。

    ○処理タイプ
        ・Type-A
            マウスボタンを押して、離したときにカーソル位置を押した位置に戻す処理方法
            ボタンの押し離しのタイミングはそのままでカーソル位置だけ手を加えるので処理がシンプル
            Webサイトの画像ボタンなどはこの処理でもドラッグになってしまうことがある
        ・Type-B
            マウスボタンを押したときにはボタンを押したことにせず、離したときにボタンを押し離し処理を行う方法
            クリックしている時間が短くなるのでドラッグになりにくい
            実際にクリック処理されるタイミングが、マウスのボタンを離したときなのでワンテンポ遅れるのが欠点

    ○有効時間
        マウスのボタンを押してから離すまでの時間設定
        設定した時間よりも長くマウスのボタンを押されていると、位置補正機能は働きません

    ○有効距離
        マウスのボタンを押してから離すまでに移動した距離の設定
        設定した距離よりも長く移動していたら、位置補正機能は働きません

    ○クリック時間(ms)
        Type-Bで、クリックしている時間を設定します
        あまり短すぎるとクリックしたことにならない場合があります

    ○クリック処理中カーソル移動抑制
        Type-Bで、マウスボタンの押し離しの間はカーソルが移動しないようにします。
        クリック成功率が上がりますが、クリック時間の設定が長めの設定だと違和感を覚えるかもしれません
    ○有効アプリを限定する
        クリック位置補正機能を、設定したアプリケーションがアクティブの時だけ働くようにします
        アプリケーションの選択は「アプリ選択」ボタンを押してでてくる許可リストウィンドウで行います

        ・許可リストウィンドウ
            アプリケーションリストには起動後から今までにアクティブになったことがあるウィンドウが列挙されます
            リストになければ指定したいアプリケーションを一度アクティブにしてください
            位置補正機能が働いてほしいアプリケーションを選択して、
            「許可リストへ追加」ボタンを押すことで、位置補正機能が有効になります。
            「リストから削除」を押すと選択中の項目を削除できます

            位置補正機能を無効にしたいアプリケーションがある場合は、
            許可リストから該当アプリケーションを選択し「許可リストから削除」ボタンを押すことで無効にできます

    ○ログ表示
        ログ表示機能のオンオフを行います

    ○ログ表示数
        表示するログの数の設定


    ●ログ表示
        有効時間、有効距離の設定にお役立てください

        ○補正
            位置を補正する機能が働いたら○が、
            時間が超過しているかカーソルの移動距離が設定より大きいと×が表示されます
        ○マウス
            マウスの右クリック、左クリック、中クリックのどれであるかが表示されます
        ○経過時間(ms)
            マウスのボタンが押されてから離されるまでに経過した時間が表示されます
        ○距離
            マウスのボタンが押されてから離されるまでに移動した距離が表示されます



■そのほか
    このソフトウェアを使用し、何らかの障害が発生しても責任を取りかねますのでご了承ください。



■更新履歴
    2017-02-18 ver 1.30b
        補正機能が有効なアプリケーションかを判別する方法を変更
    2015-07-05 ver 1.30a
        アプリケーションリストから1時間以上アクティブになってないパスを削除する機能を追加
    2015-06-07 ver 1.30
        「有効アプリを限定する」機能を追加
    2015-05-25 ver 1.01a
        フック内の処理を最適化
    2015-05-16 ver 1.01
        TypeBのクリック処理中、カーソル移動しない処理を追加
    2015-05-14
        クリック時間に1msを追加
    2015-05-12
        フックをマルチスレッド化
    2015-03-17
        フックが外れたときに、再フックするよう修正
        クリック後カーソル位置がずれるのが一瞬見えるのを修正
    2015-03-12 ver1.01a
        アイコン変更
        終了確認をするよう修正
        Type-Bにてクリック後カーソル位置がずれることがあるのを修正
        Type-Bにてカーソルが有効距離以上動くと、ただちにボタン押し処理が実行されるよう修正
        Type-Bにてボタンを押しているときホイールをまわすと、ただちにボタン押し処理が実行されるよう修正
    2015-03-07 ver1.00

2016年10月13日木曜日

32bitDIBから無圧縮AVI2.0 (2)





実際に出力した動画ファイル
[161013]avi20_output.avi
filesize: 19.8MB




 ざっくりとしたファイルフォーマット図。
青字が記述に使用する構造体、緑字がその構造体のサイズになります。
自分がプログラムを組む際どこに何を書き込むのかよくわからなかったのでまとめました。
矢印は各インデックスがさすデータ位置で、
チャンク先頭だったりデータ先頭だったりするようです。
 WindowsMediaPlayerで再生するには、
AVIStreamHeaderのdwLengthをファイル全体のトータルフレーム数にしないとRIFF-AVI部分を再生後にエラーがでるっぽいです。
仕様ではRIFF-AVI内のフレーム数を指定するはずです。



ソースファイル
file: [20161013]AVI20.cpp
 前回のソースコードはどうもチャンクサイズが間違ってるっぽいので新しく書き直しました。
ファイル作成後ヘッダー部分を飛ばし、画像データを書き込みつつRIFFリストを追記更新。
記録終了時にヘッダーを書きにファイル先端に戻ってヘッダー記述、という流れになります。


#include <windows.h>
#include <vector>



const int WINDOW_WIDTH = 640 / 2;
const int WINDOW_HEIGHT = 480/ 2;
const int TIMER_ID = 1000;
const int FPS = 30;


class DIB32
{
public:
 class PutColorAdd
 {
 public:
  static inline void update( DWORD& dest, DWORD src )
  {
   DWORD color = (dest & 0x00fefefe) + (src & 0x00fefefe);
   DWORD mask = color & 0x01010100;
   dest = (dest&0xFF000000) | color | (mask - (mask >> 8));
  }
 };




 DIB32()
  : m_pixel( NULL )
 {
  ZeroMemory( &m_bmi, sizeof(m_bmi) );
 }
 
 virtual ~DIB32()
 {
  release();
 }
 
 virtual bool create( LONG width, LONG height )
 {
  if ( width <= 0 || height <= 0 ) return false;
  release();
 
  m_pixel = new DWORD[ width * height ];
  if ( !m_pixel ) return false;
 
  m_bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  m_bmi.bmiHeader.biWidth = width;
  m_bmi.bmiHeader.biHeight = height;
  m_bmi.bmiHeader.biPlanes = 1;
  m_bmi.bmiHeader.biBitCount = 32;
  m_bmi.bmiHeader.biSizeImage = width * height * 4;
  m_bmi.bmiHeader.biCompression = BI_RGB;
  m_bmi.bmiHeader.biXPelsPerMeter = 3780;
  m_bmi.bmiHeader.biYPelsPerMeter = 3780;
 
  return true;
 }
 
 /** create from HBITMAP */
 bool create( LPCTSTR fileName )
 {
  HBITMAP hBmp = static_cast<HBITMAP>( LoadImage( NULL, fileName, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION | LR_LOADFROMFILE ) );
  if ( !hBmp ) return false;

  BITMAP bm = {0};
  GetObject( hBmp, sizeof(BITMAP), &bm );

  // create DC
  HDC hdc = CreateCompatibleDC( NULL );
  if ( !hdc ) return false;

  // create buf
  if ( !create( bm.bmWidth, bm.bmHeight ) )
  {
   DeleteDC( hdc );
   return false;
  }

  // copy
  GetDIBits( hdc, hBmp, 0, bm.bmHeight, m_pixel, &m_bmi, DIB_RGB_COLORS );

  DeleteDC( hdc );
  DeleteObject( hBmp );

  return true;
 }
 
 virtual void release()
 {
  if ( m_pixel )
  {
   delete [] m_pixel;
   m_pixel = NULL;
  }
  ZeroMemory( &m_bmi, sizeof(m_bmi) );
 }
 
 bool render( HDC hdc, HWND hWnd ) const
 {
  RECT rect;
  GetClientRect( hWnd, &rect );
  return GDI_ERROR != StretchDIBits(
   hdc, 0, 0, rect.right, rect.bottom,
   0, 0, getWidth(), getHeight(),
   m_pixel, &m_bmi, DIB_RGB_COLORS, SRCCOPY );
 }
 
 
 /** render to dib */
 template < class T >
 bool render( DIB32& dest,
  LONG destX, LONG destY, LONG destWidth, LONG destHeight,
  LONG srcX, LONG srcY, LONG srcWidth, LONG srcHeight ) const
 {
  LONG width = abs( destWidth );
  LONG height = abs( destHeight );
  const LONG absDestWidth = width;
  const LONG absDestHeight = height;
  const LONG absSrcWidth = abs( srcWidth );
  const LONG absSrcHeight = abs( srcHeight );
  const float magniX = static_cast<float>(width) / static_cast<float>(srcWidth);
  const float magniY = static_cast<float>(height) / static_cast<float>(srcHeight);
  LONG sw = static_cast<LONG>( static_cast<float>(getWidth()) * magniX );
  LONG sh = static_cast<LONG>( static_cast<float>(getHeight()) * magniY );
  LONG sx = static_cast<LONG>( static_cast<float>(srcX) * magniX );
  LONG sy = static_cast<LONG>( static_cast<float>(srcY) * magniY );
  
  if ( !cliping( destX, destY, width, height, sx, sy, dest.getWidth(), dest.getHeight(), sw, sh ) )
  {
   return false;
  }
  
  
  const int shift = 14;
  const LONG addX = (absSrcWidth << shift) / absDestWidth;
  const LONG addY = (absSrcHeight << shift) / absDestHeight;

  // src*magniだったsxを元に戻しつつ、固定小数点数に
  LONG fsx = static_cast<LONG>( static_cast<float>(sx << shift) / magniX );
  LONG fsy = static_cast<LONG>( static_cast<float>(sy << shift) / magniY );
   
  LPDWORD destLine = dest.m_pixel + ((dest.getHeight()-1)-destY) * dest.getWidth() + destX;
  for (LONG y=0; y<height; ++y)
  {
   LPDWORD destPixel = destLine;
   LPDWORD srcLine = m_pixel + ((getHeight()-1)-(fsy>>shift)) * getWidth();
   LONG tmpx = fsx;
   for (LONG x=0; x<width; ++x)
   {
    T::update( *destPixel++, srcLine[ tmpx >> shift ] );
    tmpx += addX;
   }

   fsy += addY;
   destLine -= dest.getWidth();
  }
  
  
  return true;
 }

 /** cliping */
 static inline bool cliping(
  LONG& destX, LONG& destY, LONG& width, LONG& height, LONG& srcX, LONG& srcY,
  const LONG destWidth, const LONG destHeight, const LONG srcWidth, const LONG srcHeight )
 {
  // 左端クリッピング
  if ( destX < 0 ) { width += destX; srcX -= destX; destX = 0; }
  if ( srcX < 0 ) { width += srcX; destX -= srcX; srcX = 0; }

  // 右端
  if ( destX + width > destWidth ) { width -= ((destX+width)-destWidth); }
  if ( srcX + width > srcWidth ) { width -= ((srcX+width)-srcWidth); }

  // 上
  if ( destY < 0 ) { height += destY; srcY -= destY; destY = 0; }
  if ( srcY < 0 ) { height += srcY; destY -= srcY; srcY = 0; }

  // 下
  if ( destY + height > destHeight ) { height -= ((destY+height)-destHeight); }
  if ( srcY + height > srcHeight ) { height -= ((srcY+height)-srcHeight); }

  return ((width > 0) & (height > 0));
 }

 /** abs */
 template<class T> static T abs(const T& a) { return ((a) < 0 ? -(a) : (a)); }
 
 LONG getWidth() const { return m_bmi.bmiHeader.biWidth; }
 LONG getHeight() const { return m_bmi.bmiHeader.biHeight; }
 const LPDWORD getPixelAddr() const { return m_pixel; }
 LPDWORD getPixelAddr() { return m_pixel;  }

 
 const BITMAPINFO& getBMPInfo() { return m_bmi; }

protected:

 LPDWORD m_pixel;
 BITMAPINFO m_bmi;
};
 
 


/*
AVISTREAMHEADER 構造体 <https://msdn.microsoft.com/ja-jp/library/cc352263.aspx>
AVIMAINHEADER 構造体 <https://msdn.microsoft.com/ja-jp/library/cc352261.aspx>
BITMAPINFOHEADER 構造体 <https://msdn.microsoft.com/ja-jp/library/cc352308.aspx>
AVISUPERINDEX structure (Windows) <https://msdn.microsoft.com/ja-jp/library/windows/desktop/ff625871(v=vs.85).aspx>
AVISTDINDEX structure (Windows) <https://msdn.microsoft.com/ja-jp/library/windows/desktop/ff625869(v=vs.85).aspx>
AVIOLDINDEX structure (Windows) <https://msdn.microsoft.com/ja-jp/library/windows/desktop/dd318181(v=vs.85).aspx>
*/

 
class AVIRecorder
{
public:
#pragma pack(1)
 struct LIST
 {
  DWORD dwList;
  DWORD dwSize;
  DWORD dwFourCC;
 };

 struct CHUNK
 {
  DWORD dwFourCC;
  DWORD dwSize;
 };

 struct MainAVIHeader
 {

  DWORD  dwMicroSecPerFrame; // frame display rate (or 0L)
  DWORD  dwMaxBytesPerSec; // max. transfer rate
  DWORD  dwPaddingGranularity; // pad to multiples of this
  // size; normally 2K.
  DWORD  dwFlags;  // the ever-present flags
#define AVIF_HASINDEX        0x00000010 // Index at end of file?
#define AVIF_MUSTUSEINDEX    0x00000020
#define AVIF_ISINTERLEAVED   0x00000100
#define AVIF_TRUSTCKTYPE     0x00000800 // Use CKType to find key frames
#define AVIF_WASCAPTUREFILE  0x00010000
#define AVIF_COPYRIGHTED     0x00020000
  DWORD  dwTotalFrames;  // # frames in file
  DWORD  dwInitialFrames;
  DWORD  dwStreams;
  DWORD  dwSuggestedBufferSize;

  DWORD  dwWidth;
  DWORD  dwHeight;

  DWORD  dwReserved[4];
 };

 struct AVIStreamHeader
 {
  FOURCC  fccType;
  FOURCC  fccHandler;
  DWORD  dwFlags; /* Contains AVITF_* flags */
  WORD  wPriority;
  WORD  wLanguage;
  DWORD  dwInitialFrames;
  DWORD  dwScale; 
  DWORD  dwRate; /* dwRate / dwScale == samples/second */
  DWORD  dwStart;
  DWORD  dwLength; /* In units above... */
  DWORD  dwSuggestedBufferSize;
  DWORD  dwQuality;
  DWORD  dwSampleSize;
  //RECT  rcFrame;
  struct
  {
   short int left;
   short int top;
   short int right;
   short int bottom;
  } rcFrame;
 };

/* Flags for index */
#define AVIIF_LIST          0x00000001L // chunk is a 'LIST'
#define AVIIF_KEYFRAME      0x00000010L // this frame is a key frame.
#define AVIIF_FIRSTPART     0x00000020L // this frame is the start of a partial frame.
#define AVIIF_LASTPART      0x00000040L // this frame is the end of a partial frame.
#define AVIIF_MIDPART       (AVIIF_LASTPART|AVIIF_FIRSTPART)

#define AVIIF_NOTIME     0x00000100L // this frame doesn't take any time
#define AVIIF_COMPUSE       0x0FFF0000L // these bits are for compressor use

 // 1022 -> 16384 byte
 // single -> 32 + 16*n byte
 struct AVISUPERINDEX
 {
  FOURCC fcc;
  DWORD cd;
  WORD wLongsPerEntry;
  BYTE bIndexSubType;
  BYTE bIndexType;
  DWORD nEntriesInUse;
  DWORD dwChunkID;
  DWORD dwReserved[3];
 };

 struct AVISUPERINDEX_ENTRY
 {
  DWORDLONG qwOffset;
  DWORD dwSize;
  DWORD dwDuration;
 };

#define AVI_INDEX_OF_INDEXES       0x00
#define AVI_INDEX_OF_CHUNKS        0x01
#define AVI_INDEX_OF_TIMED_CHUNKS  0x02
#define AVI_INDEX_OF_SUB_2FIELD    0x03
#define AVI_INDEX_IS_DATA          0x80

 struct AVISTDINDEX
 {
  FOURCC fcc;
  DWORD cd;
  WORD wLongsPerEntry;
  BYTE bIndexSubType;
  BYTE bIndexType;
  DWORD nEntriesInUse;
  DWORD dwChunkID;
  DWORDLONG qwBaseOffset;
  DWORD dwReserved;  
 };

 struct AVISTDINDEX_ENTRY
 {
  DWORD dwOffset;
  DWORD dwSize;
 };

 struct AVIINDEXENTRY
 {
  DWORD  ckid;
  DWORD  dwFlags;
  DWORD  dwChunkOffset;  // Position of chunk
  DWORD  dwChunkLength;  // Length of chunk
 };

 struct AVIEXTHEADER
 {
  FOURCC  fcc;                    // 'dmlh'
  DWORD   cb;                     // size of this structure -8
  DWORD   dwGrandFrames;          // total number of frames in the file
  DWORD   dwFuture[61];           // to be defined later
 };
#pragma pack()

 enum
 {
  MAX_CHUNK_SIZE = 8 * 1024 * 1024,  //< 1チャンクの最大サイズ
  SUPER_INDEX_ENTRY_SIZE = 32 * 1024,  //< SuperIndexEntryを確保するサイズ。
  PADDING_GRANULARITY = 2048,
 };



 AVIRecorder()
  : m_file( INVALID_HANDLE_VALUE )
  , m_nowRiffFrame( 0 )
  , m_totalFrame( 0 )
  , m_numOfRiff( 0 )
  , m_nowRiffSize( 0 )
  , m_mostLargeImageSize( 0 )
  , m_maxNumOfRiff( 0 )
  , m_moviChunkOffset( 0 )
  , m_isAVIX( false )
  , m_width( 0 )
  , m_height( 0 )
  , m_imageSize( 0 )
  , m_fps( 0 )
  , m_riffAVIChunkFrame( 0 )
  , m_riffAVIChunkSize( 0 )
 {
  ZeroMemory( &m_nowRiffFileTopPos, sizeof(m_nowRiffFileTopPos) );
  ZeroMemory( &m_moviListPos, sizeof(m_moviListPos) );
 }

 ~AVIRecorder()
 {
  close();
 }



 /** create */
 bool create( LPCTSTR fileName, DWORD width, DWORD height, DWORD fps )
 {
  close();

  m_file = CreateFile( fileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
  if ( isOpen() )
  {
   m_width = width;
   m_height = height;
   m_fps = fps;
   m_imageSize = width * height * 4;

   // 何も書き込んでないので0のままだけど、一応現在RiffListの先端位置を記憶
   ZeroMemory( &m_nowRiffFileTopPos, sizeof(m_nowRiffFileTopPos) );
   m_nowRiffFileTopPos.LowPart = SetFilePointer( m_file, 0, &m_nowRiffFileTopPos.HighPart, FILE_CURRENT );

   // ファイル作成時にすっとばすヘッダ分の領域
   const DWORD preFileHeaderSkipSize =
    sizeof(LIST)              //< RIFF-AVI LIST
     + sizeof(LIST)             //< hdrl List
      + sizeof(CHUNK) + sizeof(MainAVIHeader)      //< avih Chunk + MainAVIHeader
      + sizeof(LIST)            //< list-strl
       + sizeof(CHUNK) + sizeof(AVIStreamHeader)    //< strh Chunk + AVIStreamHeader
       + sizeof(CHUNK) + sizeof(BITMAPINFOHEADER)    //< strf Chunk + BITMAPINFOHEADER
       + sizeof(AVISUPERINDEX) + SUPER_INDEX_ENTRY_SIZE  //< AVISUPERINDEX + SUPER_INDEX_ENTRY_SIZE
     + sizeof(LIST)            //< odml List
      + sizeof(AVIEXTHEADER)         //< AVIEXTHEADER
     + sizeof(LIST);            //< movi LIST


   DWORD fileHeaderSkipSize = preFileHeaderSkipSize;

   if ( PADDING_GRANULARITY )
   {
    const DWORD PaddingGranularity = PADDING_GRANULARITY;   //< 0だとコンパイル時に0除算で怒られるので適当に変数にいったん入れる
    fileHeaderSkipSize = preFileHeaderSkipSize + (PADDING_GRANULARITY - (preFileHeaderSkipSize % PaddingGranularity));
   }

   // 'movi'は直後に追記するのでその分は省く
   fileHeaderSkipSize -= sizeof(LIST);

   
   // ヘッダ部分をスキップ
   SetFilePointer( m_file, fileHeaderSkipSize, NULL, FILE_BEGIN );

   // 格納可能RIFF数
   m_maxNumOfRiff = SUPER_INDEX_ENTRY_SIZE / sizeof(AVISUPERINDEX_ENTRY);
   m_numOfRiff = 1;
   m_nowRiffSize = fileHeaderSkipSize;
   


   // 'movi'開始位置記憶。movi-LISTのサイズを書き換えに戻ってくる
   ZeroMemory( &m_moviListPos, sizeof(m_moviListPos) );
   m_moviListPos.LowPart = SetFilePointer( m_file, 0, &m_moviListPos.HighPart, FILE_CURRENT );
  

   // 'movi'LIST 書き込み
   {
    LIST movi;
    movi.dwList = mmioFOURCC('L','I','S','T');
    movi.dwSize = (MAX_CHUNK_SIZE - fileHeaderSkipSize) - 8;
    movi.dwFourCC = mmioFOURCC('m','o','v','i');

    DWORD writeSize;
    WriteFile( m_file, &movi, sizeof(movi), &writeSize, NULL );

    m_nowRiffSize += sizeof(LIST);
   }
 
   m_moviChunkOffset = 0;

   return true;
  }

  return false;
 }

 void update( const DIB32& image )
 {
  if ( isOpen() )
  {
   
   // 2048境界に合わせてJUNKつめ
   {
    const DWORD junkSize = writePaddingJUNK();
    m_nowRiffSize += junkSize;
    m_moviChunkOffset += junkSize;
   }
   

   // 画像データ書き込み
   CHUNK chunk;
   chunk.dwFourCC = mmioFOURCC('0','0','d','b');
   chunk.dwSize = m_imageSize;

   DWORD writeSize;
   WriteFile( m_file, &chunk, sizeof(chunk), &writeSize, NULL );
   WriteFile( m_file, const_cast<LPDWORD>(image.getPixelAddr()), chunk.dwSize, &writeSize, NULL );


   // ix00用にデータを詰める
   {
    AVISTDINDEX_ENTRY entry = {0};
    entry.dwSize = chunk.dwSize;
    entry.dwOffset = (sizeof(LIST)-8) + sizeof(CHUNK) + m_moviChunkOffset; //< データ位置。

    m_aviStdIndexEntryList.push_back( entry );
   }

   // idx1用にデータを詰める
   if ( !m_isAVIX )
   {
    AVIINDEXENTRY aviIndexEntry = {0};
    aviIndexEntry.ckid = mmioFOURCC('0','0','d','b');
    aviIndexEntry.dwFlags = AVIIF_KEYFRAME;
    aviIndexEntry.dwChunkLength = chunk.dwSize;      //< sizeof(CHUNK)+chunk.dwSizeじゃないらしい。なんで
    aviIndexEntry.dwChunkOffset = (sizeof(LIST)-8) + m_moviChunkOffset; //< 'movi'チャンクヘッダからの距離。-8する意味が分からないが数値はこうっぽい

    m_aviIndexEntryList.push_back( aviIndexEntry );
   }

   

   m_mostLargeImageSize = (std::max)( m_mostLargeImageSize, chunk.dwSize );
   m_nowRiffSize += sizeof(CHUNK) + chunk.dwSize;
   m_nowRiffFrame++;
   m_totalFrame++;
   m_moviChunkOffset += sizeof(CHUNK) + chunk.dwSize;


   // チャンクが指定サイズに到達しそうなら次のチャンクに移る
   const DWORD freeChunkSize = MAX_CHUNK_SIZE - (m_nowRiffSize + sizeof(AVISTDINDEX) + m_nowRiffFrame*sizeof(AVIINDEXENTRY));
   if ( freeChunkSize < m_mostLargeImageSize )
   {
    if ( m_numOfRiff >= m_maxNumOfRiff )
    {
     close();
    }
    else
    {
     nextRIFF();
    }
   }
  }
 }

 /** close */
 void close()
 {
  if ( isOpen() )
  {
   exitRIFF();

   // close
   CloseHandle( m_file );
   m_file = INVALID_HANDLE_VALUE;
  }



  m_nowRiffFrame = 0;
  m_totalFrame = 0;
  m_numOfRiff = 0;
  m_nowRiffSize = 0;
  m_mostLargeImageSize = 0;
  m_maxNumOfRiff = 0;
  m_moviChunkOffset = 0;


  ZeroMemory( &m_nowRiffFileTopPos, sizeof(m_nowRiffFileTopPos) );
  ZeroMemory( &m_moviListPos, sizeof(m_moviListPos) );

  m_isAVIX = false;
  m_riffAVIChunkFrame = 0;
  m_riffAVIChunkSize = 0;
  m_fps = 0;
  m_width = 0;
  m_height = 0;
  m_imageSize = 0;

  m_superIndexEntryList.clear();
  m_aviIndexEntryList.clear();
  m_aviStdIndexEntryList.clear();
 }

 /** isOpen */
 bool isOpen() const { return m_file!=INVALID_HANDLE_VALUE; }


 DWORD getTotalFrame() const { return m_totalFrame; }
 DWORD getNowRiffFrame() const { return m_nowRiffFrame; }


private:
 void nextRIFF()
 {
  // AVISUPERINDEX_ENTRY蓄積。各チャンクのix00の位置を示す
  {
   LARGE_INTEGER nowPos = {0};
   nowPos.LowPart = SetFilePointer( m_file, 0, &nowPos.HighPart, FILE_CURRENT );

   AVISUPERINDEX_ENTRY entry = {0};
   entry.dwSize = sizeof(AVISTDINDEX) + sizeof(AVISTDINDEX_ENTRY)*m_aviStdIndexEntryList.size();
   entry.dwDuration = m_aviStdIndexEntryList.size();
   entry.qwOffset = nowPos.QuadPart;
   m_superIndexEntryList.push_back( entry );
  }


  // ix00
  {
   AVISTDINDEX standardIndex = {0};
   standardIndex.fcc = mmioFOURCC('i','x','0','0');
   standardIndex.cd = (sizeof(AVISTDINDEX) - 8) + sizeof(AVISTDINDEX_ENTRY)*m_aviStdIndexEntryList.size();
   standardIndex.wLongsPerEntry = 2;  //< must be 2
   standardIndex.bIndexSubType = 0;  //< must be 0
   standardIndex.bIndexType = AVI_INDEX_OF_CHUNKS;  //< AVI_INDEX_OF_CHUNKS == 1
   standardIndex.nEntriesInUse = m_aviStdIndexEntryList.size();
   standardIndex.dwChunkID = mmioFOURCC('0','0','d','b');
   standardIndex.qwBaseOffset = m_moviListPos.QuadPart;

   DWORD writeSize;
   WriteFile( m_file, &standardIndex, sizeof(standardIndex), &writeSize, NULL );

   // entry
   if ( !m_aviStdIndexEntryList.empty() )
   {
    WriteFile( m_file, &m_aviStdIndexEntryList[0], sizeof(m_aviStdIndexEntryList[0])*m_aviStdIndexEntryList.size(), &writeSize, NULL );
   }

   m_nowRiffSize += sizeof(AVISTDINDEX) + sizeof(AVISTDINDEX_ENTRY) * m_aviStdIndexEntryList.size();
  }


  // idx1
  if ( !m_isAVIX )
  {
   // write index entry
   CHUNK index;
   index.dwFourCC = mmioFOURCC('i','d','x','1');
   index.dwSize = sizeof(AVIINDEXENTRY) * m_aviIndexEntryList.size(); 

   DWORD writeSize;
   WriteFile( m_file, &index, sizeof(index), &writeSize, NULL );

   // write entry
   if ( !m_aviIndexEntryList.empty() )
   {
    WriteFile( m_file, &m_aviIndexEntryList[0], sizeof(m_aviIndexEntryList[0])*m_aviIndexEntryList.size(), &writeSize, NULL );
   }

   m_nowRiffSize += sizeof(CHUNK) +  sizeof(AVIINDEXENTRY) * m_aviIndexEntryList.size();
  }


  if ( !m_isAVIX )
  {
   m_riffAVIChunkSize = m_nowRiffSize;
   m_riffAVIChunkFrame = m_nowRiffFrame;
  }


  {
   // 現在位置記憶
   LARGE_INTEGER nowFilePos = {0};
   nowFilePos.LowPart = SetFilePointer( m_file, 0, &nowFilePos.HighPart, FILE_CURRENT );

   // ヘッダ位置に戻ってサイズ情報を更新する
   if ( m_isAVIX )
   {
    // 現在チャンクのヘッダ位置に移動
    SetFilePointer( m_file, m_nowRiffFileTopPos.LowPart, &m_nowRiffFileTopPos.HighPart, FILE_BEGIN );
   
    // ヘッダ書き換え
    LIST avix;
    avix.dwList = mmioFOURCC('R','I','F','F');
    avix.dwSize = m_nowRiffSize - 8;
    avix.dwFourCC = mmioFOURCC('A','V','I','X');

    DWORD writeSize;
    WriteFile( m_file, &avix, sizeof(avix), &writeSize, NULL );
   }

   // moviチャンクのサイズを正しい値に書き換える
   {
    // movi位置に移動
    SetFilePointer( m_file, m_moviListPos.LowPart, &m_moviListPos.HighPart, FILE_BEGIN );

    LIST movi;
    movi.dwList = mmioFOURCC('L','I','S','T');
    movi.dwSize = (sizeof(LIST) - 8) + m_moviChunkOffset + sizeof(AVISTDINDEX) + sizeof(AVISTDINDEX_ENTRY)*m_aviStdIndexEntryList.size();
    movi.dwFourCC = mmioFOURCC('m','o','v','i');

    DWORD writeSize;
    WriteFile( m_file, &movi, sizeof(movi), &writeSize, NULL );
   }

   // もとの位置に移動
   SetFilePointer( m_file, nowFilePos.LowPart, &nowFilePos.HighPart, FILE_BEGIN );
  }


  m_numOfRiff++;
  m_nowRiffSize = 0;
  m_nowRiffFrame = 0;
  m_moviChunkOffset = 0;

  m_aviIndexEntryList.clear();
  m_aviStdIndexEntryList.clear();

  m_isAVIX = true;

  // ヘッダの位置記憶
  ZeroMemory( &m_nowRiffFileTopPos, sizeof(m_nowRiffFileTopPos) );
  m_nowRiffFileTopPos.LowPart = SetFilePointer( m_file, 0, &m_nowRiffFileTopPos.HighPart, FILE_CURRENT );

  // RIFF-AVIX
  {
   LIST avix;
   avix.dwList = mmioFOURCC('R','I','F','F');
   avix.dwSize = MAX_CHUNK_SIZE;    //< 後で書き換えに来るけどとりあえず最大値
   avix.dwFourCC = mmioFOURCC('A','V','I','X');

   DWORD writeSize;
   WriteFile( m_file, &avix, sizeof(avix), &writeSize, NULL );

   m_nowRiffSize += sizeof(LIST);
  }

  // JUNK
  {
   const DWORD junkSize = writePaddingJUNK();
   m_nowRiffSize += junkSize;
  }


  // ヘッダの位置記憶
  ZeroMemory( &m_moviListPos, sizeof(m_moviListPos) );
  m_moviListPos.LowPart = SetFilePointer( m_file, 0, &m_moviListPos.HighPart, FILE_CURRENT );

  // 'movi'LIST 書き込み
  {
   LIST movi;
   movi.dwList = mmioFOURCC('L','I','S','T');
   movi.dwSize = (MAX_CHUNK_SIZE - m_nowRiffSize) - 8;
   movi.dwFourCC = mmioFOURCC('m','o','v','i');

   DWORD writeSize;
   WriteFile( m_file, &movi, sizeof(movi), &writeSize, NULL );

   m_nowRiffSize += sizeof(LIST);
  }
 }


 void exitRIFF()
 {
  // AVISUPERINDEX_ENTRY蓄積。各チャンクのix00の位置を示す
  {
   LARGE_INTEGER nowPos = {0};
   nowPos.LowPart = SetFilePointer( m_file, 0, &nowPos.HighPart, FILE_CURRENT );

   AVISUPERINDEX_ENTRY entry = {0};
   entry.dwSize = sizeof(AVISTDINDEX) + sizeof(AVISTDINDEX_ENTRY)*m_aviStdIndexEntryList.size();
   entry.dwDuration = m_aviStdIndexEntryList.size();
   entry.qwOffset = nowPos.QuadPart;
   m_superIndexEntryList.push_back( entry );
  }
  
  // ix00
  {
   AVISTDINDEX standardIndex = {0};
   standardIndex.fcc = mmioFOURCC('i','x','0','0');
   standardIndex.cd = (sizeof(AVISTDINDEX) - 8) + sizeof(AVISTDINDEX_ENTRY)*m_aviStdIndexEntryList.size();
   standardIndex.wLongsPerEntry = 2;  //< must be 2
   standardIndex.bIndexSubType = 0;  //< must be 0
   standardIndex.bIndexType = AVI_INDEX_OF_CHUNKS;  //< AVI_INDEX_OF_CHUNKS == 1
   standardIndex.nEntriesInUse = m_aviStdIndexEntryList.size();
   standardIndex.dwChunkID = mmioFOURCC('0','0','d','b');
   standardIndex.qwBaseOffset = m_moviListPos.QuadPart;

   DWORD writeSize;
   WriteFile( m_file, &standardIndex, sizeof(standardIndex), &writeSize, NULL );

   // entry
   if ( !m_aviStdIndexEntryList.empty() )
   {
    WriteFile( m_file, &m_aviStdIndexEntryList[0], sizeof(m_aviStdIndexEntryList[0])*m_aviStdIndexEntryList.size(), &writeSize, NULL );
   }

   m_nowRiffSize += sizeof(AVISTDINDEX) + sizeof(AVISTDINDEX_ENTRY) * m_aviStdIndexEntryList.size();
  }

  // idx1
  if ( !m_isAVIX )
  {
   // write index header
   CHUNK index;
   index.dwFourCC = mmioFOURCC('i','d','x','1');
   index.dwSize = sizeof(AVIINDEXENTRY) * m_aviIndexEntryList.size(); 

   DWORD writeSize;
   WriteFile( m_file, &index, sizeof(index), &writeSize, NULL );

   // write entry
   if ( !m_aviIndexEntryList.empty() )
   {
    WriteFile( m_file, &m_aviIndexEntryList[0], sizeof(m_aviIndexEntryList[0])*m_aviIndexEntryList.size(), &writeSize, NULL );
   }

   m_nowRiffSize += sizeof(CHUNK) +  sizeof(AVIINDEXENTRY) * m_aviIndexEntryList.size();
  }


  // まだAVI-RIFFだったらAVI-RIFFでのサイズとフレーム数記憶
  if ( !m_isAVIX )
  {
   m_riffAVIChunkSize = m_nowRiffSize;
   m_riffAVIChunkFrame = m_nowRiffFrame;
  }

  // ヘッダ位置に戻ってサイズ情報を更新する
  if ( m_isAVIX )
  {
   // 現在チャンクのヘッダ位置に移動
   SetFilePointer( m_file, m_nowRiffFileTopPos.LowPart, &m_nowRiffFileTopPos.HighPart, FILE_BEGIN );
   
   // ヘッダ書き換え
   LIST avix;
   avix.dwList = mmioFOURCC('R','I','F','F');
   avix.dwSize = m_nowRiffSize - 8;
   avix.dwFourCC = mmioFOURCC('A','V','I','X');

   DWORD writeSize;
   WriteFile( m_file, &avix, sizeof(avix), &writeSize, NULL );
  }

  // moviチャンクのサイズを正しい値に書き換える
  {
   // movi位置に移動
   SetFilePointer( m_file, m_moviListPos.LowPart, &m_moviListPos.HighPart, FILE_BEGIN );

   LIST movi;
   movi.dwList = mmioFOURCC('L','I','S','T');
   movi.dwSize = (sizeof(LIST) - 8) + m_moviChunkOffset + sizeof(AVISTDINDEX) + sizeof(AVISTDINDEX_ENTRY)*m_aviStdIndexEntryList.size();
   movi.dwFourCC = mmioFOURCC('m','o','v','i');

   DWORD writeSize;
   WriteFile( m_file, &movi, sizeof(movi), &writeSize, NULL );
  }



  // AVI header
  {
   SetFilePointer( m_file, 0, NULL, FILE_BEGIN );

   // write avi header
   // RIFF-AVI LIST
   {
    LIST aviList;
    aviList.dwList = mmioFOURCC('R','I','F','F');
    aviList.dwSize = m_riffAVIChunkSize - 8;
    aviList.dwFourCC = mmioFOURCC('A','V','I',' ');

    DWORD writeSize;
    WriteFile( m_file, &aviList, sizeof(aviList), &writeSize, NULL );
   }

   // LIST-hdrl
   {
    LIST hdrlList;
    hdrlList.dwList = mmioFOURCC('L','I','S','T');
    hdrlList.dwSize = (sizeof(LIST) - 8)
     + sizeof(CHUNK) + sizeof(MainAVIHeader)
     + sizeof(LIST)
      + sizeof(CHUNK) + sizeof(AVIStreamHeader)
      + sizeof(CHUNK) + sizeof(BITMAPINFOHEADER)
      + sizeof(AVISUPERINDEX) + sizeof(AVISUPERINDEX_ENTRY) * m_superIndexEntryList.size()
     + sizeof(LIST)
      + sizeof(AVIEXTHEADER);
    hdrlList.dwFourCC = mmioFOURCC('h','d','r','l');

    DWORD writeSize;
    WriteFile( m_file, &hdrlList, sizeof(hdrlList), &writeSize, NULL );
   }

   // avih chunk
   {
    CHUNK avihChunk;
    avihChunk.dwFourCC = mmioFOURCC('a','v','i','h');
    avihChunk.dwSize = sizeof(MainAVIHeader);

    DWORD writeSize;
    WriteFile( m_file, &avihChunk, sizeof(avihChunk), &writeSize, NULL );
   }

   // MainAVIHeader
   {
    MainAVIHeader mainAVIHeader = {0};

    
    mainAVIHeader.dwMaxBytesPerSec = m_mostLargeImageSize * m_fps;
    mainAVIHeader.dwMicroSecPerFrame = (1000*1000) / m_fps;
    mainAVIHeader.dwPaddingGranularity = PADDING_GRANULARITY;
    mainAVIHeader.dwFlags = AVIF_TRUSTCKTYPE | AVIF_HASINDEX; //<< 2064
    mainAVIHeader.dwTotalFrames = m_riffAVIChunkFrame;
    mainAVIHeader.dwInitialFrames = 0;
    mainAVIHeader.dwStreams = 1;
    mainAVIHeader.dwSuggestedBufferSize = (m_mostLargeImageSize + sizeof(CHUNK));
    mainAVIHeader.dwWidth = m_width;
    mainAVIHeader.dwHeight = m_height;
    
    DWORD writeSize;
    WriteFile( m_file, &mainAVIHeader, sizeof(mainAVIHeader), &writeSize, NULL );
   }

   // LIST-strl
   {
    LIST strlList;
    strlList.dwList = mmioFOURCC('L','I','S','T');
    strlList.dwSize = (sizeof(LIST) - 8)
      + sizeof(CHUNK) + sizeof(AVIStreamHeader)
      + sizeof(CHUNK) + sizeof(BITMAPINFOHEADER)
      + sizeof(AVISUPERINDEX) + sizeof(AVISUPERINDEX_ENTRY) * m_superIndexEntryList.size();
    strlList.dwFourCC = mmioFOURCC('s','t','r','l');

    DWORD writeSize;
    WriteFile( m_file, &strlList, sizeof(strlList), &writeSize, NULL );
   }

   // strh chunk
   {
    CHUNK strhChunk;
    strhChunk.dwFourCC = mmioFOURCC('s','t','r','h');
    strhChunk.dwSize = sizeof(AVIStreamHeader);

    DWORD writeSize;
    WriteFile( m_file, &strhChunk, sizeof(strhChunk), &writeSize, NULL );
   }

   // AVIStreamHeader
   {
    AVIStreamHeader streamHeader = {0};
    streamHeader.fccType = mmioFOURCC('v','i','d','s');
    streamHeader.fccHandler = mmioFOURCC('D','I','B',' ');
    streamHeader.dwFlags = 0;
    streamHeader.wPriority = 0;
    streamHeader.wLanguage = 0;
    streamHeader.dwInitialFrames = 0;
    streamHeader.dwScale = 1000;
    streamHeader.dwRate = m_fps*1000;
    streamHeader.dwStart = 0;
    streamHeader.dwLength = /*m_riffAVIChunkFrame*/m_totalFrame;  //< 納得いかないがWMPだと全体のフレーム数でないと途中で落ちる
    streamHeader.dwSuggestedBufferSize = m_imageSize;
    streamHeader.dwQuality = -1;
    streamHeader.dwSampleSize = m_imageSize;
    streamHeader.rcFrame.left = 0;
    streamHeader.rcFrame.top = 0;
    streamHeader.rcFrame.right = m_width;
    streamHeader.rcFrame.bottom = m_height;
    DWORD writeSize;
    WriteFile( m_file, &streamHeader, sizeof(streamHeader), &writeSize, NULL );
   }

   // strf chunk
   {
    CHUNK strfChunk;
    strfChunk.dwFourCC = mmioFOURCC('s','t','r','f');
    strfChunk.dwSize = sizeof(BITMAPINFOHEADER);

    DWORD writeSize;
    WriteFile( m_file, &strfChunk, sizeof(strfChunk), &writeSize, NULL );
   }

   // BITMAPINFOHEADER
   {
    BITMAPINFOHEADER bmpInfoHeader = {0};
    bmpInfoHeader.biSize = sizeof(bmpInfoHeader);
    bmpInfoHeader.biWidth = m_width;
    bmpInfoHeader.biHeight = m_height;
    bmpInfoHeader.biPlanes = 1;
    bmpInfoHeader.biBitCount = 32;
    bmpInfoHeader.biCompression = BI_RGB; //< =0
    bmpInfoHeader.biSizeImage = m_imageSize;
    bmpInfoHeader.biXPelsPerMeter = 3780;
    bmpInfoHeader.biYPelsPerMeter = 3780;
    bmpInfoHeader.biClrUsed = 0;
    bmpInfoHeader.biClrImportant = 0;

    DWORD writeSize;
    WriteFile( m_file, &bmpInfoHeader, sizeof(bmpInfoHeader), &writeSize, NULL );
   }

   // AVISUPERINDEX + entry
   {
    
    AVISUPERINDEX aviSuperIndex = {0};
    aviSuperIndex.fcc = mmioFOURCC('i','n','d','x');
    aviSuperIndex.cd = (sizeof(AVISUPERINDEX) - 8) + sizeof(AVISUPERINDEX_ENTRY)*m_superIndexEntryList.size();
    aviSuperIndex.wLongsPerEntry = sizeof(AVISUPERINDEX_ENTRY) / 4;  //< must be 4
    aviSuperIndex.bIndexType = AVI_INDEX_OF_INDEXES;
    aviSuperIndex.bIndexSubType = 0;
    aviSuperIndex.nEntriesInUse = m_superIndexEntryList.size();
    aviSuperIndex.dwChunkID = mmioFOURCC('0','0','d','b');
    
    DWORD writeSize;
    WriteFile( m_file, &aviSuperIndex, sizeof(aviSuperIndex), &writeSize, NULL );
    
    // entry
    if ( !m_superIndexEntryList.empty() )
    {
     WriteFile( m_file, &m_superIndexEntryList[0], sizeof(AVISUPERINDEX_ENTRY)*m_superIndexEntryList.size(), &writeSize, NULL );
    }
   }

   // LIST-odml
   {
    LIST odmlList;
    odmlList.dwList = mmioFOURCC('L','I','S','T');
    odmlList.dwSize = (sizeof(LIST) - 8)
      + sizeof(AVIEXTHEADER);
    odmlList.dwFourCC = mmioFOURCC('o','d','m','l');

    DWORD writeSize;
    WriteFile( m_file, &odmlList, sizeof(odmlList), &writeSize, NULL );
   }

   // dmlh
   {
    AVIEXTHEADER dmlh = {0};
    dmlh.fcc = mmioFOURCC('d','m','l','h');
    dmlh.cb = sizeof(AVIEXTHEADER) - 8;
    dmlh.dwGrandFrames = m_totalFrame;

    DWORD writeSize;
    WriteFile( m_file, &dmlh, sizeof(dmlh), &writeSize, NULL );
   }


   // JUNK
   {
    // ファイル作成時にすっとばしたヘッダ分の領域
    const DWORD preFileHeaderSkipSize =
     sizeof(LIST)              //< RIFF-AVI LIST
      + sizeof(LIST)             //< hdrl List
       + sizeof(CHUNK) + sizeof(MainAVIHeader)      //< avih Chunk + MainAVIHeader
       + sizeof(LIST)            //< list-strl
        + sizeof(CHUNK) + sizeof(AVIStreamHeader)    //< strh Chunk + AVIStreamHeader
        + sizeof(CHUNK) + sizeof(BITMAPINFOHEADER)    //< strf Chunk + BITMAPINFOHEADER
        + sizeof(AVISUPERINDEX) + SUPER_INDEX_ENTRY_SIZE  //< AVISUPERINDEX + SUPER_INDEX_ENTRY_SIZE
       + sizeof(LIST)            //< odml List
        + sizeof(AVIEXTHEADER)         //< AVIEXTHEADER
      + sizeof(LIST);            //< movi LIST
    DWORD fileHeaderSkipSize = preFileHeaderSkipSize;


    if ( PADDING_GRANULARITY )
    {
     const DWORD PaddingGranularity = PADDING_GRANULARITY;   //< 0だとコンパイル時に0除算で怒られるので適当に変数にいったん入れる
     fileHeaderSkipSize = preFileHeaderSkipSize + (PADDING_GRANULARITY - (preFileHeaderSkipSize % PaddingGranularity));
    }


    // 'movi'はすでに記述されているのでその分の領域は計算に入れない
    fileHeaderSkipSize -= sizeof(LIST);

    // 実際に記述したヘッダ分
    const DWORD writtenHeaderSize =
     sizeof(LIST)                       // riff-avi
      + sizeof(LIST)                      // list-hdrl
       + sizeof(CHUNK) + sizeof(MainAVIHeader)               // avih + MainAVIHeader
       + sizeof(LIST)                     // list-strl
        + sizeof(CHUNK) + sizeof(AVIStreamHeader)             // strh + AVIStreamHeader
        + sizeof(CHUNK) + sizeof(BITMAPINFOHEADER)             // strf + BITMAPINFOHEADER
        + sizeof(AVISUPERINDEX) + sizeof(AVISUPERINDEX_ENTRY) * m_superIndexEntryList.size()  // AVISUPERINDEX + AVISUPERINDEX_ENTRY
       + sizeof(LIST)                     // LIST-odml
        + sizeof(AVIEXTHEADER);                  // AVIEXTHEADER
    const DWORD junkSize = fileHeaderSkipSize - writtenHeaderSize;
    writeJUNK( junkSize );
   }
  }

 }


 // 指定サイズのJUNKチャンクを書き込む。CHUNK込み
 void writeJUNK( DWORD junkSize )
 {
  CHUNK chunk;
  chunk.dwFourCC = mmioFOURCC('J','U','N','K');
  chunk.dwSize = junkSize - sizeof(chunk);

  DWORD writeSize;
  WriteFile( m_file, &chunk, sizeof(chunk), &writeSize, NULL );
  //SetFilePointer( m_file, chunk.dwSize, NULL, FILE_CURRENT );
  std::vector< BYTE > blankData( chunk.dwSize );
  WriteFile( m_file, &blankData[0], chunk.dwSize, &writeSize, NULL );
 }

 // 2048境界になるようJUNKを書き込む。
 DWORD writePaddingJUNK()
 {
  // 2048バイト境界になるようJUNKをつめる
  if ( PADDING_GRANULARITY )
  {
   DWORD padding = SetFilePointer( m_file, 0, NULL, FILE_CURRENT );
   
   const DWORD PaddingGranularity = PADDING_GRANULARITY;   //< 0だとコンパイル時に0除算で怒られるので適当に変数にいったん入れる
   const DWORD paddingOverSize = padding % PaddingGranularity;
   if ( paddingOverSize )
   {
    // つめるjunkサイズを計算する。
    // chunk構造体より大きいサイズでないとならないので、それより小さければかさ増しする
    // PADDING_GRANULARITYがsizeof(CHUNK)より小さいと問題が出るかも
    const DWORD junkSize = PADDING_GRANULARITY - paddingOverSize;
    if ( junkSize > sizeof(CHUNK) )
    {
     writeJUNK( junkSize );
     return junkSize;
    }
    else
    {
     writeJUNK( junkSize + PADDING_GRANULARITY );
     return junkSize + PADDING_GRANULARITY;
    }
   }
  }

  return 0;
 }




 HANDLE m_file;
 DWORD m_nowRiffFrame;
 DWORD m_totalFrame;
 
 DWORD m_numOfRiff;
 DWORD m_nowRiffSize;
 DWORD m_mostLargeImageSize;

 DWORD m_maxNumOfRiff;
 std::vector< AVIINDEXENTRY > m_aviIndexEntryList;
 std::vector< AVISUPERINDEX_ENTRY > m_superIndexEntryList;
 
 DWORD m_moviChunkOffset;   //< 'movi'チャンク位置からの距離
 std::vector< AVISTDINDEX_ENTRY > m_aviStdIndexEntryList;

 LARGE_INTEGER m_nowRiffFileTopPos; //< 各チャンクサイズを書き換えに戻る際に使用する、チャンク開始位置
 LARGE_INTEGER m_moviListPos;  //< 'movi'開始位置
 
 bool m_isAVIX;

 DWORD m_width, m_height;
 DWORD m_imageSize;
 DWORD m_fps;
 DWORD m_riffAVIChunkFrame;   //< AVI-RIFF 内のフレーム数
 DWORD m_riffAVIChunkSize;   //< AVI-RIFFのサイズ
};






struct ObjPos
{
 float x, y;
 float vx, vy;
};

 
 
LRESULT CALLBACK wndProc(
 HWND hWnd,
 UINT msg,
 WPARAM wParam,
 LPARAM lParam )
{
 static DIB32 back, image;
 static ObjPos pos[ 64 ];
 static AVIRecorder avi;

 switch (msg)
 {
 case WM_DESTROY:
  //SendMessage( hWnd, WM_TIMER, 0, 0 );
  avi.close();
  ShowWindow( hWnd, SW_HIDE );
  PostQuitMessage(0);
  break;
 case WM_CREATE:
  SetTimer( hWnd, TIMER_ID, 1000/FPS, NULL );

  back.create( WINDOW_WIDTH, WINDOW_HEIGHT );
  image.create( TEXT("image.bmp") );

  // init obj
  {
   srand( GetTickCount() );

   const int n  = sizeof(pos) / sizeof(*pos);
   for (int i=0; i<n; ++i)
   {
    pos[i].x = static_cast<float>( std::rand() % WINDOW_WIDTH );
    pos[i].y = static_cast<float>( std::rand() % WINDOW_HEIGHT );
    pos[i].vx = (static_cast<float>( std::rand() & 1023 ) / 1023.f) * 8 - 4;
    pos[i].vy = (static_cast<float>( std::rand() & 1023 ) / 1023.f) * 8 - 4;
   }
  }
  
  if ( !avi.create( TEXT("output.avi"), back.getWidth(), back.getHeight(), FPS ) )
  {
   MessageBox( hWnd, TEXT("errir create avi"), NULL, MB_OK );
  }

  break;
 case WM_PAINT:
  {
   PAINTSTRUCT ps = {0};
   HDC hdc = BeginPaint( hWnd, &ps );
   back.render( hdc, hWnd );
   EndPaint( hWnd, &ps );
  }
  break;
 case WM_TIMER:
  ZeroMemory( back.getPixelAddr(), back.getWidth() * back.getHeight() * 4 );
  
  // move obj + render
  {
   const int n  = sizeof(pos) / sizeof(*pos);
   for (int i=0; i<n; ++i)
   {
    if ( pos[i].x + pos[i].vx < 0 || pos[i].x + pos[i].vx > WINDOW_WIDTH ) pos[i].vx = -pos[i].vx;
    if ( pos[i].y + pos[i].vy < 0 || pos[i].y + pos[i].vy > WINDOW_HEIGHT ) pos[i].vy = -pos[i].vy;
    pos[i].x += pos[i].vx;
    pos[i].y += pos[i].vy;

    image.render< DIB32::PutColorAdd >( back, pos[i].x, pos[i].y, image.getWidth(),image.getHeight(), 0,0,image.getWidth(),image.getHeight() );
   }
  }

  InvalidateRect( hWnd, NULL, FALSE );
  avi.update( back );


  // title
  {
   TCHAR title[ 256 ] = {0};
   wsprintf( title, TEXT("%d(%d)"), avi.getNowRiffFrame(), avi.getTotalFrame() );
   SetWindowText( hWnd, title );
  }
  break;

 default:
  return DefWindowProc( hWnd, msg, wParam, lParam );
 }
 
 return 0;
}
 



 
 
 
int WINAPI WinMain(
 HINSTANCE hInstance,
 HINSTANCE, PSTR, int )
{
 LPCTSTR WINDOW_NAME = TEXT("sample");
 
 WNDCLASSEX wc;
 wc.style  = CS_HREDRAW | CS_VREDRAW;
 wc.lpfnWndProc = reinterpret_cast<WNDPROC>( wndProc );
 wc.cbClsExtra = 0;
 wc.cbWndExtra = 0;
 wc.cbSize  = sizeof( WNDCLASSEX );
 wc.hInstance = hInstance;
 wc.hIcon  = NULL;
 wc.hIconSm  = NULL;
 wc.hCursor  = LoadCursor( NULL, IDC_ARROW );
 wc.hbrBackground= reinterpret_cast<HBRUSH>( GetStockObject(WHITE_BRUSH) );
 wc.lpszMenuName = NULL;
 wc.lpszClassName= WINDOW_NAME;
 if ( !RegisterClassEx(&wc) ) return 0;
 
 LONG winWidth = WINDOW_WIDTH
  + GetSystemMetrics(SM_CXEDGE)
  + GetSystemMetrics(SM_CXBORDER)
  + GetSystemMetrics(SM_CXDLGFRAME);
 LONG winHeight = WINDOW_HEIGHT
  + GetSystemMetrics(SM_CYEDGE)
  + GetSystemMetrics(SM_CYBORDER)
  + GetSystemMetrics(SM_CYDLGFRAME)
  + GetSystemMetrics(SM_CYCAPTION);
 HWND hWnd = CreateWindowEx(
  0, WINDOW_NAME, NULL, WS_OVERLAPPEDWINDOW ^ WS_THICKFRAME,
  CW_USEDEFAULT, CW_USEDEFAULT, winWidth, winHeight,
  NULL, NULL, hInstance, NULL);
 if ( !hWnd ) return -1;
 
 ShowWindow( hWnd, SW_SHOWNORMAL );
 UpdateWindow( hWnd );

 
 MSG msg;
 for (;;)
 {
  if ( !GetMessage(&msg, NULL, 0, 0) ) break;
  TranslateMessage( &msg );
  DispatchMessage( &msg );
 }
 
 
 UnregisterClass( WINDOW_NAME, hInstance );
 return msg.wParam;
}

2016年5月29日日曜日

ActiveWindowLogger Log2CSV ver 1.01




■ダウンロード

File: AWL_Log2CSV_v101.zip
Size: 69.2KB
分類: フリーソフト




■概要
    ActiveWindowLoggerのログファイル(.bin2)をCSV形式に変換して指定フォルダに保存します



■動作環境
    Windows XP, 7
    CPU 800MHz以上
    メモリ 256MB以上



■アンインストール
    レジストリは触っておりません。
    削除の際はフォルダごとゴミ箱へどうぞ。



■使い方
    ログファイル(.bin2)をウィンドウにD&Dしてください。
    設定してある出力先フォルダにcsvに変換されて出力されます。

    フォルダ1個のみをD&Dした際に表示されるメニューから「フォルダ内のログを変換」を選ぶと、
    フォルダ直下にあるログファイルのみが指定出力先にCSVに変換されて出力されます。

    実行ファイルに直接D&Dするとあらかじめ設定された出力先に変換されて出力されます。


    *出力先フォルダを設定する都合上、
    複数フォルダおよびサブフォルダ以下のファイルはファイル名がぶつかるため走査しません。

    *ActiveWindowRecorderのログファイル(.bin)も変換できます。


    ●マルチスレッド数の設定
        複数コアCPUをご使用の方はスレッド数の値を増やすと変換速度が上がるかもしれません。

    ●出力先の変更
        CSVが出力されるフォルダを設定します。
        フォルダ1個のみをD&Dをした際に表示されるメニューから「出力先フォルダに設定」を選ぶと、
        ドロップされたフォルダを出力先に設定できます。


    ●出力文字設定
            出力される文字列の設定ができます。
            テストボタンで押すと、指定文字が置き換えられたあとの文字列がテストのところに表示されます。
            指定文字以外はそのまま出力されます。
            指定文字は以下のものが使えます
                「%h」時
                「%m」分
                「%s」秒
                「%l」ミリ秒
                「%H」時(2桁)
                「%M」分(2桁)
                「%S」秒(2桁)
                「%L」ミリ秒(3桁)
                「%i」ミリ秒(時刻*60*60*1000 + 分*60*1000 + 秒*1000 + ミリ秒。時刻を一項目で表現します)
                「%n」表示時間 分
                「%e」表示時間 秒
                「%c」表示時間 ミリ秒
                「%E」表示時間 秒(2桁)
                「%C」表示時間 ミリ秒(3桁)
                「%d」表示時間 ミリ秒(分*60*1000 + 秒*1000 + ミリ秒。表示時間を一項目で表現します)
                「%t」ウィンドウタイトル
                「%x」実行ファイルパス
                「%%」文字の%

    ●ファイルにBOMをつける
        ファイル先頭にBOMを記述します
    ●改行コード
        改行コードをLFかCRLFのどちらかを使用するようにします



■そのほか
    このソフトウェアを使用し、何らかの障害が発生しても責任を取りかねますのでご了承ください。

    zlibライブラリを使用しています。
        Copyright (C) 1995-2005 Jean-loup Gailly and Mark Adler
        http://www.winimage.com/zLibDll/



■更新履歴
    2016-05-29 ver1.01
        出力文字をいじくれるよう修正
        BOMの有無を選択できるよう修正
        改行コードを選択できるよう修正
    2013-02-10 ver1.00
        作成