→.NETプログラミング
→ExcelのVBA
→PythonでExcel制御
#contents
*参考ページ [#k466a2d3]
-[[消えないExcelのプロセス>http://rucio.a.la9.jp/main/technique/teq_15.htm]]
-[[.NET 開発者の観点から捉えたExcelオブジェクト:http://www.microsoft.com/japan/msdn/officedev/vsto2003/ExcelObj.asp]]
*Excel出力サンプル [#ke14c6cd]
public class OutputExcel : Output
{
public String m_path = "";
Application oXL = null;
Workbooks oWBs = null;
_Workbook oWB = null;
Sheets oWSs = null;
_Worksheet oSheet = null;
int CurRow = 1;
public override int Open()
{
oXL = new Application();
oXL.Visible = false;
oWBs = oXL.Workbooks;
oWB = oWBs.Add(Type.Missing);
oWSs = oWB.Worksheets;
oSheet = (_Worksheet)oWB.ActiveSheet;
oXL.ScreenUpdating = false; // 画面更新をしない(最初から隠してるけど)
return rc;
}
public override int Write( OutRow r ) // OutRowはArrayListから派生
{
for( int i = 0; i < r.Count; i++ )
{
Cell c = (Cell)r[i]; // Cellは独自定義。一つのセルに入れる情報
String s = c.GetValue();
oSheet.Cells[CurRow,i+1] = s;
}
CurRow++; // 行数カウント
// 列幅の最適化
Range oRng = oSheet.get_Range( "A1", "Z256");
oRng.EntireColumn.AutoFit();
ReleaseCom(oRng);
oRng = null;
return 0;
}
public override int Close()
{
oXL.ScreenUpdating = true;
ReleaseCom(oSheet);
oSheet = null;
ReleaseCom(oWSs );
oWSs = null;
oXL.DisplayAlerts = false; // 「上書きしてもいいですか?」の問い合わせを抑制
oWB.SaveAs(m_path,Type.Missing,
Type.Missing,Type.Missing,
Type.Missing,Type.Missing,XlSaveAsAccessMode.xlExclusive,
Type.Missing,Type.Missing,Type.Missing,Type.Missing );
oWB.Close(false,Type.Missing, Type.Missing);
ReleaseCom(oWB);
oWB = null;
ReleaseCom(oWBs);
oWBs = null;
oXL.Quit();
ReleaseCom(oXL);
oXL = null;
GC.Collect(); //ガーベジコレクト起動
return 0;
}
void ReleaseCom(Object o)
{
System.Runtime.InteropServices.Marshal.ReleaseComObject(o);
}
}
*WorkSheetなどの特定範囲を参照する [#r5a69829]
-[[MSのKB:http://www.microsoft.com/japan/msdn/library/default.asp?url=/japan/msdn/library/ja/dv_wrcore/html/wrtskhowtorefertoworksheetrangesincode.asp]]
-例)指定された範囲の行の選択範囲を得る
// VBAの Rows("XX:XX")に相当
Range rng = oSheet.Rows[ st + ":" + end, Type.Missing] as Range
または
Range rng = oSheet.get_Range( st + ":" + end, Type.Missing);
*VBAからの翻訳のコツ [#v36803d5]
-Selection は ExcelオブジェクトのSelectionなどが使える
-例:
oSheet.Select(Type.Missing);
Debug.Assert((oXL.Selection as Range) !=null);
(oXL.Selection as Range).Font.ColorIndex = 0;
*遅延バインディング [#ydcb6c91]
-http://support.microsoft.com/default.aspx?scid=kb;JA;302902
--Office2000のExcelに事前バインディング(Excel9.olbを参照)するとOffice 2003を入れている開発環境でコンパイルが通らなくなる。これを解決するには遅延バインディングにすればよいのだが、開発の手間が格段にかかってしまうのであまり現実的ではない。Excel制御の部分だけ別プロジェクトにしてCOMと相性のいいVB.NETでやった方がいいかもしれない。
*Excelのプロセスが残る問題 [#h16d6fef]
※ここに書いたことは2006年頃の状況なので、最近のバージョンであれば改善されているかもしれない。
-オブジェクトの使用が終了したら、System.Runtime.InteropServices.Marshal.ReleaseComObject を使用して開放する。
-このメソッドによって RCW の参照カウントを減らします。あとGC.Collect()も呼んだ方が良いらしい。
--http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=13903&forum=7&start=0
--http://support.microsoft.com/default.aspx?scid=kb;ja;317109
--http://support.microsoft.com/kb/306683/
--http://naka.wankuma.com/site/column/dotnet/00001.htm
-Excelのプロセスが残らないようにするポイントは以下の通り。
--各オブジェクト(Application,Workbooks,_Workbook, Sheets, _Worksheet, Rangeなど)をすべて変数で受けて、ReleaseComObject()で一つずつ明示的にリリースする。
--Application のQuit()メソッドは他のオブジェクトをすべてリリースし終えてから呼ぶ。
--Cellsなんかも見落としがちだが、必ず個別に受けてリリースしないといけないので激しく注意!
---良い例
//Cells と Cell は別であることを激しく注意せよ!
Range cells = oSheet.Cells;
Range cell = cells[1,1] as Range;
cell.Select();
ReleaseCom(cell);
cell = null;
ReleaseCom(cells);
cells = null;
---ダメな例
Range cell = oSheet.Cells[1,1] as Range; // <--ここがダメ!!
cell.Select();
ReleaseCom(cell);
cell = null;
-とにかく原則として''ピリオドでオブジェクトを2つ以上つないではいけない''と思ったほうがよい
-よく「↑のようなことはちゃんとやってるのにプロセスが残ってしまう」と言ってくる人がいるが、たいていソースを見ると「ちゃんとやって」ない。そのくらい、わかっていてもついうっかりやってしまうほどこれは難しい。細心の注意が必要。
*枠線を引く [#ve0e1a14]
-サンプル
void DrawFrameLines()
{
try
{
oSheet.Activate();
Range ac = oXL.ActiveCell;
Range lastcell = ac.SpecialCells(XlCellType.xlCellTypeLastCell,Type.Missing);
Range all = oSheet.get_Range("A1:A1", lastcell);
int i = 0;
Border[] bdr = new Border[6];
bdr[i++] = all.Borders[XlBordersIndex.xlEdgeTop];
bdr[i++] = all.Borders[XlBordersIndex.xlEdgeBottom];
bdr[i++] = all.Borders[XlBordersIndex.xlEdgeLeft];
bdr[i++] = all.Borders[XlBordersIndex.xlEdgeRight];
bdr[i++] = all.Borders[XlBordersIndex.xlInsideHorizontal];
bdr[i++] = all.Borders[XlBordersIndex.xlInsideVertical];
foreach(Border b in bdr)
{
// 列や行が1つのときにxlInsideHorizontal,xlInsideVerticalのボーダーがエラーとなるので
if( (int)b.LineStyle < 10 ) { //これでいいのかどうかもうひとつわからないが
b.LineStyle = XlLineStyle.xlContinuous;
b.Weight = XlBorderWeight.xlThin;
b.ColorIndex = XlColorIndex.xlColorIndexAutomatic;
}
}
for(int j = bdr.Length-1; j >= 0; j--)
{
ReleaseCom(bdr[j]);
}
ReleaseCom(all);
ReleaseCom(lastcell);
ReleaseCom(ac);
}
catch(Exception ex)
{
Debug.Assert(false, ex.Message);
}
}
*1行目をタイトル行として背景色をつける [#c3d1f6b0]
-サンプル
// タイトル行に背景色をつける
void ColorTitleLine()
{
try
{
oSheet.Activate();
Range a1 = oSheet.get_Range("A1:A1", Type.Missing);
a1.Select();
Range end = a1.get_End(XlDirection.xlToRight);
a1.Select();
Range lastcell = a1.SpecialCells(XlCellType.xlCellTypeLastCell,Type.Missing);
int col = lastcell.Column;
//データが1列しかない場合、こうしないと256列扱いになってしまう。もっとスマートな方法ないかな?
Range line;
if( col == 1 )
line = a1;
else
line = oSheet.get_Range(a1, end );
Interior i = line.Interior;
i.ColorIndex = 34;
i.Pattern = XlPattern.xlPatternSolid;
ReleaseCom(i);
ReleaseCom(line);
ReleaseCom(end);
ReleaseCom(lastcell);
ReleaseCom(a1);
}
catch(Exception ex)
{
Debug.Assert(false, ex.Message);
}
}
*Excelのワークシート名に使えない記号の処理 [#gbaa2200]
-ワークシート名に使えない文字がいくつかあるので注意したい
-以下はあらかじめ安全な名前に変換する例
String MakeSafeName(String Name)
{
//禁止されてる文字はピリオドに変換する
Char[] ErrChar = { ':', '\\', '/', '?', '*', '[',']'};
foreach(Char c in ErrChar)
{
Name = Name.Replace(c, '.');
}
Name = Name.Trim();
//長すぎる名前はダメ
if( Name.Length > 31 )
{
Name = Name.Substring(0, 31);
}
return Name;
}