[[20200219181222]] 『Workbook.Save(Close)と手作業での保存(終了)の違』(てゃ) ページの最後に飛ぶ

[ 初めての方へ | 一覧(最新更新順) | 全文検索 | 過去ログ ]

 

『Workbook.Save(Close)と手作業での保存(終了)の違い』(てゃ)

Workbook_BeforeSaveにおいてCurrentRegionが正しく取得できない不具合が発生しました。

色々試してみると手作業での保存時には問題ないが、Workbook.Saveを用いると発生することがわかりました。

以下の点について質問させてください。
1. Workbook.SaveからWorkbook_BeforeSaveを呼び出した際にはどのような制限がかかっていますか。
2. この制限を解消する(もしくは手作業での保存と判別する)方法はありますか。
3. Save以外にClose時にも発生していますが、ほかにどのような状況で発生しますか。

また、Workbook.Saveは別のブック(orアドイン)から呼ばれているらしく、
Workbook.Saveを使用しないというのは難しそうです。

よろしくお願いします。


 '----不具合再現用のコード------

 Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)

  With ActiveSheet
    .Range("A1").Value = "a"
    .Range("A2").Value = "b"

    Debug.Print .Range("A1").CurrentRegion.Address

  End With

 End Sub

 Sub SaveTest()
  ThisWorkbook.Save  '$A$2ではなく$A$1が表示される
 End Sub

< 使用 Excel:Excel2016、使用 OS:Windows10 >


再現しますね。
>手作業での保存と判別する
だけ気づいた一例
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
    Dim msg As String
    On Error Resume Next
    With ActiveSheet
        .Protect
        Debug.Print .Range("A1").CurrentRegion.Address,   'CurrentRegionプロパティは、保護されたワークシートでは使うことができません。
        .Unprotect
    End With
    If Err Then
        msg = "manual"
    Else
        msg = "code"
    End If
    Debug.Print msg
    Cancel = True
End Sub
 Sub SaveTest()
  ThisWorkbook.Save
 End Sub

(kazuo) 2020/02/19(水) 21:42


Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
    With ActiveSheet
        .Range("A1").Value = "a"
        .Range("A2").Value = "b"
        Debug.Print .UsedRange.Address(0, 0, 1, -1)
        Debug.Print Application.Range(.Range("A1"), _
                                      .Cells(.Rows.Count, "A").End(xlUp)) _
                                      .Address(False, False, xlA1, True)
    End With
End Sub

ん〜〜〜。
なんで、事前にA1とA2になにか入力したって解っているのに、
敢えて後から動的に取得しようとするのですか?

Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)

    With ActiveSheet.Range("A1:A2").Cells
        .Item(1).Value = "a"
        .Item(2).Value = "b"
        Debug.Print .Address(False, False, xlA1, True)
    End With
End Sub

何をどうしたいか、想像が付きません。。。。

(まっつわん) 2020/02/19(水) 22:55


 再現します。不思議です。

 別プロシージャで処理させようと、Application.Ontimeを仕込みましたが、
 そのメソッドもワークしなかったです。

 そうなると、何かユーザー定義に似たところもありますね。
 ※ただ、ユーザー定義では、これも出来ないので、全く同じとも言えないですけど。
               ↓
           .Range("A1").Value = "a"

 何か代替案を考えるにしても
 この事情が理解しにくいので考えが進まないです。
  ↓
 >Workbook.Saveは別のブック(orアドイン)から呼ばれているらしく、 
 >Workbook.Saveを使用しないというのは難しそうです

 > この制限を解消する(もしくは手作業での保存と判別する)方法はありますか。 
 CurrentRegion.Address が $A$1 なんてことは通常あり得ないから、
 その事実を以て簡易判定できるんじゃないですか?

(半平太) 2020/02/20(木) 09:11


ご回答ありがとうございます。

(kazuo)さん
やはりBeforeSave内で正しい値が取得できるかあらかじめ確認するしかなさそうですね...。
ありがとうございます。

(まっつわん)さん
すみません、言葉足らずでした。上記のコードはCurrentRegionが正しくないことを再現するためのコードですので、意味はありません。

(半平太) さん

>この事情が理解しにくいので考えが進まないです。
> ↓
>>Workbook.Saveは別のブック(orアドイン)から呼ばれているらしく、

こちらで作成したマクロ付きブックを配布し、各々の端末で利用してもらう想定です。
ですので、その人がどのようなアドインを入れているか、またどのようなブックを同時に開いているかは把握できない状況です。
(このブック以外は開くなとはなかなか言えません...)

例として、以下のようなマクロを含むブックを同時に開いていた場合は不具合が発生してしまいます。
このような状況でも正しく動作するor手作業での保存をさせる必要があります。

 '~同時に開いている別ブック.xlsm~

 '--さすがにこんなマクロはないと思いますが--
 Public sub 開いているブックを全部保存しちゃうぜマクロ()
   dim wb as workbook
   for each wb In Workbooks
     wb.Save
   next
 end sub

とりあえずは、BeforeSave内でCurrentRegionなどを呼び出して正しい値が返ってくるかで判定をしようと思います。また、こちらでもなにかわかりましたら追記いたします。

重ねてお礼申し上げます。
(てゃ) 2020/02/20(木) 12:44


 BeforeSaveはダメだったですけど、
 AfterSaveならCurrentRegionが取得できました。

 なので、ワンテンポ遅ればせながら、
 そのイベントを回復処理に利用する訳には行かないですか?

 ※CurrentRegionを使って何をしようとしているのか分からないので
   具体案まで行かないですけども。

(半平太) 2020/02/20(木) 19:13


 >(まっつわん)さん
 >すみません、言葉足らずでした。上記のコードはCurrentRegionが正しくないことを
 >再現するためのコードですので、意味はありません。

ならば、どのような場面で不具合がありましたか?
代替案も示しましたが、そちらでは不都合ですか?

単にブックを保存するのに、セル範囲に対する操作が関係する場面が思いつけません。
多分UsedRangeの方が使い勝手が良いような気がしますが。。。。
まさか、CSV形式で強制的に保存するためにシート上の数式を消そうとかしている????
(まっつわん) 2020/02/20(木) 22:23


(半平太)さん
AfterSaveでの処理も考えましたがこの場合は別ブックから「Workbook.Close」が呼ばれた場合にも同様の不具合が発生するため、見送っています。

 Workbook.Close '別ブックから呼び出し
 ↓
 Workbook_BeforeSave
 ↓
 Workbook_AfterSave '同様の不具合発生

閉じる際に保存させなければよいのですがあまりよろしくないですね...。

(まっつわん)さん
CurrentRegionをどうしても利用しなければならないわけではなく、CurrentRegionのみが使用できないならば別の方法で実装します。
ただし、kazuoさんのコードにもあるようにこの状態ではWorkshhetのプロパティ操作もできなくなっており、どのような操作で不具合が発生するのかわからない状態です。ですので、

 >1. Workbook.SaveからWorkbook_BeforeSaveを呼び出した際にはどのような制限がかかっていますか。
 >2. この制限を解消する(もしくは手作業での保存と判別する)方法はありますか。
 >3. Save以外にClose時にも発生していますが、ほかにどのような状況で発生しますか。

このような質問にいたしました。ご確認ください。

(てゃ) 2020/02/21(金) 12:31


 >このような質問にいたしました。ご確認ください。
ん〜。。。。りょーかいです。。。。

他のマクロからイベントが発生した場合、
一部のメソッド等が無視される仕様みたいです。

Ontimeメソッドとか、
オートフィルターとかも?

WorkBookのイベントで多いのかな?制限?

しかしながら、何が出来て何が出来ないかの文書は公開されてないのかな?

時々、英文の文書を探し出して提示してくれる強者がいたりするので、
そういう人が出てくるのを期待しましょう^^;
(まっつわん) 2020/02/21(金) 16:13


 > Workbook_BeforeSave
 > ↓
 > Workbook_AfterSave '同様の不具合発生

 なるほどです。

 AfterClose なんてイベントは無いですからねぇ。。
 お手上げです。

 私も一つ、手作業との判別方法を考えました。

 ※ Application.OnTimeが無視されるのを逆手にとって

     Dim calledByCode As Boolean

     On Error Resume Next
         Application.OnTime Now(), "TEST", , False
         calledByCode = Err.Number = 0
     On Error GoTo 0

(半平太) 2020/02/21(金) 19:27


 ↓じゃだめですか?

 Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
    Cancel = True
 End Sub

 Sub SaveTest()
    With ActiveSheet
        .Range("A1").Value = "a"
        .Range("A2").Value = "b"
        Debug.Print .Range("A1").CurrentRegion.Address
    End With
    '----
    ThisWorkbook.Save
 End Sub

(チオチモリン) 2020/02/21(金) 21:53


 検索結果。
 マイクロソフトも該当ページに書き写してくれればいいのに。

 一部省略してるので、気になる人はアドレスからどうぞ。
 898511はGoogleの翻訳を通してるので、原文が気になる人も。

 ******************************************************************************************************************************************************
 https://poweraddress.jp/help/term/menu-command/

 メニューから実行する命令、操作、処理
 メニューコマンド(menu command)とは、メニューバーやコンテキストメニューから実行する命令のことです。
 モードの違いやフィールド選択状態などにより、使用できる項目が変わります。

 メニューバーには、[ファイル] や [編集] など、実行可能な命令のメニューが表示されています。
 例えば、メニューバーの [編集] には、[コピー] や [貼り付け](Mac では [ペースト])などのメニューコマンドがあります。
 ******************************************************************************************************************************************************
 https://oshiete.goo.ne.jp/qa/3611324.html

 エクセルでWorkbook_BeforeSaveイベントについての疑問

 質問者:merlionXX質問日時:2007/12/19 16:04回答数:3件
 エクセル2000です。
 http://odn.okwave.jp/qa3608360.html の関連質問ですが、これだけでも結構ですのでなにとぞご教示ください。

 標準モジュールに以下の3つのマクロを書きました。

 Sub text_表示()
 MsgBox "表示させました。"
 End Sub

 Sub text_非表示()
 MsgBox "非表示にしました。"
 End Sub

 Sub 保存()
 ActiveWorkbook.Save
 End Sub

 ThisWorkBookモジュールにこう書きました。

 Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
 Call text_表示
 Application.OnTime Now, "text_非表示"
 End Sub

 これで、手動でBOOKを保存すると、まず、MsgBox "表示させました。" そして MsgBox "非表示にしました。" が実行され、当然ながら書いたとおりの働きをします。
 ところが、Sub 保存() でマクロからBOOKを保存すると、MsgBox "表示させました。" だけが実行され、MsgBox "非表示にしました。"は実行されません。
 どうして、Application.OnTime Now, "text_非表示"は無視されたのでしょうか?

 ------------------------------------------------------------------------------------------------------------------------------------------------------

 No.2

 回答者: KenKen_SP 回答日時:2007/12/20 00:23
 仕様でしょう。

 プログラムでブックを保存する場合、BeforeSave イベント内に記載された
 メニューコマンドは無視されるようです。

 恐らく、別スレッドで保存コマンドが実行(非同期)されるようですから
 保存中にブックに変更があっては困るからではないかと。(推測・未確認)
 それで、そのような可能性のあるコマンドは無効化されるのでしょうね。

  # Msgbox は大丈夫なんですね。まあ、ブックに変更を与える可能性は
  # ありませんが、、中途半端だ。

 機械翻訳
 http://support.microsoft.com/kb/898511/ja?spid=1743&sid=434
 原版
 http://support.microsoft.com/kb/898511/en-us

 余談: 上記 URL の回避策を引用
 この問題を回避するために、手動でブックを保存します。

 ( ゜д゜)ポカーン
 ******************************************************************************************************************************************************
 https://web.archive.org/web/20140310215514/http://support.microsoft.com/kb/898511

 プログラムでExcelでブックを保存すると、BeforeSaveイベントでメニューコマンドが実行されません。

 記事ID:898511-この記事が適用される製品を表示します。

 症状
   プログラムでブックをMicrosoft Excelで保存します。
   BeforeSaveイベントなどのイベントでメニューコマンドを実行しようとすると、メニューコマンドは実行されません。

 原因
   この問題は、プログラムで[保存]コマンドなどのメニューコマンドを呼び出すときに、メニューコマンドをコードにネストできないために発生します。
   BeforeSaveイベントのメニューコマンドは、実行が明確にブロックされます。

 回避策
   この問題を回避するには、ブックを手動で保存します。ブックを手動で保存するには、実行しているExcelのバージョンに応じて、次のいずれかの手順を使用します。
     ・Microsoft Office Excel 2007で、Microsoft Officeボタンをクリックし、[保存]をクリックします。
     ・Microsoft Office 2003およびそれ以前のバージョンのExcelでは、[ファイル]メニューの[保存]をクリックします。
   この回避策を使用すると、BeforeSaveイベントで次のメニューコマンドのいずれかを実行できます。
     ・[書式]メニューの任意のコマンド。
     ・[編集]メニューの[クリア]コマンド。
     ・[編集]メニューの[削除]コマンド。
     ・[編集]メニューの[行の削除]コマンド。
     ・[ファイル]メニューの[印刷]コマンド。

 MORE INFORMATION
   マイクロソフトは、明示または黙示の保証なしに、例示のみを目的としてプログラミング例を提供しています。
   これには、商品性または特定の目的への適合性の暗黙の保証が含まれますが、これに限定されません。
   この記事は、デモされているプログラミング言語と、プロシージャの作成とデバッグに使用されるツールに精通していることを前提としています。
   マイクロソフトのサポートエンジニアは、特定の手順の機能の説明を支援できますが、これらの例を変更して追加機能を提供したり、特定の要件を満たす手順を構築したりすることはありません。

   この問題を再現するには、実行しているExcelのバージョンに応じて、次の手順を実行します。

   Excel 2007
      1. Excelで新しいブックを開きます。
      2. [Microsoft Office]ボタンをクリックし、[名前を付けて保存]をクリックし、[ファイルの種類]ボックスで[Excelマクロ有効ワークブック(* .xlsm)]をクリックし、[ファイル名]ボックスにファイル名を入力し、[保存]をクリックします。
      3. セルA1にテキストを入力し、Enterキーを押します。
      4. [開発者]タブをクリックします。 [開発]タブが表示されない場合は、次の手順を実行します。
         a. [Microsoft Office]ボタンをクリックし、[Excelオプション]をクリックします。
         b. [人気]をクリックします。
         c. [リボン]チェックボックスの[開発者の表示]タブをクリックします。
         d. [OK]をクリックして、[Excelオプション]ダイアログボックスを閉じます。
      5. [コード]グループの[Visual Basic]をクリックして、Visual Basic Editorを起動します。
      6. [プロジェクト-VBAProject]ウィンドウで、[ThisWorkbook]をダブルクリックします。
      7. 次の手順を実行します:
         a. [オブジェクト]ボックスで、[Workbook]をクリックします。
         b. [プロシージャ]ボックスで、[BeforeSave]をクリックします。
         c. BeforeSaveイベントに次のマクロコードを追加します。

            Range("a1").ClearContents

         d. 手順7cで入力したマクロコードの下に、次のマクロコードを入力します。

            Public Sub Test()

               ThisWorkbook.Save

            End Sub

      8. ブックで、Microsoft Officeボタンをクリックし、[保存]をクリックします。
         セルA1のテキストはクリアされます。
      9. [キャンセル]をクリックして、[名前を付けて保存]ダイアログボックスを閉じます。
     10. セルA1にテキストを入力し、Enterキーを押します。
     11. [開発]タブで、[コード]グループの[マクロ]をクリックします。
     12. ThisWorkbook.Testマクロをクリックして、[実行]をクリックします。

         ブックが保存されると、セルA1のテキストはクリアされません。

     プロパティ
     記事ID:898511-最終レビュー:2007年1月20日-改訂:4.2

     APPLIES TO
     Microsoft Office Excel 2007
     Microsoft Office Excel 2003
     Microsoft Excel 2002 Standard Edition
     Microsoft Excel 2000 Standard Edition

(2u) 2020/02/25(火) 19:47


(2u)さん

ありがとうございます。非常に助かります。

特定のメニューコマンドは実行できないんですね。
確かにフォントの変更やオートフィルタも利用できませんでした。
(フォントの色変更やコメント追加などは動作しますので何が正しく動作するのかはわかりませんが。)

Microsoftも別段有用な回避策を提示していませんのでこちらとしても諦めがつきます。

ご回答いただいた方々、本当にありがとうございます。
また何かありましたらよろしくお願いいたします。

(てゃ) 2020/02/26(水) 12:39


コメント返信:

[ 一覧(最新更新順) ]


YukiWiki 1.6.7 Copyright (C) 2000,2001 by Hiroshi Yuki. Modified by kazu.