tagCANDY CGI VBレスキュー(花ちゃん) の Visual Basic 2010 用 掲示板(VB.NET 掲示板) [ツリー表示へ]   [Home]
一括表示(VB.NET VB2005)
タイトルVB2005からAccess2000への連携時のトラブル
記事No7734
投稿日: 2008/06/25(Wed) 22:50
投稿者kiRITYAN
すいません。非常に困っています。どなたか手を貸してください。
開発言語 VB2005 Access2000
VB2005でメインの制御を行い、データはAccessに保存する処理を作成しています。
そこでVB2005からテキストデータをAccessにインポートする命令を与えるために、Accessのモジュールに記入した処理をVBから起動しています(大体下に示した感じです)
・txtはAccess側の処理が終わったのをVBが判別するのに使用しています。
  →終了を判別する別の方法ありますか?(結局これでうまく行ってないのですが…)

問題はこの方法で取り込みを行うとテーブルに追加されるのに時間がかかるらしく
普通に動かすと追加するテーブルデータが0件になります。時間を空けて走らせると18件。

データテーブルのRowのcountをループ文で取ってみたのですが(txtが削除された後です)
2秒ぐらいたつと元レコードにデータが追加されたようでいきなり0件から18件になります。
どなたかこの問題を解決するアドバイスをお願いいたします。
・ちなみにSQLでの抽出はこの前処理(同じプロシジャ)で同じSQLを使用して起動しています。
 この処理で0件の場合のみ、Accessにtxtデータを取り込み、再度テーブルの件数が
 どうなったかを確認しています。

Accessの処理
Public sub Test()
 DoCmd.TransferText acImportFixed,"T_目マスタ","インポート定義","C:\i.txt",True
 kill("\\192.168.1.3\c\end.txt") ←終わった合図
End sub

VB2005の処理
Private Sub CommandButton1_Click()
 はじめに\\192.168.1.3\c\end.txtにファイルを作成しておく
 Dim i as long
  Dim a As Object
 Dim Adapter As New OleDbDataAdapter(SQLCm)
 Dim Table As New DataTable  
 Set a = GetObject("\\192.168.1.3\c\test.mdb")
 a.Run "Test"  
  a=nothing
  do
    If System.IO.File.Exists("\\192.168.1.3\c\end.txt") Then
       i = i + 1
    else
       exit do
    end if
  loop
  do
   SQLCm.CommandText = "SELECT 説明 FROM T_目マスタ WHERE 目ID = 2"
    SQLCm.ExecuteNonQuery()
   Adapter.Fill(Table) 
   Table.Rows.Count ←ここを記録
  loop
End Sub

[ツリー表示へ]
タイトルRe: VB2005からAccess2000への連携時のトラブル
記事No7735
投稿日: 2008/06/26(Thu) 10:29
投稿者kiRITYAN
すいません。解決しました。
・クローズ、オープンをしたらうまく行きました。
・終了の判別は無くてもAccessが終了しなければVBの処理は進みませんでした

[ツリー表示へ]
タイトルRe^2: VB2005からAccess2000への連携時のトラブル
記事No7736
投稿日: 2008/06/26(Thu) 10:43
投稿者魔界の仮面弁士
解決したようで何よりです。が、少し気になった点を。

・Quit 処理が無いようですが、インポート後に
 Access を終了させていますか?

・変数 a に対する解放処理が書かれていませんが、
 インポート後に、オブジェクトを解放させていますか?
 (VBA とは異なり、Nothing の代入では解放されません)


投稿された 2005 のコードには、(.NET 構文ではなく)VBA の構文が
混じっていましたので、もしかしたら、単に掲示板投稿時のミスで
実際のコードでは対処済みかも知れませんが…一応念のため。

[ツリー表示へ]
タイトルRe^3: VB2005からAccess2000への連携時のトラブル
記事No7738
投稿日: 2008/06/26(Thu) 23:21
投稿者kiRITYAN
>>魔界の仮面弁士
ありがとうございます。
Quit は書いていません。
タスクに残っていなかったのでOKと思っていました。最後にQuitと入れておきます。
変数 a に対する解放処理は私の中ではNothingだったのですが違うのでしょうか?
よろしければ教えてください。
ちなみにこの処理ではVBからExcelに数値を代入しているのですがちょっと遅くこんなもんかな?と思っています。速度は30ぐらいのセルに値を代入→保存で1シート1秒くらいかかっています。普通ですか?
方法としては、DataTableにAccessのデータを代入し、1レコードずつヘッド部分、ボディー
部分と値を代入していっています。

[ツリー表示へ]
タイトルRe^4: VB2005からAccess2000への連携時のトラブル
記事No7739
投稿日: 2008/06/27(Fri) 01:33
投稿者魔界の仮面弁士
> 変数 a に対する解放処理は私の中ではNothingだったのですが違うのでしょうか?
それは VBA の場合ですよね、.NET ではなく。

> よろしければ教えてください。
Marshal クラスの ReleaseComObject メソッドを呼び出してください。

> ちなみにこの処理ではVBからExcelに数値を代入しているのですが
「この処理」というのは、No.7734 のコードの事ですか?
Excel への代入というのは、その中のどの部分を指しているのでしょう?

[ツリー表示へ]
タイトルRe^5: VB2005からAccess2000への連携時のトラブル
記事No7740
投稿日: 2008/06/27(Fri) 10:03
投稿者kiRITYAN
解放処理出来ました。ありがとうございます。
下の処理です。No.7734 のコードでデータを抽出した後したの標準モジュールを読みだしています。
Excel_Write_Open
Excel_Write_Main
Excel_Write_Close ← 30回くらい繰り返し
Excel_Write_End
このようにしたのは他の処理でも使いまわししたいからです。

    Dim Excel_App As New Excel.Application
    Dim Excel_Books As Excel.Workbooks
    Dim Excel_Book As Excel.Workbook
    Dim Excel_Sheets As Excel.Sheets
    Dim Excel_Sheet As Excel.Worksheet

Public Function Excel_Write_Open(ByVal Excel_Open_File_Pass As String, ByVal Excel_Open_Sheet As Integer) As Boolean
        Try
            Excel_Books = Excel_App.Workbooks
            If Excel_Open_File_Pass = "" Then                     '新規ファイルを開く
                Excel_Book = Excel_Books.Add
            Else                                                  '既存のファイルを開く
                Excel_Book = Excel_Books.Open(Excel_Open_File_Pass)
            End If
            Excel_Sheets = Excel_Book.Worksheets
            Excel_Sheet = Excel_Sheets.Item(Excel_Open_Sheet)     'Sheet1を選択
            Excel_Write_Open = True
        Catch Err As Exception
            MessageBox.Show(Err.Message & "Err", "警告", MessageBoxButtons.OK)
            Excel_Write_Open = False
        End Try
    End Function
    '******************************************************************************
    '* OpenされたExcelのセルに値を代入する
    '******************************************************************************
    Public Function Excel_Write_Main(ByVal Excel_Place As String, ByVal Excel_Value As String) As Boolean
        Try
            Excel_Sheet.Range(Excel_Place).Value = Excel_Value
            Excel_Write_Main = True
        Catch Err As Exception
            MessageBox.Show(Err.Message & "Err", "警告", MessageBoxButtons.OK)
            Excel_Write_Main = False
        End Try
    End Function
    '******************************************************************************
    '* Excelの終了と保存 
    '******************************************************************************
    Public Function Excel_Write_Close(ByVal Excel_Close_File_Pass As String) As Boolean
        Try
            Excel_App.DisplayAlerts = False   '保存時の問合せのダイアログを非表示
            If Excel_Close_File_Pass <> "" Then
                Excel_Sheet.SaveAs(Excel_Close_File_Pass)     'ファイルに保存
            End If
            Excel_App.DisplayAlerts = True              '問い合わせ設定を元に戻す

            MRComObject(Excel_Sheet)                        'Sheet の解放
            MRComObject(Excel_Sheets)                      'Sheets の解放
            Excel_Book.Close(False)                           'Book を閉じる
            MRComObject(Excel_Book)                        'Book の解放
            MRComObject(Excel_Books)                       'Books の解放
            Excel_App.Quit()                                  'Excelを閉じる
            Excel_Write_Close = True
        Catch Err As Exception
            MessageBox.Show( "失敗", "警告", MessageBoxButtons.OK)
            Excel_Write_Close = False
        End Try
    End Function
    '******************************************************************************
    '* Excelを開放する
    '******************************************************************************
    Public Function Excel_Write_End() As Boolean
        Try
            Close_Com_Object(Excel_App)                       'Appを開放する
            Excel_Write_End = True
        Catch Err As Exception
            MessageBox.Show( "Err", "警告", MessageBoxButtons.OK)
            Excel_Write_End = False
        End Try
    End Function

[ツリー表示へ]
タイトルRe^6: VB2005からAccess2000への連携時のトラブル
記事No7741
投稿日: 2008/06/27(Fri) 10:06
投稿者kiRITYAN
Close_Com_Object(Excel_App)    → MRComObject(Excel_App)
ですお願いします

[ツリー表示へ]
タイトルRe^6: VB2005からAccess2000への連携時のトラブル
記事No7746
投稿日: 2008/06/28(Sat) 08:59
投稿者魔界の仮面弁士
> 解放処理出来ました。ありがとうございます。
解決したようで何よりです。


問題は解決したようなので、以下、第三者からのたわ言をつらつらと。


> Public Function Excel_Write_Open(…
このメソッドが False を返すような状況における設計が、不十分であるように見えます。
・Excel_App 変数が Dispose 済み、あるいは Nothing 状態だった場合。
・ファイルを開くことができなかった場合。
・該当するシートが見つからなかった場合。
・Excel がビジー状態だった時(ユーザー応答のダイアログを表示中など)。
などの状況では、このメソッドは False を返す可能性がありますが、
その場合、それぞれの変数(Excel_Bookなど)の後始末はどのように行われるのでしょうか?

たとえば、存在しないシート番号を指定して Excel_Write_Open が呼ばれた場合
「Sheets は取得できたが、Sheet は Nothing な状態」になる可能性がありますが、
この場合の解放処理は、どこにその責務が発生するでしょうか?

もし、モジュール側にその責があるのなら、Excel_Write_Open 内にて、
失敗時の後始末を行わねばなりませんが、現在はそうなっていません。

あるいは、呼びだし側に解放の責任を負わせるならば、このメソッドが
False を返した場合に、それらを解放させるためのメソッドが用意されるべきですが
現在はそれが見当たりません。(事前の処理が正常に行われた場合には、
Excel_Write_Close/Excel_Write_End のメソッドで解放できますけれども…)


> Public Function Excel_Write_Main(…
メソッド内で使われている Excel.Range オブジェクトの解放処理が抜けています。
ここのサイトの Excel 関係の項目を再読ください。
http://hanatyan.sakura.ne.jp/dotnet/excelme.htm

なお、参照設定しているアセンブリによっては、Range や WorkSheets が
 「(ReleaseComObject の呼び出しが必須な) COM オブジェクトである場合」と、
 「(ReleaseComObject してはいけない) マネージオブジェクトである場合」とが
ありますので、この点も注意しておいた方が良いでしょう。


> Public Function Excel_Write_Close(…
このメソッドも True / False を返すように設計されていますが、呼び出し側では
False が返された場合に、どのように対処することが想定されているのでしょうか?


> MRComObject
そもそも、このメソッドの実装はどのようになっているのでしょうか。
同名のメソッドを実装したサンプルは幾つかありますが、それぞれ
微妙にコードが異なるようですので、少々気になります。


> このようにしたのは他の処理でも使いまわししたいからです。
Module でそのような実装を行うと、その分、ステート管理の手間が増えますよ。

Private Class の場合は、“メソッドの呼び出し順”を親クラス側で制限できるので
あまり問題にはなりませんけれども、今回は Module だそうなので、現状の
実装手法には少々不安を感じます。


現在の実装は、それぞれのモジュールレベル変数について
 ・その変数は、どのメソッドによって生成されるのか。
 ・その変数は、どのメソッドによって破棄されるのか。
 ・破棄する前に次の生成が行われた場合に、対処できているか。
 ・処理の途中で何らかのエラーが発生した場合、変数の状態はどうなるのか。
といった点の管理が、モジュール側で行われるのか、呼び出し側に任せるのかが
曖昧になってしまっているように見えました。

たとえば、Excel_Write_End や Excel_Write_Close を呼び出した後で、再度、
Excel 処理を行うような要件が今後発生した場合、現状のコードでは対応できません。
(クラスではなくモジュールであるため、Application インスタンスを再生成できない)

現時点の実装はそれで良いとしても、たとえば数ヵ月後、自分あるいは他人が
バグ修正や機能追加のために手を加えるような場合には、その開発者が
それぞれのモジュールレベル変数のスコープを十分に理解していないと、
オブジェクトの解放漏れといった、見つけにくい問題を引き起こしやすくなるかと。


----
以下、今回の実装に必ずしも必要というわけではありませんが、
COM オブジェクトをカプセル化する場合によく使われる
「Dispose Finalize パターン」という実装手法に関する技術資料を。

http://msdn.microsoft.com/ja-jp/library/s9bwddyx%28VS.80%29.aspx
http://msdn.microsoft.com/ja-jp/library/b1yfkh5e%28VS.80%29.aspx
http://hongliang.seesaa.net/article/14551716.html

[ツリー表示へ]
タイトルRe^7: VB2005からAccess2000への連携時のトラブル
記事No7747
投稿日: 2008/06/28(Sat) 12:34
投稿者kiRITYAN
そこまで考えて処理を作成していませんでした。
いろいろアドバイスしていただきありがとうございます。
クラスは理解しきれてないので、作ろうにもプロパティぐらいしか作れません。
(今後がんばって生きます)
MRComObjectはVBレスキューに書いてあったのを使用しました。
いろいろなところでtrueを返したりしているのは初めて作成したExcelの処理でしたので
どこでどのようなエラーが出るかわからなかったのでいろいろなところに返り値を設定しました。
ちなみにエラー処理はまったくありません。ストップしてエラーを通知するだけです。
>>現在の実装は、それぞれのモジュールレベル変数について
これを考えるためにしたの資料を参考にがんばって見ます。
ちょっとさっき見た感じでは???となっていますがもう少し詳しく見ていきます。

[ツリー表示へ]
タイトルRe^8: VB2005からAccess2000への連携時のトラブル
記事No7748
投稿日: 2008/06/28(Sat) 15:47
投稿者魔界の仮面弁士
> (今後がんばって生きます)
誤変換ですね。私も良く間違えます…。(^^;
http://www.google.com/search?hl=ja&q=%E3%81%8C%E3%82%93%E3%81%B0%E3%81%A3%E3%81%A6%E7%94%9F%E3%81%8D%E3%81%BE%E3%81%99+-%E3%81%8C%E3%82%93%E3%81%B0%E3%81%A3%E3%81%A6%E3%81%84%E3%81%8D%E3%81%BE%E3%81%99


> MRComObjectはVBレスキューに書いてあったのを使用しました。
どの方法の事でしょうか?
このサイトには、過去ログやサンプルを含め、数種類以上の
MRComObject の実装例があるので…。

なお、個人的には
http://hanatyan.sakura.ne.jp/dotnet/Excel07.htm
の方法は避けるべきかと思います。Nothing の判定も IsComObject も判定無く、
しかも例外を握りつぶしてしまっているので。(下記参照)
http://msdn.microsoft.com/ja-jp/library/ms182137.aspx

また、
http://hanatyan.sakura.ne.jp/dotnet/Excel08.htm
といった実装例もありましたが、これも上記と同様に例外の握りつぶしを
行ってしまっていますし、そもそもデバッグ目的以外においては、
このような強制解放は少々乱暴な処理となるきらいがあります。

あるいは、こんな実装もあります。手前味噌ですが。
http://hanatyan.sakura.ne.jp/dotnet/Excel01.htm

> いろいろなところに返り値を設定しました。
Visual Basic においては、「戻り値」という表現の方が一般的です。

[ツリー表示へ]
タイトルRe^9: VB2005からAccess2000への連携時のトラブル
記事No7749
投稿日: 2008/06/28(Sat) 17:51
投稿者kiRITYAN
すいません。ひとつアドバイスをお願いします。
このような処理をクラスで行う場合、クラスはどのようにするのが良いのでしょうか?
はじめにExcelにレイアウトを保存しておく(元)。
そしてAccessからデータを取り込み、上で用意したExcelにを開き、Accessの値を入れて
保存、次にまだAccessのデータがあれば元のExcelをまた開き別名で保存。これの繰り返し。
このようにして元を増やしていっていろいろな帳票を保存や印刷していきたいのです。

元のExcel→プロパティ
セルの指定と値の→プロパティ?
書き込みの実行→1セルごと?(書き込み左記のExcelに既に書き込まれている項目があるので一度には無理ですよね?)
ファイルのOpenとClose→これを行うのは元のPassが指定されたタイミングですか?

すいません。たぶん基本的なこと(的外れかも知れませんが…)とは思いますがクラスを作成したことが無いのでよろしくお願いします

[ツリー表示へ]
タイトルRe^10: VB2005からAccess2000への連携時のトラブル
記事No7750
投稿日: 2008/06/28(Sat) 18:32
投稿者魔界の仮面弁士
> このような処理をクラスで行う場合、クラスはどのようにするのが良いのでしょうか?
前提条件を崩すようですが、私なら VBA か VBScript で実装します。
.NET からだと制御が煩雑になるので…。


.NET でやるにしても、COM を真面目にラッピングするのは大変なので、
フィールド変数による状態管理は行わず、すべてプロシージャ内のローカル変数にして、
Open から Quit まで、一つのメソッドで完結させてしまう事が多いです。

# 全てのコードを一つの巨大なメソッドとしてコーディングするという意味ではなく、
# そこから、Shared な自作プロシージャをヘルパメソッドとして呼んだりはします。


> 書き込みの実行→1セルごと?
できれば、1 セルごとの制御は避けた方が良いでしょう。効率が悪すぎます。

出力先の問題は、読み込んだ後から移動させれば済む話なので、まずは
Workbooks.OpenDatabase メソッドや ListObjects を使って、mdb データを
直接シートに読み込んだり、あるいは、複数セル範囲(Range.Value)に対して
配列を割り当てるなどして、一括して読み込ませる事ができないかどうかを
検討してみてください。

まぁ、元となるテンプレートやデータ構造の仕様次第では、一括読みこみが
使いにくい場面もありえますけれども。


> ファイルのOpenとClose→これを行うのは元のPassが指定されたタイミングですか?
Pass というのは、パスワード(password)のことですか?
それとも、ファイルの位置(path) のことでしょうか?

[ツリー表示へ]
タイトルRe^11: VB2005からAccess2000への連携時のトラブル
記事No7752
投稿日: 2008/06/29(Sun) 00:21
投稿者kiRITYAN
いろいろありがとうございます。
ただいまがんばって作成中です。一連で書くほうが効率はいいですか・・・ -_-。
けど今回は勉強と思って今クラスで書いてみます。
>>出力先の問題
ありがとうございます。この方法でがんばってみます。
>>Pass というのは、パスワード(password)のことですか?それとも、ファイルの位置(path) のことでしょうか?
はいスペルミスPathです。しかも大量にしていますね-_-
ひとつ質問なんですが、リンク先のDisposeを使用した場合なんですが。
disposingの値をOpenした後はTrueに。そしてCloseした後はFalseにして、
Disposeを起動したときにIf disposing Thenの後に終了の処理(MRComObject(Excel_App))を記述して終了処理を行うようにしようと思うのですが、この項目は使用しないほうがいいのでしょうか?
別にOpenしたかどうかのフラグを作成したほうが良いのでしょうか?
よろしくお願いします。
終了処理はhttp://hanatyan.sakura.ne.jp/dotnet/Excel01.htmです。使用させていただきました。ありがとうございます

[ツリー表示へ]
タイトルRe^12: VB2005からAccess2000への連携時のトラブル
記事No7754
投稿日: 2008/06/29(Sun) 21:35
投稿者魔界の仮面弁士
> 一連で書くほうが効率はいいですか・・・ -_-。
クラス化するにせよしないにせよ、データは一括で読み書きした方が効率が良いですね。

セル一つ一つに対して読み書きを行うと、そのたびに、.NET と Excel の間で
通信と変換処理(マーシャリング)が発生してしまうので、できるかぎり、
その回数を減らした方が良いです。相当の速度差になりますので。


> リンク先のDisposeを使用した場合なんですが。
ちなみに Dispose を呼び出すのは、そのクラスを呼び出す側(Form等)の仕事であり、
短期のスコープに対しては、「Using ステートメント」が併用される事になります。


> disposingの値をOpenした後はTrueに。そしてCloseした後はFalseにして、
…あれ? 何か食い違いがあるような気がします。
もしかして、いわゆる「Disposed プロパティ」と混同されてはいないでしょうか。

リンク先にある「Sub Dispose(ByVal disposing As Boolean)」というのは、
 False を指定された場合 → .NET が管理する資源「だけを」破棄する。
 True を指定された場合 → .NET が管理する資源「も」破棄する。
のように実装されるものであって、Open / Close 時に書き換わるものではありません。

それに disposing とは、日本語訳すると「破棄されている」という意味なのですから、
Open 時に True (破棄されている)で、Close したら False (破棄されていない)では、
意味が逆になってしまうかと思いますよ。

[ツリー表示へ]
タイトルRe^13: VB2005からAccess2000への連携時のトラブル
記事No7779
投稿日: 2008/07/07(Mon) 23:12
投稿者kiRITYAN
すいません。PCがお亡くなりになってしまいました。
いろいろとありがとうございます
うまくいったっぽいです。

[ツリー表示へ]