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

... 181  182  183  184  185  186  187  188  189  190  191  192  [193]  194  195  ...
NoWriterDateCnt.TitleFile(s)
111정성태3/7/200515304    답변글 VS.NET IDE: 28.1. [추가] SQL 2005 / VS.NET 2005 2005-02 CTP 버전이 올라왔네요. [1]
112정성태11/14/200516548        답변글 VS.NET IDE: 28.2. [추가] VS.NET 2005 2005-02 CTP 버전에서 달라진 점 ( VC++ )
127정성태3/29/200514593        답변글 VS.NET IDE: 28.4. [추가] SQL 2005 2005-02 CTP 버전에서 달라진 점
123정성태3/25/200518497    답변글 .NET Framework: 28.3. Uninstalling software without using Add Remove Programs...
108정성태3/4/200517947.NET Framework: 27. 시스템 이벤트 로그에 쌓이는 {00020906-0000-0000-C000-000000000046} 보안에러
107정성태3/1/200518162COM 개체 관련: 15. COM: Control 유형인 경우, IObjectWithSite 를 구현해도 SetSite/GetSite 가 호출이 안됨
106정성태2/28/200517471COM 개체 관련: 14. 탐색기 "처럼" 파일 열기
105정성태2/28/200516523.NET Framework: 26. VS.NET 2005 : 설치 프로젝트 - .NET Framework 설치 강제화
139정성태11/14/200514714    답변글 .NET Framework: 26.1. ^^ 역시, 배려가 되어 있네요. 제가 못 찾은 것이었습니다.
104정성태2/27/200517412VS.NET IDE: 23. MSI 설치 중에 GetLocalTime / GetSystemTime API 사용
132정성태3/30/200517034    답변글 VS.NET IDE: 23.1. [추가]: MSI 설치 동작 원리
102정성태2/16/200519686.NET Framework: 25. Verify that you are a member of the 'Debugger Users' group on the server. [2]
101정성태2/15/200517413.NET Framework: 24. WMI Win32_NTLogEvent 관리 이벤트를 Windows 2000 에서는 "Access Denied" 가 발생하는 문제파일 다운로드1
100정성태2/15/200523503VS.NET IDE: 22. 방화벽 환경에서의 WMI 연결을 위한 포트 설정 [2]
99정성태2/15/200521519COM 개체 관련: 13. 비동기 Drag & Drop 구현 : IAsyncOperation
103정성태2/23/200517731    답변글 COM 개체 관련: 13.1. [관련 자료] 그외 Drag & Drop 링크파일 다운로드1
97정성태2/14/200520690VS.NET IDE: 21. 설치된 Platform SDK 버전확인 방법
96정성태2/14/200522704기타: 9. http://www.google.com/webhp?complete=1&hl=en 검색에 관해서.
95정성태2/14/200532046VS.NET IDE: 20. Win32 특권 정리 [1]
94정성태1/29/200523332VC++: 13. VS.NET 2005 VC++ 컴파일러 에러 : cannot instantiate abstract class
93정성태1/29/200519238VS.NET IDE: 19. 혹시 VS.NET 2005 Beta2(2004/12 CTP Team System 버전)이 다운이 자주 되나요? [2]
92정성태1/29/200518060.NET Framework: 23. Unmanaged 환경에서 Managed DLL에 정의된 메서드 호출 시 오류 확인하는 방법
91정성태11/14/200518678VC++: 12. VS.NET 2005 VC++ Debug: Expression: ( (state != ST_INVALID ) )
90정성태1/27/200519479.NET Framework: 22. Debug: The underlying connection was closed: Unable to connect to the remote server.
89정성태1/26/200523974VC++: 11. Delay Loaded DLL
87정성태1/23/200517548VS.NET IDE: 18. VS.NET 2005 Beta 1 - VC++ 프로젝트에서 Connection Point 구현시 버그
... 181  182  183  184  185  186  187  188  189  190  191  192  [193]  194  195  ...