Microsoft MVP성태의 닷넷 이야기
Windows: 192. Power Automate Desktop (Preview) 소개 - Bitvise SSH Client 제어 [링크 복사], [링크+제목 복사],
조회: 8953
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 

Power Automate Desktop (Preview) 소개 - Bitvise SSH Client 제어

요즘 RPA(Robotic Process Automation)라는 영역이 뜨겁군요. ^^

RPA를 아주 단순하게 생각하면 기존의 AutoHotkey 프로그램이 하던 것이라고 떠올리면 됩니다. 단지, 이것을 좀 더 업무 친화적으로 포장하고 심지어 AI와도 연동을 한다면 고부가 가치의 RPA 솔루션이 될 수 있습니다.

당연히 운영체제와 오피스 제품군을 소유하고 있는 마이크로소프트가 이 분야를 보고만 있을 수는 없겠지요,

Microsoft Power Automate 설명서
; https://docs.microsoft.com/ko-kr/power-automate/

그리고, 바로 며칠 전의 Ignite 2021 행사에서 (Power Automate 기능의 일부를 담당하는) Power Automate Desktop을 (Windows 10 운영체제를 대상으로 Preview 버전을) 무료 배포하기 시작했습니다.

Easily automate anything from your desktop
; https://flow.microsoft.com/en-us/desktop/

Download the Power Automate Desktop installer
; https://go.microsoft.com/fwlink/?linkid=2102613

실제로 이걸 가지고 한 번 실습을 해볼까요? ^^

마침, 제 경우에 언제나 운영체제를 시작 후 Bitvise SSH Client를 실행시켜 로그인하는 작업이 좀 귀찮았습니다. 즉, 다음과 같은 작업을 꼭 해야 했는데,

  1. Action - Bitvise SSH Client 실행
  2. Action - Login 버튼 클릭
  3. Action - Bitvise SSH Client 윈도우 닫기

이것을 Power Automate Desktop으로 자동화해보겠습니다. ^^




자, 그럼 "Power Automate Desktop" 프로그램을 실행하고, "New flow" 링크를 눌러 새로운 자동화 기능 추가를 시작합니다.

pad_bitvise_login_1.png

"Create" 버튼을 누르면 이제 각각의 "Actions"를 구성할 수 있는 윈도우가 새로 뜨는데요,

pad_bitvise_login_2.png

첫 번째로 필요한 Action이 "Bitvise SSH Client"를 실행하는 것이므로, 위의 화면에서 보는 바와 같이 좌측의 "Actions" 패널에서 "System" 범주의 "Run application"을 선택, Bitvise 프로그램의 경로를 "Application path"에 넣고 "Save" 버튼을 누릅니다.

이후, 실행되는 화면에서 "Login" 버튼을 클릭하면 되지만 여기서 주의해야 할 것이 있습니다. 즉, exe 프로세스가 실행해 윈도우 화면까지 나오려면 시간이 걸리는데 이를 위한 적절한 대기가 필요하게 됩니다. 관련해서 방식은 다양하게 처리할 수 있는데요, 가령, 위의 "Run application" 설정 창에서도 "After application launch:" 설정에서 "Continue immediately" 옵션이 아닌, "Wait for application to load" 옵션과 함께 "Timeout"을 5초 정도로 지정할 수 있습니다. 그런 경우, EXE 실행 후 무조건 5초 대기 후 다음 Action을 실행하는 단계로 진행합니다.

물론, 저렇게 무조건 대기를 하기보다는 Actions의 "Wait" 범주에서 "Wait for window"를 추가해 다음의 매개변수로 설정하는 것이 좀 더 매끄러울 것입니다.

Find window: By window UI element
Window: %appmask['Window \'Bitvise SSH Client 8.43\'']%
Wait for window to: Open

여기서, 위의 "Window" 조건 값이 난해할 수 있는데, 해당 설정을 할 수 있는 "Added a new UI element" 버튼이 제공되므로 이를 이용해 (텍스트 입력 없이) 마우스 클릭만으로 윈도우 지정을 할 수 있습니다. (해당 UI는 다음 단계에서 소개합니다.)

자, 이렇게 해서 프로그램을 실행해 윈도우가 뜨는 것까지 진행했다면 이제 "Login" 버튼을 눌러 줄 수 있습니다. 이를 위해 "Actions" 패널에서 "UI automation" 범주의 "Click UI element in window"를 선택해,

pad_bitvise_login_3.png

보이는 우측의 대화창에서 "UI element:" 콤보 박스를 아래로 펼치면 "Add a new UI element"를 버튼이 보입니다. 해당 버튼을 누르면 다음과 같은 화면으로 진입하고,

pad_bitvise_login_4.png

"Login" 위로 마우스를 가져가면 위의 화면에서 보는 것처럼 대상 버튼이 빨간색 테두리로 인식이 되면서 이때 "Ctrl + 마우스 우 클릭"을 하면 그 버튼의 조건이 다음과 같은 식으로 등록이 됩니다.

UI element: %appmask['Window \'Bitvise SSH Client 8.43\'']['Button \'Log in\'']%
Click type: Left click

대충 등록 방식이 감이 오시죠? ^^

참고로, 위와 같이 Action을 직접 정의하는 것뿐만 아니라 "Actions" 문자열 우측에 있는 "Web Recorder", "Desktop Recorder"를 이용해,

pad_bitvise_login_5.png

사용자의 동작을 기록해 재생하는 것도 가능합니다.

Record flows in Power Automate Desktop
; https://docs.microsoft.com/en-us/power-automate/desktop-flows/recording-flow

Generate Power Automate Desktop flows by recording
; https://docs.microsoft.com/en-us/learn/modules/pad-power-automate-desktop-recording-features/




자, 그런데 마지막 동작이 애매합니다. Bitvise 응용 프로그램은 Login 후 더 이상 화면에 있을 필요가 없고 작업 표시줄에도 아이콘이 나와 있을 필요가 없습니다. 이를 위해서는 윈도우의 상단 우측에 있는 "X" 버튼을 눌러야 하는데요. 이를 위해 이미 앞 단계에서 진행한 "UI automation" / "Click UI element in window"로 "X" 버튼을 누르게 처리할 수 있겠지만, 아쉽게도 Bitvise 내부 동작은 일반 윈도우와는 달라서인지 정상적으로 통하지 않습니다. 심지어 다음과 같이 오류가 발생하기까지 합니다.

Flow 'bitvise login' failed
Error in 'main', action #3 'Click UI element in window'

Subflow: Main, Action: Click UI element in window

Click failed (failed to get UI element)

: Robin.Core.ActionException: Click failed (failed to get UI element)
   at Robin.Modules.UIAutomation.Actions.ClickElementInWindow.Execute(ActionContext context)
   at Robin.Runtime.Engine.ActionRunner.RunAction(String action, Dictionary`2 inputArguments, Dictionary`2 outputArguments, IActionStatement statement)

이에 대한 또 다른 처리 방식으로 "UI automation" / "Windows" / "Close window" 동작이 있지만, 일반적인 윈도우 응용 프로그램이라면 역시 이것도 동작했겠지만 Bitvise는 이 동작이 통하지 않습니다.

대신 Bitvise는 "UI automation" / "Windows" / "Set window visibility" 동작을 정의하는 것으로 해당 목적을 이룰 수 있습니다.

즉, 일반적인 동작으로 대부분의 응용 프로그램이 자동화될 수는 있지만 간혹 특정 프로그램들은 '일반적인 동작'을 따르지 않을 수도 있기 때문에 적절한 우회 방법을 찾아야 한다는 것을 고려해야 합니다.




여기까지 action을 정의했으면 이제 "File" / "Save (Ctrl + S)" 메뉴를 선택해 저장, 다음과 같이 해당 자동화 작업을 목록에서 확인할 수 있습니다.

pad_bitvise_login_6.png

이제 삼각형 모양의 Run 버튼을 누르면, 정의한 action에 따라 작업들이 차례대로 실행이 됩니다.

어떠세요? RPA라는 게 별거 아니죠? ^^ 사실 개발자 입장에서는 이미 CI/CD나 각종 WebHook을 이용한 처리들이 RPA 영역에 속한다고 볼 수 있습니다. 단지, 근래의 RPA 솔루션들이 의미가 있는 것은 그러한 자동화 작업들을 "코드 한 줄 없이" 가능하게 만들어 준다는 점입니다. 따라서 비-개발자들, 일례로 지식 근로자(knowledge worker)라고 불리거나 심지어 시민 개발자(citizen developer)들이 회사 등에서의 업무를 쉽게 자동화할 수 있다는 데에 의미가 있습니다. 혹은, 그냥 일반적인 윈도우 사용자라면 괜찮은 Toy 프로그램 하나 나온 정도가 되겠군요. ^^

그러고 보니, 이 분야의 전통적인 전문가라면 게임 프로그램들에 대한 각종 매크로 제작자들을 빼놓을 수 없을 것입니다. ^^




아직 Preview 버전이라 앞으로 어느 정도까지 기능이 발전할지는 모르겠지만 아쉬운 것들이 좀 있습니다. 가령, 저렇게 저장한 flow를 사용자가 직접 "Run" 버튼을 누르지 않고 프로그램에 의해 실행하는 방법이 아직 제공되지 않는 듯합니다.

참고로, Power Automate Desktop 프로그램이 flow를 실행할 때 자식 프로세스로 PAD.Runtime.Robot.exe를 다음과 같은 인자로 실행하는 것을 확인할 수 있습니다.

"C:\Program Files (x86)\Power Automate Desktop\PAD.Runtime.Robot.exe" --path "%LOCALAPPDATA%\Microsoft\Power Automate Desktop\Console\Workspace\b2564e2f-e5fe-47ce-9d94-5b94ead3762b\package" --instanceId 2e1bfe8462cd4b7ca000c78007cbadef --varPreviewChars 128 --varPreviewLines 5


아쉽게도 저 인자들 중에는 우리가 저장한 flow와 연관 지을 수 어떠한 값도 없지만, 실제로 그냥 단일 cmd 창에서 저대로 다시 실행해도 flow가 실행되지 않고 이런 오류만 발생합니다.

Unhandled Exception: System.Resources.MissingManifestResourceException: Could not find any resources appropriate for the specified culture or the neutral culture.  Make sure "Robin.Runtime.Robot.Properties.Resources.resources" was correctly embedded or linked into assembly "PAD.Runtime.Robot" at compile time, or that all the satellite assemblies required are loadable and fully signed.
   at System.Resources.ManifestBasedResourceGroveler.HandleResourceStreamMissing(String fileName)
   at System.Resources.ManifestBasedResourceGroveler.GrovelForResourceSet(CultureInfo culture, Dictionary`2 localResourceSets, Boolean tryParents, Boolean createIfNotExists, StackCrawlMark& stackMark)
   at System.Resources.ResourceManager.InternalGetResourceSet(CultureInfo requestedCulture, Boolean createIfNotExists, Boolean tryParents, StackCrawlMark& stackMark)
   at System.Resources.ResourceManager.InternalGetResourceSet(CultureInfo culture, Boolean createIfNotExists, Boolean tryParents)
   at System.Resources.ResourceManager.GetString(String name, CultureInfo culture)
   at Robin.Runtime.Robot.HostProgram.OnExecute()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at McMaster.Extensions.CommandLineUtils.Conventions.ExecuteMethodConvention.Invoke(MethodInfo method, Object instance, Object[] arguments)
   at McMaster.Extensions.CommandLineUtils.Conventions.ExecuteMethodConvention.<OnExecute>d__1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at McMaster.Extensions.CommandLineUtils.Conventions.ExecuteMethodConvention.<>c__DisplayClass0_0.<<Apply>b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at McMaster.Extensions.CommandLineUtils.CommandLineApplication.<>c__DisplayClass142_0.<OnExecute>b__0()
   at McMaster.Extensions.CommandLineUtils.CommandLineApplication.Execute[TApp](CommandLineContext context)
   at Robin.Runtime.Robot.HostProgram.Main(String[] args)

어쨌든 해당 flow가 저장된 경로는 위의 검토를 통해서 알게 되었는데요, 실제로 우리가 추가한 4가지 action은 "%LOCALAPPDATA%\Microsoft\Power Automate Desktop\Console\Workspace\b2564e2f-e5fe-47ce-9d94-5b94ead3762b\script.robin" 파일에서 찾을 수 있습니다.

IMPORT '%LOCALAPPDATA%\\Microsoft\\Power Automate Desktop\\Console\\Workspace\\b2564e2f-e5fe-47ce-9d94-5b94ead3762b\\controlRepo.appmask' AS appmask
IMPORT '%LOCALAPPDATA%\\Microsoft\\Power Automate Desktop\\Console\\Workspace\\b2564e2f-e5fe-47ce-9d94-5b94ead3762b\\imageRepo.imgrepo' AS imgrepo


System.RunApplication ApplicationPath: $'''C:\\Program Files (x86)\\Bitvise SSH Client\\BvSsh.exe''' WindowStyle: System.ProcessWindowStyle.Normal ProcessId=> AppProcessId
WAIT (UIAutomation.Windows.ToOpen Window: appmask['Window \'Bitvise SSH Client 8.43\''] FocusWindow: False) 
UIAutomation.Click Element: appmask['Window \'Bitvise SSH Client 8.43\'']['Button \'Log in\''] ClickType: UIAutomation.ClickType.LeftClick MousePositionRelativeToElement: UIAutomation.RectangleEdgePoint.MiddleCenter OffsetX: 0 OffsetY: 0
UIAutomation.Windows.SetVisibility Window: appmask['Window \'bit ...  Bitvise SSH Client\''] Visibility: UIAutomation.Visibility.Hidden

따라서 나중에 Bitvise 프로그램의 버전이 바뀌어 8.44가 되었을 때, Power Automate Desktop을 이용해 action을 UI 상에서 편집할 수도 있겠지만 script.robin 파일의 appmask 값을 직접 수정해 저장하는 것도 가능합니다.




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]







[최초 등록일: ]
[최종 수정일: 3/28/2021]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 



2021-03-11 12시13분
정성태

... 31  32  33  34  35  36  37  38  39  40  41  42  [43]  44  45  ...
NoWriterDateCnt.TitleFile(s)
12555정성태3/8/20218945Windows: 190. C# - 레지스트리에 등록된 DigitalProductId로부터 라이선스 키(Product Key)를 알아내는 방법파일 다운로드2
12554정성태3/8/20218775.NET Framework: 1027. 닷넷 응용 프로그램을 위한 PDB 옵션 - full, pdbonly, portable, embedded
12553정성태3/5/20219234개발 환경 구성: 548. 기존 .NET Framework 프로젝트를 .NET Core/5+ 용으로 변환해 주는 upgrade-assistant, try-convert 도구 소개 [4]
12552정성태3/5/20218501개발 환경 구성: 547. github workflow/actions에서 Visual Studio Marketplace 패키지 등록하는 방법
12551정성태3/5/20217416오류 유형: 702. 비주얼 스튜디오 - The 'CascadePackage' package did not load correctly. (2)
12550정성태3/5/20217084오류 유형: 701. Live Share 1.0.3713.0 버전을 1.0.3884.0으로 업데이트 이후 ContactServiceModelPackage 오류 발생하는 문제
12549정성태3/4/20217571오류 유형: 700. VsixPublisher를 이용한 등록 시 다양한 오류 유형 해결책
12548정성태3/4/20218338개발 환경 구성: 546. github workflow/actions에서 nuget 패키지 등록하는 방법
12547정성태3/3/20218885오류 유형: 699. 비주얼 스튜디오 - The 'CascadePackage' package did not load correctly.
12546정성태3/3/20218481개발 환경 구성: 545. github workflow/actions에서 빌드시 snk 파일 다루는 방법 - Encrypted secrets
12545정성태3/2/202111237.NET Framework: 1026. 닷넷 5에 추가된 POH (Pinned Object Heap) [10]
12544정성태2/26/202111458.NET Framework: 1025. C# - Control의 Invalidate, Update, Refresh 차이점 [2]
12543정성태2/26/20219805VS.NET IDE: 158. C# - 디자인 타임(design-time)과 런타임(runtime)의 코드 실행 구분
12542정성태2/20/202112139개발 환경 구성: 544. github repo의 Release 활성화 및 Actions를 이용한 자동화 방법 [1]
12541정성태2/18/20219368개발 환경 구성: 543. 애저듣보잡 - Github Workflow/Actions 소개
12540정성태2/17/20219686.NET Framework: 1024. C# - Win32 API에 대한 P/Invoke를 대신하는 Microsoft.Windows.CsWin32 패키지
12539정성태2/16/20219606Windows: 189. WM_TIMER의 동작 방식 개요파일 다운로드1
12538정성태2/15/20219980.NET Framework: 1023. C# - GC 힙이 아닌 Native 힙에 인스턴스 생성 - 0SuperComicLib.LowLevel 라이브러리 소개 [2]
12537정성태2/11/202111010.NET Framework: 1022. UI 요소의 접근은 반드시 그 UI를 만든 스레드에서! - 두 번째 이야기 [2]
12536정성태2/9/202110007개발 환경 구성: 542. BDP(Bandwidth-delay product)와 TCP Receive Window
12535정성태2/9/20219125개발 환경 구성: 541. Wireshark로 확인하는 LSO(Large Send Offload), RSC(Receive Segment Coalescing) 옵션
12534정성태2/8/20219754개발 환경 구성: 540. Wireshark + C/C++로 확인하는 TCP 연결에서의 closesocket 동작 [1]파일 다운로드1
12533정성태2/8/20219370개발 환경 구성: 539. Wireshark + C/C++로 확인하는 TCP 연결에서의 shutdown 동작파일 다운로드1
12532정성태2/6/20219888개발 환경 구성: 538. Wireshark + C#으로 확인하는 ReceiveBufferSize(SO_RCVBUF), SendBufferSize(SO_SNDBUF) [3]
12531정성태2/5/20218868개발 환경 구성: 537. Wireshark + C#으로 확인하는 PSH flag와 Nagle 알고리듬파일 다운로드1
12530정성태2/4/202113029개발 환경 구성: 536. Wireshark + C#으로 확인하는 TCP 통신의 Receive Window
... 31  32  33  34  35  36  37  38  39  40  41  42  [43]  44  45  ...