Autodesk Inventor API Hacking (InteractionEventsは必須)

# 0. はじめに
内容的には、['だいたい一度はハマること'](https://qiita.com/yamarah/items/43d5858d22bf8604c7fe)なんですが、重要かつ文章量が多いので別に分けました。
Documentに変更を加える前に、必ず`InteractionEvents.Start()`しなさい、という内容です。
**2019.07.17 --- 5.に追記事項があります。併せて読んでください**

# 1. Command実行中に、Command実行すると、まずい
例えば、以下のような状況を考えます。
![Extrude.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424028/5413bd0e-f2ca-73f2-4f8d-1776fb45ca7f.png)
まさに、押し出しCommandが実行中です。
この状態で、別のCommandを実行すると、どうなるでしょうか?
そのCommandが`InteractionEvents`を使っておらず、かつ、Documentに変更を加える場合は、問題が発生する(≒InventorがCrashする)ことがあります。
例えば上記画像の例でいうと、この状態でフィーチャー`押し出し1`を削除すると、押し出しCommandがパニックになるわけです。

# 2. 実行中のCommandを終了させる
では、Documentを変更する前に、実行中のCommandがあれば終了させるのには、どうすれば良いでしょうか。
条件付きですが(後で述べます)、`InteractionEvents.Start()`すると、実行中のCommandを終了させることが出来ます。

具体的には、以下のようなCodeになるでしょう。

```C#
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](https://qiita.com/yamarah/items/07cf620528ae48f83505)を使っています。

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を終了させようと書きましたが、終了させる単純な方法が別にありました。

```c#
Inventor.CommandManager.StopActiveCommand();
```
DummyのInteractionEventsを作るより、こちらの方がスマートなので、用途によってはこのAPIを使うことをお勧めします。

# 99. 親の記事に戻る
[Autodesk Inventor API Hacking (概略)](https://qiita.com/yamarah/items/cee08eb8f2b7aecbda83)