In order to stop a “macro” method during execution the macro method must be invoked on a separate thread. This is because macro methods invoked on the same thread as the host will block the host. In the ShapeAppCSharp SDK samples macro methods are called on a separate thread if the VSTA IDE is in the debugging state. It is necessary to change all macro method invocations to a separate thread in order to stop them .
Once all macro methods are invoked in separate threads, the easiest way to interrupt the execution of the macro method is to load and unload the macro assembly. To do this, you must catch the AppDomainUnloaded exception in the ExecuteMacroInternal method.
/// <summary>
/// Execute the first public instance method with name
/// <paramref name="macroName"/> which takes no parameters.
/// </summary>
/// <param name="macroName">Name of the macro to execute.</param>
internal void ExecuteMacro(string macroName)
{
//SDK warning- no issues adding shapes, but maybe other UI issues?
// If the macro calls host API that interacts with host UI, the call needs to happen on the main UI thread.
// However, during out-of-process debugging, if we execute macro on the main UI thread, the main UI thread may be blocked
// and cannot process any reentrant method calls.
// Here we spawn a separate thread so that the main UI thread can service other reentrant method calls.
//all macros called on new thread in order not to block the host during execution, so that macros can be stopped.
Thread t = new Thread(() =>
{
ExecuteMacroInternal(macroName);
});
t.SetApartmentState(Thread.CurrentThread.GetApartmentState());
t.Start();
// Do not call t.Join(); Otherwise it'll block and the host will be frozen.
}
/// <summary>
/// Execute the first public instance method with name
/// <paramref name="macroName"/> which takes no parameters.
/// </summary>
/// <param name="macroName">Name of the macro to execute.</param>
internal void ExecuteMacroInternal(string macroName)
{
// If no Macros loaded
if (this.application.VstaRuntimeIntegration.MacroAddIn == null)
return;
IExtendedEntryPoint entryPoint =
this.application.VstaRuntimeIntegration.MacroAddIn as IExtendedEntryPoint;
System.Diagnostics.Debug.Assert(entryPoint != null);
if (entryPoint == null)
return;
object obj = entryPoint.GetEntryPointObject();
Type t = obj.GetType();
try
{
t.InvokeMember(macroName, BindingFlags.Public |
BindingFlags.Instance | BindingFlags.InvokeMethod, null, obj, null);
return;
}
catch (System.Runtime.Remoting.RemotingException ex)
{
// Swallow remoting exception.
System.Diagnostics.Debug.WriteLine(ex.ToString());
}
catch (AppDomainUnloadedException macroStopUnloadEx)
{
// Swallow remoting exception.
System.Diagnostics.Debug.WriteLine(macroStopUnloadEx.ToString());
}
catch (Exception e)
{
System.Windows.Forms.MessageBox.Show("Error executing macro\n" + e.Message,
"ShapeAppCSharp", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
// Didn't find a method to invoke, so show a message box.
System.Windows.Forms.MessageBox.Show("Unable to execute macro: " + macroName,
"ShapeAppCSharp", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
internal void StopMacro_UnloadLoad()
{
//IF the macro project is debugging, stop the debugger to force unload/load
if (this.application.VstaDesignTimeIntegration.isDebugging)
{
this.application.VstaDesignTimeIntegration.StopDebugging(false);
}
else //not debugging- unload and reload the add-in
{
this.application.VstaRuntimeIntegration.UnloadCurrentMacroAddin(false);
this.application.VstaRuntimeIntegration.LoadMacrosInProcess();
}
}
Posted
Mar 25 2010, 03:51 PM
by
Melody