Worksheet_Changeイベントの引数Target(Excel)

Worksheet_Changeイベントの引数Target

Worksheet_Changeイベントについては、イベントを起こすセル範囲を限定するのによく使う。

引数「Target」に関する注意事項

Worksheet_Changeイベントが発生したときに、プロシージャに渡される引数Targetについて、ちょっと気をつけておいた方が良いことに気づいたので、備忘録的に記しておく。

まず、Worksheet_Changeのイベントプロシージャとして、次のコードを書いておく。

Private Sub Worksheet_Change(ByVal Target As Range)
  Debug.Print "引数TargetのCountプロパティ:" & Target.Count
End Sub

引数TargetのCountプロパティをイミディエイト・ウインドウに表示するだけのプロシージャ。

フィルハンドルでドラッグしたとき

f:id:akashi_keirin:20180505214923j:plain

こんなふうに、ドラッグしてコピーしたときの引数Targetは、

f:id:akashi_keirin:20180505214933j:plain

これでお分かりのように、ドラッグした範囲全てである。

行ごと削除した場合

f:id:akashi_keirin:20180505214942j:plain

こんなふうに、行を丸ごと選択して、削除する。

f:id:akashi_keirin:20180505214953j:plain

147456!!!!!!!!

すさまじい数のRangeオブジェクトが渡されている。

行ごと/列ごと削除の場合に何もせずにExitする

Targetの中身を調べて、その中身次第でイベントプロシージャの処理を実行するかどうかを分岐したいとき、列ごと削除や行ごと削除された日には、すさまじい回数の計算が生ずることになる。かといって、通常の操作におけるセルの上限個数なんて決められない場合がある。

たとえば、フィルハンドルで値をコピーしたときにはそれぞれのセルの値に応じて処理をしたい、というようなとき、ドラッグする範囲の上限なんて決められない。そんなときに、【列ごと削除した】とか【行ごと削除した】ようなときにはイベント処理をしない、というふうにできれば良い。

次のようなコードを考えた。

リスト1
With Target
  If .Count Mod Rows.Count = 0 Or _
     .Count Mod Columns.Count = 0 Then Exit Sub
End With

こいつをWorksheet_Changeプロシージャの先頭に入れる。

要するに、TargetのCountプロパティがRows.CountとかColumns.Countで割り切れる場合は、行ごと/列ごと変化したとみなして処理を飛ばすわけだ。

f:id:akashi_keirin:20180505215013j:plain

行ごと削除してみると、TargetのCountプロパティが98304になっていることが分かる。

f:id:akashi_keirin:20180505215024j:plain

Columns.Countの値(1行あたりのセルの総数)は16384。

f:id:akashi_keirin:20180505215035j:plain

ご覧のように、98304は16384で割り切れるので、何もせずにExitすることになる。

おわりに

Targetに複数のセルが渡されたときは、Target.Valueとか書いているとエラーになるので、注意が必要。

追記

よく考えたら、

TargetのCountプロパティがRows.CountとかColumns.Countで割り切れる場合は、行ごと/列ごと変化したとみなして処理を飛ばす

というのは余りにも乱暴なやり方だった。

値を書き換えたセル範囲のセルの数(Countプロパティ)がたまたまRows.CountとかColumns.Countの倍数だったりすると、行・列削除だとみなされてしまうことになる(まあ、そんなことは滅多にないだろうけれど)。

それはいくらなんでも、いくらなんでもそれはご勘弁願いたい。というわけで、コードを書き換えてみた。

っていうか、ついでにセル範囲が行または列全体かどうかを判定するFunctionを作ってみた。

リスト1改
Public Function isWholeRowORColumn(ByVal targetRange As Range) As Boolean
  With targetRange
    If .Rows.Count = Rows.Count Or _
       .Columns.Count = Columns.Count Then _
      isWholeRowORColumn = True: Exit Function
  End With
  isWholeRowORColumn = False
End Function

引数で渡されたセル範囲targetRangeの縦幅(targetRange.Rows.Count)がシート全体の縦幅(Rows.Count)と等しかったら列全体、targetRangeの横幅(targetRange.Columns.Count)がシート全体の横幅(Columns.Count)と等しかったら行全体が変化したとみなす。

問題は、列全体または行全体に値が書き込まれた場合だな……。

ExcelVBAでWordドキュメントを軽量PDF化する

ExcelでWordドキュメントを軽量PDF化する

ExcelでWordドキュメントのファイル名の一覧表を作って、必要なものだけPDF化する、というのがお題。

ExportAsFixedFormatメソッドを使うやつは既に作成済みなので、PDF化部分を差し替えるのが目標。

ドキュメントを軽量PDF化するメソッド

ひとまず、コードだけ載っけておこう。

テストが不十分なので、ご利用の際は自己責任でどうぞ。

リスト1 標準モジュール
    Public Function convertDocumentToLightPDF( _
                ByVal targetDocument As Word.Document, _
       Optional ByVal PDFPrinter As String, _
       Optional ByVal printRange As WdPrintOutRange = wdPrintAllDocument, _
       Optional ByVal printFrom As Long, _
       Optional ByVal printTo As Long, _
       Optional ByVal printPages As String) As Boolean
On Error GoTo errorHandler
  Dim wordApp As Word.Application
  Set wordApp = targetDocument.Parent
  If PDFPrinter = "" Then PDFPrinter = wordApp.ActivePrinter
  If InStr(PDFPrinter, "PDF") < 1 Then _
    convertDocumentToLightPDF = False: Exit Function
  Dim currentPrinter As String
  currentPrinter = wordApp.ActivePrinter
  wordApp.ActivePrinter = PDFPrinter
  If printRange = wdPrintFromTo And _
     printFrom * printTo = 0 Then
    printFrom = 1
    printTo = 1
  End If
  If printRange = wdPrintRangeOfPages And _
     printPages = "" Then
    printPages = "1"
  End If
  If Not wrappedPrintOutMethod( _
           targetDocument:=targetDocument, _
           printRange:=printRange, _
           printFrom:=printFrom, _
           printTo:=printTo, _
           printPages:=printPages) Then GoTo errorHandler
  DoEvents
  convertDocumentToLightPDF = True
errorHandler:
  If Err.Number > 0 Then Err.Clear
  wordApp.ActivePrinter = currentPrinter
  Set wordApp = Nothing
  If Not convertDocumentToLightPDF Then _
    convertDocumentToLightPDF = False
End Function

Public Function wrappedPrintOutMethod( _
            ByVal targetDocument As Word.Document, _
            ByVal printRange As WdPrintOutRange, _
            ByVal printFrom As Long, _
            ByVal printTo As Long, _
            ByVal printPages As String) As Boolean
  With targetDocument
    If printRange = wdPrintAllDocument Or _
       printRange = wdPrintCurrentPage Or _
       printRange = wdPrintSelection Then _
         Call .PrintOut(Range:=printRange): GoTo Finalizer
    If printRange = wdPrintFromTo Then _
         Call .PrintOut(Range:=printRange, _
                        From:=printFrom, _
                        To:=printTo): GoTo Finalizer
    If printRange = wdPrintRangeOfPages Then _
         Call .PrintOut(Range:=printRange, _
                        Pages:=printPages): GoTo Finalizer
  End With
errorHandler:
  Err.Clear
  wrappedPrintOutMethod = False: Exit Function
Finalizer:
  wrappedPrintOutMethod = True
End Function

Boolean型のメソッドにした。途中でエラーが出たりしたら、Falseを返す。

あと、渡された引数に合わせてWord.Document.PrintOutメソッドを呼び出す分岐がめんどくさかったので、別途Word.Document.PrintOutメソッドをラップしたメソッドを作った。

2つ目のwrappedPrintOutMethodがそれ。

Word.Document.PrintOutは結構引数が多く、引数チェックが煩雑になるので、メインの方では致命的な不正引数だけチェックして、後はサブのメソッドの方で印刷範囲別(引数「Range」の値別)に対応することにした。

実行結果

Xドライブに「中野 浩一.docx」というドキュメントを置いて、次のコードで実験。

スト2 標準モジュール
Public Sub testConvertDocumentToLightPDF()
  Dim wordApp As New Word.Application
  Dim targetDoc As Word.Document
  Set targetDoc = wordApp.Documents.Open("X:中野 浩一.docx")
  Call convertDocumentToLightPDF(targetDoc, PDF_PRINTER)
  Set wordApp = Nothing
  Set targetDoc = Nothing
End Sub

途中「PDF_PRINTER」とあるのは、ウチの環境での「JUST PDF 3」のプリンタ名が入った定数。ちなみに、JUST PDF 3 on Ne03:という文字列が当てられている。

JUST PDF 3 側の設定で、黙って指定済みのフォルダに保存するようにしているので、実行するといつの間にか

f:id:akashi_keirin:20180430172716j:plain

こんな風にPDFができている。

画像ではPDFが2つできているが、上がconvertDocumentToLightPDFメソッドで作成したもの。

下は、

f:id:akashi_keirin:20180430173141j:plain

Wordの「名前を付けて保存」でPDF化したもの。

「最小サイズ」を指定しても48キロバイトもある。それに比べてconvertDocumentToLightPDFメソッドで作成した方は27キロバイト

なかなか優秀といえるのではなかろうか。

おわりに

ちなみに、元のドキュメントは16キロバイトである。

プリンタ名を調べる

プリンタ名を取得する

akashi-keirin.hatenablog.com

コチラをうpしたところ、Twitterで次のサイト(『パソコンの小技・備忘録(ExcelのVBAで、パソコンのプリンタ一覧を取得したい。)』)をご紹介いただいた。

たぶん、

プリンタ名を比較するときにApplication.ActivePrinterの返り値をそのまま使うことができないのだ。
そんなわけで、仕方なく純粋なプリンタ名だけをまず切り出して、比較しているのだ。

のところを見て、教えてくださったのだろう。

サンプルコードに学ぶ

前掲サイトに掲載されているコードは

'変数の定義'
Dim tempShell As Object
Dim tempObj As Object
Dim intRow As Integer

'オブジェクトの設定と繰り返し処理'
Set tempShell = CreateObject("Shell.Application")    '……(1)'
intRow = 1
For Each tempObj In tempShell.NameSpace(4).Items    '……(2)'
  If intRow > 1 Then
    '取得したプリンタ名をセルに書き込む'
    Cells(intRow, 1) = tempObj.Name
  End If
  intRow = intRow + 1
Next

Set tempShell = Nothing

こんなの。

そういえば、「Shell」とか、今ひとつよく分かっていなかったので、この機会にちょっと調べてみた。

Shellオブジェクト

まず、(1)の

Set tempShell = CreateObject("Shell.Application")

では、Shell.Applicationというクラスのインスタンスを生成して変数tempShellにぶち込んでいる。

Windows Dev Centerの「Shell Object」の項には、

Represents the objects in the Shell. Methods are provided to control the Shell and to execute commands within the Shell. There are also methods to obtain other Shell-related objects.

とあるので、「Shell」というものの中にあるオブジェクトを操れるようにするオブジェクトぐらいの意味なのだろう(適当)。

(2)の

For Each tempObj In tempShell.NameSpace(4).Items

からは、おなじみFor Eachを用いて、[Shellオブジェクト].NameSpace(4).Itemsコレクションの要素を1つ1つ取り出す処理であることが読み取れる。

結局、tempObjという変数には何が入ることになるのか、ちょっと面倒だけれど、せっかくなので丁寧にたどってみる。

まず、ShellオブジェクトのNameSpaceメソッド。

Windows Dev Centerの「Shell NameSpace method」の項によると、

Creates and returns a Folder object for the specified folder.

とある。特殊フォルダの「Folder」オブジェクトを生成したり取得したりするメソッドっぽい(適当)。

ちなみに、上掲コードでは

tempShell.NameSpace(4)

と、NameSpaceメソッドの引数に「4」を渡している。

先ほどの「Shell NameSpace method」の項VB用のサンプルコードを見ると、

Set objFolder = objShell.NameSpace(ssfPERSONAL)

と、引数のところに「ssf」で始まる定数が使われている。

これは、「ShellSpecialFolderConstants」という組み込み列挙体らしい。

VBEでも、参照設定で「Microsoft Shell Controls And Automation」にチェックを入れて、オブジェクト・ブラウザーを見ると、

f:id:akashi_keirin:20180430092514j:plain

こんなふうに表示される。

「4」というのは、「ssfPRINTERS」のことだと分かる。

ちなみに、Windows Dev Centerの「ShellSpecialFolderConstants enumeration」の項によると、「ssfPRINTERS」というのは、

0x04 (4). Virtual folder that contains installed printers.

ということらしい。「Virtual folder」というのがよく分からんが、インストールされたプリンタを管理しているフォルダなんだろう(適当)。

tempShell.NameSpace(4)という式で、めでたくFolderオブジェクトが取得できるので、今度は、tempShell.NameSpace(4).ItemsでFolderオブジェクトのItemsメソッドを使う。

Windows Dev Centerの「Folder object」の項によると、「Items」メソッドというのは、

Retrieves a FolderItems object that represents the collection of items in the folder.

とある。

FolderItemsオブジェクトというのを返すみたい(適当)。

FolderItemsオブジェクトというのはFolderItemオブジェクトのコレクションなので、For Eachで取り出されるのはFolderItemオブジェクトということになる。

Windows Dev Centerの「FolderItem object」の項を見れば、メンバが分かる。

ちゃんとNameプロパティもある。

上掲コード中、For Eachブロック内の

If intRow > 1 Then

という条件分岐がイマイチ意味がよく分からないが、全体として

プリンタ情報を管理している特殊フォルダの中身を一つ一つチェックして、Nameプロパティをセルに書き出す

というコードになっていることが分かる。

実行結果

上掲サンプルコードにテキトーなプロシージャ名を付けて実行すると、

f:id:akashi_keirin:20180430092600j:plain

このとおり。

なるほど、純粋なプリンタ名が取得できている。

ただし、これを

akashi-keirin.hatenablog.com

このときの軽量PDF出力メソッドにどう生かすか、となると、ちょっとめんどくさそう。

参考

コチラもどうぞ!

akashi-keirin.hatenablog.com

ワークシートの軽量PDF化(Excel)

アクティブシートのPDF化

akashi-keirin.hatenablog.com

このときにも書いたように、アクティブシートをPDF化するときにはWorksheet.ExportAsFixedFormatメソッドを使う。

ただ、この場合、やたらファイルサイズがデカくなってしまうのが悩みのタネだった。かといって、いちいちJUST PDFなんかを使ってPDF化するのは死ぬほどダルいので、仕方がないなあと思っていた。

ところが、ひょんなことから、VBAでプリンタを指定できると知り、ちょっとやってみた。

Application.ActivePrinterプロパティ

Application.ActivePrinterプロパティというものを使えば、カンタンにプリンタを切り替えることができる。

まず、イミディエイト・ウインドウで現在のApplication.ActivePrinterプロパティを調べてみる。

イミディエイト・ウインドウに、

?Application.ActivePrinter

と入力して[Enter]。

f:id:akashi_keirin:20180428105851j:plain

自宅の環境では、

Canon MP470 series Printer on Ne06:

が返った。

んで、Excelの「ファイル」タブから「印刷」メニューを選び、「プリンター」で「JUST PDF 3」を選んでから、再度イミディエイト・ウインドウに

?Application.ActivePrinter

と入力して[Enter]してみると、今度は

f:id:akashi_keirin:20180428105900j:plain

JUST PDF 3 on Ne03:

が返っている。

このことを踏まえて、コーディングしてみた。

軽量PDFを出力するメソッド

リスト1 標準モジュール宣言セクション
Private Const MAIN_PRINTER As String = "Canon MP470 series Printer on Ne06:"
Private Const PDF_PRINTER As String = "JUST PDF 3 on Ne03:"

まず、プリンタ名を定数にしておく。これはあくまでもウチの環境のもの。環境が変わったら、その環境でのプリンタ名を調べて、定数を書き換えたらよい。

スト2 標準モジュール
Public Sub convertActiveSheetToLightPDF()
  Dim Sh As Worksheet
  Set Sh = ActiveSheet
  With Application
    Dim tmp As String    '……(1)'
    tmp = .ActivePrinter
    Dim printerNameLength As Long
    printerNameLength = InStrRev(tmp, " on ") - 1
    tmp = Left(tmp, printerNameLength)
    If tmp <> Left(PDF_PRINTER, printerNameLength) Then    '……(2)'
      .ActivePrinter = PDF_PRINTER
      Sh.PrintOut
      .ActivePrinter = MAIN_PRINTER
    Else
      Sh.PrintOut
    End If
  End With
End Sub

まず、(1)からの5行

Dim tmp As String    '……(1)'
tmp = .ActivePrinter
Dim printerNameLength As Long
printerNameLength = InStrRev(tmp, " on ") - 1    '……(*)'
tmp = Left(tmp, printerNameLength)

で、変数tmpに、ポートを除いたプリンタ名を格納している。

ポートは、「on ……」の形で表されるので、前後の半角スペースを含めた「 on 」で後ろから検索したら〈プリンタ名+1文字〉目が返るので、返り値から1を引いた数をプリンタ名の文字数として変数printerNameLengthに格納する。

あとは、Left関数で切り出しているだけ。

なぜこんなことをするのか。

実は、Application.ActivePrinterの返り値では、ポートの所が「Ne0X:」となっているんだが、ネットワーク上のプリンタの場合、ポートの所を「Ne0X:」で指定するとエラーになる(プリンタのIPアドレスで指定しないとダメ。「JUST PDF 3」の場合だと、「on JUST PDF 3 Port:」と指定する)。

しかも、ウチみたいにプリンタをジカで接続している場合は、逆にポートを「Ne0X:」で指定しないとエラーになるんである(たとえば、「Canon MP470 series Printer on Ne06:」を「Canon MP470 series Printer on USB001:」にするとエラーになる)。

従って、プリンタ名を比較するときにApplication.ActivePrinterの返り値をそのまま使うことができないのだ。

そんなわけで、仕方なく純粋なプリンタ名だけをまず切り出して、比較しているのだ。

あとは、(2)以下の7行

If tmp <> Left(PDF_PRINTER, printerNameLength) Then
  .ActivePrinter = PDF_PRINTER
  Sh.PrintOut
  .ActivePrinter = MAIN_PRINTER
Else
  Sh.PrintOut
End If

でメインの処理を行う。

プリンタがJUST PDF 3でなかったら、一旦プリンタをJUST PDF 3に切り替えてからプリントアウトし、メインのMP470に戻す。

もともとプリンタがJUST PDF 3になっているのなら、単純にプリントアウトするだけ。

使ってみる

f:id:akashi_keirin:20180428105908j:plain

こんなシートを準備して実行する。

ただし、JUST PDF 3の設定がデフォルトのままだと、保存先を指定したり、ファイル名を指定したりせにゃんらんわ、PDF化後、PDFファイルが開いてしまうわ、といろいろめんどくさいので、コントロールパネルのプリンタの設定のところへ行って、

f:id:akashi_keirin:20180428105916j:plain

f:id:akashi_keirin:20180428105923j:plain

こんなふうに設定しておくとよい(「保存先」は、好きなところを指定したらよい)。

これで黙って指定したフォルダにPDFを保存してくれる。

f:id:akashi_keirin:20180428105932j:plain

フォルダ内の「test.pdf」というのが、できあがったPDF。ファイルサイズは15KB。もう一つの「hoge.pdf」というのが、同じシートをWorksheet.ExportAsFidxedFormatメソッドでPDF化したもの。38KBもある。

おわりに

Application.ActivePrinterプロパティの指定の仕方(プリンタのポートの所)なんかがまだよく分からないのだが、まあ、個人使用する限りでは使えるレベルかな。

追記

f:id:akashi_keirin:20180501220411j:plain

コントロール・パネルのプリンタのところでJUST PDF 3 のアイコンを右クリックして「印刷設定」を選び、

f:id:akashi_keirin:20180501221118j:plain

「詳細セットの選択・設定」をクリック。

f:id:akashi_keirin:20180501220432j:plain

「フォント」のドロップダウンリストで「埋め込まない」を選択すると、

f:id:akashi_keirin:20180501220442j:plain

なんと、わづか3KBに!!!!!!!!

追記

実は、Application.ActivePrinterの返り値では、ポートの所が「Ne0X:」となっているんだが、ネットワーク上のプリンタの場合、ポートの所を「Ne0X:」で指定するとエラーになる(プリンタのIPアドレスで指定しないとダメ。「JUST PDF 3」の場合だと、「on JUST PDF 3 Port:」と指定する)。

と書いていたのだが、現在、自宅(USBジカ付け)でも職場(ネットワーク上)でも「[プリンタ名] on Ne0X:」の形でフツーに指定できている。何がどうなっているのか、よく分からないのだが……。

akashi-keirin.hatenablog.com

プリンタ名の取得については、コチラもどうぞ。

メソッド呼び出し時にモジュール名の記述を強制する

画期的なアイディア(嘘)

akashi-keirin.hatenablog.com

このときに、

モジュール名を指定しないと使えない、とかだったら便利だと思ったんだけどなあ。

とか書いていたんだが、画期的な解決法が思い浮かんだので記しておく。

同一メソッドを持つダミーモジュール

akashi-keirin.hatenablog.com

このときにも述べたことだが、2つ以上のモジュールに同一名のメソッド(プロシージャ)があると、メソッド名だけでは呼び出すことができない。

この習性を利用する。

f:id:akashi_keirin:20180423212225j:plain

このように、FoobarというモジュールとFoobar_というモジュールを用意する。

どうでもいいコードなので、画像だけ載せる。

まず、Foobarモジュールがコチラ。

f:id:akashi_keirin:20180423212237j:plain

Foobarモジュールのコードはコチラ

んで、Foobar_モジュールがコチラ。

f:id:akashi_keirin:20180423212245j:plain

プロパティとかメソッドの中身だけ全部コメントアウトしてある。

使ってみる

次のコードで実行してみる。

リスト1 標準モジュール
Public Sub testFoobar()
  hoge
  Foobar.hoge
  Foobar.fuga
End Sub

1行目は、単にhogeメソッドを呼んでいる。Foobarモジュールだけなら、フツーに実行できるはずだが、今回はFoobar_モジュールにもhogeメソッド(中身は空だけど)があるので、エラーになるはず。

2、3行目は、それぞれモジュール名を指定しているので、ちゃんと呼び出せるはず。

ちなみに、コード入力中は

f:id:akashi_keirin:20180423212300j:plain

このようにintellisenseが効く。便利。

実行すると、

f:id:akashi_keirin:20180423212306j:plain

予定どおりコンパイル・エラーになる。

hogeコメントアウトしてから再度実行すると、

f:id:akashi_keirin:20180423212315j:plain

f:id:akashi_keirin:20180423212328j:plain

意図どおり呼び出すことができた。当たり前だけれど。

おわりに

めちゃくちゃしょうもない対処法だけれど、これでメソッド呼び出し時にモジュール名付きで呼び出すことを強制できるのではなかろうか。

メソッド呼び出しに制約をかけることで、モジュールを多少細かく切り分けてもメインのコードの可読性が上がったりしないかなあ。

@akashi_keirin on Twitter

列挙体にはメンバを何個列挙できるか

列挙体はいくつまで列挙できるか

列挙体のメンバ数の最大って、いくつなのだろうか。

ちょっと調べてみた。

Integer型の最大値

Integer型の最大値は32767。へえ。意外と小さいな。

標準モジュールの宣言セクションに次のように書いてみる。

Public Enum MaxSize
  maxElement = 32767
End Enum

んで、イミディエイトに

Debug.Print maxsize.maxElement

と打ち込んで[Enter]。

f:id:akashi_keirin:20180422072316j:plain

とりあえずクリア。

少なくとも3万2千768+1個までは列挙できるみたい。

Integer型の限界を超える

次に、Integer型の限界を超えられるかどうかテスト。

Public Enum MaxSize
  maxElement = 32767
  nextElement
End Enum

32767の次はあるのか。イミディエイトに次のように打ち込む。

Debug.Print maxsize.nextElement

んで、[Enter]。

f:id:akashi_keirin:20180422072324j:plain

フツーに「32768」が返った。

Long型の最大値

次はLong型でやってみる。

Public Enum MaxSize
  maxElement = 32767
  nextElement
  realMaxElement = 2147483647
End Enum

イミディエイトには次のように打ち込む。

Debug.Print maxsize.realMaxElement

んで、[Enter]!

f:id:akashi_keirin:20180422072332j:plain

ふええ! 「2147483647」が返ったよ……。

ということは、21億個以上列挙できるのか!

次はどうなるんだろう。

Long型の限界を超える

お次は、

Public Enum MaxSize
  maxElement = 32767
  nextElement
  realMaxElement = 2147483647
  nextRealMaxElement
End Enum

これまで同様、イミディエイトに

Debug.Print maxsize.nextRealMaxElement

と打ち込んで[Enter]。

f:id:akashi_keirin:20180422072338j:plain

ファッ!?

ま、ー2147483648???

ループして負の数の最小値に飛んで行ったのか!

……ということは……、

「21億個以上」どころか、43億個近く列挙できる

ということじゃないか!

えらいこっちゃなあ……。

ちなみに、

Public Enum MaxSize
  maxElement = 32767
  nextElement
  realMaxElement = 2147483647
  nextRealMaxElement
  nextNextRealMaxElement
End Enum

こうしておいて、イミディエイトに

Debug.Print maxsize.nextNextRealMaxElement

と打ち込んで[Enter]すると、

f:id:akashi_keirin:20180422072346j:plain

やっぱり、正の数の最大値まで行くと、負の数の最小値にループしてインクリメントしていく模様。

Long型の限界を超えるとどうなるか

そこで、

Public Enum MaxSize
  maxElement = 32767
  nextElement
  realMaxElement = 2147483647
  nextRealMaxElement
  nextNextRealMaxElement
  overFlowElement = 2147483648#
End Enum

としてみる。

「2147483648」と打ち込んで[Enter]を押したら、勝手に右端に「#」がついた。

イミディエイトに

Debug.Print maxsize.overFlowElement

と打ち込んで[Enter]。すると、

f:id:akashi_keirin:20180422072356j:plain

エラーになった。

おわりに

というわけで、列挙体のメンバ数の最大値は、(たぶん)

4294967296個

です。

@akashi_keirin on Twitter

「名前の定義」再入門[Excel]

名前の定義

セル範囲に名前を付ける方法

akashi-keirin.hatenablog.com

コチラにExcelVBAer (id:x1xy2xyz3) さんからコメントをいただいた。その中に

①名前の定義で以下の2つを定義
ActCell = INDIRECT(ADDRESS(ROW(),COLUMN()))
UpCell = OFFSET(ActCell,-1,0)

というのがあって、「ん?」となったのだ。

というのも、私は、セル範囲に名前を付ける方法として

セル範囲を選択してから左上の名前ボックスに名前を入力して[Enter]という方法しか知らなかった

のである!!!!!!!!

一瞬私が何を言っているのか分からなかったかも知れないが、これは紛れもない事実なのである!!!!!!!!(←江田島平八の自己紹介風)

というわけで、「名前の定義」に再入門してみた。

「名前の定義」をイチからやってみる

f:id:akashi_keirin:20180421070646j:plain

おなじみ、「数式」タブ。

f:id:akashi_keirin:20180421070655j:plain

「名前の管理」をクリック。ここまでは知ってた。

すると、

f:id:akashi_keirin:20180421070704j:plain

ほんまやwww 「新規作成」というのがあるじゃないか!

f:id:akashi_keirin:20180421070728j:plain

f:id:akashi_keirin:20180421070737j:plain

ちょっとやってみた。

[OK]をクリックすると、

f:id:akashi_keirin:20180421070746j:plain

www

「ち~んw」は不適切な名前っぽいw

気を取り直して、

f:id:akashi_keirin:20180421070804j:plain

これなら文句あるまい。

f:id:akashi_keirin:20180421070821j:plain

おおっ! ちゃんと名前が定義されておる!

おわりに

体系的に学んでいないので、あちこちに大穴が開いているということを再認識させられたのであった。MOSか何か勉強してみようかなあ。