- PR -

VB.NETでClipBoardクラスを使用しつつ排他処理

投稿者投稿内容
Yakisoba
ベテラン
会議室デビュー日: 2006/05/12
投稿数: 62
お住まい・勤務地: 渋谷苦
投稿日時: 2007-03-07 18:17
こんばんは。
いま、VB.NETでExcel帳票作成アプリを作成しています。
環境はVS2003です。


直接Excelのセルに値を埋め込むと、処理に時間がかかってしまうため、
一度CSVを作成してからClipBoardを使用して
Excelに貼り付けようとしました。
貼り付けまではうまくいき、処理にかかる時間も大幅に短縮したのですが、
このアプリはサーバーに置いておき、
複数同時アクセスを可能にしたいのです。
しかし同時にアプリを起動すると、タイミングによって前後のClipBoardの内容が
貼り付けられてしまいます。

そこで、ClipBoard使用時に排他処理をかませたいのですが、
何かいい方法はあるでしょうか。
アドバイスを頂けたらと思います。

以下、ソース。


' Excelアプリ起動
xlsApp = CreateObject("Excel.Application")
xlsApp.DisplayAlerts = False
xlsBook = xlsApp.Workbooks.Open(m_xlsFilePath)
xlsApp.Application.Visible = False

' シートを選択
With xlsBook.Worksheets(1)
' クリップボードにデータをコピーする
System.Windows.Forms.Clipboard.SetDataObject(m_StrExcelData)
' 貼り付け先のシートをActivateにする
.Activate()
' 貼り付け先のセル
.Range("C13").Select()
' ペースト
.Paste()
' クリップボードの内容をクリア(現在はコメントアウト)
'System.Windows.Forms.Clipboard.SetDataObject(NewSystem.Windows.Forms.DataObject)
End With

' ファイルを保存
xlsBook.SaveAs(m_xlsFilePath)

xlsBook.Close() : xlsBook = Nothing
xlsApp.Application.Visible = False
xlsApp.Quit() : xlsApp = Nothing


[ メッセージ編集済み 編集者: Yakisoba 編集日時 2007-03-07 18:21 ]
ぶさいくろう
ぬし
会議室デビュー日: 2005/11/22
投稿数: 1232
お住まい・勤務地: 川崎市(は俺も含めてロクな人間が住んでないよw)
投稿日時: 2007-03-07 19:09
引用:

Yakisobaさんの書き込み (2007-03-07 18:17) より:
こんばんは。
いま、VB.NETでExcel帳票作成アプリを作成しています。
環境はVS2003です。


直接Excelのセルに値を埋め込むと、処理に時間がかかってしまうため、
一度CSVを作成してからClipBoardを使用して
Excelに貼り付けようとしました。
貼り付けまではうまくいき、処理にかかる時間も大幅に短縮したのですが、
このアプリはサーバーに置いておき、
複数同時アクセスを可能にしたいのです。
しかし同時にアプリを起動すると、タイミングによって前後のClipBoardの内容が
貼り付けられてしまいます。

そこで、ClipBoard使用時に排他処理をかませたいのですが、
何かいい方法はあるでしょうか。
アドバイスを頂けたらと思います。


二重で起動できなくする。
って書こうと思ったけどその前にこれが実行されるのってクライアント側なの?
引用:

以下、ソース。


' Excelアプリ起動
xlsApp = CreateObject("Excel.Application")
xlsApp.DisplayAlerts = False
xlsBook = xlsApp.Workbooks.Open(m_xlsFilePath)
xlsApp.Application.Visible = False

' シートを選択
With xlsBook.Worksheets(1)
' クリップボードにデータをコピーする
System.Windows.Forms.Clipboard.SetDataObject(m_StrExcelData)
' 貼り付け先のシートをActivateにする
.Activate()
' 貼り付け先のセル
.Range("C13").Select()
' ペースト
.Paste()
' クリップボードの内容をクリア(現在はコメントアウト)
'System.Windows.Forms.Clipboard.SetDataObject(NewSystem.Windows.Forms.DataObject)
End With

' ファイルを保存
xlsBook.SaveAs(m_xlsFilePath)

xlsBook.Close() : xlsBook = Nothing
xlsApp.Application.Visible = False
xlsApp.Quit() : xlsApp = Nothing


[ メッセージ編集済み 編集者: Yakisoba 編集日時 2007-03-07 18:21 ]


これはひどいwこのサイトをReleaseComObject(だっけ?)で検索しなさい。

[ メッセージ編集済み 編集者: ぶさいくろう 編集日時 2007-03-07 19:10 ]
Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2007-03-07 19:18
ツリーからExcelでもいいぞ

配列でコピー
ODBC経由で流し込む



ディレクトリだった

[ メッセージ編集済み 編集者: Jitta 編集日時 2007-03-07 19:20 ]
Yakisoba
ベテラン
会議室デビュー日: 2006/05/12
投稿数: 62
お住まい・勤務地: 渋谷苦
投稿日時: 2007-03-07 21:31
早速のご返答ありがとうございます。

ぶさいくろうさんのおっしゃった通り、

xlsBook.Close() : xlsBook = Nothing
xlsApp.Application.Visible = False
xlsApp.Quit() : xlsApp = Nothing

の部分を以下のように変えました。

Finally
' オブジェクトの解放
If Not (xlsRange Is Nothing) Then
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlsRange)
End If
If Not xlsSheet Is Nothing Then
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlsSheet)
End If
If Not xlsBook Is Nothing Then
Try
xlsBook.Close()
Finally
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlsBook)
End Try
End If
If Not xlsApp Is Nothing Then
Try
xlsApp.Quit()
Finally
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlsApp)
End Try
End If
End Try


アプリが終了するまで、プロセスが残ったままなのですが、
この動きは正しいのでしょうか。
解放した時点で消えるものだと思っていたもので・・・
もちろんアプリ終了時にはプロセスは消えています。

最初の方で、複数同時アクセスと書きましたが、
サーバー上で複数同時実行される、ということです。


また、Jittaさんのおっしゃっていることですが、
オートメーションを使用してデータの配列をブックの特定のセル範囲に転送する
ということでしょうか。
認識違いでしたら申し訳ないです。


[ メッセージ編集済み 編集者: Yakisoba 編集日時 2007-03-07 21:46 ]
Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2007-03-07 23:04
http://www.atmarkit.co.jp/fdotnet/index/bbs/dir91.html

苦労しているんだ(笑)見てくれ。


モバイルからだった and 降りるバス停がすぐそこだったので簡単に書いてごめん。

方法は2つ。

1.Excel を“データベース”として扱い、ODBC 経由でデータを流し込む方法。
2.オートメーションで Excel を操作し、配列データで一気にデータを流し込む方法。

があります。どちらも過去ログにあるので、上の URL で、タイトルを一通り眺めてみてください。


引用:

アプリが終了するまで、プロセスが残ったままなのですが、
この動きは正しいのでしょうか。


いいえ。正しく解放されていれば、xlsApp.Quit() か、xlsApp をリリースした時点でプロセスが終了します。
_________________
じゃんぬねっと
ぬし
会議室デビュー日: 2004/12/22
投稿数: 7811
お住まい・勤務地: 愛知県名古屋市
投稿日時: 2007-03-08 09:11
引用:

Yakisobaさんの書き込み (2007-03-07 21:31) より:

xlsBook.Close() : xlsBook = Nothing
xlsApp.Application.Visible = False
xlsApp.Quit() : xlsApp = Nothing

の部分を以下のように変えました。


この部分以外のところも NG です。

引用:

Yakisobaさんの書き込み (2007-03-07 18:17) より:

xlsBook = xlsApp.Workbooks.Open(m_xlsFilePath)


Excel.Workbooks インスタンスの参照の取り忘れています。
取り忘れる == ReleaseComObject メソッドを使って参照カウントをデクリメントする機会を完全に失います。

引用:

xlsApp.Application.Visible = False


意味のない参照カウントを増やすだけの実装になっています。

xlsApp は Application インターフェイス。
xlsApp.Application も Application インターフェイス。
そして、どちらも実体は同じものを示します。

引用:

With xlsBook.Worksheets(1)
  :
End With


Excel.Worksheets (Excel.Sheets) インスタンスの参照の取り忘れています。
取り忘れる == ReleaseComObject メソッドを使って参照カウントをデクリメントする機会を完全に失います。

With ステートメントを使っているため、With ブロック内はほとんど全滅です。

引用:

.Range("C13").Select()


Excel.Range インスタンスの参照の取り忘れています。
取り忘れる == ReleaseComObject メソッドを使って参照カウントをデクリメントする機会を完全に失います。

で、Office PIA をここまでふんだんに使う方法を選択するのは賢明ではなく、
  1. データを Row と Column の配列にして、VB.NET から VBA の関数を起動して Excel 側から Fill する
  2. Jet OLE DB プロバイダを使って、INSERT INTO で流し込んでしまう
のいずれかが良いでしょう。

# Jitta さんの言う、"ODBC 経由でデータを流し込む" は、2. に当たる...?

_________________
C# と VB.NET の入門サイト
じゃんぬねっと日誌
KI
大ベテラン
会議室デビュー日: 2007/01/10
投稿数: 239
投稿日時: 2007-03-08 10:04
サーバー側で同時実行される可能性があるのであれば、
クリップボードを使う以上は無理だと思います。
仮に二重起動を防止したり、ロックファイルを使用したりして解決したとしても、
別のプログラムがクリップボードを使用したりする可能性もありますし、
保守作業中にコピーを行ってしまうこともあるかも知れません。
少なくとも安全とは言えないと思います。

引用:

Yakisobaさんの書き込み (2007-03-07 18:17) より:

一度CSVを作成してからClipBoardを使用して
Excelに貼り付けようとしました。



CSVを作成とありますが、物理ファイルとして作成しているのでしょうか?
物理ファイルがあるならQueryTables.Add メソッドを使って取り込む手も使えるかも知れません。
当然CSVも排他を考慮して作成する必要がありますが。
Yakisoba
ベテラン
会議室デビュー日: 2006/05/12
投稿数: 62
お住まい・勤務地: 渋谷苦
投稿日時: 2007-03-08 13:46
ご返答ありがとうございます。

ご指摘頂いた通り、Excelオブジェクトを細かく宣言し、
その1つ1つに対して解放することで
Excelのプロセスが消えるようになりました。

引用:

1.Excel を“データベース”として扱い、ODBC 経由でデータを流し込む方法。
2.オートメーションで Excel を操作し、配列データで一気にデータを流し込む方法。

3.データを Row と Column の配列にして、VB.NET から VBA の関数を起動して Excel 側から Fill する。
4.Jet OLE DB プロバイダを使って、INSERT INTO で流し込んでしまう。
5.QueryTables.Add メソッドを使って取り込む。



様々な方法があるのですね。勉強になります。
とりあえず配列を使用しRangeで貼り付けるところから試してみようと思います。

スキルアップ/キャリアアップ(JOB@IT)