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が居るはずです。バージョンの数字は適当に読み替えてください。

起動してみます。

画像1

いかにも「ファイル開かないと何も表示できねーぜ」って感じのツラ構え、素敵!ファイルメニューから適当なアセンブリを開きましょう。

Q:適当なアセンブリってなんですか
A:とりあえず "C:\Users\(ユーザー名)\.nuget\packages\uipath.uiautomation.activities\(バージョン)\lib\net461" あたりにある、”UiPath.UiAutomation.Activities.dll”あたりでいいんじゃないっすかね。UiPathの基本機能の部分だし。

画像2

ほらほら、なんか出た。
ファイルの中身がツリー表示されてるわけっすよ。
あ、M A N I F E S T とかいうのは、無視していいっす。日本ではマニフェストは無視するのがお約束、みたいな空気あるし?

とりあえずツリーでUiPathからたどってみよう。Clickアクティビティとか初心者向け感があっていいね。

何をたどればいいか、だって?

画像3

アクティビティのプロパティの上の部分、ここに書いてあるよ。
UiPath.Core.Activities.Click
ってのがClickアクティビティの正体だ。ここを開けばいいってこと。

開いてみた。

画像4


「騙して悪いが」とボス・サヴェージは言った

Q:UiPath.Core.Activities.Clickを開いたらいっぱいあるやんけ
A:そうだよ

とりあえずExecuteAsyncかExecuteってのがある筈なので、そこを見る。
(少しまじめな話をすると、Windows Workflow Foundation(WF)の世界では、Activityはクラスという単位になってます。そのクラスの中にExecuteとかExecuteAsyncというメソッドがあって、Workflow上で実行される順番が来たら、そのメソッドが呼び出される仕様になっとるわけです)

でも細かいことは考えずに、ダブルクリックすれば開けるZe!

画像5

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を見れば良い、って単純な問題じゃないので、ものっそい広範囲の比較をするハメになるからお勧めはしません)


そんじゃねー!みんな頑張って!

この記事が気に入ったらサポートをしてみませんか?