DOBON.NET DOBON.NETプログラミング掲示板過去ログ

vb.netで2つのエクセルファイルを処理する場合の挙動について(その2)

環境/言語:[Windwos7 64bit VB.net Excel2010 .NET Framework3.5]
分類:[.NET]

2012/09/13(Thu) 17:33:13 編集(投稿者)

先日No30892を投稿したものです。

No30892の問題は、魔界の仮面弁士さんのアドバイスにより解決したのですが、
今週に入って社内端末の総入れ替えがありまして、同じような問題が再発しました。

OSがWindowsXP から Windows7 64bitに
Officeが2007 から 2010に変更になりました。
開発環境は以前のまま、VB2008です。

今回の問題もBookの保存時にエラーが出るというものです。
テンプレートファイルを解放すると、処理を行う方のBookを保存する際にエラーとなります。
同じような質問で恐縮ですが、問題点と解決法をアドバイス頂けると幸いです。

以下ソースと問題箇所
Public Sub openExcel(ByVal tmpPath As String, ByVal filePath As String, ByVal sheetName As String)
'テンプレートファイルのオープン
xlApp = New Excel.Application
xlApp.Visible = False
xlApp.DisplayAlerts = False
xlBooks = xlApp.Workbooks

xlTmpBook = xlBooks.Open(tmpPath)
xlTmpSheets = xlTmpBook.Worksheets
xlTmpSheet = DirectCast(xlTmpSheets.Item(1), Excel.Worksheet)

Dim shipGuide As String = IO.Path.Combine(My.Application.Info.DirectoryPath, FILE_NAME)
xlBook = xlBooks.Open(shipGuide)
xlSheets = xlBook.Worksheets
xlSheet = DirectCast(xlSheets.Item(1), Excel.Worksheet)

'テンプレートのシートをコピー
xlTmpSheet.Copy(After:=xlSheet)
'既に存在するSheet1を削除
xlSheet.Delete()
MRComObject(xlSheet)

'コピーしたシートの名称変更
xlSheet = DirectCast(xlSheets.Item(1), Excel.Worksheet)
xlSheet.Name = sheetName
xlBook.SaveAs(filePath) '※1この保存は問題なし。※2でエラーとなるので一旦保存ができるか確認用

closeTmpExcel()

closeExcel(filePath)
End Sub

Public Sub closeTmpExcel()
If IsNothing(xlTmpSheet) = False Then
MRComObject(xlTmpSheet)
End If
MRComObject(xlTmpSheets)
xlTmpBook.Close()
MRComObject(xlTmpBook)
End Sub

Public Sub closeExcel(ByVal saveFile As String)
Try
xlBook.SaveAs(saveFile) ※2この部分でエラー。エラーメッセージは後述
Catch ex As Exception
MsgBox(ex.Message)
End Try
MRComObject(xlSheet)
MRComObject(xlSheets)

xlBook.Close()
MRComObject(xlBook)
xlBooks.Close()
MRComObject(xlBooks)
xlApp.Visible = True
xlApp.DisplayAlerts = True
xlApp.Quit()
MRComObject(xlApp)
End Sub

Private Shared Sub MRComObject(Of T As Class)(ByRef objCom As T, Optional ByVal force As Boolean = False)
If objCom Is Nothing Then
Return
End If
Try
If System.Runtime.InteropServices.Marshal.IsComObject(objCom) Then
If force Then
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(objCom)
Else
System.Runtime.InteropServices.Marshal.ReleaseComObject(objCom)
End If
End If
Finally
objCom = Nothing
End Try
End Sub

※エラーメッセージ
System.Runtime.InteropServices.COMException がキャッチされました
ErrorCode=-2146827284
HelpLink="xlmain11.chm"
Message="'xxx.xlsx' にアクセスできません。"
Source="Microsoft Excel"
■No30927に返信(やむさんの記事)
> 同じような問題が再発しました。
同じコードを試してみましたが、当方では再現しませんでした。


使用したファイルは、
 "あいうえお.xlsx" (tmpPath)   → 新規ブックでSheet1に「あいうえお」と書いただけ。
 "かきくけこ.xlsx" (shipGuide) → 新規ブックでSheet1に「かきくけこ」と書いただけ。
 "さしすせそ.xlsx" (filePath/saveFile) → 存在しないファイル名。
という単純なものです。他のシート/他のブックへの参照や数式などは含まれていません。

ただし、上記ファイル群の保存先は マイ ドキュメント に変更しています。
これは権限周りの問題で、「読み込みはできるが保存はできない」とか、
あるいは「ファイルを生成できるが、既存ファイルの編集は出来ない」などの
別の問題が発生する可能性を除外しておくためです。


なお、実験環境は下記のとおり。
コードの実行前後で、Excel.exe が起動されていない事を確認しています。

Windows 7 Enterprise (64bit) Service Pack 1
Excel 2010 (32bit) バージョン 14.0.6123.5001
Visual Studio 2008 バージョン 9.0.30729.4462 QFE
.NET Framework 3.5 Service Pack 1 + Office 14 PIA
Debug 構成 x86 ビルド

追加で用意したフィールド変数は下記の通り。
いずれも、WithEvents や AddHandler は利用していません。

Private xlApp As Excel.Application
Private xlBooks As Excel.Workbooks

Private xlTmpBook As Excel.Workbook
Private xlTmpSheets As Excel.Sheets
Private xlTmpSheet As Excel.Worksheet

Private xlBook As Excel.Workbook
Private xlSheets As Excel.Sheets
Private xlSheet As Excel.Worksheet


> 同じような質問で恐縮ですが、問題点と解決法をアドバイス頂けると幸いです。
原因にはまったく思い当らないのですが、とりあえず、
気になる点を列挙してみます。


> OSがWindowsXP から Windows7 64bitに
> Officeが2007 から 2010に変更になりました。
Office 2010 は、64bit 版をインストールしましたか?
それとも、32bit 版の 2010 をインストールしましたか?

それと一応念のために聞いておきますが、複数バージョンの Office を
共存インストールさせていたりはしますか?


> 開発環境は以前のまま、VB2008です。
ビルドは AnyCPU でしょうか、x86 でしょうか。それとも x64?

また、参照設定しているのは Excel 14 の PIA でしょうか?
(Microsoft.Office.Interop.Excel.dll)


> テンプレートファイルを解放すると
テンプレートという事は、*.xls/*.xlsx ではなく、*.xlt/*.xltx ファイルでしょうか。
(動作上の差異は無いでしょうけれども)


> 今回の問題もBookの保存時にエラーが出るというものです。
MRComObject を呼び出した時に、ReleaseComObject の戻り値が 0 になっているかを
確認してみてください。COM オブジェクトを引数に渡すメソッドを呼び出した場合、
参照カウントが増加してしまい、正しく解放されない場合があるためです。

恐らく、今回はすべて 0 が返されるであろうと予想しますが、万一、
1 以上が返されるオブジェクトが見つかった場合には、強制破棄のために
MRComObject の force 引数に True を渡して呼び出す事ができます。


> Public Sub closeTmpExcel()
>     If IsNothing(xlTmpSheet) = False Then
>         MRComObject(xlTmpSheet)
>     End If
>     MRComObject(xlTmpSheets)

xlTmpSheet は事前に Nothing 判定をかけているのに対し、
xlTmpSheets はその判定を行っていないのは何故でしょうか?

そもそも、Nothing 判定は MRComObject 内でも実施されていますので、
一連の処理コードに対称性が無いように見えます。動きとしては問題ないですが。



>   Message="'xxx.xlsx' にアクセスできません。"
エラーメッセージ中のファイル名は、テンプレートファイル(tmpPath)の名前でしょうか。
shipGuide でしょうか、あるいは filePath(saveFile)のパスでしょうか。


また、xlBook の方を、Workbooks.Open → Workbook.SaveAs の流れでは無く、
Workbooks.Add → Workbook.SaveAs にした場合はどうでしょうか。
2012/09/14(Fri) 11:03:41 編集(投稿者)

魔界の仮面弁士さん、今回も回答して頂きありがとうございます。

■No30929に返信(魔界の仮面弁士さんの記事)
>>OSがWindowsXP から Windows7 64bitに
>>Officeが2007 から 2010に変更になりました。
> Office 2010 は、64bit 版をインストールしましたか?
> それとも、32bit 版の 2010 をインストールしましたか?
->Office2010は32bit版がインストールされています。


> それと一応念のために聞いておきますが、複数バージョンの Office を
> 共存インストールさせていたりはしますか?
->インストールされているOfficeは2010(32bit)のみです。


>>開発環境は以前のまま、VB2008です。
> ビルドは AnyCPU でしょうか、x86 でしょうか。それとも x64?
->当方、Expressを使用しているため、AnyCPUとなります。


> また、参照設定しているのは Excel 14 の PIA でしょうか?
> (Microsoft.Office.Interop.Excel.dll)
->これは何を参照設定で選択したかということでしょうか?
参照設定で選択したのは、【Microsoft Excel 14.0 Object Library】です。
ソリューションエクスプローラに表示されているのは、
【Microsoft.Office.Core】と【Microsoft.Office.Interop.Excel】です。

>>テンプレートファイルを解放すると
> テンプレートという事は、*.xls/*.xlsx ではなく、*.xlt/*.xltx ファイルでしょうか。
> (動作上の差異は無いでしょうけれども)
->テンプレートとして使用している【*.xlsx】ファイルです。

>>今回の問題もBookの保存時にエラーが出るというものです。
> MRComObject を呼び出した時に、ReleaseComObject の戻り値が 0 になっているかを
> 確認してみてください。COM オブジェクトを引数に渡すメソッドを呼び出した場合、
> 参照カウントが増加してしまい、正しく解放されない場合があるためです。
>
> 恐らく、今回はすべて 0 が返されるであろうと予想しますが、万一、
> 1 以上が返されるオブジェクトが見つかった場合には、強制破棄のために
> MRComObject の force 引数に True を渡して呼び出す事ができます。
->全てのReleaseComObjectの戻り値を確認しましたが、【0】以外は返ってきてませんでした。


>>Public Sub closeTmpExcel()
>> If IsNothing(xlTmpSheet) = False Then
>> MRComObject(xlTmpSheet)
>> End If
>> MRComObject(xlTmpSheets)
>
> xlTmpSheet は事前に Nothing 判定をかけているのに対し、
> xlTmpSheets はその判定を行っていないのは何故でしょうか?
>
> そもそも、Nothing 判定は MRComObject 内でも実施されていますので、
> 一連の処理コードに対称性が無いように見えます。動きとしては問題ないですが。
->そのNothing判定に特に意味はないです。


>> Message="'xxx.xlsx' にアクセスできません。"
> エラーメッセージ中のファイル名は、テンプレートファイル(tmpPath)の名前でしょうか。
> shipGuide でしょうか、あるいは filePath(saveFile)のパスでしょうか。
->エラーメッセージ中のファイル名は、 filePath(saveFile)のパスです。


> また、xlBook の方を、Workbooks.Open → Workbook.SaveAs の流れでは無く、
> Workbooks.Add → Workbook.SaveAs にした場合はどうでしょうか。
->Workbooks.Add(shipGuide) → Workbook.SaveAsに変更してみましたが、
結果は同じでした。
また、すでに※1部分で名前を付けて保存しているので、
※2の部分を上書き保存(xlBook.Save())にしてみましたが今度は
【ファイルを保存できませんでした。】というエラーとなります。
ちなみにエラーコード等は
ErrorCode=-2146827284
HelpLink="xlmain11.chm"
Message="ファイルを保存できませんでした。"
Source="Microsoft Excel"
となります。

xlBookに対してアクセスできないから保存ができないということは、
アクセス権的な問題かとも思ったんですが、そうなると※1の部分で一旦は保存ができていることに説明がつきません。
なぜアクセスができないのか、その原因がわかれば対処法も見えてくるのではとも
思うのですが、エラーメッセージではそこまで読み取れないのでそれもできません。
■No30932に返信(やむさんの記事)
> アクセス権的な問題かとも思ったんですが、
その可能性を調査するための実験は行われましたか?

フルコントロール許可を持つパスを用意して検証すれば、
アクセス権の問題かどうかを切り分けられますよね。

アクセス権に原因があるかどうかまでは分かりませんが、少なくとも
当方では提示いただいたコード絵期待動作するコードだったので、
恐らくは環境の問題であろうかと推察しています。


その他、特定のファイルに対してのみ発生する問題かどうかの切り分けのため、
新規に用意した、ごく単純なファイルで試してみるのも良いかと思います。



> そうなると※1の部分で一旦は保存ができていることに説明がつきません。
たとえば、
 新規ファイルの作成   → 可能
 既存ファイルの実行   → 可能
 既存ファイルの読み込み → 不可
 既存ファイルの編集   → 不可
というアクセス権だった場合はどうでしょうか。

今回のパスが、これと同様のアクセス権になっていたとすれば、
1 回目の Workbook.SaveAs は成功しますが
2 回目の Workbook.SaveAs はエラーになってしまうはずです。
(同一ファイル名の時と別ファイル名の時では、異なるエラーになるようです)


また、こうした権限の元、手動で新規ファイル保存すると、

「ドキュメントは保存されましたが、共有違反のため、
 保存したドキュメントを再び開くことができません。
 ドキュメントを閉じて、再度開いてみてください。」

という警告メッセージが保存時に表示されることになるはずです。

この場合でも操作は継続できますが、ファイルの上書き保存は出来ません。
(保存ボタンを押しても、別ファイル名を付けるように促されます)

また、別名保存しようとしても、正しく書き込めない可能性があります。
(いったん閉じて、再度開きなおすことはできます)
> ->当方、Expressを使用しているため、AnyCPUとなります。

これが問題ではないでしょうか?
■No30935に返信(チャーさんの記事)
>>->当方、Expressを使用しているため、AnyCPUとなります。
> これが問題ではないでしょうか?

Express は使っていないので確認できませんが、
VB2008 Express でも、x86 ビルド等に
変更させることはできると聞いたことが。
http://blog.zige.jp/vvbw/kiji/22618.html
■No30933に返信(魔界の仮面弁士さんの記事)
> ■No30932に返信(やむさんの記事)
>>アクセス権的な問題かとも思ったんですが、
> その可能性を調査するための実験は行われましたか?
>
> フルコントロール許可を持つパスを用意して検証すれば、
> アクセス権の問題かどうかを切り分けられますよね。
>
> アクセス権に原因があるかどうかまでは分かりませんが、少なくとも
> 当方では提示いただいたコード絵期待動作するコードだったので、
> 恐らくは環境の問題であろうかと推察しています。
→実験としては行っておりませんでしたが、アクセス権の確認は行っておりました。
ログインユーザはAdministrator権限を持っているユーザでログインしており
ファイル・ディレクトリ共にフルコントロールとなっているため
それ以上深くは考えていませんでした。

> その他、特定のファイルに対してのみ発生する問題かどうかの切り分けのため、
> 新規に用意した、ごく単純なファイルで試してみるのも良いかと思います。
→実行環境のディレクトリにまったく別のテンプレートを作成し、
試行してみると、問題なく処理は実行されましたので、ファイルそのものの問題であるという結論に達しました。
もちろん、新しく作成したファイルと、元々使用していたファイルのアクセス権設定は全く同じにしてあります。
まだそれ以上に突っ込んだことは試してはいませんが、使用するファイルを変更することで
問題なく処理が実行されることから、この問題に関しては解決としたいと思います。

魔界の仮面弁士さん、今回もありがとうございました。
また、チャーさんもありがとうございます。
解決済み!

DOBON.NET | プログラミング道 | プログラミング掲示板