えくせるちゅんちゅん

ことりがエクセルをちゅんちゅんするブログ

【永久保存版】VBAにおける真のイミディエイトウィンドウの使い方

VBAの開発画面(VBE)には様々なウィンドウがありますが、その中でも特に奥が深いのがイミディエイトウィンドウです。

イミディエイトウィンドウをどれだけ使いこなしているかで、その人のVBAプログラミングスキルがどれ程のものかひと目で分かる(かもしれません)

今回はそんなイミディエイトウィンドウだけに焦点を絞って、徹底的に紹介していきます。



イミディエイトウィンドウとは

1行のプログラムを書いてEnterキーを押すだけで、瞬時に実行結果が分かるというVBA開発環境の便利な機能です。

Excelを開いて Alt+F11Ctrl+G → コードを入力 → Enter と入力するだけで実施できます。

例えば、 Selection = "Hello World" と入力すれば、選択しているセルに Hello World と入力され、 ?Selection と入力すれば Hellow World とイミディエイトに出力 されます。そして Range("Z10").Select と入力すれば、セル Z10 を選択させることだって出来ます。

Windowsコマンドプロンプトの実行が Win+RcmdEnter → コマンドを入力 → Enter なのと、同じです。

いわば、Office版のコンソールだと言えるでしょう。

個人的にVBAプログラミングで一番初めにすべきことは、「開発タブの表示」でも、「標準モジュールの作成」でも、「言語仕様の勉強」でもなく、「イミディエイトでの1行プログラムの実行」だと思っています。

プログラミングは1行のプログラムの組み合わせなのですから、1行ごとの挙動を正確に理解することが重要です。そのためにはイミディエイトウィンドウが最も適しています。

※イミディエイトウィンドウで下書きしたコードを組み合わせて開発を進めることを、即時駆動開発と勝手に命名して呼ぶことにしました。( IDD : Immediate-Driven Development )

使っている様子を少しだけ動画にしてみました。

f:id:Kotori-ChunChun:20190211203421g:plain


なぜイミディエイトウィンドウを使うのか

イミディエイトの利用シーンは、大まかに3種類に分類できます。

  1. コーディング中に、VBAコードの動作検証や文法確認を行うため
  2. デバッグ中に割り込みで、変数の読み書きを行うプログラムを実行するため
  3. 作業を支援するワンライナー(1行のプログラム)や開発済みのマクロを直ちに実行するため


1.コーディング中に文法や関数を確認する

長年VBAを書いている人でも、仕様を完璧に頭に入れているわけではありません。

記憶がただしいか不安に感じる時や、テストコードだけでは納得できない時があります。

コーディング中に少しでも疑問を感じたら、即座にイミディエイトで確認すると、潜在バグを潰し開発が効率的に進められます。

?IsNumeric("123,456")
True
?UBound(Split("a,b,c",","))
 2 


2.デバッグ中に変数の書き換えや情報出力を行う

下記のいずれかの方法でプログラムを中断させることで、デバッグモードに入ることが出来ます。

  1. ブレークポイントの設定
  2. Stop や Debug.Assert
  3. 実行時エラー
  4. ウォッチウィンドウに設定した条件一致
  5. ESC、Ctrl+Pause/Breakキー

デバッグモードでは、現在の実行スコープにおいて、イミディエイトからプログラムを割り込みで実行できます。

例えば、ブックのパスを調べるとか

?ThisWorkbook.Path
C:\~~~~.xlsm

強引に再計算を行うとか

Application.CalculateFullRebuild

For文のカウンタを書き換えるとか

i = 1

このような場面で、本当によく使われます。

また、 Ctrl+L で【呼び出し履歴】を開くことで、対象のプロシージャを移動してバグの原因を調べることもできます。


マクロの中断方法については下記を参照。

www.excel-chunchun.com

StopとDebug.Assertについては下記を参照。

www.excel-chunchun.com


3.作業を支援するために使用する

VBAに慣れてくると、手作業で行うより即興でプログラムを書いたほうが早い場合があります。


例えば、Excelで「全ての非表示シートを再表示する」とか、「選択範囲の大きさを変えずに1行下にずらした範囲を選択する」とかの用途で使います。

For Each ws in worksheets : ws.visible=-1 : next
Selection.Offset(1).Select


VBAのコーディングにおいても、イミディエイトを使うと便利なことがたくさんあります。

例えば、テストプロシージャを実行する時とか、数値が法則性のあるソースコードを作るときとか。

開発支援ツールとしても、使うことが出来るのです。


なお、ちょっと複雑なものや利用頻度の多いものは事前にマクロを作っておき、イミディエイトから呼び出すだけにしたほうが簡単です。でも、簡単なものなら準備不要でいつでも使えるのが強みの一つです。


VBEのイミディエイトウィンドウの使い方

便利な操作テクニック全集

これだけ抑えておけば、貴方も立派なイミディエイトマスターです!

  1. Ctrl+Gでイミディエイトウィンドウを開きフォーカスを移動できる。
  2. F7でコードウィンドウにフォーカスを戻れる。
  3. Debug.Printの結果がイミディエイトウィンドウに出力される。
  4. イミディエイトではDebug.を省略して Print と書いても良い。
  5. ?(クエスチョンマーク) を使えば Print すら書かなくて良い。(?1+12と出力される。)
  6. (VBE共通)Ctrl+Spaceでインテリセンス(入力補完)が使える。
  7. (VBE共通)Ctrl+Endで末尾に、Ctrl+Homeで先頭に飛べる。
  8. Enterを押すと選択中の行が実行されて、次の行に結果が出力される。
  9. カレントのプロジェクト/モジュールに対して実行される。(VBEタイトルバー参照)
  10. モジュール名.プロシージャ名と書けば、異なるモジュールや Privateレベルプロシージャ も実行できる。
  11. デバッグモード中に行うイミディエイトからの実行は現在のプロシージャに対して実行されるので、ローカル変数の書き換えができる。
  12. (VBE共通)Ctrl+L (呼び出し履歴) で、書き換え対象のプロシージャは移動できる。
  13. デバッグ中でもモジュールレベル以上の広域変数は書き換えられる。
  14. Ctrl+Enter現在の行を実行せずに改行を挿入できる。
  15. (VBE共通)Ctrl+N現在の一つ上に行を挿入できる。
  16. (VBE共通)Ctrl+Y現在の行を切り取りできる。
  17. ;(セミコロン)は出力が改行されない。また、連続して書ける。 ?"a";"b";abと出力される。
  18. 数値は前後に半角スペースが1つ入る ?1;2;3_1__2__3_と出力される。
  19. ,(カンマ)は出力が改行されない。 8文字毎や14文字毎になるように0文字以上の半角スペースが入る。 また、連続して書ける。?"a","b"a_____________bと出力される。
  20. :(コロン)を使えばステートメントを連結して一行にまとめる事が出来る。(マルチステートメント
  21. 行末に _(アンダースコア) を付けると、ステートメントを次の行へ折り返すことができる。
  22. ログに残るのは200行までセミコロンで終わらせた場合を除いて通常は199行しか残らない。
  23. イミディエイトへの出力は結構重い。ループの中に記載するとマクロが急激に遅くなるので、本番ではコメントアウトするか条件付きコンパイルで無効化するのが望ましい。
  24. イミディエイトウィンドウがポップアップ表示のときだけ Alt+Enterでコードウィンドウに戻れる。(厳密には直前のウィンドウに戻るショトカ)

ここまで徹底的に網羅している書籍・記事はきっと無いでしょう(笑)


上記のうち、分かりづらいものを解説します。

データ型による整形

Debug.Print では、値のデータ型によって整形が行われます。

数値では、前後に1字付与されます。

先頭には、値がプラスなら半角スペース、マイナスならマイナス符号が付きます。

末尾に必ず半角スペースが1字付きます。

(※以下、見やすいように半角スペースはアンダースコアで可視化)

?1
_1_
?-1
-1_
?1.234
_1.234_

日付では、末尾に半角スペースが1字付与されます。

?#2018/1/1#
2018/01/01_

文字列型ではなにも付与されません。

?"a"
a

セミコロンとカンマによる整形と連結

Debug.Print 及びセミコロンやカンマを使った文法は、VBAの中でもかなり特殊な文法で、プロシージャで引数の区切りを表すために使われるカンマとは全く別の性質を持ちます。

Debug.Print は、原則として末尾に改行を挿入します。 しかし、末尾にセミコロンやカンマを記入すると改行を防止することができます。

Sub Test()
  Debug.Print "これは";
  Debug.Print "セミコロンを使った";
  Debug.Print "分割出力の";
  Debug.Print "テストです。"
End Sub
これはセミコロンを使った分割出力のテストです。


また、セミコロンやカンマで区切って複数の値を指定することもできます。

それぞれの値がデータ型による整形された状態で、続けて出力されます。

たとえば、セミコロンの場合は以下のような動作をします。

?1;1
_1__1_
?1;-1
_1_-1_
?"a";"b"
ab


カンマで区切った場合は、セミコロンの性質に加えて半角スペースの自動挿入が行われます。

いわゆるTABのように一定間隔になるように半角スペースが挿入された上で(以下、これをブロック幅と呼びます)、データ型によって整形される文字が付与されます。

ブロック幅は環境によって違うときがあるため、法則がよく分かっていません。

執筆時(2019/2/10)は下記のように1ブロック14桁毎でした。(数値は11桁まで、文字列は13桁までが1ブロックに収まる限界となった)

しかし、数カ月後(2019/9/28)に試したら1ブロック8桁になっていました。

挿入される空白の個数は0以上であり、カンマを入れても半角スペースが挿入されないという罠があるので文字列型の出力では注意が必要です。

?12345678901,2
_12345678901___2_
?123456789012,2
_123456789012________________2_

?"1234567890123","1234567890123"
1234567890123_1234567890123
?"12345678901234","1234567890123"
12345678901234______________1234567890123

コロンとアンダースコアによるステートメントの連結と折り返し

どちらも通常のソースコードで使えるVBの記法ですが、イミディエイトでも使用可能です。

イミディエイトは「Enterを押した瞬間にカーソルの行のプログラムを実行する」というコマンドプロンプトみたいな機能です。

そのため、複数のステートメントから構成されるForなどは書けません。

でも、コロンを使えば後述するForが1行で書けます。

For i = 1 To 5 : ?i : Next

しかし、末尾にアンダースコアがある場合「まだ続きがある」と認識するため、実行されません。 その場合、アンダースコアの付いていない行でEnterした時に、そこまでのステートメントを連結して一気に実行します。

つまり、アンダースコアを組み合わせることで、擬似的に複数行For文を書いて実行することができます。

For i = 1 To 5 : _
  ?i : _
Next


バッドノウハウ

  • Ctrl+Zを押してもイミディエイトウィンドウは戻せない。

    • コードの方が戻って大変なことになります。
    • でも、直前に書いていたコードに飛べるので便利なことも結構あったり・・・。
  • 未定義の変数でもエラーにならない。

    • イミディエイトウィンドウはOption Explicit されません。
    • 気をつけないとタイプミスでハマります。
    • 一方でForやForEachのカウンタが無宣言で使えるので便利なことも・・・。
  • VBAからイミディエイトウィンドウを消すコードが提供されていない。

    • 手動操作(Ctrl+ADeleteCtrl+XCtrl+BackSpace)くらいしか方法が無い。
    • ただし強引に消す方法は存在する。サンプルはTIPSに記載した。
  • 切り取りコピー貼り付けが使えなくなる時がある。

    • コマンドがグレーアウトしており、Ctrl+XCtrl+CCtrl+Vも使えない。
    • 原因はカレントのプロジェクトがパスワードでロックされているから。
    • しかしイミディエイトへの直接入力と実行は出来るので、パスワードでロックされたプロジェクトに対してもコードの実行ができてしまう。(悪用厳禁)
  • 数値を右寄せで整列させたくても、適切な関数が存在しない。

    • サンプルの汎用関数をTIPSに記載した。


イミディエイトウィンドウの活用事例集

以上の仕様を理解できたとして、実際に使いこなせるかどうかは別の話。

アイディアの詰まった素敵なワンライナーを紹介します。(実用性はマチマチですが)

プロシージャ(Sub)を実行する

テストプロシージャを実行したり、ちょっと使いたいマクロを名前から実行するのに便利です。

Proc "a"    'Procプロシージャが実行される

Subの実行結果は、基本的にイミディエイトに現れません。

意図的に Debug.Printを書けば出力できますが、それで完成とするのは望ましくありません。

戻り値が設定可能なら、次のFunctionに変更することも検討してください。

プロシージャ(Function)を実行する

関数は先頭に はてなマーク を付けて実行すると、イミディエイトに結果が出力されるのでデバッグしやすくなります。

?FuncSum(10,3)
 13 

簡易的なデバッグは、ここで値を変えながら繰り返し実行してテストをします。

本気でテストが必要な場合は、テスト用プロシージャを作るか、テスト用ワークシートを作成して、シートから関数を実行する方法があります。

www.excel-chunchun.com

文字コードが知りたい

文字コードが思い出せなくて困る時。ありますよね?

?asc("A") 'A の文字コードが知りたい
 65 
?asc("""") 'ダブルクォーテーションの文字コードが知りたい
 34 

IF文を使いたい

IF文は少し特殊な文法となっており、(コロンを使わずとも)複数のステートメントを一行で書く事ができます。

この時、End Ifは不要になるのでご注意を。

If a=1 Then ?1 Else ?2
 2 

For文を使いたい

For文などは最低でも3ステートメント必要なのでイミディエイトウィンドウで使えないように思えますが、コロンを使ってマルチステートメントにすればできます。

For i = 1 To 5 : ?i : Next
 1 
 2 
 3 
 4 
 5 

上記のままだと、ログがすごい勢いで流れてしまうので、末尾にセミコロンを付けると勝手が良くなります。

For i = 1 To 5 : ?i; : Next
 1  2  3  4  5 

でもカーソルが遥か彼方の右端に飛ぶので、Ctrl+Enter を使って改行を挿入してから終わりましょう。(そうしないと、次の出力が同じ行の続きからになります)

なお、Ctrl+Enter はイミディエイトを実行せずに改行を挿入するショートカットキーです。

For文の中でIfを使いたい

このような使い方はあまりしないかもしれませんが、知らないと苦労するので書いておきます。

実行出来ない悪い例

For i = 1 To 5 : If i Mod 2 Then : ?1 Else ?2 : End If : Next
'コンパイルエラー 行頭のみ許されるステートメントです。
For i = 1 To 5 : If i Mod 2 Then ?1 Else ?2 : Next
'コンパイルエラー Next に対応する For がありません。

IF文とは別物ですが、IIF関数を使えば対応できます。

For i = 1 To 5 : ?iif(i Mod 2, 1, 2); : Next
 1  2  1  2  1

デバッグ中にループカウンタを少し戻したい

For文のデバッグ中にもう一度同じ行をやり直したい時とか、一つ前に戻りたい時とかに。

i=i-1

Trueが整数の何に当たるか知りたい

私、たまに混乱するんですよね。(いい加減に覚えてもいいんじゃないか?と思いますが。)

?true
True
?0+true
-1

覚えられない原因はExcelの数式とVBAで仕様が違うからだったりします。

表に整理するとこんな感じ。

- FALSE TRUE ことりちゅん式の思い出し方
EXCEL 0 1(0以外) TRUEをSUMしてカウントするテクニックで使うから
VBA 0 -1(0以外) 0のビットを反転させると-1だから

VBAの式がどのように評価されるのか知りたい

稀にしか使わない演算子は、細かい仕様が思い出せないときがあります。

複数言語をやっている人は、演算子の違いで混乱するので、重宝すると思います。

?10 / 3
 3.33333333333333
?10 \ 3
 3 
?10 mod 3
 1 
?10 and 3
 2 
?10^3
 1000 
?"1"=1
True

いますぐApplication配下のスイッチを変えたい

高速化等のために切ることがありますが、逆に遅くなっている原因やエラーを発見しづらくなってしまうという問題があります。

ちょっとした作業中に機能をON/OFFしたい時もあります。

Application.EnableEvents = True
Application.ScreenUpdating = True
Application.DisplayAlerts = True
Application.Calculation = xlCalculationAutomatic

マニュアルなんかを作成している時は、作業スペースを確保するために不要なツールバーを消したいときがあります。

そんな時はこの辺のコマンドを叩いてウィンドウをスッキリさせます。

Application.DisplayStatusBar = False
Application.DisplayFormulaBar = False
Application.DisplayFullScreen = False   'Alt+V+Uでも出来る

マクロ実行中に式の値が狂っている時に、再計算目的でもよく使います。

'開いている全てのブック、シートの全数式を再計算する
Application.CalculateFullRebuild

全てのシートを表示状態にしたい

Excelはシートの非表示は一括でできるのに、一括で再表示する機能がありません。

シート数が多いとかなり面倒なことになります。

For Each ws in worksheets : ws.visible=-1 : next

※ちなみに非表示シートの一括削除ならドキュメントの検査から出来ます。 ※Office365 2020年末頃のアップデートで、再表示画面でシートの複数選択がサポートされました。

全てのシートを保護したい/保護解除したい

Twitterにて情報をいただきました。

GUIからはシートの一括操作が出来ないので、配布前とかに便利です。

For Each ws In Worksheets: ws.Protect: Next
For Each ws In Worksheets: ws.Unprotect: Next

全てのシートの選択セルをA1に戻したい

これも配布前に是非使いたいやつ。

For Each ws In Worksheets: ws.Select: [A1].Select: Next

全てのシートの選択セルをA1に戻すと共に先頭シートに戻りたい

上記をより実用的にしたものです。

  • シートが逆順になったことで先頭シートがアクティブな状態で完了します。
  • ウィンドウ枠の固定がされた状態でも、必ずスクロール状態が先頭に戻ります。
For i = Sheets.Count To 1 Step -1: Application.Goto Sheets(i).[A1], True: Next

全てのシートのズーム倍率を統一したい

これまた配布前に是非使いたいやつ。

For Each ws In Worksheets: ws.Select: ActiveWindow.Zoom=80: Next

配布用前に実行しておきたいコマンドは結構多いので、まとめて個人用マクロやアドインに組み込んだほうが良いかもしれません。

全てのシートにウィンドウ枠の固定を適用したい

Accessからエクスポートしたテーブルをエクセルで使う時に真っ先に実行するやつ。

For Each ws In Worksheets: ws.Select: [B2].Select: ActiveWindow.FreezePanes=True: Next

ウィンドウが非表示のブックを閉じたい

※閉じたいプロジェクトを選択してから実行

アドインとかPERSONAL.XLSBが邪魔な時に。

ThisWorkbook.Close

開いてるエクセルブックのパスが知りたい

筆者、これ、とても良く使います。未保存のブックだと何も出力されないので気をつけて~

?ThisWorkbook.Path
C:\~~~~.xlsm

アドインのシートを表示したい

アドイン固有の設定値をシートに保存させている場合に。(悪用厳禁?)

保存に支障が出るので、用事が済んたらTrueに戻しましょう。

ThisWorkbook.IsAddin = False

直前のエラーのメッセージをコピペしたい

誰かに質問する時は必ずコードと一緒にエラーメッセージも送りましょう。

?Err.Description

選択セルのデータ型が知りたい

謎の動きをした時はとりあえず型をチェック!

?typename(Selection.Value)
String
?typename(Selection)
Range

配列の中身を全部出力したい

一次元配列ならJoinを使えば一発です。

?Join(arr, vbTab)
a   b   c

二次元配列だとForしかなさそうです。

?for i=1 To ubound(arr):?arr(i,1);:next

表の値をArrayのリテラルに使いたい

For Each r In Selection : ?"""" & r & ""","; :Next
"あああ","いいい","ううう","えええ",

これをコピペして、Arr = Array("あああ","いいい","ううう","えええ")で完成っと!

広範囲のセルを選択したい

例えば、現在の選択しているセルを基準に100x100の範囲を選択したい時。

Selection.Resize(100,100).Select

セルを一行おきに選択したい

このコマンドを実行した後、削除、挿入、着色なんでも出来ます。

For i = 1 To 100 Step 2: Union(Selection, Rows(i)).Select: Next

もちろん列もOK

For i = 1 To 100 Step 2: Union(Selection, Columns(i)).Select: Next

選択範囲のセルを絞り込みたい

選択範囲を絞り込む時Intersect()を使うと便利です。

Intersect(Selection, Columns("A:H")).Select
Intersect(Selection, ActiveSheet.UsedRange).Select

選択範囲にアドレスを埋め込みたい

for each r in Selection:r.value=r.address(0,0):next

選択範囲のセルを結合する数式(CONCATENATE関数)を作りたい

Office365やExcel 2019以降にはCONCATやTEXTJOINがありますが、無いものは仕方ありません。

=CONCAT(A1:I1)と同等のものと作りたいとしたら、A1:I1を選択して下記を実行

for each r in Selection:?r.address(0,0);",";:next
A1,B1,C1,D1,E1,F1,G1,H1,I1,

結果をコピーして=CONCATENATE(A1,B1,C1,D1,E1,F1,G1,H1,I1)として完成!

=TEXTJOIN("-",False,A1:I1)と同等のものと作りたいとしたら、A1:I1を選択して下記を実行

for each r in Selection:?r.address(0,0);",""" & "-""";",";:next
A1,"-",B1,"-",C1,"-",D1,"-",E1,"-",F1,"-",G1,"-",H1,"-",I1,"-",

=OCONCATENATE(A1,"-",B1,"-",C1,"-",D1,"-",E1,"-",F1,"-",G1,"-",H1,"-",I1)として完成!

入力規則の入ったセルのアドレスを列挙したい

for each r in cells.SpecialCells(xlCellTypeAllValidation):?r.address:next

全てのシートから入力規則を削除したい

リンクの解除でブックがクラッシュする場合は、結構な確率でデータの入力規則のリストが外部ファイルを参照している。

for each ws in Worksheets:ws.cells.validation.delete:next

EXCEL方眼紙の文字列を結合したい

Office365 / Excel2019はCONCAT関数で結合できますが、以前のバージョンでは一苦労です。

これならシートを汚さず素早く結合できますね。

for each r in Selection:?r;:next

結合セルを分解して同じ値で埋めたい

間違ったエクセルの使い方をする人から送られてきたデータの加工に!

for each r in Selection:r.unmerge:Selection=r.value:next

ダブルクォーテーションを含む文字列の生成式が難しいので練習したい

?"""C:\""" & "hoge\" & "file.jpg"""
"C:\"hoge\file.jpg"

例えばShell関数で「出力したファイルを選択した状態でエクスプローラを開きたい」ときとかですね。

?"explorer.exe /select,""" & "path" & """
explorer.exe /select,"path"

テーブルを管理するクラスのヘッダの宣言文を作りたい

これは教えていただいて、初めて気がついたテクニックです。

ID 名前 住所 時間 ←ヘッダ部を選択して
0001 hoge ほげほげ 102030
0002 fuga ふがふが 112233
0003 piyo ぴよぴよ 443322
for each r in selection:?"Public ";r;" As String":next
Public ID As String
Public 名前 As String
Public 住所 As String
Public 時間 As String

今までEXCEL関数で作ってましたが、これのほうが超かっこいいですね!

同様にTypeや列番号を定義する定数Enum、Constにも応用が効きそうです。


その他のTIPS

出力を右寄せにしたい

様々な利用方法を説明しました。

しかし一つだけどうしても解決したい問題があります。

それが数値が左寄せで出力されてしまう。ということです。

数値を確認するときは右寄せにしたいですよね。

いっその事ワークシート上に出力してしまえば済む話なのですが、イミディエイトウィンドウに出したいという時のために関数を紹介します。

適当に書いたものなので、使う時は都合の良いように直して下さい。

'14桁毎になるように右寄せにする
'※14桁以上のデータは上位の桁が消える
Function dpr(ParamArray vals() As Variant) As String
    Dim v As Variant
    For Each v In vals
        dpr = dpr & Right(String(13, " ") & CStr(v), 14)
    Next
End Function

id:Z1000S 様よりコメント欄にてFormat関数を使う方法も提案して頂きました。

こちらのほうが可読性は良くなりそうです。

sFunction dpr(ParamArray vals() As Variant) As String
    Dim v As Variant
    For Each v In vals
        dpr = dpr & Format(v, String(14, "@"))
    Next
End Function

id:umeyoshioka 様よりコメント欄にて固定長文字列を使ったサンプルを頂きました。

素晴らしいアイディアをありがとうございます。

'14桁毎になるように右寄せにする
'※14桁以上のデータは上位の桁が消える
Function dpr(ParamArray vals() As Variant) As String
    Dim v As Variant, str14 As String * 14
    For Each v In vals
        RSet str14 = CStr(v)
        dpr = dpr & str14
    Next
End Function

テスト結果

?"1234567890123","12345"
1234567890123 12345
?"1234567890123","123"
1234567890123 123
?dpr("1234567890123","12345")
 1234567890123         12345
?dpr("1234567890123","123")
 1234567890123           123


イミディエイトウィンドウが行方不明になった

イミディエイトウィンドウが行方不明になるというトラブルが稀にあります。

多くはデュアルディスプレイ環境で、ウィンドウが増減したことに伴うトラブルだと思いますが、対処法は2つみつかりました。

まずは、Windows共通の基本奥義。アプリケーションメニューからの移動十字キーです。

Ctrl+G → Alt+Space → 移動(M) → 十字キー → マウスに極小のウィンドウがついてくる

もう一つはVBE特有のオプションの変更です。

ツール → オプション → ドッキングタブ → チェックをOFF → コードウィンドウに出現する → 同様に操作してON


イミディエイトウィンドウをExcelのコンソールのようにしたい。

イミディエイトで右クリックして、「ドッキング可能」のチェックを外して、他のウィンドウ全てを非表示にするとイミディエイト単体のVBEが出来上がります。

これをExcelと並べて配置すれば、ほら!


VBAからイミディエイトウィンドウの中身をリセットしたい

たまに、実行のたびにイミディエイトウィンドウを空にしたいときがあります。

ところが、コードからイミディエイトウィンドウを操作する方法は存在しません。

そのため、開発者自身の手で毎回Ctrl+A → Deleteで消すことんあるのですが、テスト回数があまりにも多い時は自動で削除したいわけです。

そんなことを考えるのは私だけではないようで、ネットで調べたところ3種類の方法が見つかりました。

  1. Debug.Printで流し切る
  2. SendKeyで消す
  3. APIで消す


1. ログが流れきるまで出力する

イミディエイトウィンドウは200行しか保持されないという性質を利用して、199回この改行を出力して流してしまえ。というアイディアです。

基本命令のみなのでシンプルなのが最大のメリットです。

また後に紹介する方法のような、エラーの可能性もありません。

エラーにはならないものの、Debug.PrintはVBEに負荷がかかるため、ループの中で大量に実行するとマクロ全体が遅くなります。使う場所には注意したほうが良いでしょう。

できれば条件付きコンパイルで制御し、本番では出力されないようにしておくと良いと思います。

またDebug.Printはカーソルの位置から出力するという性質であることから、カーソルの位置が末尾にないと完全には消えないので注意が必要です。

ちなみに末尾に飛ぶにはCtrl+ENDキーがおすすめです。

'空欄でログを流す
'カーソルが末尾にないと意味がない
'イミディエイトウィンドウは200行しか表示できないので改行が199個出力した時点でログが全滅する
Sub DebugPrint_Flush()
    Debug.Print String(199, vbLf)
End Sub


2. SendKeyで消す

ちょっと有名なのが Application.SendKey で操作を再現して消す方法

VBEを開いてない時は働かないように判定しているため、「Excelのオプション」→「マクロの設定」→「VBAプロジェクトオブジェクトモデルへのアクセスを信頼する。」を有効にしないと使えないのが難点です。 この設定を変えること無く、判定する方法がないか捜索中ですですが未だに見つかりません。

また、イミディエイトの表示状態には4種類あり、状態によっては動かないとか副作用があるとか安定してないです。

'イミディエイトウィンドウを全て削除する
Sub DebugPrint_Clear()
    On Error GoTo ENDPOINT
    If Application.VBE.MainWindow.Visible And _
        Application.VBE.Windows("イミディエイト").Visible Then
            SendKeys "^{g}", False
            DoEvents
            SendKeys "^{Home}", False
            SendKeys "^+{End}", False
            SendKeys "{Del}", False
            SendKeys "{F7}", False
    End If
ENDPOINT:
End Sub

しかし、イミディエイトウィンドウからプロシージャを呼び出して、結果を確認するというデバッグをする場合は全て消されては困る。

そこで新たに考えたのがこちら。

'イミディエイトウィンドウの先頭行を除いてすべて削除する
'フォーカスをイミディエイトウィンドウに残す
Sub DebugPrint_ClearAfter2ndLine()
    If Application.VBE.MainWindow.Visible And _
        Application.VBE.Windows("イミディエイト").Visible Then
            SendKeys "^{g}", False
            DoEvents
            SendKeys "^{Home}", False
            SendKeys "{Down}", False
            SendKeys "^+{End}", False
            SendKeys "{Del}", False
    End If
End Sub


3. APIで消す

SendKeyなんて方法は使いたくない。もっと良い方法はないのか。と調べていたら、StackOverflow に良さげなのがありました。

https://stackoverflow.com/questions/10203349/use-vba-to-clear-immediate-window

ただし、結局はPostMessageしており、SendKeyの問題も解決出来ておらず、色々と問題があったので放置です。

重要なヒントが隠れていそうなので、記録として残しておきます。


VBAからイミディエイトウィンドウのカーソルを末端に戻したい

カーソルの位置を末尾に移動するにはCtrl+Endです。

プロシージャでDebug.Printを始める前に次のプロシージャを呼ぶようにしておくと、常に最終行に出力されるのようになります。これもSendKeysなので確実性はないです。

'イミディエイトウィンドウの末尾にフォーカスを移動する
Sub DebugPrint_CursolMoveToLast()
    If Application.VBE.MainWindow.Visible And _
        Application.VBE.Windows("イミディエイト").Visible Then
            SendKeys "^{g}", False
            DoEvents
            SendKeys "^+{End}", False
            SendKeys "{F7}", False
            DoEvents
    End If
End Sub


イミディエイトウィンドウの内容をファイル出力したい

イミディエイトウィンドウの文字列を取得する方法が見つけられませんでした。

現実的な方法は2つあります。

  1. Debug.Print の代わりのプロシージャを使うようにする。
  2. イミディエイトウィンドウの内容をコピーで読み取ってファイルに出力する。※199行制限、開発画面の表示が必須

いずれにせよ本番運用向けであれば、エラー出力用の専用クラスを作って出力しましょう。


配列やDictionaryを見やすい表で出力したい

イミディエイトを有効活用するためには、配列やDictionary等のオブジェクトを出力するための関数を整備するのがキモとなります。

一次元配列は、先の通り Join(array, vbTab) で対応できます。

二次元配列は、各列の文字幅をカウントして、半角スペースで埋めて幅を合わせるという処理が必要になります。

Dictionaryは、更にKeyとValueをどのように表現するかでレイアウトが変わります。

実際には各要素に更に配列やオブジェクトが入れ子になっていることも予想されます。

私は大抵のデータ構造に対応できる関数を保有していますが、ここにソースコードを載せると長くなりすぎてしまうので、いずれ記事を書いたらここにリンクを載せるかもしれません。

必要な人は作成しておくと良いでしょう。


こんなの覚えきれない

たくさんの事例を上げましたが、全部を覚えるのは不可能です。

使えそうだなと思ったものだけ、覚えておいて何度も使っているうちにスラスラ打てるようになるでしょう。

通常、ワンライナーはその場で必要になった時に即興で書くものですが、頻繁に使うようなコードはいっその事IMEに登録しておくと便利かもしれません。

たとえば、下記の記事をご覧ください。

www.excel-chunchun.com


ワンライナーである必要がなくなりますが、便利なので個人用マクロなどに登録しておき、リボンからワンクリックで呼び出せるようにすると便利かもしれませんね。


関連ツイート

元ネタは2018/11/30にTwitterにて投稿したツイートでした。

記事を書くにあたって、ネタを提供して下さった方々には心からの感謝を。


参考資料

Microsoft公式のイミディエイトウィンドウに関する説明はこちらです。

内容が分かりにくいことで定評ですが、公式の文章には大事なことが書いてあったりするので出来れば目を通しましょう。


まとめ

「イミディエイトウィンドウ」というネタから、随分と壮大な記事になってしまいました。

イミディエイトも、なかなか、どうして、侮れません。

ここに載せていない便利なコマンドがあれば、是非コメント等頂けると幸いです。

以上

https://www.excel-chunchun.com/archive/category/%E9%96%8B%E7%99%BA%E6%94%AF%E6%8F%B4www.excel-chunchun.com


何か御座いましたらコメント欄、またはTwitterからどうぞ♪

幸せなVBAライフを♪ ちゅんちゅん(・8・)