0. はじめに
内容的には、'だいたい一度はハマること'なんですが、重要かつ文章量が多いので別に分けました。
Documentに変更を加える前に、必ずInteractionEvents.Start()
しなさい、という内容です。
2019.07.17 --- 5.に追記事項があります。併せて読んでください
1. Command実行中に、Command実行すると、まずい
例えば、以下のような状況を考えます。
まさに、押し出しCommandが実行中です。
この状態で、別のCommandを実行すると、どうなるでしょうか?
そのCommandがInteractionEvents
を使っておらず、かつ、Documentに変更を加える場合は、問題が発生する(≒InventorがCrashする)ことがあります。
例えば上記画像の例でいうと、この状態でフィーチャー押し出し1
を削除すると、押し出しCommandがパニックになるわけです。
2. 実行中のCommandを終了させる
では、Documentを変更する前に、実行中のCommandがあれば終了させるのには、どうすれば良いでしょうか。
条件付きですが(後で述べます)、InteractionEvents.Start()
すると、実行中のCommandを終了させることが出来ます。
具体的には、以下のようなCodeになるでしょう。
private InteractionEvents interactionEvents;
public void ControlEvents_OnExecute(NameValueMap options)
{
if (なにか不都合)
{
return;
}
interactionEvents = m_Application.CommandManager.CreateInteractionEvents();
interactionEvents.Name = "MyCommandName";
interactionEvents.OnActivate += InteractionEvents_OnActivate;
interactionEvents.OnTerminate += InteractionEvents_OnTerminate;
interactionEvents.Start();
}
private void InteractionEvents_OnActivate()
{
using (var transaction = new TinyTransaction(m_Application.ActiveEditDocument, "コマンド名"))
{
try
{
//
// 実際の処理
//
transaction.End();
}
catch (System.Exception ex)
{
//
// エラー処理
//
}
finally
{
interactionEvents.Stop();
}
}
}
private void InteractionEvents_OnTerminate()
{
if (interactionEvents != null) {
//
// 必要なら、後処理
//
interactionEvents = null;
}
}
なお、Transaction処理は、別記事のTinyTransactionを使っています。
19.06.14 訂正
初出で上記ソース中に
// TransactionがAbortで終了する場合(デフォルト)は、ここでのStop()と、
// Inventorが自動発行するStop()で、InteractionEvents_OnTerminate()が2度呼ばれる。
// 神経質な人は、transaction.End()の直後でinteractionEvents.Stop()して、
// ここではしないようにすると良い。
と書きましたが、勘違いだったようです。何か複合条件があるのかもしれませんが、今のところ再現できないので、積極的にfinally
でInteractionEvents.Stop()
するのを良しとします。
3. InteractionEventsはInteractionEventsを終了させる
分かってしまえば簡単な話しなのですが、InteractionEvents.Start()
は、既に実行中のInteractionEvents
を終了させます。
ですので、先ほど後述すると言った条件とは、先に実行しているCommandがInteractionEvents
を実行していることになります。
そして、見逃しがちなのですが重要なことは、Documentを変更するCommandは、ユーザーによる操作が無くてもInteractionEventsを使うべきだということです。
既に述べた通り、そうすることで先に実行中のCommandがあれば、終了させることが出来ます。
(5.に追記あり)
4. ユーザーのInteractionがあるのに、InteractionEventsを使わずに済む例 (危険)
例えば、自前のFormでユーザー操作を受け付ける場合は、InteractionEvents
を使わなくても実装できます。
こういった場合でも、くどいようですがInteractionEvents
を使用すべきです。
(5.に追記あり)
5. 先行のCommandを終了させる方法 (19.07.17追記)
3.と4.で、InteractiveではないCommandでも、InteractionEvents
を作ることで先行して実行中のCommandを終了させようと書きましたが、終了させる単純な方法が別にありました。
Inventor.CommandManager.StopActiveCommand();
DummyのInteractionEventsを作るより、こちらの方がスマートなので、用途によってはこのAPIを使うことをお勧めします。