C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[5]: C#-エクセル 遅延バインディングでの アクティブセルの取得


(過去ログ 113 を表示中)

[トピック内 7 記事 (1 - 7 表示)]  << 0 >>

■66697 / inTopicNo.1)  C#-エクセル 遅延バインディングでの アクティブセルの取得
  
□投稿者/ きのこる (1回)-(2013/05/17(Fri) 15:08:35)

分類:[C#] 

環境:C#2010Express Excel2010

C#から遅延バインディングを用いてエクセルへのデータ入力を行いたいのですが、
アドレスの指定ではなく、エクセル上でアクティブなセルをターゲットにしたいと考えています。

ですが、エクセルを表示させたり番地指定の入力はできたのですが、
アクティブセルへの入力方法やアドレスの取得方法がわかりません。

方法をご教授頂けませんでしょうか?宜しくお願い致します。
引用返信 編集キー/
■66699 / inTopicNo.2)  Re[1]: C#-エクセル 遅延バインディングでの アクティブセルの取得
□投稿者/ shu (318回)-(2013/05/17(Fri) 15:32:24)
No66697 (きのこる さん) に返信
> アクティブセルへの入力方法やアドレスの取得方法がわかりません。
選ばれている物はSelectionで取得出来ます。SelectionがRangeなのか、Shapeなのか
他のものなのかはそのときの状況で変わるので判定が必要になります。

引用返信 編集キー/
■66700 / inTopicNo.3)  Re[1]: C#-エクセル 遅延バインディングでの アクティブセルの取得
□投稿者/ 魔界の仮面弁士 (216回)-(2013/05/17(Fri) 15:34:58)
No66697 (きのこる さん) に返信
> C#から遅延バインディングを用いて
2010 なら、dynamic が使えるので楽ですね。

> エクセル上でアクティブなセルをターゲットにしたいと考えています。
ActiveCell プロパティ あるいは Selection プロパティで取得できます。

なお、そもそもいずれのセルも選択されていない状態の場合、
null が返却されることもあるのでご注意を。(稀なケースですが)
引用返信 編集キー/
■66702 / inTopicNo.4)  Re[2]: C#-エクセル 遅延バインディングでの アクティブセルの取得
□投稿者/ きのこる (2回)-(2013/05/17(Fri) 18:50:09)
2013/05/17(Fri) 18:56:11 編集(投稿者)
2013/05/17(Fri) 18:50:40 編集(投稿者)

<pre><pre>お二方ともありがとうございました!

excelObject.Selection (ActiveCell)とするべきところを、
worksheetObject.Selection (ActiveCell)としてしまっていたのが間違いだった様です。
「そもそもSelectionやActiveCellを使用するのが間違いなのかもしれない?」
と混乱していたため、お二人の助言により間違いを特定することができました。

拙いながら作成したサンプルコードを記載させて頂きます。
ActiveCellに"test"を入力後、一つ下のセルを選択した後、保存終了します。
プロセスは残らない事を確認しましたが、問題点があればご指導頂ければ幸いです。

C#4.0(VisualStudio2010)用
追加参照:using System.Runtime.InteropServices;

Type t = Type.GetTypeFromProgID("Excel.Application");
dynamic excel = Activator.CreateInstance(t);
dynamic workbooks = excel.workbooks;
dynamic workbook1 = workbooks.Open(textBox1.Text);
dynamic worksheets = workbook1.Sheets;
dynamic worksheet1 = worksheets[1];
excel.visible = true;
worksheet1.Activate();
dynamic targetCell = excel.ActiveCell; //場合によりActiveCellがnullである可能性あり
targetCell.value = "test";
targetCell.Offset(1, 0).Select();
workbook1.Save();
workbook1.Close();
excel.Quit();
Marshal.ReleaseComObject(targetCell);
Marshal.ReleaseComObject(worksheet1);
Marshal.ReleaseComObject(worksheets);
Marshal.ReleaseComObject(workbook1);
Marshal.ReleaseComObject(workbooks);
Marshal.ReleaseComObject(excel);</pre></pre>
解決済み
引用返信 編集キー/
■66704 / inTopicNo.5)  Re[3]: C#-エクセル 遅延バインディングでの アクティブセルの取得
□投稿者/ 魔界の仮面弁士 (217回)-(2013/05/17(Fri) 21:11:31)
No66702 (きのこる さん) に返信
> excel.visible = true;
visible ではなく
Visible ですね。

大文字小文字が区別されるわけではありませんが、一応念のため。


> worksheet1.Activate();
> dynamic targetCell = excel.ActiveCell; //場合によりActiveCellがnullである可能性あり
たとえば、先頭シートである worksheet1 ワークシートではなくグラフシートだった場合、
ActiveCell は null を返すことになります。

必ず Worksheet にアクセスするのであれば、.Sheets プロパティではなく
.Worksheets プロパティを利用するという手があります。
(ワークシートが 0 枚だった場合は、どちらにせよ駄目ですが)


> targetCell.Offset(1, 0).Select();
Offset プロパティの戻り値は COM の Range 型です。
他と同様、結果を変数に受け取って ReleaseComObject しましょう。

なお、Offset は「省略可能な引数付きプロパティ」なので、
 dynamic kinokoru = targetCell.Offset[1, 0];
 kinokoru.Select();
のように、インデクサ表記で呼び出すのが本来の C# 4.0 の表記法です。
まぁ、.Offset(1, 0) でも一応呼べますけれどね。


ちなみに C# 2.0 以下で参照設定して実装する場合は、
.get_Offset メソッドに変換されることになります。
(実際には相互運用機能アセンブリ次第ですが)


> 問題点があればご指導頂ければ幸いです。
手元の環境で試したところ、
 Marshal.ReleaseComObject(workbook1);
を実行する際に、Visual Studio が下記を表示して一時停止する現象が発生しました。
http://www.vb-user.net/junk/replySamples/2013.05.17.20.48/DisconnectedContext.png

そのまま再開し、同じ行を再実行させると、今度は警告も無く解放されるのですけれども。
(開発環境から実行させず、EXE 単体実行では再現しない模様)

解放順を入れ替えてもみたり、Debug/Release を切り替えてみたり、
Visual Studio ホスティングプロセスを無効にしてみても駄目。

かといって、該当行をコメントアウトすると、やっぱり解放漏れでプロセスが残ってしまうジレンマ。
引用返信 編集キー/
■66727 / inTopicNo.6)  Re[4]: C#-エクセル 遅延バインディングでの アクティブセルの取得
□投稿者/ きのこる (3回)-(2013/05/20(Mon) 12:00:12)
No66704 (魔界の仮面弁士 さん) に返信
あらためてありがとうございます。
まだまだわかっていない部分が多いですが、
おかげさまで少しずつ理解が進んで来たように思います。


>>excel.visible = true;
> visible ではなく
> Visible ですね。
>
> 大文字小文字が区別されるわけではありませんが、一応念のため。
大文字小文字の表記が正確でない部分が何カ所かありましたorz
VB6.0より先日から移行を開始したせいか、ミスが多いです。


>>worksheet1.Activate();
>>dynamic targetCell = excel.ActiveCell; //場合によりActiveCellがnullである可能性あり
> たとえば、先頭シートである worksheet1 ワークシートではなくグラフシートだった場合、
> ActiveCell は null を返すことになります。
>
> 必ず Worksheet にアクセスするのであれば、.Sheets プロパティではなく
> .Worksheets プロパティを利用するという手があります。
> (ワークシートが 0 枚だった場合は、どちらにせよ駄目ですが)
例外の状況がイマイチ想像できていませんでしたが、なるほどです。
今回のアプリは職場で限定された環境で使用するのですが、
少なくとも今回の内容にはSheetsよりWorksheetsの方が適していますね!


>>targetCell.Offset(1, 0).Select();
> Offset プロパティの戻り値は COM の Range 型です。
> 他と同様、結果を変数に受け取って ReleaseComObject しましょう。
なるほど! 他の命令はA.Bというスタイルなのに対して、
ここだけA.B.Cというスタイルなので違和感はあったのですが…
問題は宣言も解放もされていない中間のオブジェクトが使用されているという事ですね。


> なお、Offset は「省略可能な引数付きプロパティ」なので、
>  dynamic kinokoru = targetCell.Offset[1, 0];
>  kinokoru.Select();
> のように、インデクサ表記で呼び出すのが本来の C# 4.0 の表記法です。
> まぁ、.Offset(1, 0) でも一応呼べますけれどね。
>
>
> ちなみに C# 2.0 以下で参照設定して実装する場合は、
> .get_Offset メソッドに変換されることになります。
> (実際には相互運用機能アセンブリ次第ですが)
すこし勉強してみましたが、残念ながらイマイチ理解できていません。
2.0だとget_Offset(1,0)と表記する必要がある(すべてのパラメータを省略せず記載する必要あり)
4.0だとOffset[1,0]と表記(xを省略してOffset[1]と書く事もできる?(この場合意味不明だけど))
…ちゃんとした勉強が必要な様です。
HNとは言え名前が変数名に出てくると妙に気恥ずかしいですねw


>>問題点があればご指導頂ければ幸いです。
> 手元の環境で試したところ、
>  Marshal.ReleaseComObject(workbook1);
> を実行する際に、Visual Studio が下記を表示して一時停止する現象が発生しました。
> http://www.vb-user.net/junk/replySamples/2013.05.17.20.48/DisconnectedContext.png
>
> そのまま再開し、同じ行を再実行させると、今度は警告も無く解放されるのですけれども。
> (開発環境から実行させず、EXE 単体実行では再現しない模様)
>
> 解放順を入れ替えてもみたり、Debug/Release を切り替えてみたり、
> Visual Studio ホスティングプロセスを無効にしてみても駄目。
>
> かといって、該当行をコメントアウトすると、やっぱり解放漏れでプロセスが残ってしまうジレンマ。
テスト頂きありがとうございます!
こちらでも色々パターンを試してみましたが、自分の環境では再現できませんでした。
エクセルのバージョン等に影響を受けたりするのでしょうか?
単体起動では問題が出ないにしても少し気持ち悪いですね。


一応修正版を貼らせて頂きます。
ActiveCellに"test"を入力し、一つ下のセルを選択した後、上書き保存終了します。

C#4.0(VisualStudio2010)用
追加参照:using System.Runtime.InteropServices;

//場合により各オブジェクトが存在しなかった際の例外処理が必要
Type t = Type.GetTypeFromProgID("Excel.Application");
dynamic excel = Activator.CreateInstance(t);
dynamic workbooks = excel.Workbooks;
dynamic workbook1 = workbooks.Open(textBox1.Text);
dynamic worksheets = workbook1.Worksheets;
dynamic worksheet1 = worksheets[1];
excel.Visible = true;
worksheet1.Activate();
dynamic targetCell = excel.ActiveCell;
targetCell.Value = "test";
dynamic offsetCell = targetCell.Offset[1,0];
offsetCell.Select();
workbook1.Save();
workbook1.Close();
excel.Quit();
Marshal.ReleaseComObject(offsetCell);
Marshal.ReleaseComObject(targetCell);
Marshal.ReleaseComObject(worksheet1);
Marshal.ReleaseComObject(worksheets);
Marshal.ReleaseComObject(workbook1);
Marshal.ReleaseComObject(workbooks);
Marshal.ReleaseComObject(excel);

解決済み
引用返信 編集キー/
■66735 / inTopicNo.7)  Re[5]: C#-エクセル 遅延バインディングでの アクティブセルの取得
□投稿者/ 魔界の仮面弁士 (222回)-(2013/05/20(Mon) 22:05:51)
2013/05/20(Mon) 22:06:47 編集(投稿者)

No66727 (きのこる さん) に返信
> 2.0だとget_Offset(1,0)と表記する必要がある(すべてのパラメータを省略せず記載する必要あり)
> 4.0だとOffset[1,0]と表記(xを省略してOffset[1]と書く事もできる?(この場合意味不明だけど))
「xを省略して」というのは、Offset(行, 列) プロパティの、第二引数(列)を省略する場合のことですね。
実際 VBA の世界では、.Offset(1) や .Offset(, 1) といった表現も見かけます。


> すこし勉強してみましたが、残念ながらイマイチ理解できていません。
省略可能な引数については理解頂いているように見えたので、『get_Offset』の件について。


今回の get_ 表記は、C# にて参照設定で呼び出す場合に限定した話です。
(とりあえずは「そういうものである」という程度の認識で OK かと思います)


そもそも、Excel の Offset プロパティは、引数が必要なプロパティであるにも関わらず、
肝心の C# が、『引数付きプロパティ』をサポートしていないという点から生じる問題です。


COM コンポーネント、あるいは VB 4 以降や VBA においては、
引数付きプロパティがサポートされており、たとえば VB.NET であれば
 Public Interface ISample
  ReadOnly Property Foo(ByVal a As Integer) As Integer
  ReadOnly Property Bar(ByVal a As Integer) As Integer
 End Interface
という定義を持った DLL を容易に作成できます。

しかしながら、C# の言語仕様ではこのような定義を作成できません。
(一応、近い機能としてインデクサーが用意されてはいますが)

ただし作成できないだけで、利用することはできます。

上記をコンパイルすると、自動的に get_Foo/get_Bar という名のアクセサメソッドが
生成される仕様になっています。これにより、引数付きプロパティをサポートしてない言語や
そもそもプロパティをサポートしていない言語などからも呼び出すことが出来る仕組みです。
(書き込み可能なプロパティの場合は、同様に set_系メソッドが用意されます)


その結果、上記の VB 製 DLL を C# から参照設定した場合、その定義が
 public interface ISample
 {
  int get_Bar(int a);
  int get_Foo(int a);
 }
というメソッド表記であるかのように振る舞われることになります。
実際はプロパティなのに、メソッドが並んでいるように見えますね。

この点は C# 5.0 になった今でも変わりません。
これが、「.get_Offset メソッド」を使う事になる理由です。


Excel の PIA である Microsoft.Office.Interop.Excel の .NET ライブラリを、
Visual C# 2005 や 2008 から参照した場合、Excel.Range クラスの Offset プロパティは、
  object x = rng.get_Offset(1, 0); // OK
 //object y = rng.Offset[1, 0]; // NG
のような動作となります。

一方、C# 4.0 からは COM 対応が強化されており、C# 2010 や 2012 から参照した場合に、
  object x = rng.get_Offset(1, 0); // OK
  object y = rng.Offset[1, 0]; // OK
のように、元のプロパティのままでも扱えるように拡張されました。
(相手が COM オブジェクトでない場合は、プロパティとメソッドが区別されます)


ただし、C# から Type.InvokeMember で呼び出す場合や、dynamic から呼び出す場合、
あるいは VB から利用する場合には、get_Offset メソッドではなく、
Offset プロパティとして扱う必要があります。

長々書きましたが、要するに、get_Offset 形式での指定が必要になるのは、
VS200X 系の C# にて参照設定して開発している場合に限定されるということです。



>>> C#から遅延バインディングを用いてエクセルへのデータ入力を行いたいのですが、
遅延バインディングの場合、実行する Excel バージョンによって書き方が変わったり、
同じコードが別のメソッド呼び出しになる可能性を秘めているため、ご注意ください。

たとえば、Range.Value プロパティも、先の getter/setter の問題を抱えています。
しかも Offset プロパティとは異なり、Excel バージョンによって動作が異なります。

Range オブジェクトの Value プロパティは、
Excel 2000 以下では引数の無いプロパティでしたが、
Excel 2002 以上では、省略可能な引数として XlRangeValueDataType を
受け取るようになっているためです。


バージョンによる違いが大きいところでは、Range.Insert メソッド。
今回は使用されていないので関係ありませんが、
 Excel 2002 が『Function Insert(Optional Shift, Optional CopyOrigin)』
 Excel 2000 が『Function Insert(Optional Shift)』
 Excel 97 が、『Sub Insert(optional Shift)』
という、バージョンごとに異なる実装を持っている為、要件次第では
バージョンチェックのコードも必要になる可能性があります。


まぁ、2010 以降では dynamic が使えるようになったため、
この手の問題に対処しやすくなってはいるのですけれども。
解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -