从 64 位 .NET 与 32 位 Access 通信

Communicating with 32 bit Access from 64 bit .NET

我有一个必须编译为 64 位的 .NET (C#) 应用程序。这使用 ADO.NET 来处理各种数据库,包括 Excel 和 OLEDB 访问(是的,我知道 - Excel 不喜欢被当作数据库对待,但客户真的很想这样做,所以我允许它有很多警告)

一切都很好,直到我们遇到一个 32 位 Office 系统。 64 位应用程序无法使用 32 位 OLEDB 驱动程序。

现在,32 位 Excel 通信确实可以使用 COM。所以我已经 运行 进行了一些测试,我可以为 Excel 编写一个使用 Excel COM 的替代接口。

所以,我也尝试了Access。这个在线的一些描述暗示 DAO 会起作用。然而,深入挖掘,这似乎并非如此。 DAO 遇到相同的 32 位/64 位问题。

使用 Access COM 接口,我可以查询 table 个名称的列表,但仅此而已。例如

  Access.Application accessApp = new Access.Application();
  accessApp.Visible = false;
  accessApp.OpenCurrentDatabase("mydb.accdb", false, "");

  Access._CurrentData dt = accessApp.CurrentData;
  foreach ( dynamic tbl in dt.AllTables )
  {
      string name = tbl.Name;
      if (! name.StartsWith("MSys"))
          Console.WriteLine(">" + tbl.Name+"< ");
  }
  accessApp.Quit();

这是我能做的极限吗?我希望能够查询 table 列及其类型。查询数据行(简单 "fetch everything" 就足够了),并更新行(简单 UPDATE WHERE 就足够了)。

我不需要支持 JET。

如何在不使用 DAO 或 OLEDB 的情况下使用 Access COM 接口读取数据行并更新它们,这两者都不会跨越 32 位/64 位边界进行通信?

很遗憾,您不能这样做。

虽然互操作确实有很多技巧,并且使用一种“编组”形式来允许 in-process x64 启动 + 自动化 32 位版本的 word,或者在这种情况下是 Access,互操作编组系统不支持数据库引擎。

因此,如果“如果”可行,您可以通过以下方式编写代码:

    Dim accessApp As New Access.Application
    accessApp.OpenCurrentDatabase("\ALBERTKALLAL-PC\test\vbtest\test44.accdb")
    Dim strSQL As String
    accessApp.Visible = True
    Application.DoEvents()

    strSQL = "select * from tblHotels3"
    Dim rst As Access.Dao.Recordset

    Dim strBuf As String = ""
    rst = accessApp.CurrentDb.OpenRecordset(strSQL)
    Do While rst.EOF = False
        strBuf += vbCrLf & rst("ID").Value & "," & rst("HotelName").Value
        rst.MoveNext()
    Loop
    Me.TextBox1.Text = strBuf
    accessApp.Quit()

所以,如果可能的话,上面的代码就是您可以使用互操作的方式。

但是,请记住,您需要完整版本的 Access 才能使用 "createObject()" 或创建 Access 实例。 运行time 版本不支持此功能(因此,请记住,虽然您已成功创建 Access 实例,但您无法使用 运行time 执行此操作。(现在在您的如果您显然确实安装了完整版的访问权限)。

并且通过创建一个完整的访问实例,然后所有启动代码和该访问应用程序的部分将开始 运行。更好的方法是简单地创建数据库引擎的实例,因此您不启动 Access + VBA + 整个应用程序。要使用 inter-op,此代码可以工作:

    Dim db As New Access.Dao.DBEngine
    Dim MyDatabase As Access.Dao.Database

    db.OpenDatabase("\ALBERTKALLAL-PC\test\vbtest\test44.accdb")
    Dim rst As Access.Dao.Recordset
    Dim strSQL As String = "select * from tblHotels3"
    Dim strBuf As String = ""
    rst = db.Workspaces(0)(0).OpenRecordset(strSQL)
    Do While rst.EOF = False
        strBuf += vbCrLf & rst("ID").Value & "," & rst("HotelName").Value
        rst.MoveNext()
    Loop
    rst.Close()
    db.Workspaces(0)(0).Close() 
    Me.TextBox1.Text = strBuf

然而,再一次,当您尝试使用 .net 本地数据引擎时,您正在跨 in-process (x64) 进行编组,因此上述操作将再次无效。

但是,如果你想修改一个文本文件,你可以让word打开文本文档,执行word命令修改文档,然后保存。这在理论上可以并且应该在 x32/x64 桥上工作。

事实上,下面的 Access 代码 inter-op 应该可以工作,但它不工作:

    Dim accessApp As New Access.Application
    accessApp.OpenCurrentDatabase("\ALBERTKALLAL-PC\test\vbtest\test44.accdb")

    Dim strSQL As String

    strSQL = "update tblHotels3 set City = 'zoo' "

    accessApp.DoCmd.RunSQL(strSQL)
    accessApp.Quit()

请注意,在上面,我们尝试使用 Access 模型来执行 "update"。但是,即时 Access 尝试 运行 + 使用数据库引擎时失败了。上面的例子应该可以工作,但是如果上面的 .net (vb.net) 代码是 运行 作为 x64,那么发现:

代码确实创建了正确的 运行ning Access 实例(.net = x64,Access = x32)。

但是,似乎在我们尝试尝试 运行 + 使用 Access 数据引擎的那一刻,然后 .net 抛出了一条错误消息 (所以上面失败了 accessApp.DoCmd.RunSQL(strSQL) - 那是我们尝试使用数据引擎的时候。

因此,虽然 inter-op 可以启动并为您提供 运行ning Access 副本,但尝试使用数据引擎失败。

我确实发现您可以告诉 MSAccess 运行 一些 VBA 代码。所以这会起作用:

    Dim accessApp As New Access.Application
    accessApp.OpenCurrentDatabase("\ALBERTKALLAL-PC\test\vbtest\test44.accdb")

    accessApp.Visible = True
    Application.DoEvents()
    MsgBox("access open = ok - show access ok")
    accessApp.Run("MyUpdate")
    accessApp.Quit()

还有,上面的 VBA 代码?

VBA 代码 - 不是 vb.net:

  Public Sub MyUpdate()

     MsgBox "about to update"
     CurrentDb.Execute "update tblHotels3 set city = 'zoozoo'"

     MsgBox "upate ok"


  End Sub

我可以确认以上确实有效:

因此,这里的问题是 Access 数据库引擎被视为外部数据库对象,并且 inter-op 编组不支持来自 .net 的数据库引擎。

解决此问题的唯一方法是安装 Access 数据引擎的 x64 位版本。您可以单独安装引擎而不必安装 Access,但据我所知,从 .net 开始,除非您拥有 x64 位版本的数据引擎,否则您无法执行此操作。