从 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 位版本的数据引擎,否则您无法执行此操作。
我有一个必须编译为 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 位版本的数据引擎,否则您无法执行此操作。