0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Autodesk Inventor API Hacking (Entitlement API)

Last updated at Posted at 2019-12-02

#0. はじめに
AppStoreに有料AddInを公開する場合に使える、購入済みユーザーかどうかを確認する仕組みをAutodeskが提供しています。それをEntitlement APIと呼びます。
実際にcodeを書くにあたっての注意点をまとめます。

#1. 大まかな流れ
権限確認は、次のフローで行います。

  1. 前提として、AppStoreに登録してApplication IDを取得しておく。
  2. CWebServicesManagerを使って、UserIdを取得する。
  3. Application IDとUserIdをAutodeskのServerに送る(REST)。
  4. 権限情報(json)が返ってくる。

#2. Application IDの取得
このIDは、AppStoreに登録すると発行されます。多くの場合は、このIDをAddInにハードコードすることでしょう。
「登録するAddInに『登録すると発行されるID』をハードコードするなら、デッドロック(鶏と卵)じゃないか!」と思われた方は、正しいです。
厳密には、登録完了しなくても、下書き状態でIDが発行されるので、それを組み込んで提出してください。
AppStore.PNG
上図の赤枠で囲った部分が、Application IDです。ステータスが不完全でも発行されています。

#3. CWebServicesManagerを使う
#3.1 これは何をするもの?
Autodesk Accountのユーザー名は後から変更される可能性があるので、Autodesk内では一意のUserIdがAccountに対して割り当てられているようです。
AutodeskにServerに権限確認するには、このUserIdが必要なので、CWebServicesManagerを使って取得します。
#3.2 CWebServicesManagerはどこにあるの?

<Inventorのinstall場所>\Bin\AddinNETFramework.AdWebServicesWrapper.dll

が実体です。
#3.3 アセンブリが署名されていない!!
非常に残念なことに、AddinNETFramework.AdWebServicesWrapper.dllは署名されていません。ですので、AddIn自体に署名するならば、参照することが出来ません。
(署名されたアセンブリから、署名がないアセンブリを参照できない)

こういった場合は、次の2つの解決方法があります。

  1. 自分で勝手に署名する。
  2. 実行時にDLLを指定して動的Loadする。
    (3. 開発元に署名してもらう)

もちろん3.がお勧めなのですが、現実的ではないので「2つの解決方法」としました。
ここでは、2.の方法を取ります。
#3.4 アセンブリの動的Load
実行時にLoadすると、署名されてなくても警告もでないので、この方法で行きます。Load済みのDLLを再Load(重複Load)すると異常な挙動を取らないか心配でしたが、これは問題がありませんでした。
実際のcodeは以下の通りです。

/// <summary>
/// Autodesk User Idを取得します。
/// </summary>
/// <returns>取得に成功すれば、true。</returns>
private bool TryGetUserInformation()
{
    try
    {
        var cwsmAsm = System.Reflection.Assembly.LoadFrom(InventorApplication.InstallPath + @"\Bin\AddinNETFramework.AdWebServicesWrapper.dll");
        var typeInfo = cwsmAsm.GetType("Autodesk.WebServices.CWebServicesManager");
        using dynamic mgr = Activator.CreateInstance(typeInfo);
        bool isInitialized = mgr.Initialize();

        if (isInitialized)
        {
            InventorApplication.Login();
            string userId = "";
            mgr.GetUserId(ref userId);
            UserId = userId;
            string userName = "";
            mgr.GetLoginUserName(ref userName);
            UserName = userName;
        }
    }
    catch
    {
        return false;
    }

    if (!string.IsNullOrWhiteSpace(UserId))
    {
        return true;
    }
    else
    {
        return false;
    }
}

注意点として、Autodesk AccountにLoginしていない状態では、GetUserId()が空文字列を返すことです。ですので、本当に取得できたかどうかはUserIdの中身を確認する必要があります。

#4. Autodeskの権限Serverとの通信
この内容は、RESTfulやjsonで検索すると優秀な情報があるでしょうから、最終codeを示しておきます。

GetEntitlement()
/// <summary>
/// Autodeskのサーバーに権限を問い合わせます。
/// </summary>
/// <returns></returns>
private async Task GetEntitlement()
{
    try
    {
        // 通信設定、データの下準備
        var parameters = new Dictionary<string, string>()
        {
            { "userid", UserId },
            { "appid", AppId },
        };
        var handler = new HttpClientHandler()
        {
            Proxy = WebRequest.GetSystemWebProxy(), // システムのProxyを使う
        };
        using var client = new HttpClient(handler)
        {
            Timeout = TimeSpan.FromSeconds(timeOut), // TimeOut秒数
        };
        client.DefaultRequestHeaders.ConnectionClose = true;    // KeepAliveしない

        // 通信する
        var response = await client.GetAsync($"https://apps.autodesk.com/webservices/checkentitlement?{await new FormUrlEncodedContent(parameters).ReadAsStringAsync()}");
        var st = await response.Content.ReadAsStreamAsync();

        if (response.StatusCode != HttpStatusCode.OK)
        {
            SetResult(Result.HttpStatusCodeIsNotOk, $"{response.ReasonPhrase} ({(int)response.StatusCode})");
            return;
        }

        // 得られたjsonを解析する
        var serializer = new DataContractJsonSerializer(typeof(ServerResponse));
        var serverResponse = (ServerResponse)serializer.ReadObject(st);

        // 結果の判定
        if (serverResponse.IsValid != true)
        {
            SetResult(Result.EntitlementIsNotValid, serverResponse.IsValid.ToString());
        }
        else if (serverResponse.Message?.ToUpper() != "OK")
        {
            SetResult(Result.MessageIsNotOk, serverResponse.Message ?? "(null)");
        }
        else
        {
            SetResult(Result.Success);
        }
    }
    catch (HttpRequestException ex)
    {
        SetResult(Result.HttpRequestException, ex.Message);
    }
    catch (Exception ex)
    {
        SetResult(Result.ExceptionThrown, ex.ToString());
    }
    finally
    {
        IsGetEntitlementRunning = false;
    }
}
class ServerResponse
public class ServerResponse
{
    public string? UserId;
    public string? AppId;
    public bool IsValid;
    public string? Message;
}

SetResult()は結果を保存するための自作関数です。排他制御が必要だったため、直接メンバ変数を変更せずに、関数経由で結果を保存しています。
その他にも名前しか出てこない変数がありますが、適宜調整してください。

#5. 参考資料

#99. 親の記事に戻る
Autodesk Inventor API Hacking (概略)

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?