2016年8月15日月曜日

C# プログレスダイアログ(進捗ダイアログ)の表示

世の中的にはたくさん情報があるが、備忘録をかねて、書く

プログレスダイアログの表示とはWindowsアプリケーションで、なんか重たい処理をした時に、進捗を表すためのFormである。
今時はWPFでやるべきだと思いつつ、まだ、Formアプリケーションを勉強中。

目標としては、以下を意識した

  • 進捗表示中にアプリケーションがフリーズしない
  • 進捗表示に数値情報を出して、あと、どれくらいで終わるかわかるようにする。
  • 今後も、別のアプリケーションで利用するように汎用性を持たせること



まず、僕が作ったProgressFormクラスをのせておきたいのだが、どうも上手く行かなかった。普通にのせるには容量多い。なんぞ、Gistという方法があるみたいなので、後日頑張ってのせる予定。多分、きっと。ぶっちゃけ、誰も見てないか。見てたら、コメントが無性にほしい。ほしがりさんですいません。どうでもいいか。


以下で、ひとまず、載せてもいないコードについて書く。

自分の作成した関数の中身

  • ポイントはまず表示をShow関数で行うこと。この時に、全ての初期化を行う。
  • 次にMessageプロパディを編集して、プログレスダイアログの表示を更新させる。これはクライアントのコードに記載する。この中で、Invokeも行っているので、更新がされないことは無い。
  • 最後に、Close関数で、プログレスダイアログを閉じる。これはfinallyから、呼び出すようにすると良い。
呼び出し側の重い箇所はTaskクラスで、スレッド化する必要がある。そうしないと、アプリケーションがフリーズしたように見えて、気持ち悪い。書き方は以下の感じ。間違ってたら、ごめん。ラムダ式便利。
Task.Run( () => { Console.WriteLine("test");} );

後、この処理ではプログレスダイアログを呼び出したフォームはEnableプロパディをfalseにすることで、操作できないようにしてある。これも最初はどうしたら良いかと迷った。。。
http://qiita.com/OneK/items/6ca3a71511cfe53caabc

これを作っていて、一番、混乱したのはスレッドの扱い方だった。進捗表示はGUIタスクの中で処理すべきかという話で、最初は別スレッドを作った。でも、結果として、GUI表示はGUIタスクの中にまとめて、重たい処理をTaskクラスで、別スレッドにするのが良いと感じた。今後も、その方針で行こうと決めれたのが良かった。


ひとまず、以上で!


2016年5月1日日曜日

openTKの警告 CS0618 'GL.DrawArrays(BeginMode, int, int)' は旧形式です ('Use PrimitiveType overload instead')解決方法

最近、Windowsで3D描画をしたくて、OpenTKを勉強している。

以下のページが非常に参考になっているが、コードを実行時に警告が出て、戸惑ったので、備忘録を残す。
http://masuqat.net/programming/csharp/OpenTKindex.php


OpenTKで以下のような警告がいっぱい出た。
「警告 CS0618 'GameWindow.Mouse' は旧形式です ('Use MouseMove, MouseDown, MouseUp and MouseWheel events or OpenTK.Input.Mouse, instead.') 」
とか
「警告 CS0618 'Matrix4.Rotate(Quaternion)' は旧形式です ('Use CreateRotation instead.')」
とか
「警告 CS0618 'GL.DrawArrays(BeginMode, int, int)' は旧形式です ('Use PrimitiveType overload instead')」
え、旧形式を勉強してるの。。。と少し萎えたが、簡単に新形式に置き換えできた。
備忘録として残しておく。

this.Mouse.ButtonDown
↓
this.MouseDown

this.Mouse.ButtonUp
↓
this.MouseUp

this.Mouse.Move
↓
this.MouseMove

this.Mouse.WheelChanged
↓
this.MouseWheel

Matrix4.Rotate
↓
Matrix4.CreateFromQuaternion

if (Keyboard[Key.Escape])
{
this.Exit();
}
↓
KeyDown += (sender, eKey) =>
{
        if (eKey.Keyboard[Key.Escape])
        {
                this.Exit();
        }
}

GL.DrawArrays(BeginMode.Lines, 0, 2);     // 単位表示
↓
GL.DrawArrays(PrimitiveType.Lines, 0, 2);     // 単位表示



分かれば、たいしたことではないが、初心者には中々心理的ハードルが高かった 笑。

基本的にはエラー文を参考にすれば、丁寧に解説してくれているので、簡単に直すことが可能。一瞬大掛かりな形式に変わったのかと身構えてしまうがそんなことは無いので、安心しよう。


壁にめげずに学ぶぞ 笑

参考ページ
http://stackoverflow.com/questions/32503171/how-to-use-gl-drawarrays-to-draw-linestrip-using-opentk








2016年3月13日日曜日

GetBufferの考え方(MFC)

MFCのGetBufferについて、いろいろとはまった。。。

まだまだ確信を持てないことが多いが、自分なりに理解したことを備忘録として残しておく。



GetBufferはMFCでCStringを利用時に、固定メモリ領域を確保するために利用される。

GetBufferを利用時には同時に確保したいメモリ領域長を指定する必要がある。
例: GetUserName(strName.GetBuffer(UNLEN + 1), &dwNameLength)
参考サイトhttp://rui.primasm.com/2012/12/bug_getbugger_string_1/

その際、ReleaseBufferを忘れないようにする。
また、ReleaseBuffer呼び出し前に、CStringの別関数を呼び出すと、メモリの再確保が行われ、GetBufferの確保領域のポインタが無効になるので、呼ぶような書き方はしないほうが良い。



ちなみに、GetBufferは以下のようの呼び出し方でも問題はない。
デバッガを使用して確認した。

strName.GetBuffer(UNLEN + 1);
GetUserName(strName, NameLength)

この場合でも、GetBufferの返り値と同様のメモリポインタ(LPCTSTR)が渡されていることを確認した。
ただ、結局GetBuffer後はReleaseBufferをするまで、別の関数を呼び出すことは出来ないので、このような書き方はしないほうが良い。

自分は仕事がらみで、この書き方をしてしまい、あとから大分悩んだ 笑。デバッガを使用すれば、ポインタを直接見れることに気付いて、問題ないことを確認した。
自分の戒めのためにも、今後のためにもブログに一応残しておく。

その時にGetBuffer絡みで参考になったサイト
http://www.kab-studio.biz/Programing/PragmaTwice/Main/223.html


ぜんぜん違う話だが、Format関数の%sでCStringを直接利用してはいけないことを知らなくて、驚いた。余談なので、リンクのみ残しておく。
http://drumken.blog8.fc2.com/blog-entry-246.html


今日は以上なり。


2016年2月13日土曜日

IFileOperationのCopyItemsについて

こんにちは

最近、windows内で、ファイルをコピーするときに、SHFileOperationより、IFileOperationを利用したほうが良いと知りました。windows XP SP3以降はそうらしいです。

しかし、ネット上にIFileOperationの情報が少なくて、実装に大分時間がかかりました。。。それで、ネット上に備忘録も記載します。

参考になったサイトは以下で、そこを見れば、大体のことはわかると思います。

単体ファイルコピーのCopyItem関数についてはmsdnのサイト、そのままでいけました。その後、の複数ファイルをコピーするCopyItems関数の使い方を調べ出して、時間がかかりました。CopyItemをループで回してる人も多そうですね。もしかすると、そっちの方が、内部処理的に早いかもといまさら思いました 笑

CopyItemsを使う場合は以下のようにすれば、可能です。

int CFileManager::CopySelectedFileToSpecifiedFolder(list<CString>* listCopiedFilePath, CString csImportFolder)
{
if (listCopiedFilePath == NULL || listCopiedFilePath->empty() == true) {
return -1;
}

HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); 
if (!SUCCEEDED(hr))
{
return -1;
}

PIDLIST_ABSOLUTE abSourcePidl = NULL;
SFGAOF attrs;

int srcFileNum = listCopiedFilePath->size();
PIDLIST_ABSOLUTE[] pPidlRawArray = new PIDLIST_ABSOLUTE[srcFileNum]);

int no = 0;
for (list<CString>::iterator it = listCopiedFilePath->begin(); it != listCopiedFilePath->end(); it++)
{
CString strSource = *listCopiedFilePath->begin();
hr = SHParseDisplayName(*it, NULL, &abSourcePidl, 0, &attrs);    
if (FAILED(hr))
{
return -1;
}
pPidlRawArray[no] = abSourcePidl;
no++;
}
    delete[] PIDLIST_ABSOLUTE;

CComPtr<IShellItemArray> pShellItemArr = NULL;
hr = SHCreateShellItemArrayFromIDLists(srcFileNum, (LPCITEMIDLIST*)pPidlRawArray.get(), &pShellItemArr);
if (FAILED(hr))
{
return -1;
}


PIDLIST_ABSOLUTE abDestPidl = NULL;

hr = SHParseDisplayName(csImportFolder, NULL, &abDestPidl, 0, &attrs);
if (FAILED(hr))
{
return -1;
}

CComPtr<IShellItem2> pDestShellItem;

hr = SHCreateItemFromIDList(abDestPidl, IID_IShellItem2, reinterpret_cast<LPVOID*>(&pDestShellItem));
if (FAILED(hr))
{
return -1;
}

CComPtr<IFileOperation> fileOp;
hr = fileOp.CoCreateInstance(CLSID_FileOperation);
if (FAILED(hr))
{
return -1;
}

hr = fileOp->CopyItems(pShellItemArr, pDestShellItem);
if (FAILED(hr))
{
return -1;
}

hr = fileOp->PerformOperations();
if (FAILED(hr))
{
return -1;
}

CoUninitialize();

return 0;
}

std::listを使ってたり、一部コード修正したので、コンパイルは未確認だったりしますが、ほぼ同じコードで動作確認しています。
ご参考までにご利用ください。

ではでは~