Ответ 1
Обновление 3:
Я нашел этот пост на Форумы MSDN: Вызовите Outlook VBA sub из VSTO.
Очевидно, он использует VSTO, и я попытался преобразовать его в VBE AddIn, но столкнулся с проблемами при работе с x64 Windows с проблемой Register Class:
COMException (0x80040154): получение COM-класса factory для компонент с CLSID {55F88893-7708-11D1-ACEB-006008961DA5} не удалось выполнить к следующей ошибке: 80040154 Класс не зарегистрирован
В любом случае это ребята отвечают, кто считает, что он заработал:
Начало публикации форума MSDN
Я нашел способ! Что может быть вызвано как VSTO, так и VBA? Clipboard!!
Поэтому я использовал буфер обмена для передачи сообщений из одной среды в Другие. Вот несколько кодов, которые объяснят мой трюк:
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:
Я получаю это потому, что, несмотря на таргетинг на платформу x86, когда я переводил код с VSTO VB.Net на VBE С#, я создавал объекты, например:
Microsoft.Office.Core.CommandBars mObj_of_CommandBars = new Microsoft.Office.Core.CommandBars();
Потеряв еще несколько часов, я придумал этот код, который побежал!!!
Код VBE С# (из моего ответа сделайте ответ 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
}
}
Код Form1, который я создаю и загружаю из метода InitializeAddIn() VBE IDE:
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
Метод VBA Outlook:
Public Sub Test()
MsgBox ("Will this be called?")
End Sub
К сожалению, я с сожалением сообщаю вам, что мои усилия не увенчались успехом. Может быть, это работает от VSTO (я не пробовал), но, попробовав, как собака, достающая кость, теперь я готов отказаться!
Тем не менее, как утешение, вы можете найти сумасшедшую идею в истории изменений этого ответа (он показывает способ Mocking для объектной модели Office) для запуска модульных тестов Office VBA, которые являются частными с параметрами.
Я поговорю с вами в автономном режиме о том, чтобы внести свой вклад в проект RubberDuck GitHub, я написал код, который делает то же самое, что Диаграмма взаимоотношений рабочей группы Prodiance прежде чем Microsoft купила их и включила их продукт в Office Audit и Version Control Server.
Возможно, вы захотите изучить этот код, прежде чем полностью его отклонить, я даже не мог получить событие mpubObj_Explorer_BeforeItemCopy, поэтому, если вы можете нормально работать в Outlook, вы можете улучшить его. ( Я использую Outlook 2013 дома, поэтому 2010 год может быть другим).
ps Вы могли бы подумать после прыжка на одной ноге в направлении против часовой стрелки, щелкая пальцами, потирая мою голову по часовой стрелке как метод Обходной путь 2 в этом KB Статья, что я бы прибил ее... nup Я просто потерял больше волос!
Обновление 2:
Внутри вашего Outlook.Application.TestMethod1
вы не можете просто использовать метод CallByName классики VB, поэтому вам не нужно отражать? Вам нужно будет установить свойство строки "Sub/FunctionNameToCall" перед вызовом метода, содержащего CallByName, чтобы указать, какую суб/функцию вызывать.
К сожалению, пользователям потребуется вставить код котельной плиты в один из модулей.
Обновление 1:
Это будет звучать действительно изворотливо, но поскольку объектная модель Outlook полностью зажала свой метод Run, вы можете прибегнуть к... SendKeys
(да, я знаю, но это сработает).
К сожалению, описанный ниже метод oApp.GetType().InvokeMember("Run"...)
работает для всех приложений Office, кроме Outlook, на основе раздела "Свойства" в этой статье базы знаний: https://support.microsoft.com/en-us/kb/306683, извините, я до сих пор не знал этого и нашел, что это очень расстраивает попытку и статья MSDN вводит в заблуждение, в конечном итоге Microsoft заблокировала ее:
**
Обратите внимание, что поддерживается
SendKeys
, и единственный известный способ с использованием ThisOutlookSession
- это не:
https://groups.google.com/forum/?hl=en#!topic/microsoft.public.outlook.program_vba/cQ8gF9ssN3g - хотя Сью не Microsoft PSS она бы "спросил и узнал его неподдерживаемый.
OLD... Метод ниже работает с приложениями Office, за исключением Outlook
Проблема заключается в том, что объект Outlook Application не предоставляет метод Run, поэтому я, ну, застрял. Этот ответ ссылается на сообщение в блоге MSDN, которое выглядит многообещающим, поэтому я пробовал это... но процесс OUTLOOK.EXE завершает работу с кодом -1073741819 (0xc0000005) "Нарушение доступа"
Вопрос в том, как я могу сделать эту работу с Reflection?
1) Вот код, который я использую, который работает для Excel (должен работать для Outlook точно так же), используя ссылку .Net: Microsoft.Office.Interop.Excel v14 (а не ActiveX COM Reference):
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: