トップ回答者
データをExcelに出力するときのエラーや警告への対処法及び高速に出力する方法

質問
-
zen73です。質問攻めの様相になっていますがお許しください。
配列に納めたデータ http://social.msdn.microsoft.com/Forums/ja-JP/csharpexpressja/thread/589df236-16d3-469c-9d8d-fc7fde76d99d をExcelに出力していますが,コーディングの際にそれぞれ1つのエラーと警告がでています。かまわず実行してみますと,きちんとExcelにデータが表示されていることを確認できるのですが,これらを解消する対処法を教えていただきたいと思います。また,データを高速に出力する方法も併せて知りたいと思いますのでよろしくお願いします。
ア <エラー> 未割り当てのローカル変数 'exApp' が使用されました。
イ <警告> 参照コンポーネント Excel ,Office ,VBIDE には、更新された使用可能なカスタム ラッパーがあります。
ウ データを高速に出力する方法
イについて 現在作成中のプロジェクトはVS2005のものをVS2008にコンバートしたものを基にしています。VS2005で作成時にインストールされていたOfficeは2000でしたが現在は2008もインストールされています。
現在のプロジェクトで参照しているもの
[Excel]Microsoft Excel 9.0 Object Library
[Office]Microsoft Office 9.0 Object Library
[VBIDE}Microsoft Visual Basic for Applications Extensibility 5.3
以前にもこのフォーラムで質問させていただいたのですが,有効な対策はありませんでしたので・・・
private void btnExcel_Click(object sender, EventArgs e) { rowSum = 0; sRow = new int[hanmei.Length]; //グループの開始行 eRow = new int[hanmei.Length]; //グループの終了行 for (int i = 0; i < hanmei.Length; i++) { dvGroup.RowFilter = "group='" + hanmei[i] + "'"; rowCnt = suitoDataGridView.RowCount;//班のレコード数 sRow[i] = rowSum; eRow[i] = rowSum + rowCnt + 3; toRead(i, sRow[i], eRow[i]); //データ整形 rowSum += rowCnt + 4; //総レコード数 } int iRow = rowSum - 1; int iCol = 6; Excel.Application exApp; Excel.Workbooks wkBooks; Excel._Workbook wkBook; Excel.Sheets wkSheets; Excel._Worksheet wkSheet; Excel.Range range; exApp = new Excel.Application(); wkBooks = exApp.Workbooks; wkBook = wkBooks.Add(Missing.Value); wkSheets = wkBook.Worksheets; wkSheet = (Excel._Worksheet)wkSheets.get_Item(1); ////収入 object[,] aryData = new object[iRow + 2, iCol]; //出力データ for (int i = 1; i < iRow; i++) { for (int j = 0; j < iCol; j++) { aryData[i, j] = hyoji[i, j]; } } range = wkSheet.get_Range("A1", Missing.Value); range = range.get_Resize(iRow + 2, iCol); range.Font.Name = "MS ゴシック"; range.Value = aryData; int iR = iRow + 2; exApp.get_Range("D2", "D" + iR).HorizontalAlignment = Excel.Constants.xlRight; exApp.get_Range("B2", "C" + iR).NumberFormatLocal = "#,##0"; exApp.Visible = true; }
zen73
回答
-
ア <エラー> 未割り当てのローカル変数 'exApp' が使用されました。
これはコンパイル エラーですので、このエラーを取り除かないことには、他のところを一生懸命弄ってもモジュールは更新されないのではないかと思います。
まずはエラーが出ている行をよく見て、これを対処するべきではないかと。
イ <警告> 参照コンポーネント Excel ,Office ,VBIDE には、更新された使用可能なカスタム ラッパーがあります。
イについて 現在作成中のプロジェクトはVS2005のものをVS2008にコンバートしたものを基にしています。VS2005で作成時にインストールされていたOfficeは2000でしたが現在は2008もインストールされています。
現在のプロジェクトで参照しているもの
[Excel]Microsoft Excel 9.0 Object Library
[Office]Microsoft Office 9.0 Object Library
[VBIDE}Microsoft Visual Basic for Applications Extensibility 5.3
参照設定から一旦削除して、再度参照設定に追加し直すと警告が消えたりしないでしょうか?
参考:
Warning: There are updated custom wrappers available for the following referenved components: Office.
http://social.msdn.microsoft.com/forums/en-US/clr/thread/82f0b63f-1de1-4920-87b1-211833eb6aed
ウ データを高速に出力する方法
セルの書式も同時に設定するのであれば、NPOI を使うという手もあります。
参考になる情報が少なめなのが難点ではありますが。
NPOI
http://npoi.codeplex.com/
市販ツールでは、SpreadsheetGear というのが高速だという声を聴いたことがあります。
XLsoft エクセルソフト : SpreadsheetGear - .NET 用 Excel SDK - ASP VB C# .NET Excel API ライブラリ コンポーネント
http://www.xlsoft.com/jp/products/SSG/index.html
マネージド コードとアンンマネージド コードとの間のやりとりをなるべく少なくすると、高速化につながるのではないかと思います。- 回答としてマーク zen73 2010年9月2日 0:48
すべての返信
-
ADO.NET を使って Excel に出力する方法があります。オートメーションよりも高速に出力できるみたいです。
http://support.microsoft.com/kb/306022/ja
セルの書式は指定できませんが、出力するデータをあらかじめ加工しておくなどして、ある程度対応できると思います。
なかむら(http://d.hatena.ne.jp/griefworker) -
ア <エラー> 未割り当てのローカル変数 'exApp' が使用されました。
これはコンパイル エラーですので、このエラーを取り除かないことには、他のところを一生懸命弄ってもモジュールは更新されないのではないかと思います。
まずはエラーが出ている行をよく見て、これを対処するべきではないかと。
イ <警告> 参照コンポーネント Excel ,Office ,VBIDE には、更新された使用可能なカスタム ラッパーがあります。
イについて 現在作成中のプロジェクトはVS2005のものをVS2008にコンバートしたものを基にしています。VS2005で作成時にインストールされていたOfficeは2000でしたが現在は2008もインストールされています。
現在のプロジェクトで参照しているもの
[Excel]Microsoft Excel 9.0 Object Library
[Office]Microsoft Office 9.0 Object Library
[VBIDE}Microsoft Visual Basic for Applications Extensibility 5.3
参照設定から一旦削除して、再度参照設定に追加し直すと警告が消えたりしないでしょうか?
参考:
Warning: There are updated custom wrappers available for the following referenved components: Office.
http://social.msdn.microsoft.com/forums/en-US/clr/thread/82f0b63f-1de1-4920-87b1-211833eb6aed
ウ データを高速に出力する方法
セルの書式も同時に設定するのであれば、NPOI を使うという手もあります。
参考になる情報が少なめなのが難点ではありますが。
NPOI
http://npoi.codeplex.com/
市販ツールでは、SpreadsheetGear というのが高速だという声を聴いたことがあります。
XLsoft エクセルソフト : SpreadsheetGear - .NET 用 Excel SDK - ASP VB C# .NET Excel API ライブラリ コンポーネント
http://www.xlsoft.com/jp/products/SSG/index.html
マネージド コードとアンンマネージド コードとの間のやりとりをなるべく少なくすると、高速化につながるのではないかと思います。- 回答としてマーク zen73 2010年9月2日 0:48
-
なかむらさん,totojoさん,ありがとうございます。
アについて 下のようにするとエラーが出ないように思います。
//Excel.Application exApp; Excel.Application exApp = new Excel.Application(); //exApp = new Excel.Application();
ウについて 私の力量では無理のようですので,あきらめます。
イについて
参照設定から一旦削除して、再度参照設定に追加し直すと警告が消えたりしないでしょうか?
まったく何の変化もみられず警告は消えませんでした。
zen73 -
外池です。「ウ)高速化」について、早々に諦めておられますが・・・、ちょっとした解決策があるかもしれません。どれぐらいの量の情報をExcelに吐き出そうとされているかと、あと、実際、プログラムが動いているときの状況にもよるのですが・・・、(もし外していたら御免なさい)
確認して頂きたいことは、
1)作られているプログラムからExcelのオブジェクトを作って操作を始めると、Excelのスプレッドシートの表示は画面に現れるんですよね?
2)その上で、プログラムからExcelのセルにデータを書き込むと、書き込みの都度、Excelの表示は更新されていく感じじゃありませんか? 私の以前の経験では、デフォルトだとこのような動作でした。何百をいうセルに数値を入れるとエラク遅く感じたものです。
です。もしそうだとすれば、Excelの表示の自動更新を止めて、データを全部書き込んで、それから自働更新を再開すると遥かに速く書き込みができます。Excel側のApplication.ScreenUpdatingプロパティーです。次に、VB6時代の話なので、.Net Fremaworkで上手くいくかどうかわかりませんが、ある範囲のCell(複数)に数値の羅列を書き込みたいときは、ExcelのRangeに対して配列を放り込めば、一気に書き込めた記憶があります。
(ホームページを再開しました) -
確認して頂きたいことは、
1)作られているプログラムからExcelのオブジェクトを作って操作を始めると、Excelのスプレッドシートの表示は画面に現れるんですよね?
2)その上で、プログラムからExcelのセルにデータを書き込むと、書き込みの都度、Excelの表示は更新されていく感じじゃありませんか? 私の以前の経験では、デフォルトだとこのような動作でした。何百をいうセルに数値を入れるとエラク遅く感じたものです。
です。もしそうだとすれば、Excelの表示の自動更新を止めて、データを全部書き込んで、それから自働更新を再開すると遥かに速く書き込みができます。Excel側のApplication.ScreenUpdatingプロパティーです。
最後の最後に Application.Visible を true にしているので、現象の出方としては少し違っているかもしれません。
ただし、Application.ScreenUpdating プロパティの効果は期待できるのではないかと思います。次に、VB6時代の話なので、.Net Fremaworkで上手くいくかどうかわかりませんが、ある範囲のCell(複数)に数値の羅列を書き込みたいときは、ExcelのRangeに対して配列を放り込めば、一気に書き込めた記憶があります。
こちらの方は既に実装できているように思います。
hyoji 配列の中身を aryData 配列にコピーし直すのが余計な気はしますけれども。 -
◆アについて
書かれたコードではエラーにならないので、実際のコードから簡略化されたものだと思いますが、実際のものは以下のようになっていないでしょうか?
この場合でしたら、コンパイラは、ループに入らなかったり、if の条件が成立しない場合を想定するので、エラーになります。
Excel.Application exApp;
for (var i = 0; i < 5; i++)
{
if (i == 0) exApp = new Excel.Application();
// ・・・・・
}
exApp.Visible = true;
変数の宣言と、初期値の代入の行を分けられている理由がもしないのでしたら、zen73 が書かれたように宣言と同時に初期化すればよいと思いますが、もし上記のようなパターンに対して以下のように修正されたのでしたら、それは良くないです。
Excel.Application exApp = new Excel.Application();
for (var i = 0; i < 5; i++)
{
if (i == 0) exApp = new Excel.Application();
exApp.Visible = true;
}
実際の値を代入するのではなくて、以下のように null を代入するだけで良いと思います。
Excel.Application exApp = null;
for (var i = 0; i < 5; i++)
{
if (i == 0) exApp = new Excel.Application();
exApp.Visible = true;
}
◆ウについて
1セルごとにアクセスすると遅くなりますが、お二人が書かれているように zen73 さんのコードではすでに配列による一括代入を行われているので、十分にパフォーマンスを考慮されたコードになってると思いました。
なので、Excel を使わないという方針をとらない限り、改善は難しいと思いました。
(ScreenUpdating の件はすでに totojo さんが書かれていますが、私としては変わらない可能性が高いと思いました。配列のコピーも余計かどうかは別にして、速度には影響なさそうに思いました。)
ただ、推測ですが、実際のコードは書かれたコードをループで何度も実行されていたり、時間の計測にエクセルへの出力以外の部分も含められていたりしないでしょうか?
前のスレッドでの話やアの件などから、この可能性はありそうに思いました。
※レポートの件、試してもらってありがとうございました。
不具合や機能不足な点があったりしますが、使えるときは使うという方針もありだと思ってます。- 編集済み TH01 2010年9月2日 5:22 少し変更
-
書かれたコードではエラーにならないので、実際のコードから簡略化されたものだと思いますが、実際のものは以下のようになっていないでしょうか?
実際のコードであり簡略化はしていません。
1)作られているプログラムからExcelのオブジェクトを作って操作を始めると、Excelのスプレッドシートの表示は画面に現れるんですよね?
totojoさんも言及しておられますように,そうではありません。
ボタンをクリックして5秒近く(遅いと感じている時間なのですが…)経ってからExcelの画面が表示されます。hyoji 配列の中身を aryData 配列にコピーし直すのが余計な気はしますけれども
試してみましたが,目に見えての時間の短縮にはつながらないようです。
zen73 -
外池です。ウ)の高速化についてです。
表示が現れるまでに5秒ですか・・・。遅いようにも思いますが、環境によってはそんなものかもしれないし。私の手元のVS2005+Office2003で試してみたのですが、Excelを単独で立ち上げる時にかかる時間と同じぐらいで画面が現れます。で、配列で代入すること自体には、たとえ100×100というようなおおきなサイズであっても、見た目は「一瞬」で終わってしまいます。
問題の切り分けのためには、以下のように調べてみるとよいかもしれません。
1) 最初にお示し頂いているプログラムで、「Excel.Application exApp;」の行の上流側と下流側でプログラムを分けて、どの部分で時間がかかっているのかを調べる。(zen73さんのプログラムで時間がかかっているのか? ならば、どの部分で?)
2) 一度は試されていると思いますが、Excelのある1個のセルに1個だけデータを書き込むようなプログラムの場合は、どれぐらいの速さでExcelの画面が現れるでしょうか?
3) ちなみに、Excel単体を普通にスタートボタンから起動するのに、どれぐらい時間がかかりますか? 1回目起動して、一度閉じて、また、起動してみて。初回と2回目以降ではだいぶ時間が違うと思いますが。それぞれ、起動に要する体感時間はどんなものでしょう? (もし、これが5秒ぐらいかかっているのなら、zen73さんのプログラムだけではいかんともし難いかと。)
(ホームページを再開しました) -
ボタンをクリックして5秒近く(遅いと感じている時間なのですが…)経ってからExcelの画面が表示されます。
だとすると、外池さんのアドバイスにある、
1) 最初にお示し頂いているプログラムで、「Excel.Application exApp;」の行の上流側と下流側でプログラムを分けて、どの部分で時間がかかっているのかを調べる。(zen73さんのプログラムで時間がかかっているのか? ならば、どの部分で?)
を真っ先にやってみるべきだと思います。
TH01 さんが既に指摘されているとおり、ただ、推測ですが、実際のコードは書かれたコードをループで何度も実行されていたり、時間の計測にエクセルへの出力以外の部分も含められていたりしないでしょうか?
前のスレッドでの話やアの件などから、この可能性はありそうに思いました。
toRead の中でいっぱいループしているため、このあたりが原因である可能性が高いように思います。
# パフォーマンス改善はボトルネックを突き止めてから対策を打たないと、思い込みは結構見当はずれだったりして、痛い目を見ます。(見ました) -
zen73 さん
> 実際のコードであり簡略化はしていません。
そうですか。
でも、exApp の宣言から exApp の初期化までに、exApp を使用されている箇所はありませんので、そのエラーがでることはあり得ないと思い、実際のコードのままじゃないと想像しました。
エラーが出ていることには違いありませんので、何か私の想定外のことがあるんですね。
Excel.Application exApp; // ここで宣言
Excel.Workbooks wkBooks;
Excel._Workbook wkBook;
Excel.Sheets wkSheets;
Excel._Worksheet wkSheet;
Excel.Range range;
exApp = new Excel.Application(); // ここで割り当て
それと、速度ですが、実は5秒は十分速いと思ってしまいました。(^^; -
アの件は、なんとなくですが、わかりました。
Excel.Application はインターフェイスです。
インターフェイスは new できませんので、本来は
exApp = new Excel.Application();
はコンパイルエラーです。
しかし実際にはエラーにはならず、コンパイラが自動的に
exApp = new Excel.ApplicationClass();
に変更してくれます(コンパイル後の MSIL はそうなります)。
想像ですが、
exApp = new Excel.Application();
は本来は無効なコードのため、ソースコードの変更時の Visual Studio の簡易検証ロジックはこの部分をないものとして扱うため、未割当のままと誤認するのかなと思いました。
1行で書くとエラーにならない説明にはなりませんが、エラーになること自体が不具合だと思います。
自動変換されるので zen73 さんのコードが間違っているわけではないですけど、本来は以下が正しいコードだと思います。
これでしたらエラーにはなりませんでした。
Excel.Application exApp;
exApp = new Excel.ApplicationClass(); // 後ろに Class が付いてます。
exApp.Visible = true;