Ruby:Watir 无法连接到“默认”下非管理员帐户下运行的 IE桌面

发布于 2024-08-05 22:36:27 字数 2102 浏览 2 评论 0原文

需要一些有关应用程序架构的背景知识:

Windows 2003/Apache-v2.2/IE7/Watir-v1.6.2/Ruby-v1.8.5

  1. Apache 在“localsystem”帐户下运行。
  2. 收到运行 Watir 脚本的请求。Apache
  3. CGI 在特定用户(例如“tester”)下启动 IE7,并将 IE7 窗口附加到“默认”桌面环境。这使我们能够通过 VNC 进入机器并查看所有正在运行的帐户中的所有 IE7 窗口。
  4. IE7通过插件将ruby嵌入到进程中,并在线程中执行ruby脚本。

对于单击 IE 中的链接/按钮,Watir 提供了同步“click”方法和异步“click_no_wait”方法。 'click_no_wait' 生成一个全新的 ruby​​ 进程来连接回 IE7 窗口以单击链接/按钮。

这就是我失败的地方。由于 click_no_wait 正在生成一个新进程。它似乎看不到 IE7 窗口来连接它并单击链接/按钮。 由于 IE 在某个页面上弹出一个对话框,我必须使用“click_no_wait”,以便另一个 ruby​​ 线程可以关闭它。

我尝试过一些事情: - 使用 fire_event('OnClick') 而不是 click_no_wait 会像“click”那样挂起脚本。 - 通过注释掉“@container.wait”来修改“点击”,但“点击!”它本身是挂起等待对话框关闭的 API。 - 使“click_no_wait”产生一个新线程而不是进程,但其他线程在“click!”时似乎被挂起调用在该线程中执行。这很奇怪。 - 使用与生成 IE7 进程完全相同的代码执行“click_no_wait”进程,但它仍然找不到任何 IE7 窗口。

通过上述“默认”桌面,生成 IE 的代码基本上会执行一系列 C++ 调用,将 IE7 窗口附加到“winsta0”桌面:

- LogonUser() // log in as tester account
- OpenWindowStation("winsta0") // to get default desktop
- SetProcessWindowStation()
- // add the user to interactive window station using (GetUserObjectSecurity, GetSecurityDescriptorDacl, GetAclInformation, AddAce, SetSecurityDescriptorDacl, SetUserObjectSecurity)
- // add user to "default" desktop using APIs listed above.
- CreateEnvironmentBlock
- ImpersonateLoggedOnUser
- CreateProcessAsUser('iexplore.exe')   
- // cleanup

在特定用户 IE7 下运行以下 Watir 脚本会产生以下结果:

$IE = Watir::IE.attach(:title, /Google/)
$IE.button(:name, 'btnG').click!

c:/ruby/lib/ruby/gems/1.8/gems/watir-1.6.2/lib/watir/ie-class.rb:246:in `method_missing': Windows (WIN32OLERuntimeError) OLE 错误代码:80040154 中 HRESULT 错误代码:0x80020009 发生异常。来自c:/ruby/lib/ruby/gems/1.8/gems/watir-1.6.2/lib/watir/ie-class.rb:246:in“每个”

其中包含:

c:/ruby/lib/ruby/gems/1.8/gems/watir-1.6.2/lib/watir/ie-class.rb:246
shell = WIN32OLE.new("Shell.Application")
windows = shell.Windows   ## Fails here

如果我可以提供更多详细信息,请告诉我或消歧! :)

谢谢, 谢恩.

Some Background on architecture of the app is needed:

Windows 2003/Apache-v2.2/IE7/Watir-v1.6.2/Ruby-v1.8.5

  1. Apache running under 'localsystem' account.
  2. Request to run a Watir script comes in.
  3. Apache CGI kicks off IE7 under a particular user, e.g. 'tester', and attaches the IE7 window to the "default" desktop environment. This allows us to VNC into the machine and see all IE7 windows from all the accounts running.
  4. IE7 has ruby embedded into the process through plugin and executes the ruby script in a thread.

For clicking on links/buttons in IE, Watir supplies a synchronous 'click' method and async 'click_no_wait' method. 'click_no_wait' spawns a completely new ruby process to connect back to the IE7 window to click the link/button.

This is what is failing for me. Since click_no_wait is spawning a new process. It cannot seem to see the IE7 window to connect to it and click on the link/button.
I have to use 'click_no_wait' due to a dialog box that IE pops up on a certain page, so that another ruby thread can close it.

I've tried a few things:
- using fire_event('OnClick') instead of click_no_wait hangs script like 'click' would.
- Modifying 'click' by commenting out '@container.wait', but the 'click!' itself is the API that hangs waiting for the dialog box to close.
- Make 'click_no_wait' spawn a new thread instead of a process', but other threads appear to be suspended while the 'click!' call executes in that thread. Which is strange.
- Executing the 'click_no_wait' process using the exact same code that is used to spawn the IE7 process, but it still can't find any IE7 windows.

By 'default' desktop described above, the code that spawns IE basically does a series of C++ calls to attach the IE7 window to the 'winsta0' desktop:

- LogonUser() // log in as tester account
- OpenWindowStation("winsta0") // to get default desktop
- SetProcessWindowStation()
- // add the user to interactive window station using (GetUserObjectSecurity, GetSecurityDescriptorDacl, GetAclInformation, AddAce, SetSecurityDescriptorDacl, SetUserObjectSecurity)
- // add user to "default" desktop using APIs listed above.
- CreateEnvironmentBlock
- ImpersonateLoggedOnUser
- CreateProcessAsUser('iexplore.exe')   
- // cleanup

Running the following Watir script under the particular user IE7 is running under yields:

$IE = Watir::IE.attach(:title, /Google/)
$IE.button(:name, 'btnG').click!

c:/ruby/lib/ruby/gems/1.8/gems/watir-1.6.2/lib/watir/ie-class.rb:246:in `method_missing': Windows (WIN32OLERuntimeError)
OLE error code:80040154 in

HRESULT error code:0x80020009
Exception occurred. from c:/ruby/lib/ruby/gems/1.8/gems/watir-1.6.2/lib/watir/ie-class.rb:246:in "each"

Which contains:

c:/ruby/lib/ruby/gems/1.8/gems/watir-1.6.2/lib/watir/ie-class.rb:246
shell = WIN32OLE.new("Shell.Application")
windows = shell.Windows   ## Fails here

Let me know if I can supply more details or disambiguation! :)

Thanks,
Shane.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

一绘本一梦想 2024-08-12 22:36:27

好的,我已经解决了我自己的问题。

事实证明,Watir 用于枚举 IE 窗口的方法(即 Shell.Application)无法跨用户空间工作。因此,我必须编写一个中间应用程序来检索 IE 窗口的 IWebBrowser2 COM 指针,并修改“eval_in_spawned_process”Watir 函数以将此句柄用于“pc = Watir::IE.bind(iWebBrowserPtr)”。

我可能不需要外部应用程序来检索 IWebBrowser2 对象,但我不知道如何在 Ruby 中执行此操作,并且我已经有一个 IE BHO 应用程序,我可以利用它来实现此目的。

现在效果很好。 :)

Watir/page-container.rb:56

# This evaluates a cmd (e.g. "button(:id, 1).click!") in a new ruby process
# This is to primarily avoid a script hanging while a modal dialog is displayed in IE.
# It gets an IE handle from our IE BHO based off of window handle.  It needs to do this 
# to get around the problem of finding the IE window itself using Shell.Application
# which does not work across User-contexts.
# Note: This change probably invalidates the Watir unit-test for this API.
def eval_in_spawned_process(command)
  command.strip!
  load_path_code = _code_that_copies_readonly_array($LOAD_PATH, '$LOAD_PATH')
  ruby_code = "require 'watir/ie'; "
  ruby_code << 'controller = WIN32OLE.new("HttpPlugin.Controller"); '
  ruby_code << "window = controller.BrowserFromHWND(#{hwnd}); "
  ruby_code << "pc = Watir::IE.bind(window); "
  ruby_code << "pc.#{command}; "
  exec_string = "start rubyw -e #{(load_path_code + '; ' + ruby_code).inspect}"
  result = system(exec_string)
end

Ok, I have solved my own issue.

It turns out that the method that Watir uses to enumerate IE windows (i.e. Shell.Application) does not work across user spaces. So I had to write an intermediate application to retrieve the IWebBrowser2 COM pointer for the IE window, and modify the 'eval_in_spawned_process' Watir function to use this handle for 'pc = Watir::IE.bind(iWebBrowserPtr)'.

It's possible that I didn't need the external app to retrieve the IWebBrowser2 object, but I didn't know how to do it in Ruby, and I already had an IE BHO app that I could leverage for the purposes.

This now works fine. :)

Watir/page-container.rb:56

# This evaluates a cmd (e.g. "button(:id, 1).click!") in a new ruby process
# This is to primarily avoid a script hanging while a modal dialog is displayed in IE.
# It gets an IE handle from our IE BHO based off of window handle.  It needs to do this 
# to get around the problem of finding the IE window itself using Shell.Application
# which does not work across User-contexts.
# Note: This change probably invalidates the Watir unit-test for this API.
def eval_in_spawned_process(command)
  command.strip!
  load_path_code = _code_that_copies_readonly_array($LOAD_PATH, '$LOAD_PATH')
  ruby_code = "require 'watir/ie'; "
  ruby_code << 'controller = WIN32OLE.new("HttpPlugin.Controller"); '
  ruby_code << "window = controller.BrowserFromHWND(#{hwnd}); "
  ruby_code << "pc = Watir::IE.bind(window); "
  ruby_code << "pc.#{command}; "
  exec_string = "start rubyw -e #{(load_path_code + '; ' + ruby_code).inspect}"
  result = system(exec_string)
end
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文