0. はじめに
AppStoreに有料AddInを公開する場合に使える、購入済みユーザーかどうかを確認する仕組みをAutodeskが提供しています。それをEntitlement APIと呼びます。
実際にcodeを書くにあたっての注意点をまとめます。
1. 大まかな流れ
権限確認は、次のフローで行います。
- 前提として、AppStoreに登録してApplication IDを取得しておく。
- CWebServicesManagerを使って、UserIdを取得する。
- Application IDとUserIdをAutodeskのServerに送る(REST)。
- 権限情報(json)が返ってくる。
2. Application IDの取得
このIDは、AppStoreに登録すると発行されます。多くの場合は、このIDをAddInにハードコードすることでしょう。
「登録するAddInに『登録すると発行されるID』をハードコードするなら、デッドロック(鶏と卵)じゃないか!」と思われた方は、正しいです。
厳密には、登録完了しなくても、下書き状態でIDが発行されるので、それを組み込んで提出してください。
上図の赤枠で囲った部分が、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>privateboolTryGetUserInformation(){try{varcwsmAsm=System.Reflection.Assembly.LoadFrom(InventorApplication.InstallPath+@"\Bin\AddinNETFramework.AdWebServicesWrapper.dll");vartypeInfo=cwsmAsm.GetType("Autodesk.WebServices.CWebServicesManager");usingdynamicmgr=Activator.CreateInstance(typeInfo);boolisInitialized=mgr.Initialize();if(isInitialized){InventorApplication.Login();stringuserId="";mgr.GetUserId(refuserId);UserId=userId;stringuserName="";mgr.GetLoginUserName(refuserName);UserName=userName;}}catch{returnfalse;}if(!string.IsNullOrWhiteSpace(UserId)){returntrue;}else{returnfalse;}}
注意点として、Autodesk AccountにLoginしていない状態では、GetUserId()
が空文字列を返すことです。ですので、本当に取得できたかどうかはUserIdの中身を確認する必要があります。
4. Autodeskの権限Serverとの通信
この内容は、RESTfulやjsonで検索すると優秀な情報があるでしょうから、最終codeを示しておきます。
/// <summary>/// Autodeskのサーバーに権限を問い合わせます。/// </summary>/// <returns></returns>privateasyncTaskGetEntitlement(){try{// 通信設定、データの下準備varparameters=newDictionary<string,string>(){{"userid",UserId},{"appid",AppId},};varhandler=newHttpClientHandler(){Proxy=WebRequest.GetSystemWebProxy(),// システムのProxyを使う};usingvarclient=newHttpClient(handler){Timeout=TimeSpan.FromSeconds(timeOut),// TimeOut秒数};client.DefaultRequestHeaders.ConnectionClose=true;// KeepAliveしない// 通信するvarresponse=awaitclient.GetAsync($"https://apps.autodesk.com/webservices/checkentitlement?{awaitnewFormUrlEncodedContent(parameters).ReadAsStringAsync()}");varst=awaitresponse.Content.ReadAsStreamAsync();if(response.StatusCode!=HttpStatusCode.OK){SetResult(Result.HttpStatusCodeIsNotOk,$"{response.ReasonPhrase} ({(int)response.StatusCode})");return;}// 得られたjsonを解析するvarserializer=newDataContractJsonSerializer(typeof(ServerResponse));varserverResponse=(ServerResponse)serializer.ReadObject(st);// 結果の判定if(serverResponse.IsValid!=true){SetResult(Result.EntitlementIsNotValid,serverResponse.IsValid.ToString());}elseif(serverResponse.Message?.ToUpper()!="OK"){SetResult(Result.MessageIsNotOk,serverResponse.Message??"(null)");}else{SetResult(Result.Success);}}catch(HttpRequestExceptionex){SetResult(Result.HttpRequestException,ex.Message);}catch(Exceptionex){SetResult(Result.ExceptionThrown,ex.ToString());}finally{IsGetEntitlementRunning=false;}}
publicclassServerResponse{publicstring?UserId;publicstring?AppId;publicboolIsValid;publicstring?Message;}
SetResult()
は結果を保存するための自作関数です。排他制御が必要だったため、直接メンバ変数を変更せずに、関数経由で結果を保存しています。
その他にも名前しか出てこない変数がありますが、適宜調整してください。
5. 参考資料
- Mod the Machine: Entitlement API changes in Inventor 2020
- Autodesk デベロッパー ネットワーク/App Store その中でも、特に、
- デスクトップ アプリ用の Entitlement API