WebアプリケーションからのExcel操作について

タグの編集
投稿者 たくみ  (学生) 投稿日時 2020/4/12 09:59:00
はじめまして。

下記の開発環境でExcelファイルの操作について勉強をしているのですが、
C:\inetpub\wwwroot\プロジェクト名 に配置したExcelファイルを開くことができません。

Visual Studio のデバッグ段階では開くのですが、テスト用に(localhost)に公開し開こうとしますと
「オブジェクト変数または With ブロック変数が設定されていません。」と表示されてしまいます。

初歩的な質問で申し訳ありませんが、よろしくお願いします。


※ 開発環境 
 Microsoft Visual Studio Community 2019 VisualBasic
 OSは、Windows10 
 PCにインストールされている Excelは、Office365です 
  ※Excelファイルは、C:\inetpub\wwwroot\プロジェクト名 に配置しています

※以下、コードの抜粋です

        Dim objExcel As Object = Nothing                       'Excel Application
        Dim objWorkBooks As Object = Nothing                  'Excel WorkBooks
        Dim objWorkBook As Object = Nothing                   'Excel Workbook
        Dim objSheets As Object = Nothing                     'Excel WorkSheet
        Dim objSheet As Object = Nothing                      'Excel WorkSheet
        Dim objRange As Object = Nothing                      'Excel Range
        Dim _xlsFile As String = Server.MapPath("~/prt/表.xlsx")

        Try
            'EXCEL オープン処理
            objExcel = CreateObject("Excel.Application")
            objWorkBooks = objExcel.Workbooks
            objWorkBook = objWorkBooks.Open(_xlsFile)
            objSheets = objWorkBook.Sheets
            objSheet = objSheets("01")

            'EXCEL 出力処理
            With objSheet
                .PrintOut(Copies:=1, Collate:=True)
            End With






投稿者 るきお  (社会人) 投稿日時 2020/4/12 12:50:30
Webフォームでしょうか?

>Dim _xlsFile As String = Server.MapPath("~/prt/表.xlsx")
最後の x が全角文字になっていますので半角に修正して現象が再現するかご確認ください。

解決しない場合は、アプリケーションルートパス( ~ )の使い方が間違っている可能性を疑います。
その疑いを確認するためにServer.MapPathではなく、フルパスを指定することで解決するかご確認ください。

Dim _xlsFile As String = "C:\inetpub\wwwroot\プロジェクト名\prt\表.xlsx"

なお、投稿の内容からWebアプリケーションのデプロイについて理解されていないような印象をうけました。
詳しくない場合は、Webアプリで使用するファイルはすべてVisual Studioのプロジェクトに追加してください。(動的に作成されるものはこれができないですがフォルダーくらいは追加できるはずです。)
こうすることでアプリケーションルートから見たパスが固定になるのでやりやすいです。
~は、アプリケーションルートパスを表しており、Webアプリケーションの実行方法により異なる場合があります。
※そのため、パスが何かおかしいと感じたら生成されたパス(_xlsFile)をLabelに表示するなどして実際に確認してみることをお勧めします。

そして、プロジェクトを右クリックして「発行」を使って、Webアプリケーションをデプロイしてください。


たくみさんが管理しているIIS(Webサーバー)で動作させるには発行先をフォルダーにし、IISのアプリケーションとして指定されているフォルダーに直接発行するとすぐにIISからアクセスできるようになります。

無料で使えるAzureにデプロイすると、世界中どこからでもすぐにアクセスできるので便利です。
http://rucio.o.oo7.jp/Note/Note002AzureFreeAppService.htm

※Webアプリケーションを動作させる方法は他にもあります。


今回の件とは関係ありませんが、Webアプリケーション内でExcel.ApplicationをCreateObjectすることは禁じ手です。Webアプリケーションは複数の人が同時にアクセスしてマルチスレッドで実行される場合があるのに対し、Excel.Applicationはマルチスレッドをサポートしていなかったと思います。また、ライセンス上もこのような手段で多人数が使う使い方は許可されていなかったと思います。
(もし、Webアプリケーションであるにもかかわらず使用する人がたくみさんだけであればもしかしてOKかもしれません)

一般的にはWebアプリケーションでExcelを操作したい場合は、別のライブラリを使用します。本家Excelではないので機能や使い方が違うのですが、xlsxファイルを読み書きすることはできます。
有名どころはClosedXMLです。
https://www.atmarkit.co.jp/ait/articles/1810/24/news016.html

それから、コードが抜粋されているのでわかりませんが、ExcelをCOMで操作する場合、開放処理などがいろいろ面倒ですのでこの点もお気をつけください。
投稿者 るきお  (社会人) 投稿日時 2020/4/12 13:05:17
ちょっと先走って書いてしまいましたので補足します。

投稿されたコードは Try がついていて、かつ抜粋なので、ファイルがそもそも見つからないのではないかと推測して書きました。

Tryがついていると本当に問題がある箇所がわかりにくくなるので、Tryをはずして現象が発生する最小限のコードを投稿することをお勧めします。
投稿者 たくみ  (学生) 投稿日時 2020/4/12 14:40:52
るきお様

お返事をいただき、ありがとうございます。
大変参考になります。


>Dim _xlsFile As String = Server.MapPath("~/prt/表.xlsx")
>最後の x が全角文字になっていますので半角に修正して現象が再現するかご確認ください。
 
 xを半角に修正しても同じエラーになってしまいました。
 


>解決しない場合は、アプリケーションルートパス( ~ )の使い方が間違っている可能性を疑います。
>その疑いを確認するためにServer.MapPathではなく、フルパスを指定することで解決するか
>ご確認ください。
>例
>Dim _xlsFile As String = "C:\inetpub\wwwroot\プロジェクト名\prt\表.xlsx"

 フルパスを指定してみたのですが同じエラーになってしまいました



>※そのため、パスが何かおかしいと感じたら生成されたパス(_xlsFile)をLabelに表示するなどして
>実際に確認してみることをお勧めします。

 ありがとうございます。Labelに表示した所 C:/inetpub/wwwroot/プロジェクト名/prt/表.xlsx と
 なっており、間違いなくファイルの場所を示していました。



>一般的にはWebアプリケーションでExcelを操作したい場合は、別のライブラリを使用します。
>本家Excelではないので機能や使い方が違うのですが、xlsxファイルを読み書きすることは
>できます。有名どころはClosedXMLです。

 ありがとうございます。
 早速インストールをおこない挑戦してみたのですが、シートの印刷はできないのでしょうか・・・


>投稿されたコードは Try がついていて、かつ抜粋なので、ファイルがそもそも見つからないの
>ではないかと推測して書きました。
>Tryがついていると本当に問題がある箇所がわかりにくくなるので、Tryをはずして現象が発生
>する最小限のコードを投稿することをお勧めします。

 ありがとうございます。
 Tryをはずして確認してみたいと思います。
投稿者 るきお  (社会人) 投稿日時 2020/4/12 15:18:24
現象が発生する最小限のコードの投稿をお待ちしています。

それとは別のポイントで、Webアプリはどこで実行していますか?
A:たくみさんがVisual Studioを使用しているパソコン。
B:A以外のたくみさんの自宅・会社・学校・組織内にあるサーバー。
C:Azureなどのクラウドの環境や外部のサービス

そのWebアプリを実行している環境の C:\inetpub\wwwroot\プロジェクト名\prt\表.xlsx に確かにファイルは存在しますか?
投稿者 たくみ  (学生) 投稿日時 2020/4/12 15:44:00
るきお様

お世話になります。

Webアプリは、Aで実行しています。

指定したExceファイルlを開き、印刷後終了するという流れなのですが、VisualStudioのデバッグでは、きちんと印刷されます。 localhostに公開(印刷用のWebFormは開きます)し実行するとエラーとなってしまいます。


最小限のコードになります。
申し訳ありませんが、宜しくお願い致します。


    Protected Sub BtnPrint_Click(sender As Object, e As EventArgs) Handles BtnPrint.Click
       Dim objExcel As Object = Nothing                       'Excel Application
        Dim objWorkBooks As Object = Nothing                  'Excel WorkBooks
        Dim objWorkBook As Object = Nothing                   'Excel Workbook
        Dim objSheets As Object = Nothing                     'Excel WorkSheet
        Dim objSheet As Object = Nothing                      'Excel WorkSheet
        Dim objRange As Object = Nothing                      'Excel Range
        Dim _xlsFile As String = Server.MapPath("~/prt/表.xlsx")

            'EXCEL オープン処理
            objExcel = CreateObject("Excel.Application")
            objWorkBooks = objExcel.Workbooks
            objWorkBook = objWorkBooks.Open(_xlsFile)
            objSheets = objWorkBook.Sheets
            objSheet = objSheets("01")

            'EXCEL 出力処理
            With objSheet
                .PrintOut(Copies:=1, Collate:=True)
            End With

            objWorkBook.Saved = True  '保存済みとする

            ' EXCEL終了処理
            MRComObject(objPageSetup)
            MRComObject(objRange)
            MRComObject(objSheet)
            MRComObject(objSheets)
            MRComObject(objWorkBook)
            MRComObject(objWorkBooks)
            objExcel.Quit() : MRComObject(objExcel)
            System.GC.Collect() 
    End Sub

    ' --- COM オブジェクトの解放
    Public Sub MRComObject(ByRef objCom As Object)
        'COM オブジェクトの使用後、明示的に COM オブジェクトへの参照を解放する 
        Try
            '提供されたランタイム呼び出し可能ラッパーの参照カウントをデクリメントします 
            If Not objCom Is Nothing AndAlso
       System.Runtime.InteropServices.Marshal.IsComObject(objCom) Then
                System.Runtime.InteropServices.Marshal.ReleaseComObject(objCom)
            End If
        Catch
        Finally
            '参照を解除する 
            objCom = Nothing
        End Try
    End Sub
投稿者 るきお  (社会人) 投稿日時 2020/4/12 15:51:57
ファイル名 表.xlsx の最後の x が全角になっています。
このコードでたくみさんは実行できているということでしょうか?
投稿者 たくみ  (学生) 投稿日時 2020/4/12 16:07:01
るきお様

お世話になります。
申し訳ありません。
Excelファイルは別の名前(個人名入り)なので、その部分だけ修正したのですが
全角にしてしまいました。実際のものは末尾xは半角になっております。
宜しくお願い致します。

投稿者 るきお  (社会人) 投稿日時 2020/4/12 16:19:24
承知しました。

■確認1.
たくみさんはこのコードをVisual Studio上で実行するとエラーなく印刷できるのに対し、
IISで実行すると、「オブジェクト変数または With ブロック変数が設定されていません。」が表示されるということで間違いないでしょうか?

ファイル名だけは変えているということはわかりましたが、誤解やミスをなくすため、拡張子を半角にした"~/prt/表.xlsx"にした上で、この修正版のプログラムを1文字も変更せずに実行して同じ状況かどうかご確認をお願いします。

■確認2.
確認1で同じ状況であることが確認できるならば、アプリケーションルートパスの問題と切り離すために、"~/prt/表.xlsx"を"C:\temp\prt\表.xlsx"に変更し、この場所にファイルを配置した上で、実行をお願いします。
やはりVisual Studio上では正常に実行できるのに、IISで実行すると同じエラーが表示されるでしょうか?

■確認3.
新規Webアプリケーションを作成し、投稿いただいた最小限のプログラムに確認1・確認2の変更だけを適用して、それでも現象が発生するかどうか、Visual StudioとIISでご確認をお願いします。
投稿者 るきお  (社会人) 投稿日時 2020/4/12 16:20:41
IISで確認する場合は、実行前にタスクマネージャーでExcelのプロセスをすべて終了させてからご確認をお願いします。
投稿者 たくみ  (学生) 投稿日時 2020/4/12 17:06:46
るきお様

お世話になっております。
確認1と確認2について、テストしてみました。



>■確認1.
>たくみさんはこのコードをVisual Studio上で実行するとエラーなく印刷できるのに対し、
>IISで実行すると、「オブジェクト変数または With ブロック変数が設定されていません。」
>が表示されるということで間違いないでしょうか?
   
   はい、間違いありません


  
>ファイル名だけは変えているということはわかりましたが、誤解やミスをなくすため、
>拡張子を半角にした"~/prt/表.xlsx"にした上で、この修正版のプログラムを1文字も
>変更せずに実行して同じ状況かどうかご確認をお願いします。

  同じエラーが表示されました



>■確認2.
>確認1で同じ状況であることが確認できるならば、アプリケーションルートパスの問題と
>切り離すために、"~/prt/表.xlsx"を"C:\temp\prt\表.xlsx"に変更し、この場所にファイルを
>配置した上で、実行をお願いします。
>やはりVisual Studio上では正常に実行できるのに、IISで実行すると同じエラーが表示される
>でしょうか?

   Visual Studio上では正常に実行できたのですが、IISで実行した所エラーの内容が変わりました。

   
 『アプリケーションでサーバー エラーが発生しました。
  
    ActiveX コンポーネントを作成できません。 
    説明: 現在の Web 要求を実行中に、ハンドルされていない例外が発生しました。
    エラーに関する詳細および例外の発生場所については、スタック トレースを参照して
    ください。 

    例外の詳細: System.Exception: ActiveX コンポーネントを作成できません。

    ソース エラー: 

    現在の Web 要求の実行中にハンドルされていない例外が生成されました。障害の原因
    および発生場所に関する情報については、下の例外スタック トレースを使って確認でき
    ます。 

    スタック トレース: 

    [Exception: ActiveX コンポーネントを作成できません。]
       Microsoft.VisualBasic.Interaction.CreateObject(String ProgId, String ServerName) +373
       GjAppH.WebForm1.BtnPrint_Click(Object sender, EventArgs e) in    
     C:\Users\user\source\repos\GjAppH\GjAppH\WebForm1.aspx.vb:17
       System.Web.UI.WebControls.Button.OnClick(EventArgs e) +11594496
       System.Web.UI.WebControls.Button.RaisePostBackEvent(String eventArgument) +274
       System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean 
        includeStagesAfterAsyncPoint) +1964 』
 

 と表示されております。
 




投稿者 るきお  (社会人) 投稿日時 2020/4/12 17:31:48
権限の問題かもしれないので、IISのアプリケーションプールのIDに管理者権限のあるユーザーを設定してみてください。

・・・という感じで続けて言っても良いのですが、なかなか解決しないですね。
何が起こっているかがまだわからない感じですね。

そこで、少し話しを戻したいのです。Excel.ApplicationをCreateObjectすることは禁じ手なので、本当にこれを使うつもりかはっきりさせたいです。

1.このアプリケーションは たくみさん1人だけが使うものなのでしょうか?
2.同時に実行されることはありえないのでしょうか?
→1、2のどちらかが「No」の場合、動作しない場合があるか、Excelのライセンス違反になります。

3.うまく動くようになった場合でも引き続きたくみさんのローカルPC上でうごかすのでしょうか?

全部 Yes であれば、Windowsフォームアプリケーションにしたほうが良いのではないですか?
Webアプリケーションにしたい理由はなんでしょうか?
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2020/4/12 18:37:39
IIS から Excel を呼び出すことは、基本的に『悪手』です。
https://support.microsoft.com/ja-jp/help/257757/considerations-for-server-side-automation-of-office


ご存じのように ReleaseComObject を忘れることによる解放漏れの危険もありますし、
For Each での列挙が制限されがちである、という問題もあります。
As Object からのレイトバインドだと、型判定の段階で、内部的に参照カウントが
予期せず増加してしまい、一部の解放処理を FinalReleaseComObject へと
差し替えねばならないケースもあります。
https://support.microsoft.com/ja-jp/help/317109/office-application-does-not-exit-after-automation-from-visual-studio-n
https://divakk.co.jp/aoyagi/csharp_tips_vssenum.html


…これらの点は、提示頂いたコードの範囲内ではクリアできているようですが、
るきおさんが書かれていたように、ライセンスの問題や、
シングルスレッド アパートメント固有の問題もありますので、
Web アプリから Excel を呼び出すことがあまり良い手段ではないことは確かです。


> スタック トレース: 
御提示の内容だと、BtnPrint_Click の 17 行目にある CreateObject が失敗しているようですが
先に提示していただいた「最小限のコード」だと、17行目ではないですね…?


まず、Excel は、対話型セッション(いわゆるデスクトップ)を必要とします。
一方 Web サイトは、無人実行が前提の、非対話型のサービスとして動作します。

通常、サービス 起動されたアカウントには、Excel を起動する権限が与えられていません。
実行できるようにするには、権限設定を操作する必要があります。
https://itblogdsi.blog.fc2.com/blog-entry-418.html


また、サーバーサイドの印刷だとしたら、アプリケーションプールのアカウントに対して
プリンターへのアクセス権が付与されている必要があります。
また、プリンター設定にはユーザー単位の設定も含まれるため、
偽装した実行アカウントに対してプリンターの設定が行われている必要もありそうです。
https://support.microsoft.com/ja-jp/help/2673341
https://social.technet.microsoft.com/Forums/azure/ja-JP/b40728e3-e951-40cf-bb19-4ba5f86ad0ea/aspnet?forum=iis7ja


ただ、現在のエラーは CreateObject の時点でおきているようなので、印刷以前の話のようです。
アプリケーションプールなどの権限周りに着目して調査してみてはいかがでしょう?



> 印刷後終了するという流れなのですが

「ブラウザーを呼び出したクライアント側」で印刷する想定でしょうか?
それとも「Web サイトを運営しているサーバー側」で印刷する想定でしょうか?
サーバーで印刷するなら、どのユーザに定義したプリンターを使用するのかも考慮が必要です。

localhost 上で実行する前提の場合は、クライアント側なのかサーバー側なのかが
区別されにくいところですが、動作的には全然別物となりますのでご注意ください。


> シートの印刷はできないのでしょうか・・・

クライアント側での印刷処理なら、 無料の ReoGrid を用いる選択肢もあります。
https://reogrid.net/jp/document/print/
https://reogrid.net/jp/document/

しかし ReoGrid だと、サーバー側での印刷はできません。
ファイルの読み書きはサーバー側でもできるので、Excel ファイルを HTML に変換して
ブラウザー側の機能で印刷してもらうということはできますが…本筋からは外れてしまいますね。
投稿者 たくみ  (学生) 投稿日時 2020/4/12 19:22:45
るきお様

お世話になります。


>1.このアプリケーションは たくみさん1人だけが使うものなのでしょうか?
>2.同時に実行されることはありえないのでしょうか?
>→1、2のどちらかが「No」の場合、動作しない場合があるか、Excelのライセンス違反になります。
>3.うまく動くようになった場合でも引き続きたくみさんのローカルPC上でうごかすのでしょうか?
>全部 Yes であれば、Windowsフォームアプリケーションにしたほうが良いのではないですか?
>Webアプリケーションにしたい理由はなんでしょうか?

  ライセンスについての知識がなく申し訳ありません。
  とても恥ずかしいです。
  すべてYes なのですが、Webアプリケーションの開発に興味があり勉強をしています。
  学習の一環ですので実際に使用する事は考えていません。

  WebアプリですとページのデザインにBootstrapが使えるのでそちらも勉強しています。  
  
  いろいろ勉強させていただきありがとうございました。
  お教えいただいた事を参考にもう少しトライしてみます。 

投稿者 るきお  (社会人) 投稿日時 2020/4/12 20:23:48
Webアプリケーションはともて便利で、現在主流のアーキテクチャですが、
印刷やExcelなどローカルの機能の制御には不向きです。
Excelの場合は、xlsxファイルを単なるデータとしてClosedXMLなどのライブラリで読み書きすることはできますが、Excel自体を呼び出せるわけではありません。

>Webアプリケーションの開発に興味があり勉強をしています。
>学習の一環ですので実際に使用する事は考えていません。
Webアプリケーションを勉強することはとても良いことだと思うので、題材を変えてチャレンジしてみてください。
また、これからWebアプリケーションを勉強するのであればASP.NET Webフォームではなく、ASP.NET Coreを使用されるのが良いです。
というのもASP.NET Webフォームは既に開発が停止されているからです。

ただ、ASP.NET CoreはまだVBでの開発にはほとんど対応しておらず、VBでバリバリ開発できるようになるのは早くても今年の秋 .NET 5.1 が登場するタイミングになりそうです。
今ASP.NET CoreをやるならC#です。