在EXCEL VBA中使用C#dll

在EXCEL VBA中使用C#dll,c#,vba,dll,C#,Vba,Dll,我遇到了一个小问题,需要你们的帮助 我有一个通过COM互操作公开的C#DLL。它工作正常,但显然C#interop对象的部署是一场灾难,每次更新DLL时都需要重新命名 所以我想知道如何使用这个C#DLL中的函数,如下所示: 或者我可以通过把DLL和电子表格放在一起来调用函数的任何东西 Declare Function getString Lib "<PATH of my DLL>" () as string sub test() range("A1").value = get

我遇到了一个小问题,需要你们的帮助

我有一个通过COM互操作公开的C#DLL。它工作正常,但显然C#interop对象的部署是一场灾难,每次更新DLL时都需要重新命名

所以我想知道如何使用这个C#DLL中的函数,如下所示: 或者我可以通过把DLL和电子表格放在一起来调用函数的任何东西

Declare Function getString Lib "<PATH of my DLL>" () as string

sub test()
   range("A1").value = getString
End Sub
将函数getString Lib”“()声明为字符串
子测试()
范围(“A1”)。值=getString
端接头

语法可能有误。

我多次遇到这个问题。 最后,我在regasm util上使用shell和wait方法从vba注册com dll,以注册/取消注册dll,然后通过创建com对象进行后期绑定

 CreateObject('yourclasshere')
这有点像黑客,但它是有效的,下面是shellandwait方法和一个register and unregister方法

             Private Declare Function OpenProcess Lib "kernel32" _
             (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, _
             ByVal dwProcessId As Long) As Long

             Private Declare Function GetExitCodeProcess Lib "kernel32" _
             (ByVal hProcess As Long, lpExitCode As Long) As Long

             Private Const STATUS_PENDING = &H103&
             Private Const PROCESS_QUERY_INFORMATION = &H400


             Private Function ShellandWait(ExeFullPath As String, _
             Optional TimeOutValue As Long = 0) As Boolean

                 Dim lInst As Long
                 Dim lStart As Long
                 Dim lTimeToQuit As Long
                 Dim sExeName As String
                 Dim lProcessId As Long
                 Dim lExitCode As Long
                 Dim bPastMidnight As Boolean

                 On Error GoTo ErrorHandler

                lStart = CLng(Timer)
                 sExeName = ExeFullPath

                 'Deal with timeout being reset at Midnight
                 If TimeOutValue > 0 Then
                     If lStart + TimeOutValue < 86400 Then
                         lTimeToQuit = lStart + TimeOutValue
                     Else
                         lTimeToQuit = (lStart - 86400) + TimeOutValue
                         bPastMidnight = True
                     End If
                 End If

                 lInst = Shell(sExeName, vbHide)

             lProcessId = OpenProcess(PROCESS_QUERY_INFORMATION, False, lInst)

                Do
                     Call GetExitCodeProcess(lProcessId, lExitCode)
                     DoEvents
                     If TimeOutValue And Timer > lTimeToQuit Then
                         If bPastMidnight Then
                              If Timer < lStart Then Exit Do
                         Else
                              Exit Do
                         End If
                 End If
                 Loop While lExitCode = STATUS_PENDING

               ShellandWait = True
               Exit Function

             ErrorHandler:
             ShellandWait = False

             End Function


      Private Function RegisterPayload() As Boolean

          Dim script As String
          script = "cmd /c"
          script = script + " " + "%windir%\Microsoft.NET\Framework\v2.0.50727\regasm"
          script = script + " " + Chr(34) + InstallationPath + Chr(34)
          script = script + " /codebase"

          RegisterPayload = ShellandWait(script)

      End Function

      Private Function UnRegisterPayload() As Boolean
          Dim script As String
          script = "cmd /c"
          script = script + " " + "%windir%\Microsoft.NET\Framework\v2.0.50727\regasm"
          script = script + " " + Chr(34) + InstallationPath + Chr(34)
          script = script + " /u"

          UnRegisterPayload = ShellandWait(script)
      End Function
私有声明函数OpenProcess Lib“kernel32”_
(ByVal希望访问时间尽可能长,ByVal bInheritHandle希望访问时间尽可能长_
ByVal dwProcessId As Long)As Long
私有声明函数GetExitCodeProcess Lib“kernel32”_
(ByVal HPPROCESS尽可能长,lpExitCode尽可能长)尽可能长
私有常量状态\u挂起=&H103&
Private Const PROCESS\u QUERY\u INFORMATION=&H400
私有函数ShellandWait(ExeFullPath为字符串_
可选的超时值(长=0)为布尔值
昏暗的林斯特一样长
黯淡的开始和漫长的
我会尽可能地退出
Dim sExeName作为字符串
将lProcessId变长
Dim lExitCode尽可能长
Dim bPastMidnight作为布尔值
关于错误转到错误处理程序
lStart=CLng(计时器)
sExeName=ExeFullPath
'处理在午夜重置超时
如果超时值>0,则
如果lStart+TimeOutValue<86400,则
lTimeToQuit=lStart+TimeOutValue
其他的
lTimeToQuit=(lStart-86400)+超时值
bPastMidnight=True
如果结束
如果结束
lInst=Shell(sExeName,vbHide)
lProcessId=OpenProcess(进程查询信息,False,lInst)
做
调用GetExitCodeProcess(lProcessId,lExitCode)
多芬特
如果TimeOutValue和Timer>LTI退出,则
如果是午夜
如果计时器<开始,则退出Do
其他的
退出Do
如果结束
如果结束
lExitCode=状态_挂起时循环
ShellandWait=True
退出功能
错误处理程序:
ShellandWait=False
端函数
私有函数RegisterPayload()为布尔值
将脚本变暗为字符串
script=“cmd/c”
script=script++%windir%\Microsoft.NET\Framework\v2.0.50727\regasm
脚本=脚本+“”+Chr(34)+安装路径+Chr(34)
script=script+“/codebase”
RegisterPayload=ShellandWait(脚本)
端函数
私有函数UnRegisterPayload()为布尔值
将脚本变暗为字符串
script=“cmd/c”
script=script++%windir%\Microsoft.NET\Framework\v2.0.50727\regasm
脚本=脚本+“”+Chr(34)+安装路径+Chr(34)
脚本=脚本+“/u”
UnRegisterPayload=ShellandWait(脚本)
端函数

希望有帮助:)

您可以这样做,但您必须了解VBA和.Net的区别。
首先,您必须创建一个实际的DLL(.Net程序集不是),为此,请使用。 同样,您必须知道如何封送内容。
VBA只支持stdcall作为调用约定,它不能真正处理用于DLL函数的Unicode。这本身并不坏,因为.Net中字符串的默认封送处理是VBA所期望的(指向Ansi字符的指针)。另外,stdcall是我用于导出的默认调用约定

我将重新使用最近为另一个SO线程创建的示例:

将其放入使用“我的模板”创建的项目中:

[ComVisible(true), ClassInterface(ClassInterfaceType.AutoDual)]
public class Sample
{
   public string Text
   {
      [return: MarshalAs(UnmanagedType.BStr)]
      get;
      [param: MarshalAs(UnmanagedType.BStr)]
      set;
   }

   [return: MarshalAs(UnmanagedType.BStr)]
   public string TestMethod()
   {
      return Text + "...";
   }
}

static class UnmanagedExports
{
   [DllExport]
   [return: MarshalAs(UnmanagedType.IDispatch)]
   static Object CreateDotNetObject(String text)
   {
      return new Sample { Text = text };
   }
}
以下是如何从VBA调用它:

Declare Function CreateDotNetObject Lib "The full path to your assembly or just the assembly if it is accessible from Excel" _
  (ByVal text As String) As Object

Sub test()

  Dim instance As Object

  Set instance = CreateDotNetObject("Test 1")
  Debug.Print instance.Text

  Debug.Print instance.TestMethod

  instance.text = "abc 123" ' case insensitivity in VBA works as expected'

  Debug.Print instance.Text
End Sub

以下是要放在类顶部的关键using语句:

使用系统诊断; 使用RGiesecke.DllExport

还要确保在Nuget PM语句之前启动了一个项目,以安装上面的模板。我在这方面是新手——我相信还有其他人。我正在使用AutoCAD VBA,但由于它是64位的,因此出现了另一个错误-我必须在Declare语句中使用PtrSafe(etc),以便VBA在没有错误的情况下继续运行(有关此信息,请参阅MS docs)

顺便说一句,它成功了

我的最终代码(基于上述内容)

和我的vba代码:

#If VBA7 Then
    Private Declare PtrSafe Function CreateDotNetObject Lib "G:\gitRepository\VS\ClassLibrary3\ClassLibrary3\bin\Debug\ClassLibrary3.dll" (ByVal text As String) As Object
#Else
    Private Declare Function CreateDotNetObject Lib "G:\gitRepository\VS\ClassLibrary3\ClassLibrary3\bin\Debug\ClassLibrary3.dll" (ByVal text As String) As Object
#End If

Sub test()

  Dim instance As Object

  Set instance = CreateDotNetObject("Test 1")
  Debug.Print instance.text

  Debug.Print instance.TestMethod

  instance.text = "abc 123" ' case insensitivity in VBA works as expected'

  Debug.Print instance.text
End Sub

项目模板页面不可用。当我从公司网络内部尝试时,我遇到了类似的错误,但我怀疑我们臭名昭著的内容过滤防火墙是罪魁祸首。我可以从我的Android手机上下载。不确定谷歌今天是否把事情搞砸了。不过我对此表示怀疑。我也可以从IPhone上浏览网页。但无法下载。不知道为什么会发生这种情况。但是,如果你想让我直接向你发送模板,你可以通过谷歌邮件服务的robert dot giesecke给我发邮件。我在Visual C#2010 express中使用这个模板,我无法编译空项目,因为找不到Lib.exe。
#If VBA7 Then
    Private Declare PtrSafe Function CreateDotNetObject Lib "G:\gitRepository\VS\ClassLibrary3\ClassLibrary3\bin\Debug\ClassLibrary3.dll" (ByVal text As String) As Object
#Else
    Private Declare Function CreateDotNetObject Lib "G:\gitRepository\VS\ClassLibrary3\ClassLibrary3\bin\Debug\ClassLibrary3.dll" (ByVal text As String) As Object
#End If

Sub test()

  Dim instance As Object

  Set instance = CreateDotNetObject("Test 1")
  Debug.Print instance.text

  Debug.Print instance.TestMethod

  instance.text = "abc 123" ' case insensitivity in VBA works as expected'

  Debug.Print instance.text
End Sub