[[20240205135924]] 『エラー処理について』(やわら) ページの最後に飛ぶ

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

 

『エラー処理について』(やわら)

お世話になっております。

エラー処理が分からず、質問させてください。

 元々あるExeにbat経由で引数を渡して実行させるコードがあります。
(元コードは別人が書いたもので、改変中)

 〜前略〜
    'Make Batch File
    BatchFile = "make_wave.bat"
    TxtFile = "raw2wave_param.txt"
    Open BatchFile For Output As #1
        Print #1, Cells(5, 4).Value; " ";
        Print #1, Cells(6, 4).Value; " ";
        Print #1, CsvFile; " ";
        Print #1, TxtFile; " ";
        Print #1, ""
    Close #1

    'Execute Batch
    Call BatchExec(BatchFile)
    Kill BatchFile
    Kill TxtFile
 〜中略〜
    CsvFile = "temp.csv"

    On Error Resume Next

    Set WbTemp = Workbooks.Open(Filename:=CsvFile)

    If Err.Number <> 0 Then
        MsgBox "Pathかファイル名のどちらかが間違っています"
        Set WbTemp = Nothing
        Err.Clear
        End
    End If
 〜後略〜

 D5はExe名、D6はD2(パス)とD3(ファイル名)を合体させたものが記入されていて
D6のフルパスが間違っているとワークブックオープン時にエラーを吐きます。

 一回エラーが出てしまうと、何かを掴んだままになっているようで
再度正しいパスに直して実行してもエラーが出たままになってしまいます。
(エクセル開きなおせば正常実行できる)

 そのため、エラートラップの中にエラークリアとEndを入れて抜けたらいいのかな?と思ったのですが
やはり一度エラーが起きると正しいパスでもMsgBoxが出てしまいます。
なにか認識が誤っているのでしょうか?なぜ何回もエラーが出るのかもわからず…
お力添えいただければ幸いです。よろしくお願いいたします。

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


 開く前に
 存在するか、
 既に開いていないか、
 を確認する
 では?
(tkit) 2024/02/05(月) 14:50:40

 > 〜中略〜
 >    CsvFile = "temp.csv"

 そこをちゃんとしたフルパスにしたらどうですか?

(半平太) 2024/02/05(月) 15:13:20


すみません、返信遅くなりました。
tkitさんの案は確かにそうだなと思ったので実施します!
半平太さんの案は、temp.csvのパスをフルパス渡すということですよね?そちらもやってみます。
(やわら) 2024/02/07(水) 13:23:49

試してみました。
半平太さんの案はやはりその前後の処理で何か掴んでしまうようで、ダメでした。
(それはそうとフルパス指定したほうが安定動作しそうなので記載はしておきます)
tkitさんの案で、前略している一番先頭のところに存在チェックいれたら一度失敗しても動くようになりました!ありがとうございました。
(やわら) 2024/02/07(水) 13:36:12

 なんか納得いかないですねぇ。

 >CsvFile = "temp.csv"
            ~~~↑~~~~~
       これをフルパスにするんですけど、やりましたか?

 あと、そのフルパスが正しくなければ話になりませんけども、そこは大丈夫ですか?

(半平太) 2024/02/07(水) 13:49:01


D2に作業パスが記入されているので、CsvFile = Range("D2") & "\temp.csv"
で対応しました。
そのうえで、パス間違っているもの→パスが正しいもの の実施順で実施しましたが
挙動変わらなかったです。

あとこれは質問の際に私が端折ってしまったのですが
本当はCsvFile = Range("D2") & "\temp.csv"は〜前略〜のmain関数に書かれており、
かつ複数関数にわたって使用されているのでパブリックになります。
このあたりが原因でしたら申し訳ありません。
(やわら) 2024/02/07(水) 14:00:29


 >エラー処理が分からず、質問させてください。

 エラー処理を勘違いされていませんか?

 On Error Resume Next で実行エラーを無視することが、
 エラー処理ではありません。

 想定される実行エラーにならないように
 順を追って確認、判定し、実行エラーになる処理を
 しないことが望ましいです。

(tkit) 2024/02/07(水) 14:02:13


 >あとこれは質問の際に私が端折ってしまったのですが

 エラーの原因を探ろうと言うのに、状況説明が正確じゃなければ
 こちらはミスリードされるだけです。私は、ここまでとします。m(__)m

 何らかの反応は不要です。

(半平太) 2024/02/07(水) 14:28:35


tkitさん、理解はしていたのですが言葉の使い方が悪く申し訳ないです。
そもそもエラーを起こさないように気を付けます。
半平太さんもせっかく回答いただいたのに申し訳ないです。ありがとうございました。
(やわら) 2024/02/07(水) 16:32:09

似たようなことでさんざん悩んだことがあり気になってしまったのですが、
あまり時間をかけて考えることはできないので投げっぱなしで去ります。
参考にならなかったら申し訳ないです。

※jw_cad for windowsという図面作成用のソフト関係の掲示板等で
いろいろいと教えていただきながら作ったコードの
jww専用の記述でわかりやすい部分のみ削除し、
他はほとんど触らずコードを這っているので、関係ない部分も多いです。

エクセルをマクロ有効で起動してExcelが終了するまでbatファイルを待機するコードです。
visiblはExcelブックを非表示にするのに使っています。
(実際はfalseにして、excel非表示で起動し、excelの起動時の処理でユーザーフォームを起動させてます)

batファイルに記述

@if(0==1) //線幅線色変更
@echo off
wscript //nologo //e:jscript %~f0
goto:eof

@else
var BookName = "EJ線幅線色変更.xls"
with (new ActiveXObject("Scripting.FileSystemObject")) {

  var BookPath = BuildPath
    (GetParentFolderName(WScript.ScriptFullName),BookName);
}
with (new ActiveXObject("Excel.Application")) {
  Visible = true;
  try {
    var myBook = Workbooks.Open(BookPath);
  } catch(e) {
    // ブックが存在しない場合
    Visible=true;
    WScript.Echo(e.description + "(Error No=" + (e.number & 0xFFFF) + ")");
    WScript.Quit();
  }
  var flg = new Boolean(false);
  while (flg == false){
    try {
      with (myBook) {
        while (true) {
          WScript.Sleep(100);
        }
      }
    } catch(e) {
      if( e.number == -2147417848 ) {
         flg = true;
       }
    }
  }
   myBook=null;
}
@end

同様にbatファイル→vbsファイル→excelで起動し、
excel終了まで待機させるコードは下記です。
※vbsファイルと同じフォルダにあるvbsファイルと同名のexcelを起動になっています

batファイル

@REM Excel置換2
@echo off
start /wait Excel置換2.vbs
REM #e
:END

vbsファイル
Option Explicit
Private FSO, VbsObject , VbsPath , VbsName, FolderPath, BookName, BookPath, wb, flag
Set FSO = CreateObject("Scripting.FileSystemObject")
Set VbsObject =FSO.GetFile(WScript.ScriptFullName)
VbsName = VbsObject.Name
FolderPath = VbsObject.ParentFolder
BookName = LEFT(VbsName,LEN(VbsName)-4) & ".xls"
BookPath = FolderPath & "\" & BookName
If FSO.FileExists(BookPath) then

  With CreateObject("Excel.Application")
    .Visible = true
    .Workbooks.Open(BookPath)
    On Error resume Next
    Do 
      flag = false
      For Each wb In .Workbooks
        If wb.Name = BookName Then flag = True
      Next
      If flag = true Then
        WScript.Sleep 500
      Else
        Exit Do
      End If
    Loop 
    On Error goto 0
  End With
Else
  Wscript.echo BookPath & vblf & " が存在しません。"
End IF
(O.M) 2024/02/08(木) 21:07:37

投稿後に読み返して気が付いたのですが、
話題になっているのにvbsファイルでOn Error使ってました…すみません。

If FSO.FileExists(BookPath) thenで存在確認はしているのでなくても大丈夫なはずですが、
WScript.Sleepをひたすら繰り返すのでなんとなく怖くて保険的な意味合いでつけて
外せていないコードでした。
(O.M) 2024/02/08(木) 21:28:13


関係ないかもしれませんがbatファイル経由でexcelを起動した際に
Excel画面が他の画面の下にもぐってしまって見えなくなってしまっているのを、
起動していないと勘違いしてる可能性はないでしょうか?
もしそうでしたらExcelのハンドル取得→解除として表に出すことができました。
※個人的にずっと悩んでいて先日やっと解決できた件だったので頭に浮かんでしまいました。

関係なくてもExcelが画面の後ろに隠れて困っている場合には便利だと思うので記載しておきます。

64bitのExcelのLongptrとLongLongの使い分けがわかっておらずlongのままにしている部分が多いですが、

batファイルからExcelのブックを起動するならThisWorkbookに

'★Excelを前面に表示
#If Win64 Then

  Private Declare PtrSafe Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As LongPtr
  Private Declare PtrSafe Function SetWindowPos Lib "user32" _
  (ByVal hWnd As LongPtr, ByVal hWndInsertAfter As LongPtr, ByVal x As Long, ByVal y As Long, ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long) As Long
#Else
  Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
  Private Declare Function SetWindowPos Lib "user32" _
  (ByVal hWnd As Long, ByVal hWndInsertAfter As Long, ByVal x As Long, ByVal y As Long, ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long) As Long
#End If

Const HWND_TOP As Long = -1 '前面に表示
Const HWND_TOPMOST As Long = -1 '常に前面に表示
Const HWND_BIHIND As Long = 1 '後面に表示
Const HWND_CANCEL As Long = -2 'キャンセル
Const SWP_NOSIZE As Long = 1
Const SWP_NOMOVE As Long = 2

Private Sub Workbook_Open()
'★Excelを前面に表示

  #If Win64 Then
    Dim hWnd As LongPtr
  #Else
    Dim hWnd As Long
  #End If
  '最前面に表示するウィンドウのハンドルを取得(UserForm)
  hWnd = FindWindow("XLMAIN", vbNullString)
  'ウィンドウを常に最前面に配置
  Call SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE Or SWP_NOMOVE)
  'ウィンドウを常に最前面に配置解除
  Call SetWindowPos(hWnd, HWND_CANCEL, 0, 0, 0, 0, SWP_NOSIZE Or SWP_NOMOVE)
End sub

ユーザーフォームを起動する場合はユーザーフォームに

'★ユーザーフォームを前面に表示
#If Win64 Then

  Private Declare PtrSafe Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As LongPtr
  Private Declare PtrSafe Function SetWindowPos Lib "user32" _
  (ByVal hWnd As LongPtr, ByVal hWndInsertAfter As LongPtr, ByVal x As Long, ByVal y As Long, ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long) As Long
#Else
  Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
  Private Declare Function SetWindowPos Lib "user32" _
  (ByVal hWnd As Long, ByVal hWndInsertAfter As Long, ByVal x As Long, ByVal y As Long, ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long) As Long
#End If

Const HWND_TOP As Long = -1 '前面に表示
Const HWND_TOPMOST As Long = -1 '常に前面に表示
Const HWND_BIHIND As Long = 1 '後面に表示
Const HWND_CANCEL As Long = -2 'キャンセル
Const SWP_NOSIZE As Long = 1
Const SWP_NOMOVE As Long = 2

'ユーザーフォーム起動時設定
Private Sub UserForm_Initialize()

'★ユーザーフォームを前面に表示

  #If Win64 Then
    Dim hWnd As LongPtr
  #Else
    Dim hWnd As Long
  #End If
  '最前面に表示するウィンドウのハンドルを取得(UserForm)
  hWnd = FindWindow(vbNullString, Me.Caption)
  'ウィンドウを常に最前面に配置
  Call SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE Or SWP_NOMOVE)
  'ウィンドウを常に最前面に配置を解除
  Call SetWindowPos(hWnd, HWND_CANCEL, 0, 0, 0, 0, SWP_NOSIZE Or SWP_NOMOVE)
End sub

と記載したら前面に表示されました。
(O.M) 2024/02/08(木) 22:00:23


■1
横からですがちょっと確認。
 >本当はCsvFile = Range("D2") & "\temp.csv"は〜前略〜のmain関数に書かれており、
 >かつ複数関数にわたって使用されているのでパブリックになります。

↑の意味が分からないのですが、エラーが出るというのは↓ですよね?

 Set WbTemp = Workbooks.Open(Filename:=CsvFile)
                                       ~~~~~~~

ステップ実行して自己検証されたときに【CsvFile】に正しいフルパスが格納されていたのでしょうか?
お気軽に↓みたいに言ってますが、ブックもシートも指定してないので正しく格納されているのか気になります。

 CsvFile = Range("D2") & "\temp.csv"

(もこな2) 2024/02/09(金) 07:38:12


O.Mさん、ありがとうございます。
 コードは私には難しそうですが…似たようなというのは問題解決してるのにエラーが起きるとこですかね?
 ちなみに、Excel画面は最小化などされないので後者にかんしては大丈夫です。

もこな2さん

 もこな2さんと私の中で「正しい」フルパスの定義が合っているのかわからないため、前提からお話しさせてください。

 まずエラーが出る状態は
・D2セルが存在しないパス または
・D3セルが「D3セルのパスに」存在しないファイル名
のどちらかです。

 今回は、D2を存在しないパスに変更してエラーを起こす→存在するパスに変更してもエラーが起きる
ことを確認しています。(c:/hogehoge を C:/hogehog にして確認みたいな感じです)

 そのため、エラーが起きている状態は当然CsvFileの中身は正しいフルパスにはならないです。
ただ、存在するパスに戻したときは正しいフルパスになっています。
(やわら) 2024/02/09(金) 17:50:56

質問者独自の専門用語を使っているので、第三者には意味が分からないです。
(あれっ?) 2024/02/09(金) 18:35:31

■2
>エラーが起きている状態は当然CsvFileの中身は正しいフルパスにはならないです。
伝わっているかよくわかりませんが、↓のようなことを言っています。
    Sub 実験1()
        Dim フルパス As String
        Dim WbTemp As Workbook

        フルパス = "c:\存在しないフォルダ\temp.csv"
        On Error GoTo エラー処理
        Set WbTemp = Workbooks.Open(Filename:=フルパス)

        フルパス = "c:\存在するフォルダ\temp.csv"
        Set WbTemp = Workbooks.Open(Filename:=フルパス)

        Exit Sub

    エラー処理:
        MsgBox "ブックを開くのに失敗しました"
        Err.Clear
        Resume Next
    End Sub

全体の提示がないのでわかりかねますが、開こうとしているブック(というかcsvファイル)のパスが正しくなければそりゃ開けません
(なお、フォルダを指定しない場合はカレントフォルダを指定したことになります)

トピ主のいう「存在するパスに戻した」というのがどういうことがわからないので、【CsvFile】に正しいフルパスが格納されていたのか?と聞いています。

■3
ブックを開く前にファイルの存在チェックをするなら↓のようにするとよいです。
(ほかにも手はありますが、一番お手軽だと思います)

    Sub 実験用2()
        Dim フルパス As String

        フルパス = "c:\存在しないフォルダ\temp.csv"
        If Dir(フルパス) = "" Then
            MsgBox "フォルダまたはファイルが存在しません"
        Else
            MsgBox "指定されたファイルは存在します"
        End If

        フルパス = "c:\存在するフォルダ\temp.csv"
        If Dir(フルパス) = "" Then
            MsgBox "フォルダまたはファイルが存在しません"
        Else
            MsgBox "指定されたファイルは存在します"
        End If
    End Sub

(もこな2 ) 2024/02/09(金) 18:36:55


 > 今回は、D2を存在しないパスに変更してエラーを起こす→存在するパスに変更してもエラーが起きる
 > ことを確認しています。(c:/hogehoge を C:/hogehog にして確認みたいな感じです)

 D2に存在するパスを入れてもエラーが起きるのですね?
 その原因を知りたいのですね。

 これは他人に尋ねてもわかりません。デバッグはご自分がするしかありません。

 ご自分で
 ・D2には何が入っていて、
 ・そのときCsvFileは何になっているのか、
 ・それはどう作られたのか
 というのをご自分で確認するしかありません。

 お調べ下さい。

(xyz) 2024/02/09(金) 19:24:19


 CsvFileというのは、D2セルの値と、ファイル名とを連結しているんですか?
 これと
 CsvFile = "temp.csv"
 というコードとはどういう関係にありますか?
 CsvFile = "temp.csv"
 を後に実行しているなら、そのときのカレントフォルダのなかの"temp.csv"ファイルを探します。
 そのときそれが無ければ当然エラーになりますよ。

 そんなオチではないんですか?よく知らないけど。
(xyz) 2024/02/09(金) 19:36:59

 ちょっとだけお邪魔します。

 結果、本題とは関係ない「ツッコミ」になってしまいますが... (ご容赦ください ^^;)
 後々ココを訪れる方に向けての注釈的内容です。

 O.Mさんのコード中に出現する

 > #If Win64 Then

 このコンパイラ定数(計4か所)については、
 ディレクティブの内側に書いてある宣言文が「PtrSafeおよびLongPtrが書けるか否か」の分岐しか要しない内容なので

   #If VBA7 Then

 とするのが本来の意図に沿った条件分岐になります。
 で、
 「バージョン 7.0 互換ではない(2010よりもっと古いExcelを使う)かどうか」を判定する必要性も
 昨今かなり薄くなってきていると思われますので、アレだったら(そんな心配が無用なら)条件分岐自体不要です。

コンパイラ定数 (VBA) | Microsoft Learn
https://learn.microsoft.com/ja-jp/office/vba/language/concepts/getting-started/compiler-constants

 ついでに。
 Win64コンパイラ定数が必要になってくる場合の具体例を幾つか。

    '▼引数の指定方法を切り替える必要がある
    #If Win64 Then
        Declare PtrSafe Function WindowFromPoint Lib "user32" Alias "WindowFromPoint" (ByVal Point As LongLong) As LongPtr
    #Else
        Declare PtrSafe Function WindowFromPoint Lib "user32" Alias "WindowFromPoint" (ByVal xPoint As Long, ByVal yPoint As Long) As LongPtr
    #End If

    '▼そもそも関数名(エイリアス)を切り替える必要がある
    #If Win64 Then
        Declare PtrSafe Function GetWindowLongPtr Lib "user32" Alias "GetWindowLongPtrA" (ByVal hwnd As LongPtr, ByVal nIndex As Long) As LongPtr
    #Else
        Declare PtrSafe Function GetWindowLongPtr Lib "user32" Alias "GetWindowLongA" (ByVal hwnd As LongPtr, ByVal nIndex As Long) As LongPtr
        Declare PtrSafe Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hwnd As LongPtr, ByVal nIndex As Long) As Long
    #End If

    '▼使い方によって戻り値がDWORDだったりHWNDだったりする関数で取得したLongPtr値を、別の関数に渡す場合は64bitでは型変換を要したり...
    #If Win64 Then
        Private Type typeINT64
            Value As LongLong
        End Type
        Private Type typeINT32x2
            Value1 As Long
            Value2 As Long
        End Type
    #End If
    Declare PtrSafe Function AdjustWindowRect Lib "user32" Alias "AdjustWindowRect" (lpRect As RECT, ByVal dwStyle As Long, ByVal bMenu As Long) As Long

    ' 〜本文中〜
        Dim h As LongPtr, s As LongPtr, r As RECT
        s = GetWindowLongPtr(h, GWL_STYLE) '←64bitの場合sはLongLongで下位32bitに値が格納されるので、AdjustWindowRectには下位32bitだけ切り取って渡す
        Call GetClientRect(h, r)
        #If Win64 Then
            Dim t32x2 As typeINT32x2, t64 As typeINT64
            t64.Value = s
            LSet t32x2 = t64
            Call AdjustWindowRect(r, t32x2.Value1, False)
        #Else
            Call AdjustWindowRect(r, s, False)
        #End If

 とまあ、実際のところそれほど頻繁にはお世話にならないです。
 はい。お邪魔しました。

(白茶) 2024/02/09(金) 20:34:21


やわらさま
私の場合はbookがない場合はエラーメッセージを出して
batファイルが終了し、
ある場合は起動するので問題なく動いています。

内容ははっきりと把握できていないのですが、
どのようなことをしたいのかという全体の流れや、
なぜbatファイルやExcelやcsvファイルを使用するかなどのご説明があれば
別方向からの解決策の回答が
皆様からいただきやすいのではないかという気がしました。

ぱっと見でbatファイルを作って作ったbatファイルを起動して削除といった
動作があるような気がするのですが(間違っていたらすみません)
そのようにする理由が想像できませんでした。

白茶さまありがとうございます。
64bitのクリップボードがわからず検索してこちらのサイト様にたどり着いていたので
そちらと合わせて質問スレッドを立てました、申し訳ございません。
https://www.excel.studio-kazu.jp/kw/20240209215611.html

(O.M) 2024/02/09(金) 23:02:31


みなさま、ありがとうございます。
 書いたつもりの前提を私がどこにも書いていませんでした。すみません。

 エラーが出るのは、 Set WbTemp = Workbooks.Open(Filename:=CsvFile) です。

 CsvFileは、マクロ実行時に毎回新規に作成します(そして毎回消します)
 CsvFileの中身は、D6(D2&D3連結)で指定したファイルの解析結果です。
 元々その作成するにあたって、D2かD3が間違っていると解析結果が出せないため
 それに伴いCsvFileが作成できず、エラーが出て止まっていました。

 問題は、D2とD3の値どちらかが間違っている(存在しないファイルを解析対象に設定している)状態で実行してエラーが出る
 →D2とD3を正しく直す(存在するファイルを解析対象にする)をしても、CsvFileが作成できない現象が継続する、ということです。
 D2とD3が正しく指定されている状態 = CsvFileも正しいパスに正しく生成できる状態
 なのに、なぜか動かない という相談でした。
 そのままの状態で保存して、再度開いてマクロを実行したらマクロは正常に動きます。という状態で
 一度エラーが起きると二度と動かないのでどうしたらいいですか?という意味でした。

 現状は、上で申し上げた通りD6に指定しているファイルが存在するかを確認してから
 次の処理を実行するようにしているので、問題なくなりました。ありがとうございます。
 (元々の正しいファイルをしてもエラーが出続ける原因は解決していないですが、コードも存在しないexeの中の処理が原因っぽいのでどうしようもないなと感じています)
(やわら) 2024/02/16(金) 18:28:26

コメント返信:

[ 一覧(最新更新順) ]


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