我使用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