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

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: Merging with ToT Created 3 years, 12 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(ERROR) << "UpdateSessionDisplaySettings() failed: 0x" << std::hex
232 << result << std::dec;
Sergey Ulanov 2016/12/27 21:16:13 I don't think you need std::dec here. Each LOG() c
joedow 2017/01/04 15:47:44 Done.
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;
(...skipping 153 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 apply pending screen size changes to the desktop after a
425 UpdateDesktopResolution(); 440 // brief timeout. Attempting to set the resolution now seems to fail
441 // consistently, but succeeds after a brief timeout.
442 if (client_9_) {
443 apply_resolution_timer_.Start(FROM_HERE,
444 kReapplyResolutionPeriod,
445 base::Bind(&RdpClientWindow::ReapplyDesktopResolution, this, 0));
446 }
426 447
427 return S_OK; 448 return S_OK;
428 } 449 }
429 450
430 HRESULT RdpClientWindow::OnDisconnected(long reason) { 451 HRESULT RdpClientWindow::OnDisconnected(long reason) {
431 if (reason == kDisconnectReasonNoInfo || 452 if (reason == kDisconnectReasonNoInfo ||
432 reason == kDisconnectReasonLocalNotError || 453 reason == kDisconnectReasonLocalNotError ||
433 reason == kDisconnectReasonRemoteByUser || 454 reason == kDisconnectReasonRemoteByUser ||
434 reason == kDisconnectReasonByServer) { 455 reason == kDisconnectReasonByServer) {
435 VLOG(1) << "RDP: disconnected from " << server_endpoint_.ToString() 456 VLOG(1) << "RDP: disconnected from " << server_endpoint_.ToString()
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
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());
544 }
545
546 void RdpClientWindow::ReapplyDesktopResolution(int attempt_count) {
547 if (attempt_count > kMaxResolutionReapplyAttempts) {
Sergey Ulanov 2016/12/27 21:16:13 Maybe just don't post the task after kMaxResolutio
joedow 2017/01/04 15:47:44 Done.
548 LOG(WARNING) << "Unable to apply desktop resolution changes.";
549 return;
550 }
551
552 HRESULT result = UpdateDesktopResolution();
524 if (FAILED(result)) { 553 if (FAILED(result)) {
525 LOG(ERROR) << "UpdateSessionDisplaySettings() failed: 0x" << std::hex 554 // Only log errors on the last attempt to reduce log spam since a few errors
526 << result << std::dec; 555 // can be expected and don't signal an actual failure.
556 if (attempt_count == kMaxResolutionReapplyAttempts) {
557 LOG(ERROR) << "UpdateSessionDisplaySettings() failed: 0x" << std::hex
558 << result << std::dec;
559 }
560
561 apply_resolution_timer_.Start(FROM_HERE,
Sergey Ulanov 2016/12/27 21:16:13 It may be easier to start a repeating timer and th
joedow 2017/01/04 15:47:44 I think that's a good call. I've updated it to us
562 kReapplyResolutionPeriod,
563 base::Bind(&RdpClientWindow::ReapplyDesktopResolution, this,
564 attempt_count + 1));
527 } 565 }
528 } 566 }
529 567
530 scoped_refptr<RdpClientWindow::WindowHook> 568 scoped_refptr<RdpClientWindow::WindowHook>
531 RdpClientWindow::WindowHook::Create() { 569 RdpClientWindow::WindowHook::Create() {
532 scoped_refptr<WindowHook> window_hook = g_window_hook.Pointer()->Get(); 570 scoped_refptr<WindowHook> window_hook = g_window_hook.Pointer()->Get();
533 571
534 if (!window_hook.get()) { 572 if (!window_hook.get()) {
535 window_hook = new WindowHook(); 573 window_hook = new WindowHook();
536 } 574 }
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
575 } 613 }
576 614
577 // Close the window once all pending window messages are processed. 615 // Close the window once all pending window messages are processed.
578 HWND window = reinterpret_cast<HWND>(wparam); 616 HWND window = reinterpret_cast<HWND>(wparam);
579 LOG(WARNING) << "RDP: closing a window: " << std::hex << window << std::dec; 617 LOG(WARNING) << "RDP: closing a window: " << std::hex << window << std::dec;
580 ::PostMessage(window, WM_CLOSE, 0, 0); 618 ::PostMessage(window, WM_CLOSE, 0, 0);
581 return 0; 619 return 0;
582 } 620 }
583 621
584 } // namespace remoting 622 } // 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