Microsoft MVP성태의 닷넷 이야기
Windows: 192. Power Automate Desktop (Preview) 소개 - Bitvise SSH Client 제어 [링크 복사], [링크+제목 복사],
조회: 17191
글쓴 사람
정성태 (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분
정성태

... 106  107  108  109  110  111  112  113  114  115  116  117  118  [119]  120  ...
NoWriterDateCnt.TitleFile(s)
10949정성태4/28/201619860.NET Framework: 575. SharedDomain과 JIT 컴파일파일 다운로드1
10948정성태4/28/201623804.NET Framework: 574. .NET - 눈으로 확인하는 SharedDomain의 동작 방식 [3]파일 다운로드1
10947정성태4/27/201621650.NET Framework: 573. .NET CLR4 보안 모델 - 4. CLR4 보안 모델에서의 조건부 APTCA 역할파일 다운로드1
10946정성태4/26/201624504VS.NET IDE: 106. Visual Studio 2015 확장 - INI 파일을 위한 사용자 정의 포맷 기능 (Syntax Highlighting)파일 다운로드1
10945정성태4/26/201618230오류 유형: 327. VSIX 프로젝트 빌드 시 The "VsTemplatePaths" task could not be loaded from the assembly 오류 발생
10944정성태4/22/201619482디버깅 기술: 80. windbg - 풀 덤프 파일로부터 텍스트 파일의 내용을 찾는 방법
10943정성태4/22/201624333디버깅 기술: 79. windbg - 풀 덤프 파일로부터 .NET DLL을 추출/저장하는 방법 [1]
10942정성태4/19/201619652디버깅 기술: 78. windbg 사례 - .NET 예외가 발생한 시점의 오류 분석 [1]
10941정성태4/19/201619555오류 유형: 326. Error MSB8020 - The build tools for v120_xp (Platform Toolset = 'v120_xp') cannot be found.
10940정성태4/18/201622812Windows: 116. 프로세스 풀 덤프 시간을 줄여 주는 Process Reflection [3]
10939정성태4/18/201623847.NET Framework: 572. .NET APM 비동기 호출의 Begin...과 End... 조합 [3]파일 다운로드1
10938정성태4/13/201623419오류 유형: 325. 파일 삭제 시 오류 - Error 0x80070091: The directory is not empty.
10937정성태4/13/201631637Windows: 115. UEFI 모드로 윈도우 10 설치 가능한 USB 디스크 만드는 방법
10936정성태4/8/201642313Windows: 114. 삼성 센스 크로노스 7 노트북의 운영체제를 USB 디스크로 새로 설치하는 방법 [3]
10935정성태4/7/201626624웹: 32. Edge에서 Google Docs 문서 편집 시 한영 전환키가 동작 안하는 문제
10934정성태4/5/201625365디버깅 기술: 77. windbg의 콜스택 함수 인자를 쉽게 확인하는 방법 [1]
10933정성태4/5/201630964.NET Framework: 571. C# - 스레드 선호도(Thread Affinity) 지정하는 방법 [8]파일 다운로드1
10932정성태4/4/201623263VC++: 96. C/C++ 식 평가 - printf("%d %d %d\n", a, a++, a);
10931정성태3/31/201623532개발 환경 구성: 283. Hyper-V 내에 구성한 Active Directory 환경의 시간 구성 방법 [3]
10930정성태3/30/201621496.NET Framework: 570. .NET 4.5부터 추가된 CLR Profiler의 실행 시 Rejit 기능
10929정성태3/29/201631595.NET Framework: 569. ServicePointManager.DefaultConnectionLimit의 역할파일 다운로드1
10928정성태3/28/201637306.NET Framework: 568. ODP.NET의 완전한 닷넷 버전 Oracle ODP.NET, Managed Driver [2]파일 다운로드1
10927정성태3/25/201626526.NET Framework: 567. System.Net.ServicePointManager의 DefaultConnectionLimit 속성 설명
10926정성태3/24/201626057.NET Framework: 566. openssl의 PKCS#1 PEM 개인키 파일을 .NET RSACryptoServiceProvider에서 사용하는 방법 [10]파일 다운로드1
10925정성태3/24/201620376.NET Framework: 565. C# - Rabin-Miller 소수 생성 방법을 이용하여 RSACryptoServiceProvider의 개인키를 직접 채워보자 - 두 번째 이야기파일 다운로드1
10924정성태3/22/201620996오류 유형: 324. Visual Studio에서 Azure 클라우드 서비스 생성 시 Failed to initialize the PowerShell host 에러 발생
... 106  107  108  109  110  111  112  113  114  115  116  117  118  [119]  120  ...