その他

VBA「書き込みできません。」のエラー解消&再発防止方法[No79]

スポンサーリンク

今回の記事はSeleniumBasic(セレニウムベーシック)を例に紹介しますが、これ以外でも有効な情報だと思うので、参考までに是非最後まで読んで頂けたら嬉しいです。

エラーの詳細

エラー内容

  • Microsoft Visual Basic
  • 実行時エラー '70'
  • 書き込みできません。

エラー画像

エラー表示場面

このエラーは、ファイルが既に使われている(使用中、または開かれている)時に表示されます。

今回はVBAでSeleniumBasicを実行し、1回目の処理では発生しないが何故か2回処理を実行させると同エラーが表示されました。

そして、調べてみてもそのファイルは閉じているものと思っていました。

プログラム

紹介するプログラムは下記リンク先の記事のように、Webドライバーの実行ファイル(exeファイル)をローカルにコピーして使う場合のものです。

no image
selenium BasicのWebDriverバージョン管理について[No61]
seleniumBasicを用いるプログラムの保守性を高めるポイントに「WebDriverのバージョンを管理する」ことがあると思います。 seleniumBasicはWebDrive ...

エラーになる場合


Option Explicit

Dim testDriver As New Selenium.WebDriver

Public Sub sample_test01()
    'ChromeDriver | EdgeDriver | IEDriver | OperaDriver
    'FirefoxDriver | PhantomJSDriver
    'Dim testDriver As New Selenium.WebDriver
    Set testDriver = New Selenium.WebDriver
    
    Dim driverTargetPath:
    Dim driverOriginPath:
    
    'chrome(WebDriverまたはChromeDriver)の場合
    driverTargetPath = "D:\Selenium\WebDriver\Chrome\86_0_4_40_22\chromedriver.exe"
    driverOriginPath = Environ$("LOCALAPPDATA") & "\SeleniumBasic\chromedriver.exe"

    'MicrosoftEdge(WebDriverまたはEdgeDriver)の場合
    driverTargetPath = "D:\Selenium\WebDriver\Microsoft_Edge\85_0_564_63\msedgedriver.exe"
    driverOriginPath = Environ$("LOCALAPPDATA") & "\SeleniumBasic\edgedriver.exe"
    
    'Debug.Print Now & "driverTargetPath:" & driverTargetPath
    'Debug.Print Now & "driverOriginPath:" & driverOriginPath
    FileCopy driverTargetPath, driverOriginPath
    
    With testDriver
        'chrome(WebDriverまたはChromeDriver)の場合
        '.Start "chrome"
        
        'MicrosoftEdge(WebDriverまたはEdgeDriver)の場合
        .Start "MicrosoftEdge"

        .Get "http://www.yahoo.co.jp/"
        '.Quit
        .Close
    End With
End Sub

FileCopyのコピーのところでエラーになります。

エラーにならない場合


Option Explicit

'Dim testDriver As New Selenium.WebDriver

Public Sub sample_test02()
    'ChromeDriver | EdgeDriver | IEDriver | OperaDriver
    'FirefoxDriver | PhantomJSDriver
    Dim testDriver As New Selenium.WebDriver
    Set testDriver = New Selenium.WebDriver
    
    Dim driverTargetPath:
    Dim driverOriginPath:
    
    'chrome(WebDriverまたはChromeDriver)の場合
    driverTargetPath = "D:\Selenium\WebDriver\Chrome\86_0_4_40_22\chromedriver.exe"
    driverOriginPath = Environ$("LOCALAPPDATA") & "\SeleniumBasic\chromedriver.exe"

    'MicrosoftEdge(WebDriverまたはEdgeDriver)の場合
    driverTargetPath = "D:\Selenium\WebDriver\Microsoft_Edge\85_0_564_63\msedgedriver.exe"
    driverOriginPath = Environ$("LOCALAPPDATA") & "\SeleniumBasic\edgedriver.exe"
    
    'Debug.Print Now & "driverTargetPath:" & driverTargetPath
    'Debug.Print Now & "driverOriginPath:" & driverOriginPath
    FileCopy driverTargetPath, driverOriginPath
    
    With testDriver
        'chrome(WebDriverまたはChromeDriver)の場合
        '.Start "chrome"
        
        'MicrosoftEdge(WebDriverまたはEdgeDriver)の場合
        .Start "MicrosoftEdge"

        .Get "http://www.yahoo.co.jp/"
        '.Quit
        .Close
    End With
End Sub

エラーの仕組みと対策

エラーの仕組み

  • エラーになる「sample_test01」関数はグローバル変数を定義して「driver.Close」を行っているが、closeしているのはブラウザのみで、グローバル変数のwebDriverはプログラム処理後、閉じてない。その状態でもう一度「sample_test01」関数を実行すると「既に使っている」と判断されてエラーとなる。
  • このエラーはドライバー変数(オブジェクト)が、Global変数(グローバル変数)を使っている場合に起こりやすく、Local変数(ローカル変数)を使っている場合に起こり難い。スコープ範囲が重要となる。
  • エラーにならない「sample_test02」関数は「driver.Close」でブラウザを閉じただけで、webDriverは閉じていなくても、ローカル変数を利用している為(=グローバル変数を利用していない為)、関数終了時にwebDriverも破棄される。

対策

「driver.Close」はアクティブになっているタブを終了するだけですが、「driver.Quit」はすべてのタブを閉じてブラウザを終了することができるので、グローバル変数webDriverを利用する場合でも、「driver.Quit」でブラウザを閉じることとドライバーの破棄を行うことが可能です。


Option Explicit

Dim testDriver As New Selenium.WebDriver

Public Sub sample_test03()
    'ChromeDriver | EdgeDriver | IEDriver | OperaDriver
    'FirefoxDriver | PhantomJSDriver
    'Dim testDriver As New Selenium.WebDriver
    Set testDriver = New Selenium.WebDriver
    
    Dim driverTargetPath:
    Dim driverOriginPath:
    
    'chrome(WebDriverまたはChromeDriver)の場合
    driverTargetPath = "D:\Selenium\WebDriver\Chrome\86_0_4_40_22\chromedriver.exe"
    driverOriginPath = Environ$("LOCALAPPDATA") & "\SeleniumBasic\chromedriver.exe"

    'MicrosoftEdge(WebDriverまたはEdgeDriver)の場合
    driverTargetPath = "D:\Selenium\WebDriver\Microsoft_Edge\85_0_564_63\msedgedriver.exe"
    driverOriginPath = Environ$("LOCALAPPDATA") & "\SeleniumBasic\edgedriver.exe"
    
    'Debug.Print Now & "driverTargetPath:" & driverTargetPath
    'Debug.Print Now & "driverOriginPath:" & driverOriginPath
    FileCopy driverTargetPath, driverOriginPath
    
    With testDriver
        'chrome(WebDriverまたはChromeDriver)の場合
        '.Start "chrome"
        
        'MicrosoftEdge(WebDriverまたはEdgeDriver)の場合
        .Start "MicrosoftEdge"

        .Get "http://www.yahoo.co.jp/"
        '.Quit
        .Close
    End With
End Sub

備考

SeleniumBasicには「Selenium.chm」というユーザーマニュアルがあります。そちらに次のように書いてあります。

WebDriver.Close Method
Closes the current window.
翻訳:現在のウィンドウを閉じます。

Selenium.chm

WebDriver.Quit Method
Close the Browser and Dispose of WebDriver
翻訳:ブラウザを閉じてWebDriverを破棄します。

Selenium.chm

まとめ

今回はSeleniumBasicを例として取り上げましたが、他のことでも共通することが多いと思います。グローバル変数でオブジェクトを使う場合は必ず閉じたか、正しく終了できたか、破棄できているか、確認する必要があります。

雑記

今日10月28日は某国民的アイドルの卒業公演がありました。彼女はおよそ9年間グループに在籍したそうですが、小学校入学から中学卒業までと同じ期間と考えると相当長いことが一般人の私たちにもわかりますよね。

どんなものでも「卒業」というのは、今までありがとうという感謝の気持ちや、嬉しかったこと楽しかったことを回顧して懐かしむ気持ちなど、いろいろな感情がひしめき合うものだと思います。過去にはあんな大変なことも乗り越えてきたんだから今後何があっても大丈夫という自信もあれば、慣れ親しんだ環境から一人で一歩踏み出す不安もあるでしょう。

「習うは一生」ということわざの通り、新しいことを知り、身につけていくために、人は一生を通して、常に学び続けなければならいのだと私は思います。彼女が卒業を経て、また新たな環境に身を落として日々学び続ける彼女の姿を見守りながら、更なる飛躍を期待したいと思います。

最後までお付き合いいただきありがとうございます!

この情報が誰かの役にたてれば幸いです。

スポンサーリンク

-その他