DaoにてTable作成

解決


パンダ  2009-02-16 21:53:19  No: 141526

いつもお世話になっています。
パンダといいます。

◆質問
DaoにてMDBにテーブルを作成するプログラムを作っているのですが、エラーが出て困っています。自宅のPCだと問題ないのですが、会社のPCだとエラーが発生します。
いろいろ調べているのですが、さっぱり分りません。
ご教授願います。

◆エラー
型 'System.__ComObject' の COM オブジェクトをインターフェイス型 'DAO.TableDef' にキャストできません。

◆環境
VB2008EE,XPSp2

◆以下ソース

'Dao3.6 Object Library参照

Dim objAccess As Object
objAccess = CreateObject("Access.Application")
Dim path As String = "c:\temp\123.mdb"
'Accessファイル作成
objAccess.NewCurrentDatabase(path)

'テーブル作成
Dim tbl As New DAO.TableDef
Dim fld As New DAO.Field
Dim db As Object
db = objAccess.CurrentDb

tbl = db.createtabledef("Table")
'↑ここでエラー

fld = tbl.CreateField("Field", 4)
tbl.Fields.Append(fld)
db.tabledefs.append(tbl)

db.Close()


魔界の仮面弁士  2009-02-17 20:46:35  No: 141527

2008 なら、ADO.NET (System.Data.OleDb 名前空間)を通じて接続し、
CREATE TABLE の SQL を発行する方が楽かもしれませんよ。

COM オブジェクトを使うと、Marshal.ReleaseComObject にて
使用後にオブジェクトの破棄が必要になってしまい、面倒かと。
(Close メソッドや Nothing 代入では、正しく解放されません)

特に DAO の場合、グローバル オブジェクトやコレクションが多いため、
破棄作業の書き忘れが多くなりがちです。まぁ、DAO にしかできない事も
あるので、使わざるを得ない状況もあるのですけれども。

> objAccess = CreateObject("Access.Application")
これは、Microsoft Access を起動する構文です。今回は不要なハズです。
そもそもこれは、Access の開発環境が無いと動作しない方法です。
Access ランタイムの環境や、DAO 単体の環境では使えない事に注意してください。

また、Access のバージョンの差異も色濃く受けてしまいますので、
.NET から Access を経由させて作成するのは、個人的にはおすすめできません。
レイトバインドならば尚更です。別の方法を使う事を推奨しておきます。

> objAccess.NewCurrentDatabase(path)
DAO を利用しているのであれば、Access を経由させる必要はありません。

今回は DAO の PIA を参照設定しているのでしょうから、手順としては、
まず DAO.DBEngine のインスタンスを New して、変数に受けるようにします。

そこから CreateWorkspace メソッドで Workspace の 明示的なインスタンスを
別の変数に取得し、そこからさらに CreateDatabase (あるいは OpenDatabase)で、
mdb を作る(開く)ように変更してみれば、期待動作するかと。

> Dim db As Object
Object ではなく、DAO.Database 型を利用しましょう。
先の CreateDatabase / OpenDatabase メソッドの戻り値を格納します。

> Dim tbl As New DAO.TableDef
> tbl = db.createtabledef("Table")
型変換エラーの件を別にしても、これは問題があるかと。

変数宣言部で「New」して、tbl のインスタンスを生成しているのに、
それを(破棄せずに)捨てて、別のインスタンスを代入しようとしています。


パンダ  2009-02-18 03:58:14  No: 141528

魔界の仮面弁士さん
いつもありがとうございます。

DAOを使っている理由は、MDBにInsert処理を高速にする為です。
魔界の仮面弁士さんのソースを参考にしています。
http://madia.world.coocan.jp/cgi-bin/VBBBS2/wwwlng.cgi?print+200611/06110042.txt

アドバイスの中身は私のレベルでは理解が難しかったのですが、
何とかできるようになりました。

今後の参考までに、質問があります。
>変数宣言部で「New」して、tbl のインスタンスを生成しているのに、
>それを(破棄せずに)捨てて、別のインスタンスを代入しようとしています。
とは、どういうことでしょうか?

◆以下解決のソース

 'Dao3.6 Object Library参照
Dim path As String = "c:\temp\123.mdb"
Dim DE As DAO.DBEngine = New DAO.DBEngineClass()
Dim WS As DAO.Workspace = DE.CreateWorkspace("#Default Workspace#", "Admin", String.Empty)

'MDBファイル作成
Dim DB As DAO.Database = WS.CreateDatabase(path, DAO.LanguageConstants.dbLangJapanese, DAO.DatabaseTypeEnum.dbEncrypt)

'テーブル作成
Dim tbl As DAO.TableDef
Dim fld As DAO.Field

tbl = DB.CreateTableDef("Table")

fld = tbl.CreateField("Field", 4)
tbl.Fields.Append(fld)
DB.TableDefs.Append(tbl)

DB.Close()


魔界の仮面弁士  2009-02-18 07:10:48  No: 141529

>>> Dim tbl As New DAO.TableDef
>>> tbl = db.createtabledef("Table")
>> 変数宣言部で「New」して、tbl のインスタンスを生成しているのに、
>> それを(破棄せずに)捨てて、別のインスタンスを代入しようとしています。
> とは、どういうことでしょうか?

そのコードが、2 点の問題を抱えているということです。

最初の変数宣言部において、
> Dim tbl As New DAO.TableDef
と書かれていましたが、このコードは
  Dim tbl As DAO.TableDef = New DAO.TableDef()
の省略表記にあたります。ここまではよろしいでしょうか。

つまり、この時点で TableDef のインスタンス(仮に tbl1 とします)が
New キーワードによって生成され、それが変数 tbl に代入されています。

しかし、その tbl1 は一度も使われる事なく、その後で、
> tbl = db.createtabledef("Table")
を行われ、変数 tbl に別のインスタンス(仮に tbl2) が代入されていますね。

tbl2 が代入された事により、元の tbl1 が参照されなくなります。
ならば、そもそも tbl1 を作成する必要が無いということになります。

すなわち、
  Dim tbl As New DAO.TableDef
ではなく、
  Dim tbl As DAO.TableDef
と書くべきであったという事です。これが1つ目の問題点です。

もう一つの問題点は、破棄作業の欠落です。
COM オブジェクト(ActiveX)は、.NET のマネージオブジェクトとは
管理方法が異なるため、利用した後は、それぞれのオブジェクトに対し、
  If tbl IsNot Nothing AndAlso Marshal.IsComObject(tbl) Then
    Marshal.ReleaseComObject(tbl)
  End If
といった破棄処理を行う事が要求されます。

しかし元のコードでは、変数 tbl が指しているのは既に tbl2 に
切り替わってしまったため、tbl1 を破棄する事ができないため、
使用していた COM オブジェクトの参照を明示的に破棄できません。
これが第2の問題点です。

> tbl.Fields.Append(fld)
そして、これも本来は NG です。

Fields プロパティが返すのは、DAO.Fields 型の COM オブジェクトなので、
これも解放対象となりえるからです。

ゆえに本来は
  Dim fs As DAO.Fields = tbl.Fields
  fs.Append(fld)
  If Marshal.IsComObject(fs) Then
    Marshal.ReleaseComObject(fs)
  End If
  If Marshal.IsComObject(fld) Then
    Marshal.ReleaseComObject(fld)
  End If
  db.Close()
  If Marshal.IsComObject(db) Then
    Marshal.ReleaseComObject(db)
  End If
  WS.Close()
  If Marshal.IsComObject(WS) Then
    Marshal.ReleaseComObject(WS)
  End If
  If Marshal.IsComObject(DE) Then
    Marshal.ReleaseComObject(DE)
  End If
のようにして、それぞれの参照カウントを減ぜねばなりません。

> 魔界の仮面弁士さんのソースを参考にしています。
そのソースには、Marshal.ReleaseComObject の呼び出しも含めてあります。

> fld = tbl.CreateField("Field", 4)
これは、"Field" という名前の長整数型の列を作成している所ですよね。

「4」などのマジックナンバーを利用するのではなく、
DAO.DataTypeEnum.dbLong などを利用すべきかと。


パンダ  2009-02-18 17:09:47  No: 141530

魔界の仮面弁士さん

詳しい解説ありがとうございます。

必要な機能はネットで検索しているので本質的なところを理解しないままプログラムを作っています。
非常に勉強になります。詳しく調べてみたいと思います。

魔界の仮面弁士さんの親切に感謝いたします。
ありがとうございました。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加