如何在VBA中将字符串作为命令运行

13

我有下面这段简单的VBA代码,但是我不知道为什么它不起作用。

Sub Run()
    test = "MsgBox" & """" & "Job Done!" & """"
    Application.Run test     
End Sub
我想做的是将VBA命令作为文本放入变量中并作为命令运行。 在这种情况下,我想运行类似于 MsgBox“Job Done!” 的命令并只打印出:

Job Done!


Job Done!


请问您能否澄清一下您想要做什么?您是否希望能够直接从Excel单元格中运行VBA代码? - freginold
首先,检查MsgBox的工作原理。它需要在括号中提供一个参数。 - M--
@ECode,你能从字符串中提取出"Job Done!"吗?我知道这只是伪代码,我想问的是你实际拥有的字符串。你能将参数从中提取出来并传递给函数吗? - M--
@Masoud,你能给我举几个函数的例子吗? - ECode
@ECode,请看下面我的修改。这不是我编码的标准方式,但由于你必须按原样处理“test”,这可能会有所帮助。 - M--
显示剩余4条评论
4个回答

11

您可能会想要添加自己的字符串"Executer":

Sub StringExecute(s As String)
    Dim vbComp As Object
    Set vbComp = ThisWorkbook.VBProject.VBComponents.Add(1)
    vbComp.CodeModule.AddFromString "Sub foo()" & vbCrLf & s & vbCrLf & "End Sub"
    Application.Run vbComp.name & ".foo"
    ThisWorkbook.VBProject.VBComponents.Remove vbComp
End Sub

Sub Testing()
    StringExecute "MsgBox" & """" & "Job Done!" & """"
End Sub

@ASH 我遇到了编译错误:未定义用户定义类型。 - ECode
1
@ECode 添加对 Microsoft Visual Basic For Applications Extensibility x.y 的引用。 - A.S.H
@ECode 或者你可以使用后期绑定。我修改了代码以使用后期绑定,无需添加任何引用。 - A.S.H
1
@A.S.H 很好。我也在尝试做类似的事情。 - M--
如果我期望从中获得返回值,该怎么编辑上面的代码? - Enissay
显示剩余3条评论

2

如果您需要一种不需要特殊权限的解决方案,并且比硬编码代码具有更高的功能性,我维护了一个库,名为stdLambda,源自stdVBA,可用于此类情况:

'Ensure module name is "mainModule"
Public Sub Main()
   Call stdLambda.bindGlobal("msgbox", stdCallback.CreateFromModule("mainModule","msgbox"))
   x = stdLambda.Create("msgbox(""hello world"")").Run()
End Sub
Public Function msgbox(ByVal sMessage as string) as long
   msgbox = VBA.msgbox(sMessage)
End Function

stdLambda 语法类似于 VBA,但它是一种完全嵌入式的编程语言。有关更多详细信息,请参见 文档


这看起来非常有趣,但是你的代码不应该将函数的输出分配给一个变量吗?此外,即使我导入了stdCallback、stdICallable和stdLambda的代码,并将函数的输出分配给一个变量,我仍然无法运行代码而不出现一些错误。我应该在GitHub上创建一个问题吗? - DecimalTurn
1
@DecimalTurn 我刚刚测试了一下,发现像上面那样使用超级全局变量声明存在一个bug,感谢你的报告。我已经在Github上修改了源代码以解决这个问题。如果你下载最新版本并运行上面的代码,应该都能正常工作。未来如果有类似的问题,欢迎在Github上提出! - Sancarn

1

简短回答是,你不能这样做(你不应该这样做),但是……请阅读以下内容以了解原因并找到解决方法!

如您所知,您正在编写编译器代码。您想要运行可读的文本行作为命令,这是不可能的。当您运行程序时,所有内容都被编译成机器语言。当您将该文本行传递给它时,它无法将其识别为命令,您最终会收到错误。您可以传递参数给它:

Sub Run()
 test = "Job Done" 
 MsgBox(test)
End Sub

你也可以在中编写可执行文件并运行,该文件可以被写成文本文件,然后在同一Sub中运行(需要注意扩展名)。
如果无法更改变量(即test),则需要采取另一种方法。我建议提取可以传递给函数的参数并使用该参数。类似下面这样的东西;
Sub Run()

 test = "MsgBox" & """" & "Job Done!" & """"

 extest = Right(test, Len(test) - 7)

 MsgBox (extest)

End Sub

我相信在 Stack Overflow 上有一个相同的问题,但我找不到它。如果找到了,我会把它作为参考。

P.S. 这两篇文章可能有助于找到答案:

访问 VBA - 用字符串参数评估函数

Excel VBA - 如何将字符串作为代码行运行

另一种解决方案

这需要信任 VB 项目。引用 ExcelForum 并参考 Visual Basic 项目的编程访问未受信任 - Excel

引用:

将你的启用宏的工作簿放在你可以指定为宏友好的文件夹中。

然后打开工作簿。

点击Office按钮 -> Excel选项 -> 信任中心 -> 信任中心设置 -> 可信位置。

然后将存放Excel宏工作簿的文件夹添加为可信位置。

此外,您还需要执行以下步骤:

文件 -> 选项 -> 信任中心 -> 信任中心设置 -> 宏设置 -> 勾选“信任对VBA项目对象模型的访问”旁边的框

关闭并重新打开工作簿。

使用您的宏的人应该执行相同的步骤。

引用完毕。

然后,您可以使用此内容,该内容来自VBA -在Excel中执行字符串作为命令(未经测试)

Sub test()
 Set VBComp = ThisWorkbook.VBProject.VBComponents.Add(vbext_ct_StdModule)
 VBComp.Name = "NewModule"
 Set VBCodeMod = ThisWorkbook.VBProject.VBComponents("NewModule").CodeModule

 Dim test As String
 test = "MsgBox " & """" & "Job Done!" & """"

 With VBCodeMod
    LineNum = .CountOfLines + 1
    .InsertLines LineNum, _
    "Sub MyNewProcedure()" & Chr(13) & test & Chr(13) & "End Sub"
 End With
 'run the new module
 Application.Run "MyNewProcedure"
 UserForm1.Show
 'Delete the created module
 ThisWorkbook.VBProject.VBComponents.Remove VBComp
End Sub

@A.S.H的回答实现了上一个解决方案的意图。我在此包含它以完整性为考虑。您可以参考原始答案并点赞。

Public Sub StringExecute(s As String)
    Dim vbComp As Object
    Set vbComp = ThisWorkbook.VBProject.VBComponents.Add(1)
    vbComp.CodeModule.AddFromString "Sub foo" & vbCrLf & s & vbCrLf & "End Sub"
    Application.Run vbComp.name & ".foo"
    ThisWorkbook.VBProject.VBComponents.Remove vbComp
End Sub

Sub Testing()
    StringExecute "MsgBox" & """" & "Job Done!" & """"
End Sub

我知道这一点,但在我的情况下,我无法更改变量,因此它仍然像 test = "MsgBox" & """" & "Job Done!" & """" - ECode
在许多其他编程语言中,难道没有类似于“eval”函数的东西吗? - Iesus Sonesson
1
@IesusSonesson eval 不是 Excel 函数(Access 有它)。Excel VBA 有 Application.Evaluate,但不能胜任此任务。这很不幸,但事实就是如此。实际上,我开始回答这个问题的原因是看到了 javascript 标签。Java 有 eval,你知道的。 - M--
很遗憾,当您运行第二个子路线时,您仅跳过前7个字符,而不将MsgBox作为命令运行。 - ECode
1
我很感激你提供了详细的答案。重要的是,我同意这种“游戏”通常不建议使用,除非“真的真的”需要。我发表了一个答案,只是因为我觉得它有点“有趣”,并展示了VBA的一些反射能力。 - A.S.H

-2
我遇到了一个类似的变化:宏名称在“控制规范”工作表中的表格中。自定义功能区添加了一个' onAction '参数的功能区XML。'标签'名称在返回的功能区回调宏中返回,然后根据XML标记名称查找要运行的宏名称。到目前为止都很合理!我已经在现有的代码模块[m0_RibbonCallBack]中处理了子程序。在[m0_RibbonCallBack]模块中,当我单击具有tagName =[Reset2CellA2]的功能区按钮时,我想运行子名称=[mResetToCellA2]。在“控制规范”工作表中,我对tagname =[Reset2CellA2]进行了vLookUp(),并从第3列(onAction)返回字符串值=“mResetToCellA2”。现在我需要在VBA端运行宏字符串(macroName)名称! 我最终通过这个简单的语句解决了这个挑战: Application.Run "m0_RibbonCallBack." & macroName
干杯!

3
目前你的回答不够清晰,请编辑并添加更多细节,以帮助其他人理解它如何回答问题。你可以在帮助中心找到有关如何编写好答案的更多信息。 - Community

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接