[ 初めての方へ | 一覧(最新更新順) | 全文検索 | 過去ログ ]
『ThisWorkbookのMeについて』(ラビット)
お世話になります。
「VBAで、ThisWorkbookクラスモジュール内で、
Meを頭につける場合と、付けない場合のエラー処理結果の変化について」
原因がわからず、ご存知の方がいらっしゃったらご教示頂きたく思います。
処理概要としては、ExcelからAccessのaccdbファイルに、
ADOを用いて接続するものなのですが、
エラーのコールスタックの際に、下記のように、
Meに付ける場合と付けない場合(省略した場合)で、
Err.Descriptionの値が変わってしまいます。
(付けない場合が想定ケース)
結果としては、想定ケースも得られるので良いですが、
原因が分からないため、逆のパターンもあったり、
バージョン、環境によって異なるのではと思いました。
余計な部分は省略するため、ソースコードは変更してあります。
' *ThisWorkbookクラスモジュール
Private Sub Test_CallStack()
Dim shouldStop As Boolean
On Error GoTo Error
Call OpenDb ' 上記の場合(Meを付けない場合) ' エラー番号 : -2147467259 ' エラー内容 : ファイル 'C:\XXX\DB.accdb' が見つかりませんでした。 WithOutMe: Let shouldStop = True Call Me.OpenDb ' 上記の場合(Meを付ける場合) ' エラー番号 : -2147467259 ' エラー内容 : オートメーション エラーです。エラーを特定できません Exit Sub Error: Debug.Print CStr(Err.Number) & ":" & Err.Description If shouldStop Then Exit Sub Resume WithOutMe
End Sub
' DB接続処理
Public Sub OpenDb()
On Error GoTo Error
With CreateObject("ADODB.Connection") .Open "Provider=Microsoft.ACE.OLEDB.12.0;" _ & "Data Source=C:\XXX\DB.accdb" End With Exit Sub Error: Err.Raise Err.Number, , Err.Description
End Sub
思い当たる方がいらっしゃったら、
コメントを宜しくお願い致します。
< 使用 アプリ:Excel2007 & Excel2010、使用 OS:Windows Vista & Windows 7 >
meの出すOpenDbが失敗したというエラーか
OpenDb内のCreateObject("ADODB.Connection").Openの出すエラーか
の違いでは?
Dim m As Variant
Private Sub Test_CallStack()
Dim shouldStop As Boolean On Error GoTo Error Call Me.OpenDb ' 上記の場合(Meを付ける場合) ' エラー番号 : -2147467259 ' エラー内容 : ファイル 'C:\XXX\DB.accdb' が見つかりませんでした。 WithOutMe: Let shouldStop = True Call OpenDb ' 上記の場合(Meを付けない場合) ' エラー番号 : -2147467259 ' エラー内容 : ファイル 'C:\XXX\DB.accdb' が見つかりませんでした。 Exit Sub Error: Debug.Print m 'CStr(Err.Number) & ":" & Err.Description If shouldStop Then Exit Sub Resume WithOutMe End Sub
' DB接続処理
Public Sub OpenDb()
On Error GoTo Error
With CreateObject("ADODB.Connection") .Open "Provider=Microsoft.ACE.OLEDB.12.0;" _ & "Data Source=C:\XXX\DB.accdb" End With Exit Sub Error: m = Err.Number & Err.Description Err.Raise Err.Number, , Err.Description End Sub
とすれば、どちらもOpenDbの出すエラー内容を表示します。
(ウッシ) 2016/10/26(水) 08:35
当方、ADODB 含めて、DBハンドリング、全くの素人ですが、2つの Call 、 Me がついている、ついていないというほかに 1つめは ファイルがないというエラーが発生、それなのに 2つめでは それを また参照しようとしていますよね。 つまり、2つの Call は 実行時点の状況が異なるわけですよね。
Me をつけたから、どうこう ということではないと思います。
最初の Call に Me を付けるとどうなりますか? Me を付けないものと同じエラー、つまり ファイルが見つからない ということになると思うのですが。
想像ですけど、1つめの ファイルNFD の時点で、何かしら、その参照ポインタのようなオブジェクトが出来上がっている。 ファイルがないわけですから、その中身は正しくないわけですけど、2つめの処理では、参照ポインタが存在するので それを見に行こうとする。参照ポインタを頼りにオブジェクトアクセスをしようとしたら、たどり着けない。 なので、オートメーションエラーだと、これは ADODB に限らず、まま 発生する事象なので。
素人の想像です。間違っているかもしれません。
(β) 2016/10/26(水) 08:39
で、On Error によるトラップがネストし、おかしな事になっているのだから、プロシジャ内で発生したエラーを呼び元で得たいならば、以下の様にしてはいかがでしょうか。(または、判定用にiErrだけ返して、OpenDb内でエラー表示させてしまっても良いかと)
Private Sub Test_CallStack() Dim shouldStop As Boolean Dim iErr As Long Dim cErr As String
Call OpenDb(iErr, cErr) If iErr <> 0 Then Debug.Print iErr & ":" & cErr End If
Let shouldStop = True Call Me.OpenDb(iErr, cErr) If iErr <> 0 Then Debug.Print iErr & ":" & cErr End If End Sub
'DB接続処理 Public Sub OpenDb(iErr As Long, cErr As String) iErr = 0 cErr = ""
Err.Clear On Error GoTo sError With CreateObject("ADODB.Connection") .Open "Provider=Microsoft.ACE.OLEDB.12.0;" _ & "Data Source=C:\XXX\DB.accdb" End With
sExit: On Error GoTo 0 Exit Sub
sError: iErr = Err.Number cErr = Err.Description Resume sExit End Sub (???) 2016/10/26(水) 09:52
ここに載せたものでも、同様の結果を確認済みですが、
元々は、クラスモジュールにデータベース関連処理を閉じ込め、
そこの中で、メンバ変数として、
Private myConnection As ADODB.Connection
Private myRecordset As ADODB.Recordset
と、ADO(2.1)の参照設定済みで、宣言してあります。
DBの接続処理(OpenDb)も、メソッドとしてPublicで宣言してあります。
また、両方を1度に確認できるように、あえて、Resume 〜という
書き方をしましたが、どちらかをコメントアウトして
片一方だけで実行というのも検証済みで、同じ結果でした。
省いたつもりが、そのせいで別の要因を考えさせてしまい
申し訳ありませんでした。
>ウッシさん
ありがとうございます。
モジュール変数、グローバル変数でする方法、同じ結果でした。
こちらの方が確実な気もします。
βさん ありがとうございます。
そういう所まで考えておりませんでした。(浅いですね..)
???さん ありがとうございます。
一見、参照渡しでしょうかこれは。
時間が来てしまったので、仕事終わりにきちんとみて回答致します。
美しくない方法であるのは確か こちらについてもお話させて下さい。
(ラビット) 2016/10/26(水) 14:15
どうも、Meを付けた場合と付けない場合で挙動が違う気がします。
ウッシさんのおっしゃる、Meの出す呼び出し自体のエラーと、
Meを付けない場合は、呼び出し先のエラー内容が返ってくるということでしょうか?
これは初耳です。
以下のサンプルでもエラー内容は異なりました。
Private Sub Test_DivisionZero()
Dim shouldStop As Boolean
On Error GoTo Error
Call Me.DivisionByZero ' 上記の場合 ' エラー番号 : 11 ' エラー内容 : 0 で除算しました。 WithOutMe: shouldStop = True Call DivisionByZero ' 上記の場合 ' エラー番号 : 11 ' エラー内容 : 0で割られた!!! Exit Sub Error: Debug.Print CStr(Err.Number) & ":" & Err.Description If shouldStop Then Exit Sub Resume WithOutMe
End Sub
Public Sub DivisionByZero()
Dim num As Long
On Error GoTo Error
num = 1 / 0 Exit Sub Error: Err.Raise Err.Number, , "0で割られた!!!"
End Sub
また、最初に提示したソースコードの内容の件ですが、
今まで(といっても数か月すらありませんが...)、
Excel VBAを勉強してきて、
Public変数や参照渡しに関する否定的な意見を見てきました。
「変数のスコープは出来る限り、狭くした方が良い」
「I/Oを正しく追わないと予期せぬバグが云々...」
今回のものも、一つのプロシージャに収めれば、
On Error GoTo 〜も一つで済むのですが、
共通処理などは、別プロシージャに分けて管理したくなります。
趣味の範囲内ですが、やはり「上手い書き方」は
身に着けたいと思っております。
(その意味でこちらの"学校"は勉強になります)
複数のプロシージャにまたがるような場合として、
エラー処理は下記を参考にしておりました。
(h ttp://shuhho.hatenablog.com/entry/excelvba-102)
(h ttp://kazu-s-diary-2.cocolog-nifty.com/blog/2012/06/vbaerr-1d80.html)
上記のようなエラートラップはアブノーマルなのでしょうか。
(h ttp://yamav102.cocolog-nifty.com/blog/2015/05/post-8a80.html)
のように、最初にだけ記述する方法もありかなとは思っていますが、
皆さんの意見をお伺いしたいです。
(ラビット) 2016/10/26(水) 20:57
エラートラップは適切な場所に仕掛けて有効に使う。 これが大原則だと思っています。 仕掛ける範囲は、 ・回避できないエラー、 ・もしくは、回避しようとすると、多くのコードが必要で面倒 ・もしくは、多くのコードで回避しようとしても完全には回避できない ということが明白にわかるものに対して、範囲を絞って仕掛けるべきだと思っています。
そうではなく、自分の書いたコードにバグがあるかもしれない、だからエラートラップを掛けようというのは 「論外」であって、その極端な形、プロシジャの冒頭に On Error なんたら と書いておけば安心だねというのは もう、「邪道」だと思っています。
(β) 2016/10/26(水) 21:54
確かに、
サンプルコードでいうと、
0除算は割られる数が0かどうか事前にチェックすればよいですし、
データベースに接続するときもFileSystemObjectを使えば、簡単に事前に存在確認は出来ますね。
無闇には使用しないという方針で書いていこうと思います。
それと、
ThisWorkbook 内のMeの件は、
SheetやThisWorkbook 内の関数は、
他のモジュールから呼び出せないのだから、
イベントプロシージャしか書かない。普通は標準モジュールに書く
ということにして、他の勉強にあてます。
(ラビット) 2016/10/27(木) 07:27
>>イベントプロシージャしか書かない。普通は標準モジュールに書く
βも、オブジェクトモジュールにはイベント処理と、それに密接に関連するコードを書き、 ふつうは 標準モジュールに書きます。
でも、これは 人それぞれで、上級者さんは、わりあいと、オブジェクトモジュールを主として使い むしろ、標準モジュールは 例外 として使うかたが多いように思えます。
>>SheetやThisWorkbook 内の関数は、 >>他のモジュールから呼び出せないのだから、
呼び出せないことはありません。
以下のコードで、Test を実行してみてください。
ThisWorkbookモジュール
Sub wkbProc() MsgBox "WorkBook" End Sub
シートモジュール ("Sheet1"のシートモジュール)
Sub shtProc() MsgBox "Sheet" End Sub
標準モジュール
Sub Test() ThisWorkbook.wkbProc Sheets("Sheet1").shtProc End Sub
(β) 2016/10/27(木) 08:33
>ThisWorkbook.wkbProc
>Sheets("Sheet1").shtPric
こんな書き方も出来るんですね。(違和感はありますが)
誤って認識していました。失礼致しました。
これはPublicにしても、ちゃんと
頭にモジュール名をつけないとダメなようですね。
ありがとうございました。
この件はクローズ致します。
(ラビット) 2016/10/27(木) 17:35
[ 一覧(最新更新順) ]
YukiWiki 1.6.7 Copyright (C) 2000,2001 by Hiroshi Yuki.
Modified by kazu.