ポート名(?)付きのプリンタ名を返すFunction
ポート名(?)付きのプリンタ名を返すFunction
前回、
前々回
の集大成。
コード
プリンタの名前を渡したら、ポート名(?)付きのプリンタ名を返すFunctionを作ってみた。
前回同様、オブジェクト名は「InstalledPrinter
」とし、PredeclaredId
をTrue
にしている。
リスト1 クラスモジュール
Public Function getPrinterNameWithPort( _ ByVal printerName As String) As String getPrinterNameWithPort = "" Dim printersCount As Long printersCount = getPrintersCount Dim tmp As String tmp = Application.ActivePrinter On Error Resume Next Dim i As Long For i = 0 To printersCount - 1 '……(1)' Dim ret As String ret = printerName & " on Ne" & Format(i, "0#") & ":" If Me.isExistPrinter(ret) Then GoTo Finalizer Err.Clear Next ret = printerName & " on nul:" '……(2)' If Not Me.isExistPrinter(ret) Then ret = "" Finalizer: On Error GoTo 0 Application.ActivePrinter = tmp getPrinterNameWithPort = ret End Function Public Function getPrintersCount() As Long Dim shellApp As New Shell getPrintersCount = shellApp.Namespace(ssfPRINTERS).Items.Count Set shellApp = Nothing End Function Public Function isExistPrinter( _ ByVal printerName As String) As Boolean isExistPrinter = False Dim tmp As String tmp = Application.ActivePrinter On Error Resume Next Application.ActivePrinter = printerName Application.ActivePrinter = tmp If Err.Number > 0 Then Exit Function On Error GoTo 0 isExistPrinter = True End Function
(1)からの6行
For i = 0 To printersCount - 1 Dim ret As String ret = printerName & " on Ne" & Format(i, "0#") & ":" If Me.isExistPrinter(ret) Then GoTo Finalizer Err.Clear Next
For
ループで、 on NeXX:
の「XX」の部分をインクリメントしながらプリンタ名に附加してisExistPrinter
メソッドで判定。
存在するプリンタ名だったら、ループを抜けてreturn。
(2)からの2行
ret = printerName & " on nul:" If Not Me.isExistPrinter(ret) Then ret = ""
これは、プリンタ名の末尾が
のように「 on nul:
」となっていることがあるために追加。
For
ループを抜けるということは、プリンタ名がヒットしていないということだから、最後の手段としてプリンタ名に「 on nul:
」を附加することを試みる。
これでダメなら「""
」を返す。
実行
イミディエイト・ウインドウに、それぞれ次のように入力して[Enter]をぶっ叩く。
?InstalledPrinter.getPrinterNameWithPort("Microsoft XPS Document Writer")
?Application.ActivePrinter
?InstalledPrinter.getPrinterNameWithPort("Microsoft Print to PDF")
?InstalledPrinter.getPrinterNameWithPort("Send To OneNote 2013")
実行結果は次の通り。
おわりに
Shell
を使ったら、プリンタ名の取得はできるので、ユーザーフォームでプリンタ名を選ばせるかして、このFunctionと組みあわせたら、端末を問わずにプリンタを適切に指定できるようになるのではなかろうか。
ただ、プリンタのことがよく分かっていないので、ポート名(?)が網羅できているかは自信がない。
プリンタの数を返すFunction
プリンタの数を調べるFunction
Application.ActivePrinter
にプリンタ名をセットしたくても、プリンタ名の後ろにくっついている「on NeXX:
」の部分がわからないとセットできない。
同じプリンタに接続していても、端末ごとに「NeXX:
」の部分が異なるので困ることになる。
そこで、「XX
」の部分を「00
」からインクリメントしてプリンタ名にヒットするかどうか確認することを考えた。
そのためには、ループ回数を決めるためにインストールされているプリンタの数を取得しないといけない。
プリンタの数自体は、
このときのように、Shell.Application
オブジェクトを取得したら簡単に取得できるので、Shell.Application
オブジェクトをラップしたメソッドを作った。
コード
前回
のisExsistPrinter
メソッドとともに、クラスモジュールに書いた。オブジェクト名は「InstalledPrinter
」とし、PredeclaredId
をTrue
にして、Singleton的に運用することにした。
リスト1 クラスモジュール
Public Function getPrintersCount() As Long Dim shellApp As New Shell getPrintersCount = shellApp.Namespace(ssfPRINTERS).Items.Count Set shellApp = Nothing End Function
参照設定で「Microsoft Shell Controls And Automation」にチェックを入れているからなのか、「Microsoft Scripting Runtime」にチェックを入れているからなのか、CreateObject
を使わなくてもShell
オブジェクトをインスタンス化することができる。
あとは、NameSpace
メソッドでFolder
オブジェクトを取得し、Items.Count
プロパティでインストールされているプリンタの数が取得できる。
使ってみる
イミディエイト・ウインドウに、
?InstalledPrinter.getPrintersCount
と打ち込んで、[Enter]をズドン!
「6
」が返った。
コントロール・パネルからプリンタの数を見てみると、
ご名算!
おわりに
ひとまず今回はここまで。
プリンタが存在するかどうかを判定するFunction
プリンタが存在するかを調べるFunction
軽量PDFを作成するためには、ExportAsFixedFormat
メソッドを用いていたのでは限界があるので、
このときのように、プリンタにJUST PDFなどの、PDF出力用プリンタを選択する。
その場合、プリンタ名をApplication.ActivePrinter
プロパティにプリンタ名をセットすることになる。
このとき、存在しないプリンタ名をセットしようとすると、当然ながらエラーが出る。
このことを利用して、プリンタが存在するかどうかを判定するFunction
を作成した。
コード
リスト1 標準モジュール
Public Function isExistPrinter( _ ByVal printerName As String) As Boolean isExistPrinter = False Dim tmp As String '……(1)' tmp = Application.ActivePrinter On Error Resume Next '……(2)' Application.ActivePrinter = printerName Application.ActivePrinter = tmp If Err.Number > 0 Then Exit Function On Error GoTo 0 isExistPrinter = True End Function
まず(1)からの2行
Dim tmp As String tmp = Application.ActivePrinter
で、変数tmp
にApplication.ActivePrinter
プロパティの値を保持しておく。
次に(2)からの5行
On Error Resume Next Application.ActivePrinter = printerName Application.ActivePrinter = tmp If Err.Number > 0 Then Exit Function On Error GoTo 0
では、最初に
On Error Resume Next
でエラーが出ても無視するようにしておいて、
Application.ActivePrinter = printerName
で、引数として受け取ったprinterName
をApplication.ActivePrinter
プロパティに突っ込んでみる。
printerName
が存在するプリンタ名なら、単にActivePrinter
が切り替わるだけ。存在しないプリンタ名だとエラーが出る。On Error Resume Next
しているので、エラーが出ていたとしてもプログラムが停止することはないが、エラーが出ていればErr.Number > 0
になっており、エラーが出ていなければErr.Number = 0
のはずだ。
エラーが出るかどうかさえ分かれば良いので、この後即座に
Application.ActivePrinter = tmp
でActivePrinter
を元通りにしておく。
もし、エラーが出ていたのであれば、
If Err.Number > 0 Then Exit Function
のところでreturn。
最初にisExistPrinter
をFalse
にしてあったのだから、False
が返る。
エラーが出ていなければ、最後にisExistPrinter
がTrue
になるので、True
が返る。
たったこれだけの簡単なコード。
使ってみる
まず、イミディエイト・ウインドウに、
?Application.ActivePrinter
と打ち込んで[Enter]をドン!
アクティブなプリンタが「JUST PDF 3 on Ne03:
」だということがわかる。
で、今度はイミディエイト・ウインドウに
isExistPrinter("ち~んw")
と入力して[Enter]をポン!
「ち~んw」などというアホな名前のプリンタがあるわけがないので、当然
False
が返る。
この状態で、イミディエイト・ウインドウに
?Application.ActivePrinter
と打ち込んで[Enter]をプチッ!
ご覧のように、アクティブなプリンタは元通りになっている。
当然、イミディエイト・ウインドウに
isExistPrinter("JUST PDF 3 on Ne03:")
と打ち込んで[Enter]をズン!
キッチリTrue
が返っている。
おわりに
ひとまず、ここまで。
「フォームコントロールのボタンオブジェクトのEnabledプロパティって、意味ないんじゃね?」問題
「フォームコントロールのボタンオブジェクトのEnabledプロパティって、意味ないんじゃね?」問題
前回
の続き。
ボタンのEnabledプロパティをFalseにしてみる
イミディエイト・ウインドウに
activesheet.buttons(1).enabled = false
と打ち込んで、[Enter]をビシッ!
この画像では何のことか分からんが、イミディエイト・ウインドウに
?activesheet.buttons(1).enabled
と打ち込んで、[Enter]をペチッ!
このように、ボタンオブジェクトのEnabled
プロパティの値がFalse
になっていることが分かる。
ボタンはどうなっているか
Enabled
プロパティがFalse
である以上、ボタンは無効化されていると考えるのが通常だろう。そこで、
この状態でクリック。
ボタンにはこちらのマクロが仕込まれています。
フツーにボタンは生きとるじゃん。
おわりに
Enabled
プロパティって、何か意味あるのでしょうか?
フォームコントロールのボタンオブジェクトをVBAで捕まえる
フォームコントロールのボタンを捕まえる
マクロを呼び出すためのボタンをシート上に設置することがよくある。
シート上に設置したボタンをVBAで取得するにはどうすればよいのだろうか。
Nameプロパティを取得してみる
ボタンの上で右クリックすると、
このように、ボタンを選択した状態になる。
この状態で、イミディエイト・ウインドウに
?selection.name
と打ち込んで、[Enter]をバシッ!
このように、「Button 1
」と出た。
何オブジェクトなのかは分からんが、Name
プロパティを持っており、パラメータが「Button 1
」であることが分かった。
何型のオブジェクトなのか
今度は、TypeName
関数を使ってみる。
同じくボタンが選択された状態で、イミディエイト・ウインドウに
?typename(selection)
と打ち込んで、[Enter]をバチコーン!
ご覧のように「Button
」と出た。
親オブジェクトは何なのか
さらに、イミディエイト・ウインドウに
?typename(selection.parent)
と打ち込んで、[Enter]をドオォーーーン!
今度は、「Worksheet
」が返った。
フォームコントロールのボタンオブジェクトを取得する
上の実験により、フォームコントロールのボタンオブジェクトは、
Worksheet
オブジェクトの配下であるButton
型のオブジェクトであるName
プロパティを持つ
ということが分かった。
以上のことを念頭に、コーディングしてみる。
すると……、
なにーーーーっ!
入力候補に「Button
」がねえ!
しかし、それでもめげずにコーディングしてみた。
リスト1 標準モジュール
Public Sub testCommandButton() Dim btn As Button Set btn = ActiveSheet.Buttons(1) '……(1)' btn.Caption = ActiveSheet.Range("A1").Value '……(2)' End Sub
(1)の
Set btn = ActiveSheet.Buttons(1)
でButton
型の変数「btn
」にボタンオブジェクトをセット。
「Button
型」というぐらいだから、「Buttons
」コレクションの要素のはず。
んで、ActiveSheet
にはボタンは一つしかないのだから、Buttons(1)
で良いはず。
あとは、
コーディング中に、このように入力候補が出るぐらいだから「Caption
」プロパティがあるはず。ゆえに、(2)の
btn.Caption = ActiveSheet.Range("A1").Value
で、ボタンのテキストをA1セルの文字列にする。
実行
リスト1のtestCommandButton
をボタンに登録して、
この状態でクリック。
無事にボタンのテキストが書き換わった。
おわりに
Button
オブジェクトの構造については、オブジェクト・ブラウザーにも出てこないので、ちょっとわかりにくい。
こちらの記事にインスパイヤされて書きました。
続編
クラスのPrivateメソッドもイミディエイトで実行できる
クラスのPrivateメソッドもイミディエイトで実行できる
再び超小ネタ。
クラスモジュールの準備
クラスモジュールを作る。
リスト1 クラスモジュール
'オブジェクト名は"HiddenBooks"' Private Function isHiddenBook( _ ByVal targetBook As Workbook) As Boolean isHiddenBook = True If targetBook.IsAddin Then Exit Function Dim i As Long For i = 1 To targetBook.Windows.Count If targetBook.Windows(i).Visible Then _ isHiddenBook = False: Exit Function Next End Function
isHiddenBook
というメソッドを1つ書いただけ。
PredeclaredIdをTrueにする
イミディエイトで実行することを考えて、PredeclaredId
をTrue
にする。
一旦、クラスモジュールHiddenBooks
をエクスポートして、エディタで開く。
んで、上の方を
こんなふうにする(8行目です。)。
デフォルトでは、
Attribute VB_PredeclaredId = False
になっているので、「False
」を「True
」に書き換えて保存、再びインポートするだけ。
これで、インスタンス化しなくても[クラス名].[メソッド・プロパティ名]
でメソッドやプロパティにアクセスできる。
実験
イミディエイト・ウインドウに
?HiddenBooks.isHiddenBook(Workbooks("PERSONAL.XLSB"))
と入力して[Enter]をぶっ叩くと、
このように、意図どおりの結果が出る。
「だから何?」と言われても、特に意見はありません。
ちなみに
クラス外から呼ぶのは
当然不可能ですw
個人用マクロブックはアドインに非ず
個人用マクロブックはアドインに非ず
「当り前だろバカ!」と思ったらスルー推奨。
IsAddinプロパティ
イミディエイト・ウインドウに
?Workbooks("PERSONAL.XLSB").IsAddin
と打ち込んで[Enter]をポチッ。すると、
False
が返るのであった。
Windows.Countプロパティ
ちなみに、
?Workbooks("PERSONAL.XLSB").Windows.Count
だと、
このように、「1
」が返る。
[Window].Visibleプロパティ
またまたちなみに、
?Workbooks("PERSONAL.XLSB").Windows(1).Visible
だと、
このように、「False
」が返る。
おわりに
つまり、Workbooks
コレクションのメンバが「PERSONAL.XLSB
」かどうかは、Workbook
オブジェクトのName
プロパティで確認するのが一番簡単かつ確実ということになるのかなあ。