UiPathとかよくわからんので動作を確認してみる
なんか記事を書けーって話が飛んできたんですよ。
でも自分、まあ合間合間に触ってはいるけど、ここ2~3か月、あんまUiPath触ってないわけですよ。
で、どっかに書いたけど、UiPathって運用エンジニアに喧嘩売る^h^h^h^h^h^h^h^h^h^h^h^hバージョンアップのサイクルが速くて、気が付くと仕様とか動作とか変わってるわけですね。
つまり自分、もう置いてきぼり確定なわけですよ。話についていけないわけですよ。たとえるなら皆が令和の世界に居る中、一人だけ長和とか寛仁あたりの世界観なわけです。
これはヤバい。いやまあ、仕事では今のところUiPath使ってないから、直接的にヤバいわけではないけど、将来、RPAの世界に戻れなくなる可能性がある。
だったらUiPathの動作をなんとなく見る方法とか記事に書いとけば良いんじゃね?と思った次第。
四方山話
Q:なんでこの記事、noteなんですか。Qiitaじゃないんですか。
A:UiPathの規約的にたぶんグレーゾーンだから、Qiitaでつよつよエンジニアに殴られるの怖いんだもん。
Studioは「すとぅーでぃお」
Q:で、動作を確認するってどうやるの。
A:UiPathは.NET Framework上で動くアプリです。つまり.NET Frameworkの流儀で調べればわかるはずだ。
というわけで、みんなのパソコンにもインストールされている、.NET Framework SDKに入っているツール、IL DASM!君に決めた!
・・・え、.NET FrameworkのSDKなんかインストールされてない?そりゃそうだ、世の中はもう.NET Coreだもんね。ごめんよ頭が古くて。
https://dotnet.microsoft.com/download/visual-studio-sdks
ここからダウンロードできるのでインストールしてください。今なら.NET Framework 4.8とかのを入れとけばいい気がする(適当)
インストールすると "C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools\" にildasm.exeが居るはずです。バージョンの数字は適当に読み替えてください。
起動してみます。
いかにも「ファイル開かないと何も表示できねーぜ」って感じのツラ構え、素敵!ファイルメニューから適当なアセンブリを開きましょう。
Q:適当なアセンブリってなんですか
A:とりあえず "C:\Users\(ユーザー名)\.nuget\packages\uipath.uiautomation.activities\(バージョン)\lib\net461" あたりにある、”UiPath.UiAutomation.Activities.dll”あたりでいいんじゃないっすかね。UiPathの基本機能の部分だし。
ほらほら、なんか出た。
ファイルの中身がツリー表示されてるわけっすよ。
あ、M A N I F E S T とかいうのは、無視していいっす。日本ではマニフェストは無視するのがお約束、みたいな空気あるし?
とりあえずツリーでUiPathからたどってみよう。Clickアクティビティとか初心者向け感があっていいね。
何をたどればいいか、だって?
アクティビティのプロパティの上の部分、ここに書いてあるよ。
UiPath.Core.Activities.Click
ってのがClickアクティビティの正体だ。ここを開けばいいってこと。
開いてみた。
「騙して悪いが」とボス・サヴェージは言った
Q:UiPath.Core.Activities.Clickを開いたらいっぱいあるやんけ
A:そうだよ
とりあえずExecuteAsyncかExecuteってのがある筈なので、そこを見る。
(少しまじめな話をすると、Windows Workflow Foundation(WF)の世界では、Activityはクラスという単位になってます。そのクラスの中にExecuteとかExecuteAsyncというメソッドがあって、Workflow上で実行される順番が来たら、そのメソッドが呼び出される仕様になっとるわけです)
でも細かいことは考えずに、ダブルクリックすれば開けるZe!
Q:なにこれ
A:.NETの中間言語です。つまり名状しがたいアセンブリのようなものです。
Q:わけわからん
A:大丈夫、自分も半分ぐらいしか(ぱっと見では)わからん。
(※頑張れば解読できるけど、頑張ると疲れて天下一品のこってりが食べたくなります)
最初から「ILを解読するぜ」とか思うとだいたい死にます。
少なくとも.NET Frameworkやプログラミングを理解していない人がやると、NRS(.NET Reality Shock)で死に至る可能性があるって古事記に書いてありました。嘘です。
とりあえず、要点だけ書いておきます。
ldなんちゃら(ldrocとかldcとか)はいったん無視する。
stなんちゃら(strocとか)もいったん無視する。
nopは全力で無視する(そもそもコイツは本当に「何もしない」処理)
重要なのはcallとかcallvirtあたり。前者は変数の型をベースにメソッドを呼んでる。後者はオブジェクトの中身を見てメソッドを呼んでる。
というわけでcallとcallvirtを引っ張り出してみた。
IL_000f: call void UiPath.Shared.Contracts.ContextExtensions::ApplyPropertySettings(class [System.Activities]System.Activities.ActivityContext,
object)
IL_0016: callvirt instance class UiPath.Core.Activities.Target UiPath.Core.Activities.TargetAsyncActivity::get_Target()
IL_001b: call void UiPath.Shared.Contracts.ContextExtensions::ApplyPropertySettings(class [System.Activities]System.Activities.ActivityContext,
object)
IL_0022: call instance class UiPath.Core.Activities.CursorPosition UiPath.Core.Activities.Click::get_CursorPosition()
IL_0036: call instance class [System.Activities]System.Activities.InArgument`1<int32> UiPath.Core.Activities.CursorPosition::get_OffsetX()
IL_004b: call instance !0 class [System.Activities]System.Activities.InArgument`1<int32>::Get(class [System.Activities]System.Activities.ActivityContext)
IL_0058: call instance bool valuetype [mscorlib]System.Nullable`1<int32>::get_HasValue()
IL_0064: call instance !0 valuetype [mscorlib]System.Nullable`1<int32>::GetValueOrDefault()
IL_0070: call instance class UiPath.Core.Activities.CursorPosition UiPath.Core.Activities.Click::get_CursorPosition()
IL_0084: call instance class [System.Activities]System.Activities.InArgument`1<int32> UiPath.Core.Activities.CursorPosition::get_OffsetY()
IL_0099: call instance !0 class [System.Activities]System.Activities.InArgument`1<int32>::Get(class [System.Activities]System.Activities.ActivityContext)
IL_00a6: call instance bool valuetype [mscorlib]System.Nullable`1<int32>::get_HasValue()
IL_00b2: call instance !0 valuetype [mscorlib]System.Nullable`1<int32>::GetValueOrDefault()
IL_00be: call instance class UiPath.Core.Activities.CursorPosition UiPath.Core.Activities.Click::get_CursorPosition()
IL_00ca: call instance valuetype UiPath.Core.Position UiPath.Core.Activities.CursorPosition::get_Position()
IL_00d5: call instance class [System.Activities]System.Activities.InArgument`1<int32> UiPath.Core.Activities.Click::get_DelayBefore()
IL_00db: callvirt instance !0 class [System.Activities]System.Activities.InArgument`1<int32>::Get(class [System.Activities]System.Activities.ActivityContext)
IL_00e4: callvirt instance int32 UiPath.Core.Activities.Click::GetDelayAfter(class [System.Activities]System.Activities.ActivityContext)
IL_00f0: call instance class [System.Activities]System.Activities.InArgument`1<valuetype UiPath.Core.ClickType> UiPath.Core.Activities.Click::get_ClickType()
IL_00f6: callvirt instance !0 class [System.Activities]System.Activities.InArgument`1<valuetype UiPath.Core.ClickType>::Get(class [System.Activities]System.Activities.ActivityContext)
IL_0102: call instance class [System.Activities]System.Activities.InArgument`1<bool> UiPath.Core.Activities.Click::get_SendWindowMessages()
IL_0108: callvirt instance !0 class [System.Activities]System.Activities.InArgument`1<bool>::Get(class [System.Activities]System.Activities.ActivityContext)
IL_0114: call instance class [System.Activities]System.Activities.InArgument`1<bool> UiPath.Core.Activities.Click::get_SimulateClick()
IL_011a: callvirt instance !0 class [System.Activities]System.Activities.InArgument`1<bool>::Get(class [System.Activities]System.Activities.ActivityContext)
IL_0126: call instance class [System.Activities]System.Activities.InArgument`1<valuetype UiPath.Core.Activities.KeyModifiers> UiPath.Core.Activities.Click::get_KeyModifiers()
IL_012c: callvirt instance !0 class [System.Activities]System.Activities.InArgument`1<valuetype UiPath.Core.Activities.KeyModifiers>::Get(class [System.Activities]System.Activities.ActivityContext)
IL_0138: call instance class [System.Activities]System.Activities.InArgument`1<valuetype UiPath.Core.MouseButton> UiPath.Core.Activities.Click::get_MouseButton()
IL_013e: callvirt instance !0 class [System.Activities]System.Activities.InArgument`1<valuetype UiPath.Core.MouseButton>::Get(class [System.Activities]System.Activities.ActivityContext)
IL_014a: call instance class [System.Activities]System.Activities.InArgument`1<bool> UiPath.Core.Activities.Click::get_AlterIfDisabled()
IL_0150: callvirt instance !0 class [System.Activities]System.Activities.InArgument`1<bool>::Get(class [System.Activities]System.Activities.ActivityContext)
IL_0169: call string UiPath.Core.Activities.Properties.UiPath_Core_Activities::get_OnlyOneOutOfTwoOptionsCanBeSet()
IL_016e: call string UiPath.Core.Activities.Properties.UiPath_Core_Activities::get_SimulateClick()
IL_0173: call string UiPath.Core.Activities.Properties.UiPath_Core_Activities::get_SendWindowMessages()
IL_0178: call string [mscorlib]System.String::Format(string,
object,
object)
IL_018c: callvirt instance class [mscorlib]System.Threading.Tasks.Task`1<class UiPath.Core.UiElement> UiPath.Core.Activities.TargetAsyncActivity::GetElementAsync(class [System.Activities]System.Activities.AsyncCodeActivityContext,
valuetype [mscorlib]System.Threading.CancellationToken,
valuetype [mscorlib]System.Nullable`1<int32>)
IL_019d: call class [mscorlib]System.Threading.Tasks.Task UiPath.Core.Activities.TaskExtensions::Then<class UiPath.Core.UiElement>(class [mscorlib]System.Threading.Tasks.Task`1<!!0>,
class [mscorlib]System.Action`1<!!0>)
情報量がだばーっとあるので、なんだかわからないかもしれないけど、上からゆっくり見てみると、意外とわかりやすい(かもしれない)
最初に実行されてるのが ApplyPropertySettings なのは、名前の通り、プロパティに設定された値を読み込んでるんだろう、とか。
TargetAsyncActivity::get_Targetは、対象を取ってきてるんだろうな、とか。
あ、ちなみにILには「変数」みたいな甘い概念はなくて、基本ぜんぶスタックにぶっこむ世界なので、いわゆる引数(あるいはプロパティ値)に該当するものが何かをチェックしたければ、自分でスタックの状態をトレースしながら読んでいくしかない。本気でやるとたぶん禿げるけど。
で、何がわかるの?
頑張って読むとプログラム(UiPath)本体が何をやってるか、だいたいわかります。そうでなくとも「よくわからんトラブル」に巻き込まれて、にっちもさっちも行かなくなったら、ILを読んでみるのも良いかもしれない。
あと邪道かもしれないけど、UiPathのActivityがバージョンアップされて、Activityの挙動がちがくね?と思ったらILで差分を取ってみるとかも一応できる。一応ね。(ExecuteAsyncを見れば良い、って単純な問題じゃないので、ものっそい広範囲の比較をするハメになるからお勧めはしません)
そんじゃねー!みんな頑張って!
この記事が気に入ったらサポートをしてみませんか?