Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(108)

Side by Side Diff: remoting/host/win/rdp_client_window.cc

Issue 2600623002: Adding retry logic for applying desktop resolution changes to RDP session. (Closed)
Patch Set: Addressing CR feedback Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « remoting/host/win/rdp_client_window.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "remoting/host/win/rdp_client_window.h" 5 #include "remoting/host/win/rdp_client_window.h"
6 6
7 #include <wtsdefs.h> 7 #include <wtsdefs.h>
8 8
9 #include <list> 9 #include <list>
10 10
11 #include "base/bind.h"
11 #include "base/lazy_instance.h" 12 #include "base/lazy_instance.h"
13 #include "base/location.h"
12 #include "base/logging.h" 14 #include "base/logging.h"
13 #include "base/macros.h" 15 #include "base/macros.h"
14 #include "base/strings/string16.h" 16 #include "base/strings/string16.h"
15 #include "base/strings/utf_string_conversions.h" 17 #include "base/strings/utf_string_conversions.h"
16 #include "base/threading/thread_local.h" 18 #include "base/threading/thread_local.h"
17 #include "base/win/scoped_bstr.h" 19 #include "base/win/scoped_bstr.h"
18 20
19 namespace remoting { 21 namespace remoting {
20 22
21 namespace { 23 namespace {
22 24
23 // RDP session disconnect reason codes that should not be interpreted as errors. 25 // RDP session disconnect reason codes that should not be interpreted as errors.
24 constexpr long kDisconnectReasonNoInfo = 0; 26 constexpr long kDisconnectReasonNoInfo = 0;
25 constexpr long kDisconnectReasonLocalNotError = 1; 27 constexpr long kDisconnectReasonLocalNotError = 1;
26 constexpr long kDisconnectReasonRemoteByUser = 2; 28 constexpr long kDisconnectReasonRemoteByUser = 2;
27 constexpr long kDisconnectReasonByServer = 3; 29 constexpr long kDisconnectReasonByServer = 3;
28 30
29 // Maximum length of a window class name including the terminating nullptr. 31 // Maximum length of a window class name including the terminating nullptr.
30 constexpr int kMaxWindowClassLength = 256; 32 constexpr int kMaxWindowClassLength = 256;
31 33
32 // Each member of the array returned by GetKeyboardState() contains status data 34 // Each member of the array returned by GetKeyboardState() contains status data
33 // for a virtual key. If the high-order bit is 1, the key is down; otherwise, it 35 // for a virtual key. If the high-order bit is 1, the key is down; otherwise, it
34 // is up. 36 // is up.
35 constexpr BYTE kKeyPressedFlag = 0x80; 37 constexpr BYTE kKeyPressedFlag = 0x80;
36 38
37 constexpr int kKeyboardStateLength = 256; 39 constexpr int kKeyboardStateLength = 256;
38 40
41 constexpr base::TimeDelta kReapplyResolutionPeriod =
42 base::TimeDelta::FromMilliseconds(250);
43
44 // We want to try to reapply resolution changes for ~5 seconds (20 * 250ms).
45 constexpr int kMaxResolutionReapplyAttempts = 20;
46
39 // The RDP control creates 'IHWindowClass' window to handle keyboard input. 47 // The RDP control creates 'IHWindowClass' window to handle keyboard input.
40 constexpr wchar_t kRdpInputWindowClass[] = L"IHWindowClass"; 48 constexpr wchar_t kRdpInputWindowClass[] = L"IHWindowClass";
41 49
42 enum RdpAudioMode { 50 enum RdpAudioMode {
43 // Redirect sounds to the client. This is the default value. 51 // Redirect sounds to the client. This is the default value.
44 kRdpAudioModeRedirect = 0, 52 kRdpAudioModeRedirect = 0,
45 53
46 // Play sounds at the remote computer. Equivalent to |kRdpAudioModeNone| if 54 // Play sounds at the remote computer. Equivalent to |kRdpAudioModeNone| if
47 // the remote computer is running a server SKU. 55 // the remote computer is running a server SKU.
48 kRdpAudioModePlayOnServer = 1, 56 kRdpAudioModePlayOnServer = 1,
(...skipping 159 matching lines...) Expand 10 before | Expand all | Expand 10 after
208 SendMessage(input_window, WM_KEYUP, VK_MENU, MAKELPARAM(1, alt | up)); 216 SendMessage(input_window, WM_KEYUP, VK_MENU, MAKELPARAM(1, alt | up));
209 217
210 // Release 'Ctrl'. 218 // Release 'Ctrl'.
211 keyboard_state[VK_CONTROL] &= ~kKeyPressedFlag; 219 keyboard_state[VK_CONTROL] &= ~kKeyPressedFlag;
212 keyboard_state[VK_LCONTROL] &= ~kKeyPressedFlag; 220 keyboard_state[VK_LCONTROL] &= ~kKeyPressedFlag;
213 CHECK(SetKeyboardState(keyboard_state)); 221 CHECK(SetKeyboardState(keyboard_state));
214 SendMessage(input_window, WM_KEYUP, VK_CONTROL, MAKELPARAM(1, control | up)); 222 SendMessage(input_window, WM_KEYUP, VK_CONTROL, MAKELPARAM(1, control | up));
215 } 223 }
216 224
217 void RdpClientWindow::ChangeResolution(const ScreenResolution& resolution) { 225 void RdpClientWindow::ChangeResolution(const ScreenResolution& resolution) {
226 // Stop any pending resolution changes.
227 apply_resolution_timer_.Stop();
218 screen_resolution_ = resolution; 228 screen_resolution_ = resolution;
219 UpdateDesktopResolution(); 229 HRESULT result = UpdateDesktopResolution();
230 if (FAILED(result)) {
231 LOG(WARNING) << "UpdateSessionDisplaySettings() failed: 0x" << std::hex
232 << result;
233 }
220 } 234 }
221 235
222 void RdpClientWindow::OnClose() { 236 void RdpClientWindow::OnClose() {
223 if (!client_.get()) { 237 if (!client_.get()) {
224 NotifyDisconnected(); 238 NotifyDisconnected();
225 return; 239 return;
226 } 240 }
227 241
228 // Request a graceful shutdown. 242 // Request a graceful shutdown.
229 mstsc::ControlCloseStatus close_status; 243 mstsc::ControlCloseStatus close_status;
230 HRESULT result = client_->RequestClose(&close_status); 244 HRESULT result = client_->RequestClose(&close_status);
231 if (FAILED(result)) { 245 if (FAILED(result)) {
232 LOG(ERROR) << "Failed to request a graceful shutdown of an RDP connection" 246 LOG(ERROR) << "Failed to request a graceful shutdown of an RDP connection"
233 << ", result=0x" << std::hex << result << std::dec; 247 << ", result=0x" << std::hex << result;
234 NotifyDisconnected(); 248 NotifyDisconnected();
235 return; 249 return;
236 } 250 }
237 251
238 if (close_status != mstsc::controlCloseWaitForEvents) { 252 if (close_status != mstsc::controlCloseWaitForEvents) {
239 NotifyDisconnected(); 253 NotifyDisconnected();
240 return; 254 return;
241 } 255 }
242 256
243 // Expect IMsTscAxEvents::OnConfirmClose() or IMsTscAxEvents::OnDisconnect() 257 // Expect IMsTscAxEvents::OnConfirmClose() or IMsTscAxEvents::OnDisconnect()
(...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after
383 if (FAILED(result)) 397 if (FAILED(result))
384 return LogOnCreateError(result); 398 return LogOnCreateError(result);
385 399
386 return 0; 400 return 0;
387 } 401 }
388 402
389 void RdpClientWindow::OnDestroy() { 403 void RdpClientWindow::OnDestroy() {
390 client_.Release(); 404 client_.Release();
391 client_9_.Release(); 405 client_9_.Release();
392 client_settings_.Release(); 406 client_settings_.Release();
407 apply_resolution_timer_.Stop();
393 } 408 }
394 409
395 HRESULT RdpClientWindow::OnAuthenticationWarningDisplayed() { 410 HRESULT RdpClientWindow::OnAuthenticationWarningDisplayed() {
396 LOG(WARNING) << "RDP: authentication warning is about to be shown."; 411 LOG(WARNING) << "RDP: authentication warning is about to be shown.";
397 412
398 // Hook window activation to cancel any modal UI shown by the RDP control. 413 // Hook window activation to cancel any modal UI shown by the RDP control.
399 // This does not affect creation of other instances of the RDP control on this 414 // This does not affect creation of other instances of the RDP control on this
400 // thread because the RDP control's window is hidden and is not activated. 415 // thread because the RDP control's window is hidden and is not activated.
401 window_activate_hook_ = WindowHook::Create(); 416 window_activate_hook_ = WindowHook::Create();
402 return S_OK; 417 return S_OK;
(...skipping 11 matching lines...) Expand all
414 429
415 NotifyConnected(); 430 NotifyConnected();
416 return S_OK; 431 return S_OK;
417 } 432 }
418 433
419 HRESULT RdpClientWindow::OnLoginComplete() { 434 HRESULT RdpClientWindow::OnLoginComplete() {
420 VLOG(1) << "RDP: user successfully logged in."; 435 VLOG(1) << "RDP: user successfully logged in.";
421 436
422 user_logged_in_ = true; 437 user_logged_in_ = true;
423 438
424 // Apply pending screen size changes to the desktop. 439 // Set up a timer to periodically apply pending screen size changes to the
425 UpdateDesktopResolution(); 440 // desktop. Attempting to set the resolution now seems to fail consistently,
441 // but succeeds after a brief timeout.
442 if (client_9_) {
443 apply_resolution_attempts_ = 0;
444 apply_resolution_timer_.Start(
445 FROM_HERE, kReapplyResolutionPeriod,
446 base::Bind(&RdpClientWindow::ReapplyDesktopResolution, this));
447 }
426 448
427 return S_OK; 449 return S_OK;
428 } 450 }
429 451
430 HRESULT RdpClientWindow::OnDisconnected(long reason) { 452 HRESULT RdpClientWindow::OnDisconnected(long reason) {
431 if (reason == kDisconnectReasonNoInfo || 453 if (reason == kDisconnectReasonNoInfo ||
432 reason == kDisconnectReasonLocalNotError || 454 reason == kDisconnectReasonLocalNotError ||
433 reason == kDisconnectReasonRemoteByUser || 455 reason == kDisconnectReasonRemoteByUser ||
434 reason == kDisconnectReasonByServer) { 456 reason == kDisconnectReasonByServer) {
435 VLOG(1) << "RDP: disconnected from " << server_endpoint_.ToString() 457 VLOG(1) << "RDP: disconnected from " << server_endpoint_.ToString()
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
473 495
474 HRESULT RdpClientWindow::OnConfirmClose(VARIANT_BOOL* allow_close) { 496 HRESULT RdpClientWindow::OnConfirmClose(VARIANT_BOOL* allow_close) {
475 *allow_close = VARIANT_TRUE; 497 *allow_close = VARIANT_TRUE;
476 498
477 NotifyDisconnected(); 499 NotifyDisconnected();
478 return S_OK; 500 return S_OK;
479 } 501 }
480 502
481 int RdpClientWindow::LogOnCreateError(HRESULT error) { 503 int RdpClientWindow::LogOnCreateError(HRESULT error) {
482 LOG(ERROR) << "RDP: failed to initiate a connection to " 504 LOG(ERROR) << "RDP: failed to initiate a connection to "
483 << server_endpoint_.ToString() << ": error=" 505 << server_endpoint_.ToString() << ": error=" << std::hex << error;
484 << std::hex << error << std::dec;
485 client_.Release(); 506 client_.Release();
486 client_9_.Release(); 507 client_9_.Release();
487 client_settings_.Release(); 508 client_settings_.Release();
488 return -1; 509 return -1;
489 } 510 }
490 511
491 void RdpClientWindow::NotifyConnected() { 512 void RdpClientWindow::NotifyConnected() {
492 if (event_handler_) { 513 if (event_handler_) {
493 event_handler_->OnConnected(); 514 event_handler_->OnConnected();
494 } 515 }
495 } 516 }
496 517
497 void RdpClientWindow::NotifyDisconnected() { 518 void RdpClientWindow::NotifyDisconnected() {
498 if (event_handler_) { 519 if (event_handler_) {
499 EventHandler* event_handler = event_handler_; 520 EventHandler* event_handler = event_handler_;
500 event_handler_ = nullptr; 521 event_handler_ = nullptr;
501 event_handler->OnDisconnected(); 522 event_handler->OnDisconnected();
502 } 523 }
503 } 524 }
504 525
505 void RdpClientWindow::UpdateDesktopResolution() { 526 HRESULT RdpClientWindow::UpdateDesktopResolution() {
506 if (!client_9_ || !user_logged_in_) { 527 if (!client_9_ || !user_logged_in_) {
507 return; 528 return S_FALSE;
508 } 529 }
509 530
510 // UpdateSessionDisplaySettings() is poorly documented in MSDN and has a few 531 // UpdateSessionDisplaySettings() is poorly documented in MSDN and has a few
511 // quirks that should be noted. 532 // quirks that should be noted.
512 // 1.) This method will only work when the user is logged into their session. 533 // 1.) This method will only work when the user is logged into their session.
513 // 2.) The method may return E_UNEXPECTED until some amount of time (seconds) 534 // 2.) The method may return E_UNEXPECTED until some amount of time (seconds)
514 // have elapsed after logging in to the user's session. 535 // have elapsed after logging in to the user's session.
515 // TODO(joedow): Investigate adding retry logic for applying the settings. 536 return client_9_->UpdateSessionDisplaySettings(
516 HRESULT result = client_9_->UpdateSessionDisplaySettings(
517 screen_resolution_.dimensions().width(), 537 screen_resolution_.dimensions().width(),
518 screen_resolution_.dimensions().height(), 538 screen_resolution_.dimensions().height(),
519 screen_resolution_.dimensions().width(), 539 screen_resolution_.dimensions().width(),
520 screen_resolution_.dimensions().height(), 540 screen_resolution_.dimensions().height(),
521 /*ulOrientation=*/0, 541 /*ulOrientation=*/0,
522 screen_resolution_.dpi().x(), 542 screen_resolution_.dpi().x(),
523 screen_resolution_.dpi().y()); 543 screen_resolution_.dpi().y());
524 if (FAILED(result)) { 544 }
525 LOG(ERROR) << "UpdateSessionDisplaySettings() failed: 0x" << std::hex 545
526 << result << std::dec; 546 void RdpClientWindow::ReapplyDesktopResolution() {
547 DCHECK_LT(apply_resolution_attempts_, kMaxResolutionReapplyAttempts);
548
549 HRESULT result = UpdateDesktopResolution();
550 apply_resolution_attempts_++;
551
552 if (SUCCEEDED(result)) {
553 // Successfully applied the new resolution so stop the retry timer.
554 apply_resolution_timer_.Stop();
555 } else if (apply_resolution_attempts_ == kMaxResolutionReapplyAttempts) {
556 // Only log an error on the last attempt to reduce log spam since a few
557 // errors can be expected and don't signal an actual failure.
558 LOG(WARNING) << "All UpdateSessionDisplaySettings() retries failed: 0x"
559 << std::hex << result;
560 apply_resolution_timer_.Stop();
527 } 561 }
528 } 562 }
529 563
530 scoped_refptr<RdpClientWindow::WindowHook> 564 scoped_refptr<RdpClientWindow::WindowHook>
531 RdpClientWindow::WindowHook::Create() { 565 RdpClientWindow::WindowHook::Create() {
532 scoped_refptr<WindowHook> window_hook = g_window_hook.Pointer()->Get(); 566 scoped_refptr<WindowHook> window_hook = g_window_hook.Pointer()->Get();
533 567
534 if (!window_hook.get()) { 568 if (!window_hook.get()) {
535 window_hook = new WindowHook(); 569 window_hook = new WindowHook();
536 } 570 }
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
569 int code, WPARAM wparam, LPARAM lparam) { 603 int code, WPARAM wparam, LPARAM lparam) {
570 // Get the hook handle. 604 // Get the hook handle.
571 HHOOK hook = g_window_hook.Pointer()->Get()->hook_; 605 HHOOK hook = g_window_hook.Pointer()->Get()->hook_;
572 606
573 if (code != HCBT_ACTIVATE) { 607 if (code != HCBT_ACTIVATE) {
574 return CallNextHookEx(hook, code, wparam, lparam); 608 return CallNextHookEx(hook, code, wparam, lparam);
575 } 609 }
576 610
577 // Close the window once all pending window messages are processed. 611 // Close the window once all pending window messages are processed.
578 HWND window = reinterpret_cast<HWND>(wparam); 612 HWND window = reinterpret_cast<HWND>(wparam);
579 LOG(WARNING) << "RDP: closing a window: " << std::hex << window << std::dec; 613 LOG(WARNING) << "RDP: closing a window: " << std::hex << window;
580 ::PostMessage(window, WM_CLOSE, 0, 0); 614 ::PostMessage(window, WM_CLOSE, 0, 0);
581 return 0; 615 return 0;
582 } 616 }
583 617
584 } // namespace remoting 618 } // namespace remoting
OLDNEW
« no previous file with comments | « remoting/host/win/rdp_client_window.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698