こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

Windows APIのGetOpenFileNameで呼び出せるダイアログを改造できないでしょうか?

添付という形にすると勝手に再変換されて画像がかなり見づらくなってしまうようなので
添付ではなく別途アップへのリンクとさせてください。


今のところVC++2008EEを使ってWindows XPでアプリケーションを開発しています。


よほどメモリの断片化が進むような長時間使用しなければ
.NET FrameWorkを完全に使用せず全部ネイティブで組んだほうが、実行時については
様々な面でパフォーマンスが良い場合が多いようなので
できればマネージコード0でアプリケーションを完成させたいのですが

Windows APIと.NET Frameworkにおいて、様々な場面で「デフォルトのデザインが違う」ようです。
簡単なものなら自作したりコモンコントロールにSendMessageやプロシージャをフックしたりでいいと思ったのですが

オープン(セーブ)ファイルダイアログは、通常商用ソフトとかだと1から自作する事があるようなものなのでしょうか?

http://www.geocities.jp/shank_long7_02/FileDialog/a.html
こちらの画像の一番上がWindowsAPIのGetOpenFileNameで呼び出したダイアログで

真ん中がマネージなSystem::Windows::Formsの
OpenFileDialogの場合で

一番下はPhotoshop CS3の別名保存用ダイアログです。
見たところこれは上がSaveFileDialogと一致してて、その下がカスタマイズされてるような感じですね。

現状のWindowsAPIを上記SendMessageやプロシージャのフックなどでちょこちょこっといじって.NET的なデザインにしたり、Photoshopのやつみたいにサイズを変えてその下に自分で好きな物を付け加えたりといったことは可能なのでしょうか?

それとも、そういったことをやろうとした場合は1から作るか、混在させるかしかないのでしょうか?

投稿日時 - 2010-03-07 20:06:04

QNo.5733269

困ってます

質問者が選んだベストアンサー

WinAPIと.NET Frameworkで、表示形式(アイコンと一部の背景)が違うのがよくわかりませんが…

Photoshop CS3の様にコントロールを追加することは可能です。

「カスタムコモンダイアログ」等で検索すると、情報が得られるでしょう。
http://hp.vector.co.jp/authors/VA016117/esccd.html
など。

投稿日時 - 2010-03-08 00:42:15

お礼

ありがとうございます。
そっちが解決できれば機能的には申し分ないのですが
色々やってみてうまくいかず、最終的には丸々リンク先のコードと同じようにしても、リソースからの読み込みがなぜかうまくいきません。

VC++2008EEはリソースエディタが使えないこととか
以前アイコンがリソースを使った場合2つ以上読み込めなかったことなどと関係があるかもしれませんが、メニューは必ずうまくいくし、仮にWS_CHILDを外したうえでDialogBox関数を呼び出すとちゃんと出てくるので
生成自体は成功しているはずなのですが…

何か間違えているのでしょうか?

resource.h内で

#define IDD_ABCDIALOG1 4010
#define IDC_BUTTON1 4011

こうやって
RC内で

#include "resource.h"
#include <winresrc.h>

IDD_ABCDIALOG1 DIALOG DISCARDABLE 0, 0, 187, 94
STYLE WS_CHILD | WS_VISIBLE |DS_3DLOOK | DS_CONTROL | WS_CLIPSIBLINGS
FONT 9, "MS Pゴシック"{
PUSHBUTTON "Button",IDC_BUTTON1,54,56,75,23
LTEXT "",1119,0,0,186,49, WS_CHILD |WS_GROUP
}

こうやって
あとはopがOPENFILENAMEとして、変更部分を

op.Flags = OFN_SHOWHELP| OFN_HIDEREADONLY |OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST
|OFN_EXPLORER |OFN_ENABLESIZING |OFN_ENABLEHOOK|OFN_ENABLETEMPLATE;
op.lpfnHook = OFNHookProc;
op.lpTemplateName = MAKEINTRESOURCE(IDD_ABCDIALOG1);

とし、プロシージャも全く同じように作って

GetOpenFileNameを呼び出したとき、必ず失敗してしまいます。

CommDlgExtendedErrorを使って拡張エラーコードを調べると、常にCDERR_NOHINSTANCEになっています。
HINSTANCEはほかの場所で全て正常に機能しているものを指定していますし、OFN_ENABLETEMPLATEとlpTemplateNameへの代入箇所をコメントアウトすれば、普通の状態のオープンファイルダイアログは正常に表示されます。

リソース側の書き方が問題なのでしょうか?

投稿日時 - 2010-03-08 12:48:48

ANo.1

このQ&Aは役に立ちましたか?

1人が「このQ&Aが役に立った」と投票しています

回答(4)

ANo.4

Wr5

って、
>LTEXT "",1119,0,0,186,49, WS_CHILD |WS_GROUP
で入れていた…んですな。

そうなると……リソースの書式なんでしょうかね?

ResEditでリソース作り直してみてはどうでしょう?
http://www.forest.impress.co.jp/lib/stdy/program/progsupt/resedit.html

投稿日時 - 2010-03-09 00:52:55

補足

試してみましたが、なかなか便利ですね♪
完全に独自のものを作るなら結局コードいじることには変わりないでしょうが、実験とか簡単なものをサクッと作るには最適そうです。


肝心のダイアログテンプレートについてはやはり依然としてできないので

しょうがないからこちらでも自分用の新しいプロジェクトで試してみたら、ResEditで作ったのを貼り付けても、手打ちでやっても、やはりしっかりできました。色々コードいじってみても概ね予想通りのことが起きてくれます。

では現在作りこんでいるほうではなぜできないのかが不明瞭ですが、よく分かってないことについて何が足を引っ張っているのかをこの時点から調べ直すのは結構大変だと思うので、今のところは可能だということが分かったということで解決ということにします。

XPスタイル・クラシックスタイル(あるいは将来的にはVistaや7にも対応したい問題)については

http://mspace.sakuraweb.com/comp/prog/theme.html

http://msdn.microsoft.com/ja-jp/library/ms997646.aspx

を見て、OSチェックやXPテーマが適用されているかどうかの判定や、部分的なテーマの変更までは現在できました。
もうちょっとで完全に思いどおりかもしれません。

重ねて、ありがとうございました。

投稿日時 - 2010-03-09 19:57:16

お礼

あ、すみませんw
念のためにじっくりチェックしながら長文書いてたので、タイミングがずれましたねw

ありがとうございます。
とりあえずそれで試してみて、できたら解決
できなかったら再度そう補足することにします。

投稿日時 - 2010-03-09 01:13:07

ANo.3

Wr5

>VC++2008EEはリソースエディタが使えないこととか

フリーのリソースエディタもあります。
ResEditなど…。

>リソース側の書き方が問題なのでしょうか?

掲示したリンク先に
『次に、追加するコントロールの位置を決めるために、スタティックコントロールを作ってそのIDを0x45fにします。デフォルトプロシージャは、このスタティックコントロールを中心としてそれぞれのコントロールをダイアログに追加します。』
とありますが……

リソースファイルに
#include <Dlgs.h>
して、IDがstc32のスタティックコントロールをダイアログ中に作成しておく必要があります。

投稿日時 - 2010-03-09 00:11:54

お礼

確認ありがとうございます。
私が書いたコードは、やむなくそのリンク先のサイトから圧縮データをダウンロード、解凍し、それがビルドでき、カスタマイズした動作をしていることを確認したうえで、ほとんどそのまま載せたものです。
なので、おそらくSTATICという文字そのものこそありませんが

LTEXT "",1119,0,0,186,49

の部分がそれに当たるのだと推測します。
(stc32 == 0x45f == 十進表記で1119)

数値の直打ちしてるので必要なさそうですが
念には念のために#include <Dlgs.h>を加えてみても、やはり変化なしです。

さらにダウンロードしたファイルを端から端までネットを併用しながら調べてみるものの、今のところ見当がつきません。


MFC非対応なことと関係があるのかもしれませんが
フリーのリソースエディタを使うことで対処可能なのでしょうか…?

------------------------------


もうひとつの問題については調べまくってたら解決できました!

デザインについてはどうやら
.NETとWindowsAPIってことではなく

Windows XPスタイルとクラシックスタイル、という違いだったようです。
こちらのサイト
http://www.g-ishihara.com/vc_wi_01.htm

を参考にして(というかそのままやったら)見事完全ネイティブのままWindowsXPスタイルにできました。

当然これはコモンコントロール全体にかかわるので、メッセージボックスやらなにやらも全部影響受けます

が…

CPUやメモリ使用量は変わってないのにウインドウの応答速度が若干遅くなった気がします。
バージョン6.0.0.0の弊害でしょうか…w

これって、結局はそれ用のdll読み込むってことだから、どうにかしてプログラム起動中に動的に変更とかってできないでしょうか?

#pragma comment(lib, "comctl32.lib")

っていうのは書きましたが、comctlについて手動でdllそのものをどうこうした記憶はありません。

…動的には変更しなくてもいいと言えばいいでしょうが
せめてアプリケーション内にそういう選択肢を作っておいて、起動中に設定変更→終了→再度起動すると、そっちのスタイルになる
っていう機能があると良いと思いました。

そのいずれかは可能でしょうか?

投稿日時 - 2010-03-09 01:11:13

ANo.2

まず.NETで、言語に関わらずマネージ、アンマネージの合成は可能です。
C++から混在させるとスムーズですがGCとマーシャリングを適切に行う必要はあります。混在の場合、GCメンバ関数をうまく使ってインスタンスが勝手にリリースされないようにすることが大事です。また、VBやC#でも合成は可能です。C++で参照できるヘッダが使えないので、コードのメンテナンスが煩雑になるところが難点です。

ですのでマネージサイドとアンマネージサイドをプロセスとして分離するのが最適です。プロセスはEXEという意味でなくインプロセスという意味です。つまり、カスタムダイアログを設ける側は、DLLでCOM/DCOM等のオートメーションでの実装がマネージコードとの相性もよいと思います。
そうすれば後々再利用も簡単になりますね。

Win32APIとしてのGetOpenFileName 関数は、テンプレートを使って別のダイアログを割り当てることができます。テンプレート専用のフック関数を使いますが、独自の選択処理などを追加しなければセオリーどおりの処理しか記述しません。テンプレートを使う場合、ダイアログ内で使う必要最小限のコントロールIDはすべて規定の値である必要があります。つまりコントロールの配置は自由に移動できます。カスタマイズ部分は自由にして結構です。ダイアログは、NETより以前からあるRCリソースファイルで作ってください。マネージサイドのコードは一切使えません。

ここからは、Win32ベースで話を進めます。
カスタムダイアログの基本は、OPENFILENAME->lpTemplateNameにダイアログリソースのIDを指定します。ダイアログIDが整数IDのときは MAKEINTRESOURCEマクロで代入します。OPENFILENAME->FlagsにOFN_ENABLETEMPLATE|OFN_ENABLETEMPLATEHANDLEを論理和します。OPENFILENAME->hInstanceは起動する側から得てください。マネージ混在なら、アンマネージからではなくマネージコードからMarshal.GetHINSTANCE(...)で得てください。100% アンマネージなら、MFC混在であればAfxGetInstanceHandle 関数、そうでないならWinMainやGetModuleHandle から得ます。但し、これらのWin32系アンマネージ関数はそれぞれ動作が異なります。Windows のリリース(3.1~95/98、2000/XP等)によって返されるインスタンスの意味が異なるので注意してください。
OFN_EXPLORERフラグがエクスプローラーのシェル機能を実装するかどうかになります。OFN_EXPLORERを使うと外観がかなり変わりますがテンプレートで用意するリソースも増えますので、ご自分でチョイスしてください。OFN_EXPLORERを指定するとExplorerスタイルのフック関数(コールバック)を用意する必要があります。ちなみにPhotoshopのダイアログはOFN_EXPLORERを指定してのカスタムダイアログです。OOPENFILENAME->lpfnHookにフック関数のインスタンスをセットします。

基本はこのコードをインプリメントしてDCOMやActiveXでコンポネントを実装します。

参照URLにテンプレートに関するマイクロソフトMSDN(英語)を載せました。
VS4.2のころは日本語リソースも充実していたのですが、需要が少ないためか現在は英語でのヘルプしかありません。カスタムテンプレートはコモンダイアログ共通の機能なのでファイル保存や読み込み以外でもカラー選択、フォント選択でも同じようにして使います。C++からであればヘッダファイルやライブラリはそのまま使えます。C#やVBを経由してのマネージコードからだとヘッダファイルの定義は全てマネージコードで再実装する必要があり、またライブラリは属性クラスの[DllImport]ですね。

ご存知かも知れないですが、ヘッダのコーディングが面倒だと思うので以下を使ってください。
誰かさんたちが作ってくれた.NETから使えるAPIのヘッダ宣言がたくさん載っています。
http://www.pinvoke.net/

基本的にNETフレームワークにない機能を実装しようとしたら混在あるいはインプロセス分離で統合する以外は今のところ方法がありません。まだ中身は見ていないですが NET Framework 4.0 でも大差はないと思います。

参考URL:http://msdn.microsoft.com/en-us/library/ms646951(VS.85).aspx

投稿日時 - 2010-03-08 06:38:01

お礼

ありがとうございます。

インプロセスというのは、つまり、コード的に切り離すかどうかは任意だけども、最終的に出来上がるものは同じ実行ファイル内で混在する動作にはなるという意味でしょうか?
以前プログラミング初めて間もない辺りからベンチマークを含め長いことやっていましたので、マーシャリング等はOKです。

ただし、以前調べてこの環境ではDLLを作れない…?
(あるいは分かりづらい…?)
ような感じだったのでDLL絡む辺にはかなり疎いです。
なので

>つまり、カスタムダイアログを設ける側は、DLLでCOM/DCOM等のオートメーションでの実装がマネージコードとの相性もよいと思います。
>そうすれば後々再利用も簡単になりますね。

この文の意味が正確に分かりません。
これはもともと作られたDLLがあるから、それを使えばいいという意味なのでしょうか?
それとも自分がCOM/DCOMとかを使って作ったうえで、分離してしまえばいいという意味なのでしょうか?

また、全部同じプロジェクトに突っ込む場合だと少なくとも一行でもマネージコードを使うならそう設定することになり、時間がかなりかかってしまうようになるのですが

これはつまり、DLLを作ってマネージコードをそっちに完全に切り離せるなら、通常はビルド時間がネイティブのみなのでかなり短い、ということも可能なのでしょうか?


>OFN_EXPLORERについて
もしかしたらそっちが原因なのかもしれません。これを指定しただけだと外観が変わっていません。テンプレートが成功した場合に、それ以外も変わる
…ということなのでしょうか?

あるいはダイアログテンプレートを使う場合何らかの初期化が必要なのでしょうか?

投稿日時 - 2010-03-08 13:20:40

あなたにオススメの質問