ファイル保存ダイアログ †
ファイル選択ダイアログと説明の重なるものは説明を省いていますので、ファイル選択ダイアログも合わせて読むことをすすめます。 -- kz3
ファイル保存ダイアログを出すにはdialog命令を使います。
書式は以下のようになっています。
dialog "extension" , style , "type"
extension : 拡張子を表す文字列を指定します。
ここに指定した拡張子のファイルがダイアログボックスに表示されます。
style : ダイアログのスタイルを指定します。
16でファイル選択ダイアログ、17でファイル保存ダイアログを表示します。
type : extensionに対するファイルの種類を表す名前を指定します。
ここに指定した名前が[ファイルの種類]コンボボックスに反映します。
では実際の使用例を示します。
1
2
3
4
5
6
|
| dialog "as" , 17 , "えいちえすぴいすくりぷと"
if stat = 0 : end
mes refstr
stop
|
ファイル選択ダイアログと違うのはダイアログのファイル名に"extension"で指定した拡張子のワイルドカードを付加した文字列が設定されていることです。
これだけでも保存するファイル名の取得はできますがこの後に説明するいろいろな要求をかなえるためにはWin32APIのGetSaveFileName?()関数を使います。
説明はファイル選択ダイアログを参照してください。
1
2
3
4
5
6
|
| dialog "bmp;*.mag;*.jpg" , 17 , "画像ファイル"
if stat = 0 : end
title refstr
picload refstr
stop
|
HSP標準命令であるdialogを使った場合は"extension"に指定した文字列にワイルドカードを付加したものがデフォルトの保存名に設定される、というのは最初に説明しました。
ではこれをユーザーが任意に設定することは出来ないのでしょうか?
dialogを用いた場合は出来ません。これを解決するには直接dialogが呼び出しているWin32APIをプログラマが呼び出します。
ファイル保存ダイアログを用いて保存するファイル名を作成するにはGetSaveFileName?()関数を使います。(例によって関数のリファレンスはちょくとさんのところにあるので参照してください。)
ここではAPIの説明は省き、実際に呼び出してみることにします。APIの呼び出しにはllmodを使用しています。(省スペースのため、ファイル選択ダイアログの「選択可能なファイルの種類を増やす」と「デフォルト拡張子」も同時実装しています。)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
|
-
|
!
| #include "llmod.as"
#define BUFSIZE 260
#define FILTERSIZE 512
#define ALLTYPE "ALL files (*.*)@*.*@"
#define PICTURE "画像ファイル (*.bmp;*.mag;*.jpg)@*.bmp;*.mag;*.jpg@"
#define SOUND "音楽ファイル (*.mid;*.mp3;*.wav)@*.mid;*.mp3;*.wav@"
#define DOCUMENT "文書ファイル (*.txt)@*.txt@"
#define DEFSAVENAME exedir + "\\default.as"
#define OFN_FILEMUSTEXIST $00001000
#define OFN_OVERWRITEPROMPT $00000002
mref bmscr , 67
dim ofn , 22
sdim aplFilter , FILTERSIZE+1
sdim CustFilter , FILTERSIZE+1
sdim filepath , BUFSIZE+1
sdim filename , BUFSIZE+1
sdim DefExtension , BUFSIZE+1
aplFilter = PICTURE + SOUND + DOCUMENT
CustFilter = ALLTYPE
DefExtension = "dat"
repeat
await 0
instr mutch , aplFilter , "@" , start
if mutch = -1 : break
poke aplFilter , start + mutch , $00
start += mutch + 1
loop
start = 0
repeat
await 0
instr mutch ,CustFilter , "@" , start
if mutch = -1 : break
poke CustFilter , start + mutch , $00
start += mutch + 1
loop
ofn.0 = 88
ofn.1 = bmscr.13 ofn.2 = bmscr.14 getptr ofn.3 , aplFilter getptr ofn.4 , CustFilter ofn.5 = FILTERSIZE ofn.6 = 1 getptr ofn.7 , filepath ofn.8 = BUFSIZE getptr ofn.9 , filename ofn.10 = BUFSIZE ofn.11 = 0 ofn.12 = 0 ofn.13 = OFN_FILEMUSTEXIST | OFN_OVERWRITEPROMPT
getptr ofn.15 , DefExtension
filepath = DEFSAVENAME
getptr prm , ofn
dllproc "GetSaveFileNameA" , prm , 1 , D_COMDLG
if dllret = 0{
dialog "失敗" , 0 , "終了確認" : end
}
title filepath
stop
|
設定した保存名はlpstrFileメンバに指定したバッファに入ります(ファイル選択ダイアログも同様)が、ファイル保存ダイアログの[ファイル名]入力ボックスに設定される文字列はこのlpstrFileメンバに指定したパスのファイル名が設定されるのです。
つまりGetSaveFileName?()を呼び出す前に毎回デフォルトのファイル名(フルパス)で初期化すれば毎回デフォルトが入力されている状態になるという事です。
ここで注目して欲しいのはデフォルトを指定したとき、そのフルパスの示すフォルダの中が開かれているということです。
これは初期表示ディレクトリを指定した、と考える人もいると思います。
ということは?lpstrFileメンバに指定するバッファが・・・
filepath = "c:\windows\"
というディレクトリ名までなら[ファイル名]は空白で表示フォルダはwindowsフォルダなんじゃ・・・と思われるかも知れません(僕がこれを書いていて思いました。)がこれはエラーになります。
また存在しないディレクトリを含むフルパスを指定した場合は、そのままフルパス文字列が[ファイル名]に設定されています。
この辺りは実際にやってみるとどうなるかわかると思いますが、いずれも任意のディレクトリを開くとは違います・・・。
他にも注目したい点があります。
保存するファイル名に拡張子を指定しなかった場合はどうなるでしょうか?
この場合は現在選択されている[ファイルの種類]の拡張子のリストの最初のものが選ばれて連結されます。
では「すべてのファイル (*.*)」なんて場合はどうなるでしょう?
通常は拡張子のないファイル名が返されるだけですが、拡張子を省略した場合のデフォルトの拡張子もOPENFILENAME構造体で設定できます。
それがlpstrDefExt?メンバです。このメンバが示すバッファに拡張子を設定しておきます。
バッファへのポインタとバッファのサイズ:
関数や構造体のメンバのなかにはバッファへのポインタとバッファのサイズを指定するものと指定しないものとがあったりします。
この違いは何でしょう?
OPENFILENAME構造体ではlpstrFileメンバとnMaxFile?メンバ、lpstrFileTitle?メンバとnMaxFileTitle?がポインタとバッファサイズが対応していて、lpstrDefExt?メンバにはバッファサイズのメンバがありません。
これは関数が外部(関数外)から渡された(文字列)領域に対して書き込むか、それとも読み取るだけかの違いです。
文字列についてはヌル文字で終わるというルールがあるので(文字列を)読み取る処理はヌル文字だけを注意して領域のサイズは注意しなくていいのでサイズがいらないのです。
一方(文字列を)書き込む場合はオーバーフローしてはいけないので領域のサイズに注意しながら書き込む、場合によっては書込み領域のサイズよりも書き込むデータが大きい場合は書き込めませんというエラーを出します。
処理内容が書込みなのか読み取りなのか、これが分かるだけでも少し安心してスクリプトを書けますね?
|
本家BBS.HSP3よりさくらさんからご指摘いただきましたのでさっそく補足。
OPENFILENAME構造体の最初のメンバlStructSize?はこの構造体のサイズを指定するものですが、OSによって指定するサイズが異なります。
- Windows 2000/XPではメンバ数は23個で構造体サイズは88
- それ以前ではメンバ数は20個で構造体サイズは76
を指定するとのこと。(ちょくとさんのページを参照しました。勉強不足でした。)
- 書き途中なのでファイル選択ダイアログから貼り付けたままの説明があります。 -- kz3
- 余談・補足をコラム風に・・・。 -- kz3
- hsp2.61用とhsp3.0a用のスクリプトを載せました。 -- kz3
- すみません、hsp2.61雛形に修正漏れがありました。usrFilter -> CustFilter?です;;直しておきました;; -- kz3
- ページ内のスクリプトで変数名を一貫してないのはお許しください。まぁUPしなおせばいいだけなのですが・・・。今度追加するときについでにやっておこうと思ってます。 -- kz3
- HSP3に移植するときの注意。HSP3では文字列の代入でバッファの拡張が必要になったときに自動でバッファを再確保します。この時、元のアドレスとは違うメモリアドレスに領域が確保されるのでアドレスが変化します。
OPENFILENAME構造体の初期化あとに、フィルタ文字列に対してバッファを拡張させるような操作をした場合━━━
例えばユーザーがフィルタ文字列をカスタマイズできるシステムを用意して一時バッファに格納させ、アプリケーション側で用意したバッファに代入させた時など
━━━は再度OPENFILENAME構造体のポインタを格納しているメンバを正しく設定しなおす必要がでてきます。 -- kz3
- hirokiさんが無事解決することを願って、ダイアログ関係のスクリプトにOSの記述を付加していく予定です。改めてちょくとさん、さくらさんありがとうございます。
自分の未熟さを痛感! -- kz3
- そもそもこのXP/2000に拡張されたメンバが意味するのはプレースバーの有無です。(今のところ。)こんなものなくても、対して困りません。(本音)76でも困らないんです!! -- kz3
- defext = ".dat" のところのピリオドは必要ないようです。ピリオドが2つ付いてしまう。 -- hiroki?
- お、そうなんですか!ありがとうございます! -- kz3
- あはっ本当だ(//ー//) -- kz3
- usrFilter -> CustFilter? , defext -> DefExtension? , コメントいろいろ付加版UP。
全てのスクリプトで変数名の統一は大変なので中止・・・。まぁサンプルから加工するときは自分の好きな名前に変えられるしね^^; -- kz3
- ・・・っていうか構造体76バイトにしてもプレースバーが出る。汗。 -- kz3
- filename filepath どう違うの? こっち ofn.9 =0 にしてる -- hiroki?
- filepathはフルパスで、filename(lpstrFileTitle?メンバ)はファイル名だけが入ります。 -- kz3
- つまりこれをやっとくとgetpathなどでファイル名を切り出さなくてもいいってことですね。 -- kz3
- 納得しました。#deffuncで新規命令作ってますか、合っている所で文法エラーになって困ってます。 -- hiroki?
- #deffunc の外で#funcを定義したから文法エラーになったことが判明しました。 -- hiroki?
- スクリプトを終わらさずにfilepathにファイルを指定してGetSaveFileNameAを実行するとデフォルト拡張子にならず、filepathの拡張子になってしまう -- hiroki?
- それは恐らく[ファイルの種類]が何か拡張子を持つようになっていませんか?デフォルトが効くのは「*.*」の拡張子の特定できない場合に拡張子を省略したときです。 -- kz3
- そうでした ごめんなさい。 -- hiroki?