| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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/plugin/daemon_controller.h" | 5 #include "remoting/host/plugin/daemon_controller.h" |
| 6 | 6 |
| 7 #include <objbase.h> | 7 #include <objbase.h> |
| 8 | 8 |
| 9 #include "base/basictypes.h" | 9 #include "base/basictypes.h" |
| 10 #include "base/bind.h" | 10 #include "base/bind.h" |
| (...skipping 19 matching lines...) Expand all Loading... |
| 30 // MIDL-generated declarations and definitions. | 30 // MIDL-generated declarations and definitions. |
| 31 #include "remoting/host/elevated_controller.h" | 31 #include "remoting/host/elevated_controller.h" |
| 32 | 32 |
| 33 using base::win::ScopedBstr; | 33 using base::win::ScopedBstr; |
| 34 using base::win::ScopedComPtr; | 34 using base::win::ScopedComPtr; |
| 35 | 35 |
| 36 namespace remoting { | 36 namespace remoting { |
| 37 | 37 |
| 38 namespace { | 38 namespace { |
| 39 | 39 |
| 40 // The COM elevation moniker for the elevated controller. | 40 // The COM elevation moniker for the Elevated Controller. |
| 41 const char16 kDaemonControllerElevationMoniker[] = | 41 const char16 kDaemonControllerElevationMoniker[] = |
| 42 TO_L_STRING("Elevation:Administrator!new:") | 42 TO_L_STRING("Elevation:Administrator!new:") |
| 43 TO_L_STRING("ChromotingElevatedController.ElevatedController"); | 43 TO_L_STRING("ChromotingElevatedController.ElevatedController"); |
| 44 | 44 |
| 45 // Name of the Daemon Controller's worker thread. | 45 // Name of the Daemon Controller's worker thread. |
| 46 const char kDaemonControllerThreadName[] = "Daemon Controller thread"; | 46 const char kDaemonControllerThreadName[] = "Daemon Controller thread"; |
| 47 | 47 |
| 48 // A base::Thread implementation that initializes COM on the new thread. | 48 // A base::Thread implementation that initializes COM on the new thread. |
| 49 class ComThread : public base::Thread { | 49 class ComThread : public base::Thread { |
| 50 public: | 50 public: |
| 51 explicit ComThread(const char* name); | 51 explicit ComThread(const char* name); |
| 52 | 52 |
| 53 // Activates an elevated instance of the controller and returns the pointer | 53 // Activates an elevated instance of the controller and returns the pointer |
| 54 // to the control interface in |control_out|. This class keeps the ownership | 54 // to the control interface in |control_out|. This class keeps the ownership |
| 55 // of the pointer so the caller should not call call AddRef() or Release(). | 55 // of the pointer so the caller should not call call AddRef() or Release(). |
| 56 HRESULT ActivateElevatedController(IDaemonControl** control_out); | 56 HRESULT ActivateElevatedController(HWND window_handle, |
| 57 IDaemonControl** control_out); |
| 57 | 58 |
| 58 bool Start(); | 59 bool Start(); |
| 59 | 60 |
| 60 protected: | 61 protected: |
| 61 virtual void Init() OVERRIDE; | 62 virtual void Init() OVERRIDE; |
| 62 virtual void CleanUp() OVERRIDE; | 63 virtual void CleanUp() OVERRIDE; |
| 63 | 64 |
| 64 ScopedComPtr<IDaemonControl> control_; | 65 ScopedComPtr<IDaemonControl> control_; |
| 65 | 66 |
| 66 DISALLOW_COPY_AND_ASSIGN(ComThread); | 67 DISALLOW_COPY_AND_ASSIGN(ComThread); |
| 67 }; | 68 }; |
| 68 | 69 |
| 69 class DaemonControllerWin : public remoting::DaemonController { | 70 class DaemonControllerWin : public remoting::DaemonController { |
| 70 public: | 71 public: |
| 71 DaemonControllerWin(); | 72 DaemonControllerWin(); |
| 72 virtual ~DaemonControllerWin(); | 73 virtual ~DaemonControllerWin(); |
| 73 | 74 |
| 74 virtual State GetState() OVERRIDE; | 75 virtual State GetState() OVERRIDE; |
| 75 virtual void GetConfig(const GetConfigCallback& callback) OVERRIDE; | 76 virtual void GetConfig(const GetConfigCallback& callback) OVERRIDE; |
| 76 virtual void SetConfigAndStart( | 77 virtual void SetConfigAndStart( |
| 77 scoped_ptr<base::DictionaryValue> config, | 78 scoped_ptr<base::DictionaryValue> config, |
| 78 const CompletionCallback& done_callback) OVERRIDE; | 79 const CompletionCallback& done_callback) OVERRIDE; |
| 79 virtual void UpdateConfig(scoped_ptr<base::DictionaryValue> config, | 80 virtual void UpdateConfig(scoped_ptr<base::DictionaryValue> config, |
| 80 const CompletionCallback& done_callback) OVERRIDE; | 81 const CompletionCallback& done_callback) OVERRIDE; |
| 81 virtual void Stop(const CompletionCallback& done_callback) OVERRIDE; | 82 virtual void Stop(const CompletionCallback& done_callback) OVERRIDE; |
| 83 virtual void SetWindow(void* window_handle) OVERRIDE; |
| 82 | 84 |
| 83 private: | 85 private: |
| 84 // Converts a Windows service status code to a Daemon state. | 86 // Converts a Windows service status code to a Daemon state. |
| 85 static State ConvertToDaemonState(DWORD service_state); | 87 static State ConvertToDaemonState(DWORD service_state); |
| 86 | 88 |
| 87 // Converts HRESULT to the AsyncResult. | 89 // Converts HRESULT to the AsyncResult. |
| 88 static AsyncResult HResultToAsyncResult(HRESULT hr); | 90 static AsyncResult HResultToAsyncResult(HRESULT hr); |
| 89 | 91 |
| 90 // Procedes with the daemon configuration if the installation succeeded, | 92 // Procedes with the daemon configuration if the installation succeeded, |
| 91 // otherwise reports the error. | 93 // otherwise reports the error. |
| 92 void OnInstallationComplete(scoped_ptr<base::DictionaryValue> config, | 94 void OnInstallationComplete(scoped_ptr<base::DictionaryValue> config, |
| 93 const CompletionCallback& done_callback, | 95 const CompletionCallback& done_callback, |
| 94 HRESULT result); | 96 HRESULT result); |
| 95 | 97 |
| 96 // Opens the Chromoting service returning its handle in |service_out|. | 98 // Opens the Chromoting service returning its handle in |service_out|. |
| 97 DWORD OpenService(ScopedScHandle* service_out); | 99 DWORD OpenService(ScopedScHandle* service_out); |
| 98 | 100 |
| 99 // The functions that actually do the work. They should be called in | 101 // The functions that actually do the work. They should be called in |
| 100 // the context of |worker_thread_|; | 102 // the context of |worker_thread_|; |
| 101 void DoGetConfig(const GetConfigCallback& callback); | 103 void DoGetConfig(const GetConfigCallback& callback); |
| 102 void DoInstallAsNeededAndStart(scoped_ptr<base::DictionaryValue> config, | 104 void DoInstallAsNeededAndStart(scoped_ptr<base::DictionaryValue> config, |
| 103 const CompletionCallback& done_callback); | 105 const CompletionCallback& done_callback); |
| 104 void DoSetConfigAndStart(scoped_ptr<base::DictionaryValue> config, | 106 void DoSetConfigAndStart(scoped_ptr<base::DictionaryValue> config, |
| 105 const CompletionCallback& done_callback); | 107 const CompletionCallback& done_callback); |
| 106 void DoUpdateConfig(scoped_ptr<base::DictionaryValue> config, | 108 void DoUpdateConfig(scoped_ptr<base::DictionaryValue> config, |
| 107 const CompletionCallback& done_callback); | 109 const CompletionCallback& done_callback); |
| 108 void DoStop(const CompletionCallback& done_callback); | 110 void DoStop(const CompletionCallback& done_callback); |
| 111 void DoSetWindow(void* window_handle); |
| 112 |
| 113 // Handle of the plugin window. |
| 114 HWND window_handle_; |
| 109 | 115 |
| 110 // The worker thread used for servicing long running operations. | 116 // The worker thread used for servicing long running operations. |
| 111 ComThread worker_thread_; | 117 ComThread worker_thread_; |
| 112 | 118 |
| 113 scoped_ptr<DaemonInstallerWin> installer_; | 119 scoped_ptr<DaemonInstallerWin> installer_; |
| 114 | 120 |
| 115 DISALLOW_COPY_AND_ASSIGN(DaemonControllerWin); | 121 DISALLOW_COPY_AND_ASSIGN(DaemonControllerWin); |
| 116 }; | 122 }; |
| 117 | 123 |
| 118 ComThread::ComThread(const char* name) : base::Thread(name), control_(NULL) { | 124 ComThread::ComThread(const char* name) : base::Thread(name), control_(NULL) { |
| 119 } | 125 } |
| 120 | 126 |
| 121 void ComThread::Init() { | |
| 122 CoInitialize(NULL); | |
| 123 } | |
| 124 | |
| 125 void ComThread::CleanUp() { | |
| 126 control_.Release(); | |
| 127 CoUninitialize(); | |
| 128 } | |
| 129 | |
| 130 HRESULT ComThread::ActivateElevatedController( | 127 HRESULT ComThread::ActivateElevatedController( |
| 128 HWND window_handle, |
| 131 IDaemonControl** control_out) { | 129 IDaemonControl** control_out) { |
| 132 // Chache the instance of Elevated Controller to prevent a UAC prompt on every | 130 // Cache an instance of the Elevated Controller to prevent a UAC prompt on |
| 133 // operation. | 131 // every operation. |
| 134 if (control_.get() == NULL) { | 132 if (control_.get() == NULL) { |
| 135 BIND_OPTS3 bind_options; | 133 BIND_OPTS3 bind_options; |
| 136 memset(&bind_options, 0, sizeof(bind_options)); | 134 memset(&bind_options, 0, sizeof(bind_options)); |
| 137 bind_options.cbStruct = sizeof(bind_options); | 135 bind_options.cbStruct = sizeof(bind_options); |
| 138 bind_options.hwnd = NULL; | 136 bind_options.hwnd = GetTopLevelWindow(window_handle); |
| 139 bind_options.dwClassContext = CLSCTX_LOCAL_SERVER; | 137 bind_options.dwClassContext = CLSCTX_LOCAL_SERVER; |
| 140 | 138 |
| 141 HRESULT hr = ::CoGetObject( | 139 HRESULT hr = ::CoGetObject( |
| 142 kDaemonControllerElevationMoniker, | 140 kDaemonControllerElevationMoniker, |
| 143 &bind_options, | 141 &bind_options, |
| 144 IID_IDaemonControl, | 142 IID_IDaemonControl, |
| 145 control_.ReceiveVoid()); | 143 control_.ReceiveVoid()); |
| 146 if (FAILED(hr)) { | 144 if (FAILED(hr)) { |
| 147 return hr; | 145 return hr; |
| 148 } | 146 } |
| 149 } | 147 } |
| 150 | 148 |
| 151 *control_out = control_.get(); | 149 *control_out = control_.get(); |
| 152 return S_OK; | 150 return S_OK; |
| 153 } | 151 } |
| 154 | 152 |
| 155 bool ComThread::Start() { | 153 bool ComThread::Start() { |
| 156 // N.B. The single threaded COM apartment must be run on a UI message loop. | 154 // N.B. The single threaded COM apartment must be run on a UI message loop. |
| 157 base::Thread::Options thread_options(MessageLoop::TYPE_UI, 0); | 155 base::Thread::Options thread_options(MessageLoop::TYPE_UI, 0); |
| 158 return StartWithOptions(thread_options); | 156 return StartWithOptions(thread_options); |
| 159 } | 157 } |
| 160 | 158 |
| 159 void ComThread::Init() { |
| 160 CoInitialize(NULL); |
| 161 } |
| 162 |
| 163 void ComThread::CleanUp() { |
| 164 control_.Release(); |
| 165 CoUninitialize(); |
| 166 } |
| 167 |
| 161 DaemonControllerWin::DaemonControllerWin() | 168 DaemonControllerWin::DaemonControllerWin() |
| 162 : worker_thread_(kDaemonControllerThreadName) { | 169 : window_handle_(NULL), |
| 170 worker_thread_(kDaemonControllerThreadName) { |
| 163 if (!worker_thread_.Start()) { | 171 if (!worker_thread_.Start()) { |
| 164 LOG(FATAL) << "Failed to start the Daemon Controller worker thread."; | 172 LOG(FATAL) << "Failed to start the Daemon Controller worker thread."; |
| 165 } | 173 } |
| 166 } | 174 } |
| 167 | 175 |
| 168 DaemonControllerWin::~DaemonControllerWin() { | 176 DaemonControllerWin::~DaemonControllerWin() { |
| 169 worker_thread_.Stop(); | 177 worker_thread_.Stop(); |
| 170 } | 178 } |
| 171 | 179 |
| 172 remoting::DaemonController::State DaemonControllerWin::GetState() { | 180 remoting::DaemonController::State DaemonControllerWin::GetState() { |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 220 base::Unretained(this), base::Passed(&config), done_callback)); | 228 base::Unretained(this), base::Passed(&config), done_callback)); |
| 221 } | 229 } |
| 222 | 230 |
| 223 void DaemonControllerWin::Stop(const CompletionCallback& done_callback) { | 231 void DaemonControllerWin::Stop(const CompletionCallback& done_callback) { |
| 224 worker_thread_.message_loop_proxy()->PostTask( | 232 worker_thread_.message_loop_proxy()->PostTask( |
| 225 FROM_HERE, base::Bind( | 233 FROM_HERE, base::Bind( |
| 226 &DaemonControllerWin::DoStop, base::Unretained(this), | 234 &DaemonControllerWin::DoStop, base::Unretained(this), |
| 227 done_callback)); | 235 done_callback)); |
| 228 } | 236 } |
| 229 | 237 |
| 238 void DaemonControllerWin::SetWindow(void* window_handle) { |
| 239 worker_thread_.message_loop_proxy()->PostTask( |
| 240 FROM_HERE, base::Bind( |
| 241 &DaemonControllerWin::DoSetWindow, base::Unretained(this), |
| 242 window_handle)); |
| 243 } |
| 244 |
| 230 // static | 245 // static |
| 231 remoting::DaemonController::State DaemonControllerWin::ConvertToDaemonState( | 246 remoting::DaemonController::State DaemonControllerWin::ConvertToDaemonState( |
| 232 DWORD service_state) { | 247 DWORD service_state) { |
| 233 switch (service_state) { | 248 switch (service_state) { |
| 234 case SERVICE_RUNNING: | 249 case SERVICE_RUNNING: |
| 235 return STATE_STARTED; | 250 return STATE_STARTED; |
| 236 | 251 |
| 237 case SERVICE_CONTINUE_PENDING: | 252 case SERVICE_CONTINUE_PENDING: |
| 238 case SERVICE_START_PENDING: | 253 case SERVICE_START_PENDING: |
| 239 return STATE_STARTING; | 254 return STATE_STARTING; |
| (...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 360 | 375 |
| 361 callback.Run(scoped_ptr<base::DictionaryValue>(dictionary)); | 376 callback.Run(scoped_ptr<base::DictionaryValue>(dictionary)); |
| 362 } | 377 } |
| 363 | 378 |
| 364 void DaemonControllerWin::DoInstallAsNeededAndStart( | 379 void DaemonControllerWin::DoInstallAsNeededAndStart( |
| 365 scoped_ptr<base::DictionaryValue> config, | 380 scoped_ptr<base::DictionaryValue> config, |
| 366 const CompletionCallback& done_callback) { | 381 const CompletionCallback& done_callback) { |
| 367 DCHECK(worker_thread_.message_loop_proxy()->BelongsToCurrentThread()); | 382 DCHECK(worker_thread_.message_loop_proxy()->BelongsToCurrentThread()); |
| 368 | 383 |
| 369 IDaemonControl* control = NULL; | 384 IDaemonControl* control = NULL; |
| 370 HRESULT hr = worker_thread_.ActivateElevatedController(&control); | 385 HRESULT hr = worker_thread_.ActivateElevatedController(window_handle_, |
| 386 &control); |
| 371 | 387 |
| 372 // Just configure and start the Daemon Controller if it is installed already. | 388 // Just configure and start the Daemon Controller if it is installed already. |
| 373 if (SUCCEEDED(hr)) { | 389 if (SUCCEEDED(hr)) { |
| 374 DoSetConfigAndStart(config.Pass(), done_callback); | 390 DoSetConfigAndStart(config.Pass(), done_callback); |
| 375 return; | 391 return; |
| 376 } | 392 } |
| 377 | 393 |
| 378 // Otherwise, install it if its COM registration entry is missing. | 394 // Otherwise, install it if its COM registration entry is missing. |
| 379 if (hr == CO_E_CLASSSTRING) { | 395 if (hr == CO_E_CLASSSTRING) { |
| 380 scoped_ptr<DaemonInstallerWin> installer = DaemonInstallerWin::Create( | 396 scoped_ptr<DaemonInstallerWin> installer = DaemonInstallerWin::Create( |
| 397 window_handle_, |
| 381 base::Bind(&DaemonControllerWin::OnInstallationComplete, | 398 base::Bind(&DaemonControllerWin::OnInstallationComplete, |
| 382 base::Unretained(this), | 399 base::Unretained(this), |
| 383 base::Passed(&config), | 400 base::Passed(&config), |
| 384 done_callback)); | 401 done_callback)); |
| 385 if (installer.get()) { | 402 if (installer.get()) { |
| 386 DCHECK(!installer_.get()); | 403 DCHECK(!installer_.get()); |
| 387 installer_ = installer.Pass(); | 404 installer_ = installer.Pass(); |
| 388 installer_->Install(); | 405 installer_->Install(); |
| 389 } | 406 } |
| 390 } else { | 407 } else { |
| 391 LOG(ERROR) << "Failed to initiate the Chromoting Host installation " | 408 LOG(ERROR) << "Failed to initiate the Chromoting Host installation " |
| 392 << "(error: 0x" << std::hex << hr << std::dec << ")."; | 409 << "(error: 0x" << std::hex << hr << std::dec << ")."; |
| 393 done_callback.Run(HResultToAsyncResult(hr)); | 410 done_callback.Run(HResultToAsyncResult(hr)); |
| 394 } | 411 } |
| 395 } | 412 } |
| 396 | 413 |
| 397 void DaemonControllerWin::DoSetConfigAndStart( | 414 void DaemonControllerWin::DoSetConfigAndStart( |
| 398 scoped_ptr<base::DictionaryValue> config, | 415 scoped_ptr<base::DictionaryValue> config, |
| 399 const CompletionCallback& done_callback) { | 416 const CompletionCallback& done_callback) { |
| 400 DCHECK(worker_thread_.message_loop_proxy()->BelongsToCurrentThread()); | 417 DCHECK(worker_thread_.message_loop_proxy()->BelongsToCurrentThread()); |
| 401 | 418 |
| 402 IDaemonControl* control = NULL; | 419 IDaemonControl* control = NULL; |
| 403 HRESULT hr = worker_thread_.ActivateElevatedController(&control); | 420 HRESULT hr = worker_thread_.ActivateElevatedController(window_handle_, |
| 421 &control); |
| 404 if (FAILED(hr)) { | 422 if (FAILED(hr)) { |
| 405 done_callback.Run(HResultToAsyncResult(hr)); | 423 done_callback.Run(HResultToAsyncResult(hr)); |
| 406 return; | 424 return; |
| 407 } | 425 } |
| 408 | 426 |
| 409 // Store the configuration. | 427 // Store the configuration. |
| 410 std::string file_content; | 428 std::string file_content; |
| 411 base::JSONWriter::Write(config.get(), &file_content); | 429 base::JSONWriter::Write(config.get(), &file_content); |
| 412 | 430 |
| 413 ScopedBstr host_config(UTF8ToUTF16(file_content).c_str()); | 431 ScopedBstr host_config(UTF8ToUTF16(file_content).c_str()); |
| (...skipping 14 matching lines...) Expand all Loading... |
| 428 } | 446 } |
| 429 | 447 |
| 430 void DaemonControllerWin::DoUpdateConfig( | 448 void DaemonControllerWin::DoUpdateConfig( |
| 431 scoped_ptr<base::DictionaryValue> config, | 449 scoped_ptr<base::DictionaryValue> config, |
| 432 const CompletionCallback& done_callback) { | 450 const CompletionCallback& done_callback) { |
| 433 // TODO(simonmorris): Much of this code was copied from DoSetConfigAndStart(). | 451 // TODO(simonmorris): Much of this code was copied from DoSetConfigAndStart(). |
| 434 // Refactor. | 452 // Refactor. |
| 435 DCHECK(worker_thread_.message_loop_proxy()->BelongsToCurrentThread()); | 453 DCHECK(worker_thread_.message_loop_proxy()->BelongsToCurrentThread()); |
| 436 | 454 |
| 437 IDaemonControl* control = NULL; | 455 IDaemonControl* control = NULL; |
| 438 HRESULT hr = worker_thread_.ActivateElevatedController(&control); | 456 HRESULT hr = worker_thread_.ActivateElevatedController(window_handle_, |
| 457 &control); |
| 439 if (FAILED(hr)) { | 458 if (FAILED(hr)) { |
| 440 done_callback.Run(HResultToAsyncResult(hr)); | 459 done_callback.Run(HResultToAsyncResult(hr)); |
| 441 return; | 460 return; |
| 442 } | 461 } |
| 443 | 462 |
| 444 // Store the configuration. | 463 // Store the configuration. |
| 445 std::string file_content; | 464 std::string file_content; |
| 446 base::JSONWriter::Write(config.get(), &file_content); | 465 base::JSONWriter::Write(config.get(), &file_content); |
| 447 | 466 |
| 448 ScopedBstr host_config(UTF8ToUTF16(file_content).c_str()); | 467 ScopedBstr host_config(UTF8ToUTF16(file_content).c_str()); |
| 449 if (host_config == NULL) { | 468 if (host_config == NULL) { |
| 450 done_callback.Run(HResultToAsyncResult(E_OUTOFMEMORY)); | 469 done_callback.Run(HResultToAsyncResult(E_OUTOFMEMORY)); |
| 451 return; | 470 return; |
| 452 } | 471 } |
| 453 | 472 |
| 454 hr = control->UpdateConfig(host_config); | 473 hr = control->UpdateConfig(host_config); |
| 455 done_callback.Run(HResultToAsyncResult(hr)); | 474 done_callback.Run(HResultToAsyncResult(hr)); |
| 456 } | 475 } |
| 457 | 476 |
| 458 void DaemonControllerWin::DoStop(const CompletionCallback& done_callback) { | 477 void DaemonControllerWin::DoStop(const CompletionCallback& done_callback) { |
| 459 DCHECK(worker_thread_.message_loop_proxy()->BelongsToCurrentThread()); | 478 DCHECK(worker_thread_.message_loop_proxy()->BelongsToCurrentThread()); |
| 460 | 479 |
| 461 IDaemonControl* control = NULL; | 480 IDaemonControl* control = NULL; |
| 462 HRESULT hr = worker_thread_.ActivateElevatedController(&control); | 481 HRESULT hr = worker_thread_.ActivateElevatedController(window_handle_, |
| 482 &control); |
| 463 if (FAILED(hr)) { | 483 if (FAILED(hr)) { |
| 464 done_callback.Run(HResultToAsyncResult(hr)); | 484 done_callback.Run(HResultToAsyncResult(hr)); |
| 465 return; | 485 return; |
| 466 } | 486 } |
| 467 | 487 |
| 468 hr = control->StopDaemon(); | 488 hr = control->StopDaemon(); |
| 469 done_callback.Run(HResultToAsyncResult(hr)); | 489 done_callback.Run(HResultToAsyncResult(hr)); |
| 470 } | 490 } |
| 471 | 491 |
| 492 void DaemonControllerWin::DoSetWindow(void* window_handle) { |
| 493 window_handle_ = reinterpret_cast<HWND>(window_handle); |
| 494 } |
| 495 |
| 472 } // namespace | 496 } // namespace |
| 473 | 497 |
| 474 scoped_ptr<DaemonController> remoting::DaemonController::Create() { | 498 scoped_ptr<DaemonController> remoting::DaemonController::Create() { |
| 475 return scoped_ptr<DaemonController>(new DaemonControllerWin()); | 499 return scoped_ptr<DaemonController>(new DaemonControllerWin()); |
| 476 } | 500 } |
| 477 | 501 |
| 478 } // namespace remoting | 502 } // namespace remoting |
| OLD | NEW |