広告

この広告は30日以上更新がないブログに表示されております。
新規記事の投稿を行うことで、非表示にすることが可能です。
posted by fanblog

2016年03月16日

タスクバー(タスクトレイ)上のアプリアイコンで進捗状況表示

久々にクライアントアプリの開発をすることになった。
ネットワーク越しにファイル変換を行って、変換後のデータをもらい、ファイルに保存するという割と単純なものだが、ファイルサイズによっては時間がかかる場合もある。
そこでUI的に進捗状況を表示したいと思ったが、ダイアログ上にプログレスバーを表示するのは実装してみたが、何かしっくりこないので、別のインターフェースを模索していたら、Windows7以降で追加された、タスクバー上でのアイコンがプログレスバーのように変化するアプリがあるのを思い出し、まねをしてみようと思い立った。

WEBで検索してみると、 ITaskBar3インターフェースを使えばできそうということがわかった。

ITaskbarList3::SetProgressState

ところが、MSDNのどこにも、 ITaskbarList3のインスタンスの取得方法が書いていなかった。
昔、こうした状況だと、グローバル関数でインスタンスを作成していたという記憶があるのだが、どうも対応する生成関数などが見当たらない。

Excel のインスタンスを取得する際は、 CoGetClassObjectを使えばいいはず、ということから、関連で調べていくと、 単純に CoCreateInstanceすればいいのではないかということになったのだった。


ようやく、コードが作成でき、以下のような関数を作成して呼び出すようにしてみた

void setup_taskbar_marquee(HWND hWnd, bool bStart)
{
CComPtr pUnk;
HRESULT hr = CoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_ALL, IID_ITaskbarList, (LPVOID*)&pUnk);
if (SUCCEEDED(hr)) {
CComQIPtr tbl(pUnk);
if (tbl) {
CComQIPtr tbl3(tbl);
if (tbl3) {
if (bStart) {
tbl3->SetProgressState(hWnd, TBPF_INDETERMINATE);
} else {
tbl3->SetProgressState(hWnd, TBPF_NOPROGRESS);
}
}
}
}
}


上記コードはマーキースタイルのプログレス表示で、開始と停止があるだけなので、使い方も簡単である

開始 setup_taskbar_marquee(hWnd, true)
停止 setup_taskbar_marquee(hWnd, false)

できてみればさほど難しいコードではないのだが、最初がわかるまでは結構調べるのに時間がかかってしまうものだ
これで見た目も、最近のアプリっぽくなり (もう古いか?) めでたし、めでたし

完全仮想化方式のVPS

タグ: C C++ Win32

2015年08月27日

レジストリの Wow6432Node へのリダイレクト条件

64ビットOS上で実行される32ビット実行ファイルから見えるレジストリツリーは Wow6432Node 以下になるというのは、64ビット開発を始めた頃に知ったのだが、必ずしもそうとは限らない状況があることがわかった
たまたま、32ビットコンパイルした実行ファイルを Windows8.1 (64bit) 上でデバッグしていた際、レジストリから情報を取得するルーチンで、値が取れていないことが判明、よく見たら、32ビット環境から見える HKLM\SOFTWARE\Wow6432Node 以下には確かに値を設定していないことがわかった
例えば以下のコードを実行しようとすると

HKEY hKey;
LONG lRes = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("Software\\MyCompany\\Information"), 0, KEY_QUERY_VALUE, &hKey);
実際には HKLM\Software\Wow6432Node\MyCompany\Information キーが開かれる

これと同じスキームで、HKCU にアクセスしている箇所のキーを、Wow6432Node 以下に作成したら、見つからないという現象に見舞われた

HKEY hKey;
LONG lRes = RegOpenKeyEx(HKEY_CURRENT_USER, _T("Software\\MyCompany\\Information"), 0, KEY_QUERY_VALUE, &hKey);
コード上はおかしくないのだが、もしかしたらと思って、HKCU\Software\MyCompany\Information に値を設定したら正しく読み込めたのだった
こんな仕様だったっけ?と思い調べなおしたら、以下の解説が見つかった

https://msdn.microsoft.com/en-us/library/aa384253%28v=vs.85%29.aspx

要するに、Wow6432Node にリダイレクトされるものとそうでないものが決まっているということだった
こうした情報を見逃していたのだった(恥ずかしい)




2015年07月26日

.pdb から .map ファイルを作成する

VS2012 を使用するようになってから、通常、デバッグビルド、リリースビルド共に、実行ファイルと .pdb ファイルが生成されるように運用してきたが、ふと、アプリケーションエラー時にどのアドレスで落ちたかを知るための手掛かりを得る .map ファイルを生成すべきかどうかが気になった

以前(VS6を利用してきた期間が長い)はリリースビルド時には、.pdb ファイルではなく .map ファイルを生成していたのだが、VS2012 に移行した際に、自動的に上記のような状況になっていた
最近までは特に気にしていなかったのだが、デバッガが無い状況で、アプリケーションエラーのアドレスだけ示されたときにどう追いかければいいのかが、ふと気になってしまった

そこで改めて調べてみたら、実行ファイルと .pdb ファイルがあれば、 .map ファイルを生成できることがわかったので、メモしておく

dumpbin /map appl.exe > appl.map


ところが、この /map オプションが MSDN ドキュメントにも、 dumpbin /? としても、出てこないのだ
見つけたのは、 stackoverflow の投稿記事の応答の中 である

オリジナルのソースが今のところ不明であるが、実際上出力内容は .map ファイル内容とほぼ同じである
違いとしては、通常コンパイルオプションで生成する .map ファイルは、リンクされた関数本体がどの .obj ファイル由来(どの .lib 由来)であるかがわかりやすい
一方、dumpbin の出力では、各関数のサイズがわかりやすいという違いがある
一般的には関数のエントリポイントとエントリポイントの間のどこかで例外となるので、サイズがわかる方が便利かもしれない

ただ、常に .map を生成するようにしておくと、ディスク容量がもったいないので、現状のように .pdb だけ生成しておき、必要になったら .map 情報を生成して調査する方がいいと思う


★☆★☆★☆ ナウでヤングなレンタルサーバー!ロリポップ! ☆★☆★☆★

月額100円(税抜)〜容量最大400GB!大人気のWordpressも簡単インストール★

海外向けのネットショップ作成サービスならJugem Cart

2015年02月10日

いまさらながらの Mailslot 【続き】

前回は MailSlot を使うための基本構成を示したのだが、実際にやりたいことは、他のデスクトップ、多くは異なるセキュリティコンテキスト(つまりは異なるユーザなど)からも通知を受けたいということであった
そうすると、前回のサーバ側の実装では、同じユーザコンテキストからしか通知を受けれないということになってしまう
それを解決するのは、実は簡単で、セキュリティ属性(SECURITYP_ATTRIBUTES)を渡せばいいだけなのだった

改善したサーバ側の実装例
CDacl dacl;
dacl.AddAllowedAce(Sids::World(), FILE_ALL_ACCESS);
CSecurityDesc sd;
sd.SetDacl(dacl);
CSecurityAttributes sa(sd);

HANDLE g_hServer = CreateMailslot(SERVER_NAME, MESSAGE_SIZE, SERVER_TIMEOUT, &sa);


今回はコードを簡潔にするために ATL のセキュリティ関連テンプレートを利用してみた
ATL を使いたくなければ、地道に構造体を初期化して、セキュリティコンテキスト名から SID を求めて、などの作業が必要となる(わかってしまえば、それほどのコードではないのだが、自分はいつも一発では書けないので、面倒だと感じている)
これでめでたく当初の目的は果たせることになった
ただし、今回は Sids::World() を利用したが、セキュリティを考慮し、もっと限定的な SID を利用することも可能だし、そうすべきかもしれない




2015年01月26日

いまさらながらの Mailslot

近頃セッション境界を越えてメッセージを送信したい要求が出てきて、やりたいことと仕掛けのバランスがよいものを探していたのだが、ふと、昔読んだ本の内容を思い出し、メールスロットなるものを使ってみた

元々の要求は、異なるプロセスから一方向でメッセージを送信したいというもので、普通にやるなら引数と SendMessage で済む程度の簡単なものなのだったが、セッション境界を越えて渡せないことに気が付き、はたと困ったのであった
こうした欲求を満たすには、大掛かりなものでは TCP で TELNET プロトコルを実装するなどをしてきたのだが、ポートの割り当て問題などがあり、ちょっと面倒
名前付きパイプが妥当そうに見えたのだが、API がごちゃごちゃしていて、あまり使い勝手がよくないと感じた(双方向通信なら、こちらがいいかもしれない)

そこで、一方向で済む、メールスロットを思い出したのだが、そもそもこの機構はかなり古い時代から存在するので、今では DDE のように化石扱いなのではないかと心配になった
API の解説を読む限りは、シンプルで最低限のセキュリティを備えているので、まだまだ現役で使えそうではある

サーバの簡単な実装は以下のようである

HANDLE g_hServer = CreateMailslotW(SERVER_NAME, MESSAGE_SIZE, SERVER_TIMEOUT, NULL);
while (true) {
DWORD cnt = 0;
if (!GetMailslotInfo(g_hServer, NULL, NULL, &cnt, NULL)) {
fprintf(stderr, "GetMailslotInfo failed by %d\n", GetLastError());
break;
}
if (cnt > 0) {
CHAR szMessage[MESSAGE_SIZE+1] = {0};
DWORD dwRead;
OVERLAPPED ovl = {0};
ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
BOOL rc = ReadFile(g_hServer, szMessage, MESSAGE_SIZE, &dwRead, &ovl);
CloseHandle(ovl.hEvent);
if (!rc) {
fprintf(stderr, "ReadFile failed by %d\n", GetLastError());
break;
}
printf("%s\n", szMessage);
}
Sleep(100);
}
CloseHandle(g_hServer);


途中、 if (cnt > 0) としてあるところは、 for (DWORD j = 0; j < cnt; j++) とすべきかもしれない

一方、クライアント側は単純にファイルに書き込むだけでよい
以下のよう

HANDLE hFile = CreateFile(SERVER_NAME, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (INVALID_HANDLE_VALUE == hFile) {
fprintf(stderr, "CreateFile failed by %d\n", GetLastError());
return 1;
}
CHAR szMessage[] = "Hello world";
DWORD dwSize;
if (!WriteFile(hFile, szMessage, strlen(szMessage), &dwSize, NULL)) {
fprintf(stderr, "WriteFile failed by %d\n", GetLastError());
}
CloseHandle(hFile);


実に単純な構成だが、セッション超えには実はもう一つ落とし穴があった





2014年08月23日

エクスプローラ再起動時におけるトレイアイコンの再登録

アプリを開発していて、タスクトレイ(現在は通知エリア?)にアイコンを登録し、右クリックでメニューを出して操作するというのは定番スタイルである
通常は困らないのだが、時折エクスプローラが死んで再起動されると、アイコンが消えてしまい、操作ができなくなってしまうという困った現象が起こることがある
それに対応するには、エクスプローラが再起動したというタイミングをとらえてアイコンを再登録する必要があるのだが、そのタイミングをどうやってとらえるかが問題だった

昔、XP時代にも何かやっていた記憶があるのだが、ソースも記憶も彼方に逝ってしまったので、改めて調べてみた
WEBを検索してみると、システムアイコンなどが消えた場合の復旧方法などばかりが引っかかり、アプリの開発方法はなかなかヒットしなかった

ようやく見つかり、わかりやすかったのは ここ だった

改めて自分でも整理してみた
全体の基本構造は以下のような感じである


UINT g_uTaskbarCreatedMessage = 0;

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg) {
case WM_CREATE:
// ここ、あるいはプログラムメイン開始時などで以下を実行する
g_uTaskbarCreatedMessage = RegisterWindowMessage(_T("TaskbarCreated"));
break;
case WMU_NOTIFY_ICON:
switch (lParam) {
case WM_CONTEXTMENU:
{
// ここでメニュー表示処理を行う
// ここに来れるためには、NIM_SETVERSION で4を設定しておく必要がある
}
break;
}
return 0;
default:
if (uMsg == g_uTaskbarCreatedMessage) {
// ここでアイコンの再登録処理を行う
}
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}



ちなみに、アイコンの登録やアイコンの変更などは以下のような感じ


#define WMU_NOTIFY_ICON (WM_APP+1)
#define MAX_RETRY 2
bool ReleatedlyNotifyIcon(UINT nimCommand, NOTIFYICONDATA* pnid)
{
int count = MAX_RETRY;
while (--count > 0) {
if (Shell_NotifyIcon(nimCommand, pnid) {
return true;
}
}
Sleep(100);
}
bool RegisterIcon(HWND hWnd, HICON hIcon)
{
NOTIFYICONDATA nid = {0};
nid.cbSize = sizeof(nid);
nid.hWnd = hWnd;
nid.hIcon = hIcon;
nid.uCallbackMessage = WMU_NOTIFY_ICON;
nid.uFlags = NIF_ICON|NIF_MESSAGE;

if (!RepeatNotifyIcon(NIM_ADD, nid)) {
return false;
}
// バージョン設定
nid.uVersion= 4;
return RepeatNotifyIcon(NIM_SETVERSION, nid);
}
bool ChangeIcon(HWND hWnd, HICON hIcon)
{
NOTIFYICONDATA nid = {0};
nid.cbSize = sizeof(nid);
nid.hWnd = hWnd;
nid.hIcon = hIcon;
nid.uFlags = NIF_ICON;

return RepeatNotifyIcon(NIM_MODIFY, nid);
}



最初に作るなら、このクレジットカード!

ロリポップ!レンタルサーバー !はあなたの「やってみたい!」を応援します!

ロリポップ!なら、ホームページ、ブログ、ネットショップ…

これらが今すぐ、そして簡単にできちゃう!

マニュアルやライブチャットを完備しているので、ホームページ初心者でも安心。

これだけついてるのに月額100円(税抜)〜ととってもお得。

もちろん無料のお試し期間も10日間あるので安心ですね。


▼ロリポップ!レンタルサーバーはこちら

2014年08月22日

C, C++ WIn32 標準ファイルIOライブラリでUTF入出力ができた

今までは、 CreateFile WriteFileなどでファイル入出力することが多かったので、ファイル内のテキストエンコードはあまり気にしたことがなかったのだが、近ごろ、ログファイルをShiftJISではなく、UTF-8で出してほしいという要望があがってきた

今までのやり方と踏襲するなら MultiByteToWideCharを使って WriteFileの直前にUTF-8変換をすることにしていただろう

しかし、ログファイルの「出力には、よくある可変数個の引数を使って、いわゆる printf方式で出力したいというのも人情である
また、共通ライブラリ化してあるものには、そうした標準ファイルIOを利用している箇所も多数あったため、改めて調べてみたら、 fopen 時に、文字エンコードを指定できる機能があることがわかった

MSDNドキュメント

FILE* fp = fopen("test.txt", "a, ccs=UTF-8");


こうすると、初めてファイルを作成した際に、 BOMが書き込まれて、メモ帳でもちゃんと読めるのがうれしい
※ちなみに、Windows向けのUTF-16は ccs=UTF-16LEと記述すればよいようだ
※普通のCPUはx86系かx64系なのでLE(Little Endian)ということだろう

いつの間にか便利な機能が追加されていたものだ





2014年07月15日

GetVersionEx が Win8.1 からは使えなくなったようだ

従来のコードをメンテナンスしている際、Win8.1 対応コードを追加しようとしたが、びっくりの結果がわかった
やろうとしていたのは以下のようなこと

OSVERSIONINFOEX os = {0};
os.dwOSVersionInfoSize = sizeof(os);
GetVersionEx((OSVERSIONINFO*)&os);
switch ( os.dwMajorVersion ) {
case 6:
switch ( os.dwMinorVersion ) {
case 0: return "Vista";
case 1: return "Win7";
case 2: return "Win8";
case 3: return "Win8.1";
}
break;
}

ところが、上記 os.dwMinorVersionの値が、Win8.1 上で実行しても Win8 を示す 2が返ってきてしまったのだ(本来 3が返ってきてほしかった)

MSDNで質問してみたら、すぐに回答が付き、理由がわかった
要は GetVersionEx はマニフェストに応じて異なるバージョンを返すということだった(なぜそうしたかの合理的な理由は思い浮かばなかったが、ま、しょうがない)

GetVersionEx の解説





2014年06月02日

サーバ方式ではないログの多重追記方式を考えてみる (3)

前回までに考えた、同一ログファイルの更新の件だが、サンプルプログラムを作成し、多重スレッド・多重プロセス起動をしてみたら、どうもだめだめな感じだ

バッファの書き込み先が、別スレッドでのEOFアドレスだったりするようで、EOF越で書き込まれた領域が NUL で埋まっていた
EOF まで移動して書き込むあたりがどうもだめなのかもしれない
もちろん、多重起動しなければ普通に書き込まれるので、やはり排他制御部分が思わしくないようだ
思い付きのアルゴリズムは信頼性がないのだろうな

※一応、MSDN ではログファイルなどの書き込みで、EOF からサイズ分だけロックする方式が可能だという説明もあったので、そもそもそうしたやり方が禁止されているわけではなさそう (でも、非推奨なのかも;ドキュメントは見つけていないが)


お名前.com レンタルサーバー
やっぱり

2014年05月31日

サーバ方式ではないログの多重追記方式を考えてみる (2)

前回は LockFile の範囲が期待通りではなく、おかしい結果 (文字列がオーバーラップしていた) が得られていたが、実は、サイズ計算のミスであることがわかり、基本構造は正しかったことがわかった

前回の


void CWin32Logger::write(void* buffer, DWORD size)


の内容はそのままで、呼び出し部分でのサイズ計算を正しく実施したら、期待通り、重ならずに文字列を書き込めた
ただし、この方式は先着順ということであり、OSの制御によっては、後から書き込んだものが先に出力される可能性が残っているため、ログシステムとして考えると、ちょっと心もとない
(もちろん、後からタイムスタンプでソートすれば問題はないし、多くの場合では逆転することはなさそうだから、無視することも考えられる)

本格的に実装するなら、やはりFILOキュー方式が必要になり、そうした実装にするには、誰かがキューを制御しなくてはならない
ここでの目標は特定のサーバを設けないということなので、1つの案としては、最初に起動されたライブラリ実装が、キューを生成・管理するための仕組みを共有メモリに作成し、常に同時使用するプロセス・スレッド間で調停しながら進めるということになるのかもしれない

その雰囲気で実装できたら、ちょうど分散システムでのログ管理方式としても考えられそうだが、いかんせん、実装が Win32 に依存して (かつ、ファイルシステムAPIに依存) しているのでは、ちょっと汎用性がなさすぎだろうな

やはり、この実装が生きてくるのは、単一PC上での同一ログファイル競合くらいだという気がしてきた





Build a Mobile Site
スマートフォン版を閲覧 | PC版を閲覧
Share by: