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

1  2  3  4  5  6  7  8  9  10  11  [12]  13  14  15  ...
NoWriterDateCnt.TitleFile(s)
13328정성태4/19/20233379VS.NET IDE: 184. Visual Studio - Fine Code Coverage에서 동작하지 않는 Fake/Shim 테스트
13327정성태4/19/20233793VS.NET IDE: 183. C# - .NET Core/5+ 환경에서 Fakes를 이용한 단위 테스트 방법
13326정성태4/18/20235204.NET Framework: 2109. C# - 닷넷 응용 프로그램에서 SQLite 사용 (System.Data.SQLite) [1]파일 다운로드1
13325정성태4/18/20234499스크립트: 48. 파이썬 - PostgreSQL의 with 문을 사용한 경우 연결 개체 누수
13324정성태4/17/20234317.NET Framework: 2108. C# - Octave의 "save -binary ..."로 생성한 바이너리 파일 분석파일 다운로드1
13323정성태4/16/20234257개발 환경 구성: 677. Octave에서 Excel read/write를 위한 io 패키지 설치
13322정성태4/15/20235033VS.NET IDE: 182. Visual Studio - 32비트로만 빌드된 ActiveX와 작업해야 한다면?
13321정성태4/14/20233824개발 환경 구성: 676. WSL/Linux Octave - Python 스크립트 연동
13320정성태4/13/20233802개발 환경 구성: 675. Windows Octave 8.1.0 - Python 스크립트 연동
13319정성태4/12/20234289개발 환경 구성: 674. WSL 2 환경에서 GNU Octave 설치
13318정성태4/11/20234111개발 환경 구성: 673. JetBrains IDE에서 "Squash Commits..." 메뉴가 비활성화된 경우
13317정성태4/11/20234225오류 유형: 855. WSL 2 Ubuntu 20.04 - error: cannot communicate with server: Post http://localhost/v2/snaps/...
13316정성태4/10/20233552오류 유형: 854. docker-compose 시 "json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)" 오류 발생
13315정성태4/10/20233749Windows: 245. Win32 - 시간 만료를 갖는 컨텍스트 메뉴와 윈도우 메시지의 영역별 정의파일 다운로드1
13314정성태4/9/20233828개발 환경 구성: 672. DosBox를 이용한 Turbo C, Windows 3.1 설치
13313정성태4/9/20233911개발 환경 구성: 671. Hyper-V VM에 Turbo C 2.0 설치 [2]
13312정성태4/8/20233932Windows: 244. Win32 - 시간 만료를 갖는 MessageBox 대화창 구현 (개선된 버전)파일 다운로드1
13311정성태4/7/20234441C/C++: 163. Visual Studio 2022 - DirectShow 예제 컴파일(WAV Dest)
13310정성태4/6/20234019C/C++: 162. Visual Studio - /NODEFAULTLIB 옵션 설정 후 수동으로 추가해야 할 library
13309정성태4/5/20234205.NET Framework: 2107. .NET 6+ FileStream의 구조 변화
13308정성태4/4/20234090스크립트: 47. 파이썬의 time.time() 실숫값을 GoLang / C#에서 사용하는 방법
13307정성태4/4/20233863.NET Framework: 2106. C# - .NET Core/5+ 환경의 Windows Forms 응용 프로그램에서 HINSTANCE 구하는 방법
13306정성태4/3/20233664Windows: 243. Win32 - 윈도우(cbWndExtra) 및 윈도우 클래스(cbClsExtra) 저장소 사용 방법
13305정성태4/1/20234032Windows: 242. Win32 - 시간 만료를 갖는 MessageBox 대화창 구현 (쉬운 버전)파일 다운로드1
13304정성태3/31/20234386VS.NET IDE: 181. Visual Studio - C/C++ 프로젝트에 application manifest 적용하는 방법
13303정성태3/30/20233728Windows: 241. 환경 변수 %PATH%에 DLL을 찾는 규칙
1  2  3  4  5  6  7  8  9  10  11  [12]  13  14  15  ...