我使用Robert Giesecke http://sites.google.com/site/robertgiesecke/Home/uploads/unmanagedexports的解决方案将函数从托管代码导出到非托管代码。该解决方案工作得很好,但在办公室使用该解决方案时出现问题(excel)。从C#导出C函数并在VBA中使用它
我试图开发一种
- 使用SQLAuthentication
- 通过数据库
- 的名字传递的SQL语句
- 并返回结果连接到SQLServer的
- 一个DLL
因此,第e DLL无法看到密码,我知道它可以通过使用特殊工具来完成。这样做对我们的要求就足够了。
在C#代码:
using System;
using System.Collections.Generic;
using System.Text;
using RGiesecke.DllExport;
using ADODB;
using System.Xml;
using System.IO;
using System.Security.Cryptography;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace SqlConRVT
{
public static class SqlConRVT
{
[DllExport("SqlConRVT", CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.IDispatch)]
public static Object OpenRecordset ([MarshalAs(UnmanagedType.AnsiBStr)] string databaseName,
[MarshalAs(UnmanagedType.AnsiBStr)] string commandText)
{
if (String.IsNullOrEmpty(databaseName)) throw new ArgumentNullException("databaseName");
if (String.IsNullOrEmpty(commandText)) throw new ArgumentNullException("commandText");
try
{
var connection = new ADODB.Connection();
var intConnectionMode = (int) ConnectModeEnum.adModeUnknown;
var username = Crypto.DecryptMessage("XEj0PC2lMIs=", "FinON");
var password = Crypto.DecryptMessage("7YIDPO7eBoFAhskAX6JGAg==", "FinON");
connection.Open("Provider='SQLOLEDB';Data Source='PETER-PC\\SQLEXPRESS'; Initial Catalog='" + databaseName + "';", username, password, intConnectionMode);
var rs = new Recordset();
rs.Open(commandText, connection, CursorTypeEnum.adOpenForwardOnly, LockTypeEnum.adLockOptimistic, -1);
return rs;
}
catch (Exception ex)
{
// an exception in a DLL will most likely kill the excel process
// we really dont want that to happen
MessageBox.Show(ex.Message, ex.GetType().Name, MessageBoxButtons.OK, MessageBoxIcon.Error);
return null;
}
}
}
public partial class Crypto
{
public static string DecryptMessage(string encryptedBase64, string password)
{
TripleDESCryptoServiceProvider des = new TripleDESCryptoServiceProvider();
des.IV = new byte[8];
PasswordDeriveBytes pdb = new PasswordDeriveBytes(password, new byte[0]);
des.Key = pdb.CryptDeriveKey("RC2", "MD5", 128, new byte[8]);
byte[] encryptedBytes = Convert.FromBase64String(encryptedBase64);
MemoryStream ms = new MemoryStream(encryptedBase64.Length);
CryptoStream decStream = new CryptoStream(ms, des.CreateDecryptor(), CryptoStreamMode.Write);
decStream.Write(encryptedBytes, 0, encryptedBytes.Length);
decStream.FlushFinalBlock();
byte[] plainBytes = new byte[ms.Length];
ms.Position = 0;
ms.Read(plainBytes, 0, (int)ms.Length);
decStream.Close();
return Encoding.UTF8.GetString(plainBytes);
}
}
}
我在VBA代码:
Declare Function SqlConRVT Lib _
"C:\Users\Administrator\Documents\Visual Studio 2008\Projects\SqlConRVT\SqlConRVT\bin\Debug\x86 \SqlConRVT.dll" (ByVal databaseName As String, ByVal commandText As String) As Object
Sub SQLCon()
Dim x As Object
x = SqlConRVT("Adressen", "Select * from tblAdressen")
End Sub
在C#DLL和我引用的所有客户端应用程序 “Microsoft ActiveX数据对象2.8库”。
我试图用导出的64位DLL与C#,工作正常。 我试图使用导出的64位DLL作为静态类与C#,工作正常。 我试图用导出的32位DLL与VB6,应用程序崩溃。 我试图用导出的32位DLL与VBA(Excel),应用程序崩溃。
我用依赖关系walker检查了32位DLL中导出函数的存在。
为什么我不能在office(Excel)中使用32位DLL?
当然,我有32位的办公室!
你的“简化例子”工作正常,班级正确回馈!
我减少我的例子:
using System;
using System.Collections.Generic;
using System.Text;
using RGiesecke.DllExport;
using ADODB;
using System.Xml;
using System.IO;
using System.Security.Cryptography;
using System.Runtime.InteropServices;
using System.Windows.Forms;
[ComVisible(true), ClassInterface(ClassInterfaceType.AutoDual)]
static class SqlConRVT
{
[DllExport(CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.IDispatch)]
//[return: MarshalAs(UnmanagedType.I4)]
//[return: MarshalAs(UnmanagedType.AnsiBStr)]
static Object GetNewObject([MarshalAs(UnmanagedType.AnsiBStr)] String databaseName,
[MarshalAs(UnmanagedType.AnsiBStr)] String commandText)
{
var test = new StreamReader("C:\\lxbu.log");
return test;
//var rs = new Recordset();
//return rs;
//int A = 1;
//return A;
//String A = commandText;
//return A;
}
}
我在VBA代码:
Declare Function GetNewObject Lib "C:\Users\Administrator\Documents\Visual Studio 2008\Projects\An\An\bin\Debug\x86\An.dll" (ByVal databaseName As String, ByVal commandText As String) As Object
Sub An1()
Dim x As Object
Set x = GetNewObject("Adressen", "Select * from tblAdressen")
End Sub
如果我试图返回一个int值 - >工作两不误! 如果我尝试返回一个字符串值 - >工作正确! 如果我尝试返回一个对象(例如记录集对象或流读取器对象),Excel崩溃?必须有一个愚蠢的小错误!
谢谢罗伯特 - 因为每次你的代码是完美的!我可以看到StreamReader对象的内容,如果我在VBA
MsgBox instance.ReadtoEnd()
使用下面的代码,结果是:
“ABC AO〜EEE @dkfjf - >添加来回VBA”
问题是最初的ADODB.connection !!!!!
[DllExport(CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.IDispatch)]
static Object GetNewObject([MarshalAs(UnmanagedType.LPStr)] String databaseName, [MarshalAs(UnmanagedType.LPStr)] String commandText)
{
//if (String.IsNullOrEmpty(databaseName)) throw new ArgumentNullException("databaseName");
//if (String.IsNullOrEmpty(commandText)) throw new ArgumentNullException("commandText");
{
var connection = new ADODB.Connection();
//var rs = new Recordset();
StreamReader sr = new StreamReader("C:\\lxbu.log");
//var intConnectionMode = (int)ConnectModeEnum.adModeUnknown;
//var username = "...";
//var password = ".........";
//connection.Open("Provider='SQLOLEDB';Data Source='PETER-PC\\SQLEXPRESS'; Initial Catalog='" + databaseName + "';", username, password, intConnectionMode);
//rs.Open(commandText, connection, CursorTypeEnum.adOpenForwardOnly, LockTypeEnum.adLockOptimistic, -1);
return sr;
}
}
如果我使用“var connection = new ADODB.Connection();” Excel崩溃。问题是在32位的DLL使用ADODB(C#,并使用64位-DLL没有问题)。你的解决方案没有问题(!!!)!
为什么不使用VSTO或COM。考虑到未经管理的土地使事情变得比你需要的更复杂。 – 2011-05-27 19:35:39
@David:COM是非托管的,但是对于从VBA访问C#库而言,我肯定会建议COM路由。 – 2011-05-27 21:18:07
COM需要注册,需要管理权限。有时候这是不可能的...... – aurel 2014-03-04 14:14:22