更新 3:
我在MSDN 论坛上找到了这篇文章:Call Outlook VBA sub from VSTO。
显然它使用 VSTO,我尝试将其转换为VBE AddIn,但在使用 x64 Windows 时遇到了 Register Class 问题:
COMException (0x80040154):检索具有 CLSID {55F88893-7708-11D1-ACEB-006008961DA5} 的组件的 COM 类工厂失败,原因是以下错误:80040154 未注册类
无论如何,这是那些认为他可以正常工作的人的回答:
MSDN 论坛帖子开始
我找到了一个方法!VSTO 和 VBA 都可以触发什么?剪贴板!!
所以我使用剪贴板将消息从一个环境传递到另一个环境。这里有一些代码可以解释我的技巧:
VSTO:
'p_Procedure is the procedure name to call in VBA within Outlook
'mObj_ou_UserProperty is to create a custom property to pass an argument to the VBA procedure
Private Sub p_Call_VBA(p_Procedure As String)
Dim mObj_of_CommandBars As Microsoft.Office.Core.CommandBars, mObj_ou_Explorer As Outlook.Explorer, mObj_ou_MailItem As Outlook.MailItem, mObj_ou_UserProperty As Outlook.UserProperty
mObj_ou_Explorer = Globals.Menu_AddIn.Application.ActiveExplorer
'I want this to run only when one item is selected
If mObj_ou_Explorer.Selection.Count = 1 Then
mObj_ou_MailItem = mObj_ou_Explorer.Selection(1)
mObj_ou_UserProperty = mObj_ou_MailItem.UserProperties.Add("COM AddIn-Azimuth", Outlook.OlUserPropertyType.olText)
mObj_ou_UserProperty.Value = p_Procedure
mObj_of_CommandBars = mObj_ou_Explorer.CommandBars
'Call the clipboard event Copy
mObj_of_CommandBars.ExecuteMso("Copy")
End If
End Sub
VBA:
为 Explorer 事件创建一个类并捕获此事件:
Public WithEvents mpubObj_Explorer As Explorer
'Trap the clipboard event Copy
Private Sub mpubObj_Explorer_BeforeItemCopy(Cancel As Boolean)
Dim mObj_MI As MailItem, mObj_UserProperty As UserProperty
'Make sure only one item is selected and of type Mail
If mpubObj_Explorer.Selection.Count = 1 And mpubObj_Explorer.Selection(1).Class = olMail Then
Set mObj_MI = mpubObj_Explorer.Selection(1)
'Check to see if the custom property is present in the mail selected
For Each mObj_UserProperty In mObj_MI.UserProperties
If mObj_UserProperty.Name = "COM AddIn-Azimuth" Then
Select Case mObj_UserProperty.Value
Case "Example_Add_project"
'...
Case "Example_Modify_planning"
'...
End Select
'Remove the custom property, to keep things clean
mObj_UserProperty.Delete
'Cancel the Copy event. It makes the call transparent to the user
Cancel = True
Exit For
End If
Next
Set mObj_UserProperty = Nothing
Set mObj_MI = Nothing
End If
End Sub
MSDN 论坛帖子结束
因此,此代码的作者将 UserProperty 添加到邮件项并以这种方式传递函数名称。同样,这需要 Outlook 中的一些样板代码和至少 1 个邮件项目。
更新 3a:
我得到的80040154 类未注册是因为尽管在我将代码从 VSTO VB.Net 转换为 VBE C# 时针对 x86 平台,但我正在实例化项目,例如:
Microsoft.Office.Core.CommandBars mObj_of_CommandBars = new Microsoft.Office.Core.CommandBars();
在浪费了几个小时之后,我想出了这个代码,运行了!!!
VBE C# 代码(根据我的回答在此处制作 VBE AddIn 答案):
namespace VBEAddin
{
[ComVisible(true), Guid("3599862B-FF92-42DF-BB55-DBD37CC13565"), ProgId("VBEAddIn.Connect")]
public class Connect : IDTExtensibility2
{
private VBE _VBE;
private AddIn _AddIn;
#region "IDTExtensibility2 Members"
public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom)
{
try
{
_VBE = (VBE)application;
_AddIn = (AddIn)addInInst;
switch (connectMode)
{
case Extensibility.ext_ConnectMode.ext_cm_Startup:
break;
case Extensibility.ext_ConnectMode.ext_cm_AfterStartup:
InitializeAddIn();
break;
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
private void onReferenceItemAdded(Reference reference)
{
//TODO: Map types found in assembly using reference.
}
private void onReferenceItemRemoved(Reference reference)
{
//TODO: Remove types found in assembly using reference.
}
public void OnDisconnection(ext_DisconnectMode disconnectMode, ref Array custom)
{
}
public void OnAddInsUpdate(ref Array custom)
{
}
public void OnStartupComplete(ref Array custom)
{
InitializeAddIn();
}
private void InitializeAddIn()
{
MessageBox.Show(_AddIn.ProgId + " loaded in VBA editor version " + _VBE.Version);
Form1 frm = new Form1();
frm.Show(); //<-- HERE I AM INSTANTIATING A FORM WHEN THE ADDIN LOADS FROM THE VBE IDE!
}
public void OnBeginShutdown(ref Array custom)
{
}
#endregion
}
}
我从 VBE IDE InitializeAddIn() 方法实例化和加载的 Form1 代码:
namespace VBEAddIn
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Call_VBA("Test");
}
private void Call_VBA(string p_Procedure)
{
var olApp = new Microsoft.Office.Interop.Outlook.Application();
Microsoft.Office.Core.CommandBars mObj_of_CommandBars;
Microsoft.Office.Core.CommandBars mObj_of_CommandBars = new Microsoft.Office.Core.CommandBars();
Microsoft.Office.Interop.Outlook.Explorer mObj_ou_Explorer;
Microsoft.Office.Interop.Outlook.MailItem mObj_ou_MailItem;
Microsoft.Office.Interop.Outlook.UserProperty mObj_ou_UserProperty;
//mObj_ou_Explorer = Globals.Menu_AddIn.Application.ActiveExplorer
mObj_ou_Explorer = olApp.ActiveExplorer();
//I want this to run only when one item is selected
if (mObj_ou_Explorer.Selection.Count == 1)
{
mObj_ou_MailItem = mObj_ou_Explorer.Selection[1];
mObj_ou_UserProperty = mObj_ou_MailItem.UserProperties.Add("JT", Microsoft.Office.Interop.Outlook.OlUserPropertyType.olText);
mObj_ou_UserProperty.Value = p_Procedure;
mObj_of_CommandBars = mObj_ou_Explorer.CommandBars;
//Call the clipboard event Copy
mObj_of_CommandBars.ExecuteMso("Copy");
}
}
}
}
ThisOutlookSession 代码:
Public WithEvents mpubObj_Explorer As Explorer
'Trap the clipboard event Copy
Private Sub mpubObj_Explorer_BeforeItemCopy(Cancel As Boolean)
Dim mObj_MI As MailItem, mObj_UserProperty As UserProperty
MsgBox ("The mpubObj_Explorer_BeforeItemCopy event worked!")
'Make sure only one item is selected and of type Mail
If mpubObj_Explorer.Selection.Count = 1 And mpubObj_Explorer.Selection(1).Class = olMail Then
Set mObj_MI = mpubObj_Explorer.Selection(1)
'Check to see if the custom property is present in the mail selected
For Each mObj_UserProperty In mObj_MI.UserProperties
If mObj_UserProperty.Name = "JT" Then
'Will the magic happen?!
Outlook.Application.Test
'Remove the custom property, to keep things clean
mObj_UserProperty.Delete
'Cancel the Copy event. It makes the call transparent to the user
Cancel = True
Exit For
End If
Next
Set mObj_UserProperty = Nothing
Set mObj_MI = Nothing
End If
End Sub
Outlook VBA 方法:
Public Sub Test()
MsgBox ("Will this be called?")
End Sub
很遗憾,我很遗憾地通知您,我的努力没有成功。也许它在 VSTO 中确实有效(我没有尝试过)但是在尝试像狗取骨头一样之后,我现在愿意放弃!
无论如何,作为一种安慰,您可以在此答案的修订历史中找到一个疯狂的想法(它显示了一种模拟 Office 对象模型的方法)来运行带有参数的私有 Office VBA 单元测试。
我将离线与您讨论如何为 RubberDuck GitHub 项目做出贡献,在 Microsoft 购买 Prodiance 的工作簿关系图并将其产品包含在 Office 审计和版本控制服务器中之前,我编写的代码与Prodiance 的工作簿关系图相同。
您可能希望在完全关闭它之前检查此代码,我什至无法让 mpubObj_Explorer_BeforeItemCopy 事件工作,所以如果您可以在 Outlook 中正常工作,您可能会更好。(我在家里使用 Outlook 2013,所以 2010 可能会有所不同)。
ps 你会想,在逆时针方向跳上一条腿后,点击我的手指,同时顺时针揉我的头,就像这篇知识库文章中的解决方法 2 一样,我会搞定的......我只是失去了更多的头发!
更新 2:
在你里面你Outlook.Application.TestMethod1
不能只使用VB经典的CallByName方法所以你不需要反射吗?在调用包含 CallByName 的方法之前,您需要设置一个字符串属性“Sub/FunctionNameToCall”以指定要调用的子/函数。
不幸的是,用户需要在他们的模块之一中插入一些样板代码。
更新1:
这听起来真的很狡猾,但是由于 Outlooks 的对象模型已经完全限制了它的 Run 方法,你可以求助于...... SendKeys
(是的,我知道,但它会起作用)。
不幸的是,oApp.GetType().InvokeMember("Run"...)
下面描述的方法适用于除 Outlook 之外的所有Office 应用程序 - 基于此知识库文章中的“属性”部分:https: //support.microsoft.com/en-us/kb/306683,抱歉我直到现在才知道并发现它非常令人沮丧,并且MSDN 文章具有误导性,最终微软将其锁定:
** 请注意,这SendKeys
是受支持的,唯一已知的使用ThisOutlookSession
方式不是:
https ://groups.google.com/forum/?hl=en#!topic/microsoft.public.outlook.program_vba/ cQ8gF9ssN3g - 即使 Sue 不是't Microsoft PSS她会问并发现它不受支持。
OLD... 以下方法适用于除 Outlook 之外的 Office Apps
问题是 Outlook 的 Application 对象没有公开 Run 方法,所以我卡住了。这个答案链接到 MSDN 上看起来很有希望的博客文章,所以我尝试了这个......但是 OUTLOOK.EXE 进程退出,代码为 -1073741819 (0xc0000005) '访问冲突'
问题是,如何使用 Reflection 进行这项工作?
1)这是我使用的适用于 Excel 的代码(应该同样适用于 Outlook),使用.Net 参考: Microsoft.Office.Interop.Excel v14(不是 ActiveX COM 参考):
using System;
using Microsoft.Office.Interop.Excel;
namespace ConsoleApplication5
{
class Program
{
static void Main(string[] args)
{
RunVBATest();
}
public static void RunVBATest()
{
Application oExcel = new Application();
oExcel.Visible = true;
Workbooks oBooks = oExcel.Workbooks;
_Workbook oBook = null;
oBook = oBooks.Open("C:\\temp\\Book1.xlsm");
// Run the macro.
RunMacro(oExcel, new Object[] { "TestMsg" });
// Quit Excel and clean up (its better to use the VSTOContrib by Jake Ginnivan).
oBook.Saved = true;
oBook.Close(false);
System.Runtime.InteropServices.Marshal.ReleaseComObject(oBook);
System.Runtime.InteropServices.Marshal.ReleaseComObject(oBooks);
System.Runtime.InteropServices.Marshal.ReleaseComObject(oExcel);
}
private static void RunMacro(object oApp, object[] oRunArgs)
{
oApp.GetType().InvokeMember("Run",
System.Reflection.BindingFlags.Default |
System.Reflection.BindingFlags.InvokeMethod,
null, oApp, oRunArgs);
//Your call looks a little bit wack in comparison, are you using an instance of the app?
//Application.GetType().InvokeMember(qualifiedMemberName.MemberName, BindingFlags.InvokeMethod, null, Application, null);
}
}
}
}
2)确保将宏代码放在模块(全局 BAS 文件)中。
Public Sub TestMsg()
MsgBox ("Hello Stackoverflow")
End Sub
3) 确保启用对 VBA 项目对象模型的宏安全和信任访问: