call revit's API from external thread

call revit's API from external thread

sizy458
Advocate Advocate
2,822 Views
8 Replies
Message 1 of 9

call revit's API from external thread

sizy458
Advocate
Advocate
Hello I'am in the revit's test project, When i create transaction , since evenement 'ComponentManager_UIElementActivated' from button of ComponentManager.Ribbon, i have exception 'Starting a transaction from an external application running outside of API context is not allowed'. Have you a solution ? void ComponentManager_UIElementActivated( object sender, adWin.UIElementActivatedEventArgs e) { Transaction trans = new Transaction(uidoc.Document); //Work good. trans.Start("ReTraceWall"); //It don't work, because i have exception "Starting a transaction from an external application running outside of API context is not allowed." CommandRetraceWall.retracewall(uidoc); //function not called because there is exception before trans.Commit(); } Thank you for help . Have you sample which solve this problem ?
0 Likes
2,823 Views
8 Replies
Replies (8)
Message 2 of 9

arnostlobel
Alumni
Alumni

Hello sizy458,

 

as you have deduced and as the exception message indicates, calling into the Revit API from outside threads (which include calls from modeless dialogs, for they too run on their own threads) is not permitted in Revit. This policy applies to every API method (excluding just a very few) and some - such as transactions do enforce it with raised exceptions.

 

That said, there is a solution for those who want to communicate with the Revit API from their modeless dialogs or other external work threads. The key is that all communication between the outside thread and Revit API mist be done either inside a handler of an Idling event or an External Event. Both are Revit API features and are described in the SDK documentation. The SDK even includes a sample of a Modeless Dialog, in which both the above mentioned approaches are demonstrated - one for the Idling event, one for an External Event. Please look into the SDK, folder ModelessDialog for more information. Also, since this is so frequently asked and answered question, it may be to your benefit to search the Web or event this very forum for more details.

 

Thanks 

Arnošt Löbel
Message 3 of 9

jeremytammik
Autodesk
Autodesk

Dear Sizy458,

 

Thank you for raising this issue here.

 

This topic has probably come up and been answered a few hundred times in the past.

 

The short answer is: it is completely impossible to drive Revit or make any use whatsoever of the Revit API from an external thread.

 

The Revit API can only be used within a valid Revit API context.

 

Such a context is only available within callback methods that you provide to Revit and that Revit calls back when it is ready to accept your input.

 

A typical example of such a callback method is the Execute method of an external command.

 

Here is an overview of the main discussions and explanations regarding this on The Building Coder:

 

http://thebuildingcoder.typepad.com/blog/about-the-author.html#5.28

 

You might want to start exploring the links provided there beginning at the end.

 

I hope this clarifies.

 

Best regards,

 

Jeremy



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 4 of 9

jeremytammik
Autodesk
Autodesk

Hi Arnošt,

 

You beat me to it by one minute!

 

Thank you!

 

Cheers,

 

Jeremy



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

0 Likes
Message 5 of 9

arnostlobel
Alumni
Alumni
Jeremy, you know I take these ones with the highest priority. 🙂
Arnošt Löbel
0 Likes
Message 6 of 9

sizy458
Advocate
Advocate
It resolve my problem.
I use the class
handler_event=new ExternalEventMy();
exEvent = ExternalEvent.Create(handler_event);
In other thread i use
exEvent.Raise();

class definition
public class ExternalEventMy : IExternalEventHandler
{
public void Execute(UIApplication appui)
{
Transaction trans = new Transaction(uidoc.Document);
trans.Start("MyEvent");
.....//action in the revit's thread
trans.Commit();
}
public string GetName()
{
return "my event";
}
}
Message 7 of 9

jeremytammik
Autodesk
Autodesk

Dear Sizy458,

 

Thank you for your update and congratulations on the effective solution.

 

That looks absolutely perfect, and also looks like a really good example of how to solve this need.

 

 

Best regards,

 

Jeremy



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

0 Likes
Message 8 of 9

jeremytammik
Autodesk
Autodesk

I summarised this discussion and solution on The Building Coder blog:

 

http://thebuildingcoder.typepad.com/blog/2015/12/external-event-and-10-year-forum-anniversary.html#2

 

Thank you!

 

Cheers,

 

Jeremy



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 9 of 9

arnostlobel
Alumni
Alumni

Hello sizy485,

 

yep, that's the solution we recommend!

 

I only have two more comments on your implementation:

1. Please make sure you enclose your transaction in an using block.

2. Please give the name a better string - in case there is problem, we report the string to the end user and we hope it will give them an idea about which external application is in trouble,

3. Also, the string given to a transaction should be meaningful and explanatory. It is what the user will be able to see on the Undo/Redo stack.

 

With the using block, your handler's execute method will look as follows:

 

public void Execute (UIApplication appui)
{
   UIDocument uidoc = appui.ActiveDocument;
   if   (uidoc == null)
   {
       return;  // no document, nothing to do
   }

   using (Transaction trans = new Transaction(uidoc.Document))
   {
        trans.Start("Doing something specific");
       //action in the Revit's thread
       trans.Commit();
    }
}

 

Thanks

Arnošt Löbel