マクロ記録などを応用して作った Excel VBA マクロ。
きちんと文法どおりできているか、実行するまでわからないと思っている人も多いのでは?
Excel VBA はデバッグメニューの一番上に[VBA Project のコンパイル]があります。
2025年5月 3日(土) 21:17 JST
マクロ記録などを応用して作った Excel VBA マクロ。
きちんと文法どおりできているか、実行するまでわからないと思っている人も多いのでは?
Excel VBA はデバッグメニューの一番上に[VBA Project のコンパイル]があります。
Delphi で Excel の機能を使う場合に、CreateOleObject など COM の機能を使って呼び出すのだが、この際に下記のメッセージが表示されることがある。
CoInitializeが呼び出されていません
GUI アプリケーションを作っている場合には、Forms ユニットあたりで CoInitialize が呼び出されているのだろうが、CUI アプリだと100% エラーが表示される。
このため、CUI アプリケーションでこのエラーメッセージを回避するには、CoInitialize を呼び出すなどの対策が必要になる。また、CoInitialize と対で CoUninitialize を呼び出さないといけない。
実際の対策は実に簡単で、
COM を使うとその部分は、結構長くなることが多いと思うので、クラス化して、その Create と Destroy に それぞれ CoInitialize と CoUninitialize を追加するのがオススメである。
※複数回 COM 機能を使うときにも、全体を CoInitialize と CoUninitialize で囲むように追加すれば OK !
VBA で配列の中身を順番に処理する場合、LBound から UBound をまでを順に処理します。
しかし、要素が 0 個の空配列は LBound も UBound も同じく 0 なので、必ず一度処理されてしまいます。
空になる可能性のある配列を扱う場合には、配列の宣言後以下の処理を行っておくと便利です。
SafeArrayAllocDescriptor 1, [配列]
この関数を使用するには、以下の定義文を Decralation に記述しておく必要があります。
Private Declare Function SafeArrayAllocDescriptor Lib "oleaut32" (ByVal cDims As Long, _ ByRef ppsaOut() As Any) As Long
この関数 (Windows API) を使用すると、UBound の値が -1 になります。
ですから、
for i = LBound([配列]) to UBound([配列])
と書いても、空配列の場合には一度も処理されることはありません。
もちろん、要素がある場合には要素数の回数だけループします。
要素数を求める場合には、以下の式で求めることができます。
UBound([配列]) - LBound([配列]) + 1
TControl を継承したオブジェクトはドラッグ&ドロップ関連のプロパティやメソッドを持つが、これはそのアプリ内でDelphiのコンポーネントをドラッグする場合にしか使用できない。プロセス外からのドロップを受け取るには、Windows API を使用する必要がある。
ドロップを受け取る Delphi コンポーネント側で、DragAcceptFile を使用して Windows にドロップを受け取ることを伝える。(ShellAPI を uses に追加する必要があります。)
下記は TForm1 でドロップを受け取るための例。
TForm1.Create(AOwner: TComponent); begin inherited; DragAcceptFiles(Handle, True); end;
これで、このForm上にファイルをドラッグすると、マウスカーソルがドロップ可能な形状になる。
実際にドロップが発生すると、WM_DROPFILES メッセージが発生するので、このメッセージのハンドラを作成する。
protected procedure WMDropFiles(var Msg: TWMDropFiles); message WM_DROPFILES;
ドロップされたファイル名の取得は、DragQueryFile を使う。ドロップされた個数を調べるには、以下のようにする。
hDrop := THandle(Msg.Drop); DropCount := DragQueryFile(hDrop, UINT(-1), nil, 0);
個々のファイル名の処理は、以下のようにする。
var hDrop: THandle; i: Integer; FileName: PChar; begin FileName := StrAlloc(MAX_PATH); for i := 0 to Pred(DropCount) do begin DragQueryFile(hDrop, i, FileName, MAX_PATH - 1); // FileNameの処理 ListBox1.Items.Add(StrPas(FileName)); // 処理の一例 end; end;
MainFormのどこにドロップしたか知りたい場合は、DragQueryPointを使用する。
※注意 ドロップ処理が終わってファイル名を格納した領域が不要になったところで使用したメモリの解放が必要。
DragFinish(hDrop);
まとめると...
procedure TForm1.WMDropFiles(var Msg: TWMDropFiles); var hDrop: THandle; i, DropCount: Integer; FileName: PChar; begin hDrop := THandle(Msg.Drop); try FileName := StrAlloc(MAX_PATH); DropCount := DragQueryFile(hDrop, UINT(-1), nil, 0); for i := 0 to Pred(DropCount) do begin DragQueryFile(hDrop, i, FileName, MAX_PATH - 1); // FileNameの処理 ListBox1.Items.Add(StrPas(FileName)); // 処理の一例 end; finally DragFinish(hDrop); end; end;
Delphi2005 Win32, Delphi 2010で動作確認を実施。
DateTimePicker で中身の日付を表示したくない場合、どの様な対応をしているだろうか?
Delphi に関わらず、他の言語処理系にも共通する問題なのだ。この問題を解決するために、DateTimePicker に最初からあるチェックボックスを使用して空白を表現している方もいるのではないだろうか?くろねこも最初そう考えた。
しかし、ある時とある設定ミスから発見したある方法がこの問題の解決にぴったりだと思った。
その方法は、書式設定に "g" を使うという物である。
Windows 上での日付書式フォーマットでは "g" は元号を表す記号である。しかし、DateTimePicker はこの元号に対応していないらしく、"g", "gg", "ggg" などのフォーマットが指定された時に何も表示しない。これを逆手にとって表示すべき日付が無いときに、これを設定するのだ。
これで、データベース中の日付を表示するときなどに現れる Null を DateTimePicker で表現できる。
このときの書式には Delphi の書式ではなく、Windows の日付書式を使用する。また commctrl ユニットを uses 節に追加する必要がある。
※ Delphi で月をあらわす書式は "mm" だが、Windows では "MM" となる。
uses commctrl; procedure NullDateFormat; var szFormat: PChar; begin szFormat := 'g'; // Windows の日付書式 SendMessage(DateTimePicker1.Handle, DTM_SETFORMAT, 0, longint(szFormat)); end;
2012/04/07 追記
Delphi2010 on Windows7 で上記を試してみたところ、"g", "gg", "ggg"のいずれも"西暦"と表示された。よってこの方法はすでに使えなくなっている。おそらくOSに依存しているものと思われる。
よって、何も表示しないDateTimePickerの実現方法を別途考えないといけないようだ。