知らずに落ちると抜け出せなくなるVBAの落とし穴です。
- Saveメソッドで上書き保存したはずのブックが更新されていない
- 上書き保存なのに「同名ファイルを置き換えますか?」ダイアログがでる
あたりにお悩みの方は、この落とし穴に落ちていないかご確認ください。
発生する罠
WorkbookオブジェクトのSaveメソッドによる上書き保存は、
💾ボタンと同じ動きと見せかけて違う挙動になることがあります。
その違いは「読み取り専用ブックに対して実行したとき」に現れます。
手作業時、読み取り専用ブックで💾ボタンを押した際は、
「名前を付けて保存」ダイアログが表示されます。
対して、読取専用ブックにSaveメソッドを実行した場合は、
カレントディレクトリに同名で保存という動きになります。
カレントディレクトリとは「現在作業中のフォルダ」のことで、
Cドライブのドキュメントフォルダだったり、
最後に保存したフォルダになっていることが多いフォルダです。
VBAでは「CurDir」で調べることができますので、
気になる方はイミディエイトウィンドウに「?CurDir」と打ってみてください。
(空を返した場合はドキュメントフォルダという意味になります)
カレントディレクトリに同名で保存する場合は特にダイアログは出ず、
通常の上書き保存同様ノーメッセージで処理がなされます。
もし同名のファイルがカレントディレクトリにあった場合は、
このダイアログが表示され、
「いいえ」「キャンセル」のどちらを選んでもVBAのエラーになります。
この仕様は結構厄介で、カレントフォルダに同名ファイルがない場合は、
目に見える形では実行状況に変化が起きません。
ファイルを開いて更新するタイプのマクロを実行したのに、
ファイルは更新されず、しかもそれに気づきにくいという罠になります。
また、Thisworkbook.Saveで発生した場合は、
操作中のマクロブックが知らぬ間に別ブックになっているということになります。
共有サーバーを活用している会社で「読み取り専用」で開く可能性がある場合は、
この罠に十分に注意してください。
解決策
WorkbookオブジェクトのReadOnlyプロパティを使って、
ブックが読み取り専用かを判定してからSaveメソッドを実行すればOKです。
If Workbooks("Book1.xlsx").ReadOnly = False Then Workbooks("Book1.xlsx").Save End If
ただし、上記のコードを見るとわかりますが、
ReadOnlyがTrueだったらどうするの?問題があります。
目的が「ファイルの更新」であった場合は、
ここで判定しても時すでに遅しということですね。
つまりRaedOnlyの判定は、
「保存の直前」ではなく、「開いた直後」にやる必要があるということです。
' 開いた時点で読み取り専用か確認する Workbooks.Open "C:\~~\Book1.xlsx" If Workbooks("Book1.xlsx").ReadOnly Then MsgBox "編集ファイルが読み取り専用のため処理を中断します。" Exit Sub End If ~~~ブックを編集する処理 ' ←読み取り専用のままこれを実施しないで済む ' この上書き保存は必ず成功する Workbooks("Book1.xlsx").Save
同じくThisworkbookの場合は、
Sub Thisworkbookを更新するマクロ If Thisworkbook.ReadOnly = True Then MsgBox "このマクロは読み取り専用では実行できません。" Exit Sub End If
このようなプロテクトをかければOKです。
CloseメソッドのSaveChangeを使用した場合
ブックの上書き保存にはもう一つの方法があり、
Closeメソッドの第1引数SaveChangeをTrueにすることで、
「ブックを保存して閉じる」を実行することもできます。
Workbooks("Book1.xlsx").Close True
さてこのコードを読み取り専用ブックで実行するとどうなるかというと、
このときは手作業と同様「名前を付けて保存」ダイアログが開かれます。
こちらを使えば「無警告で勝手な動き」をされることはありませんので、
Saveメソッドの時よりは安全ですね。
しかし当然ですが、ファイルの更新に失敗している事実は変わりません。
こちらのメソッドを利用する場合もSaveメソッドと同様、
ReadOnlyの判定をブックを開いた時点で行っておきましょう。