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 "cloud_print/service/win/cloud_print_service.h" | 5 #include "cloud_print/service/win/cloud_print_service.h" |
6 | 6 |
7 #include <atlsecurity.h> | 7 #include <atlsecurity.h> |
8 #include <iomanip> | 8 #include <iomanip> |
9 #include <iostream> | 9 #include <iostream> |
10 | 10 |
11 #include "base/at_exit.h" | 11 #include "base/at_exit.h" |
12 #include "base/command_line.h" | 12 #include "base/command_line.h" |
13 #include "base/file_path.h" | 13 #include "base/file_path.h" |
14 #include "base/file_util.h" | 14 #include "base/file_util.h" |
15 #include "base/path_service.h" | 15 #include "base/path_service.h" |
16 #include "base/string_util.h" | 16 #include "base/string_util.h" |
17 #include "base/win/scoped_handle.h" | 17 #include "base/win/scoped_handle.h" |
18 #include "chrome/installer/launcher_support/chrome_launcher_support.h" | 18 #include "chrome/installer/launcher_support/chrome_launcher_support.h" |
19 #include "cloud_print/service/service_state.h" | 19 #include "cloud_print/service/service_state.h" |
20 #include "cloud_print/service/service_switches.h" | 20 #include "cloud_print/service/service_switches.h" |
21 #include "cloud_print/service/win/chrome_launcher.h" | 21 #include "cloud_print/service/win/chrome_launcher.h" |
| 22 #include "cloud_print/service/win/local_security_policy.h" |
22 #include "cloud_print/service/win/resource.h" | 23 #include "cloud_print/service/win/resource.h" |
23 | 24 |
24 namespace { | 25 namespace { |
25 | 26 |
26 const wchar_t kServiceStateFileName[] = L"Service State"; | 27 const wchar_t kServiceStateFileName[] = L"Service State"; |
27 | 28 |
28 const wchar_t kUserToRunService[] = L"NT AUTHORITY\\LocalService"; | |
29 | |
30 // The traits class for Windows Service. | 29 // The traits class for Windows Service. |
31 class ServiceHandleTraits { | 30 class ServiceHandleTraits { |
32 public: | 31 public: |
33 typedef SC_HANDLE Handle; | 32 typedef SC_HANDLE Handle; |
34 | 33 |
35 // Closes the handle. | 34 // Closes the handle. |
36 static bool CloseHandle(Handle handle) { | 35 static bool CloseHandle(Handle handle) { |
37 return ::CloseServiceHandle(handle) != FALSE; | 36 return ::CloseServiceHandle(handle) != FALSE; |
38 } | 37 } |
39 | 38 |
(...skipping 24 matching lines...) Expand all Loading... |
64 FilePath service_path; | 63 FilePath service_path; |
65 CHECK(PathService::Get(base::FILE_EXE, &service_path)); | 64 CHECK(PathService::Get(base::FILE_EXE, &service_path)); |
66 | 65 |
67 std::cout << "Usage: "; | 66 std::cout << "Usage: "; |
68 std::cout << service_path.BaseName().value(); | 67 std::cout << service_path.BaseName().value(); |
69 std::cout << " ["; | 68 std::cout << " ["; |
70 std::cout << "["; | 69 std::cout << "["; |
71 std::cout << "["; | 70 std::cout << "["; |
72 std::cout << " -" << kInstallSwitch; | 71 std::cout << " -" << kInstallSwitch; |
73 std::cout << " -" << kUserDataDirSwitch << "=DIRECTORY"; | 72 std::cout << " -" << kUserDataDirSwitch << "=DIRECTORY"; |
| 73 std::cout << " -" << kRunAsUser << "=USERNAME"; |
| 74 std::cout << " -" << kRunAsPassword << "=PASSWORD"; |
74 std::cout << " [ -" << kQuietSwitch << " ]"; | 75 std::cout << " [ -" << kQuietSwitch << " ]"; |
75 std::cout << "]"; | 76 std::cout << "]"; |
76 std::cout << "]"; | 77 std::cout << "]"; |
77 std::cout << " | -" << kUninstallSwitch; | 78 std::cout << " | -" << kUninstallSwitch; |
78 std::cout << " | -" << kStartSwitch; | 79 std::cout << " | -" << kStartSwitch; |
79 std::cout << " | -" << kStopSwitch; | 80 std::cout << " | -" << kStopSwitch; |
80 std::cout << " ]\n"; | 81 std::cout << " ]\n"; |
81 std::cout << "Manages cloud print windows service.\n\n"; | 82 std::cout << "Manages cloud print windows service.\n\n"; |
82 | 83 |
83 static const struct { | 84 static const struct { |
84 const char* name; | 85 const char* name; |
85 const char* description; | 86 const char* description; |
86 } kSwitchHelp[] = { | 87 } kSwitchHelp[] = { |
87 { kInstallSwitch, "Installs cloud print as service." }, | 88 { kInstallSwitch, "Installs cloud print as service." }, |
88 { kUserDataDirSwitch, "User data directory with \"Service State\" file." }, | 89 { kUserDataDirSwitch, "User data directory with \"Service State\" file." }, |
89 { kQuietSwitch, "Fails without questions if something wrong." }, | 90 { kQuietSwitch, "Fails without questions if something wrong." }, |
90 { kUninstallSwitch, "Uninstalls service." }, | 91 { kUninstallSwitch, "Uninstalls service." }, |
91 { kStartSwitch, "Starts service. May be combined with installation." }, | 92 { kStartSwitch, "Starts service. May be combined with installation." }, |
92 { kStopSwitch, "Stops service." }, | 93 { kStopSwitch, "Stops service." }, |
| 94 { kRunAsUser, "Windows user to run the service in form DOMAIN\\USERNAME. " |
| 95 "Make sure user has access to printers." }, |
| 96 { kRunAsPassword, "Password for windows user to run the service" }, |
93 }; | 97 }; |
94 | 98 |
95 for (size_t i = 0; i < arraysize(kSwitchHelp); ++i) { | 99 for (size_t i = 0; i < arraysize(kSwitchHelp); ++i) { |
96 std::cout << std::setiosflags(std::ios::left); | 100 std::cout << std::setiosflags(std::ios::left); |
97 std::cout << " -" << std::setw(15) << kSwitchHelp[i].name; | 101 std::cout << " -" << std::setw(15) << kSwitchHelp[i].name; |
98 std::cout << kSwitchHelp[i].description << "\n"; | 102 std::cout << kSwitchHelp[i].description << "\n"; |
99 } | 103 } |
100 std::cout << "\n"; | 104 std::cout << "\n"; |
101 } | 105 } |
102 | 106 |
(...skipping 18 matching lines...) Expand all Loading... |
121 ::SetConsoleMode(stdin_handle, saved_mode); | 125 ::SetConsoleMode(stdin_handle, saved_mode); |
122 std::cout << "\n"; | 126 std::cout << "\n"; |
123 } else { | 127 } else { |
124 std::getline(std::cin, tmp); | 128 std::getline(std::cin, tmp); |
125 } | 129 } |
126 if (tmp.empty()) | 130 if (tmp.empty()) |
127 return default; | 131 return default; |
128 return tmp; | 132 return tmp; |
129 } | 133 } |
130 | 134 |
131 HRESULT WriteFileAsUser(const FilePath& path, const wchar_t* user, | 135 HRESULT WriteFileAsUser(const FilePath& path, const string16& user, |
132 const char* data, int size) { | 136 const char* data, int size) { |
133 ATL::CAccessToken thread_token; | 137 ATL::CAccessToken token; |
134 if (!thread_token.OpenThreadToken(TOKEN_DUPLICATE | TOKEN_IMPERSONATE)) { | 138 ATL::CAutoRevertImpersonation auto_revert(&token); |
135 LOG(ERROR) << "Failed to open thread token."; | 139 if (!user.empty()) { |
136 return HResultFromLastError(); | 140 ATL::CAccessToken thread_token; |
| 141 if (!thread_token.OpenThreadToken(TOKEN_DUPLICATE | TOKEN_IMPERSONATE)) { |
| 142 LOG(ERROR) << "Failed to open thread token."; |
| 143 return HResultFromLastError(); |
| 144 } |
| 145 |
| 146 ATL::CSid local_service; |
| 147 if (!local_service.LoadAccount(user.c_str())) { |
| 148 LOG(ERROR) << "Failed create SID."; |
| 149 return HResultFromLastError(); |
| 150 } |
| 151 |
| 152 ATL::CTokenGroups group; |
| 153 group.Add(local_service, 0); |
| 154 |
| 155 const ATL::CTokenGroups empty_group; |
| 156 if (!thread_token.CreateRestrictedToken(&token, empty_group, group)) { |
| 157 LOG(ERROR) << "Failed to create restricted token for " << user << "."; |
| 158 return HResultFromLastError(); |
| 159 } |
| 160 |
| 161 if (!token.Impersonate()) { |
| 162 LOG(ERROR) << "Failed to impersonate " << user << "."; |
| 163 return HResultFromLastError(); |
| 164 } |
137 } | 165 } |
138 | |
139 ATL::CSid local_service; | |
140 if (!local_service.LoadAccount(user)) { | |
141 LOG(ERROR) << "Failed create SID."; | |
142 return HResultFromLastError(); | |
143 } | |
144 | |
145 ATL::CAccessToken token; | |
146 ATL::CTokenGroups group; | |
147 group.Add(local_service, 0); | |
148 | |
149 const ATL::CTokenGroups empty_group; | |
150 if (!thread_token.CreateRestrictedToken(&token, empty_group, group)) { | |
151 LOG(ERROR) << "Failed to create restricted token for " << user << "."; | |
152 return HResultFromLastError(); | |
153 } | |
154 | |
155 if (!token.Impersonate()) { | |
156 LOG(ERROR) << "Failed to impersonate " << user << "."; | |
157 return HResultFromLastError(); | |
158 } | |
159 | |
160 ATL::CAutoRevertImpersonation auto_revert(&token); | |
161 if (file_util::WriteFile(path, data, size) != size) { | 166 if (file_util::WriteFile(path, data, size) != size) { |
162 LOG(ERROR) << "Failed to write file " << path.value() << "."; | 167 LOG(ERROR) << "Failed to write file " << path.value() << "."; |
163 return HResultFromLastError(); | 168 return HResultFromLastError(); |
164 } | 169 } |
165 return S_OK; | 170 return S_OK; |
166 } | 171 } |
167 | 172 |
168 } // namespace | 173 } // namespace |
169 | 174 |
170 class CloudPrintServiceModule | 175 class CloudPrintServiceModule |
171 : public ATL::CAtlServiceModuleT<CloudPrintServiceModule, IDS_SERVICENAME> { | 176 : public ATL::CAtlServiceModuleT<CloudPrintServiceModule, IDS_SERVICENAME> { |
172 public: | 177 public: |
173 typedef ATL::CAtlServiceModuleT<CloudPrintServiceModule, | 178 typedef ATL::CAtlServiceModuleT<CloudPrintServiceModule, |
174 IDS_SERVICENAME> Base; | 179 IDS_SERVICENAME> Base; |
175 | 180 |
176 DECLARE_REGISTRY_APPID_RESOURCEID(IDR_CLOUDPRINTSERVICE, | 181 DECLARE_REGISTRY_APPID_RESOURCEID(IDR_CLOUDPRINTSERVICE, |
177 "{8013FB7C-2E3E-4992-B8BD-05C0C4AB0627}") | 182 "{8013FB7C-2E3E-4992-B8BD-05C0C4AB0627}") |
178 | 183 |
179 HRESULT InitializeSecurity() { | 184 HRESULT InitializeSecurity() { |
180 // TODO(gene): Check if we need to call CoInitializeSecurity and provide | 185 // TODO(gene): Check if we need to call CoInitializeSecurity and provide |
181 // the appropriate security settings for service. | 186 // the appropriate security settings for service. |
182 return S_OK; | 187 return S_OK; |
183 } | 188 } |
184 | 189 |
185 HRESULT InstallService() { | 190 HRESULT InstallService(const string16& user, const string16& password) { |
186 using namespace chrome_launcher_support; | 191 using namespace chrome_launcher_support; |
187 | 192 |
188 // TODO(vitalybuka): consider "lite" version if we don't want unregister | 193 // TODO(vitalybuka): consider "lite" version if we don't want unregister |
189 // printers here. | 194 // printers here. |
190 HRESULT hr = UninstallService(); | 195 HRESULT hr = UninstallService(); |
191 if (FAILED(hr)) | 196 if (FAILED(hr)) |
192 return hr; | 197 return hr; |
193 | 198 |
194 if (GetChromePathForInstallationLevel(SYSTEM_LEVEL_INSTALLATION).empty()) { | 199 if (GetChromePathForInstallationLevel(SYSTEM_LEVEL_INSTALLATION).empty()) { |
195 LOG(ERROR) << "Found no Chrome installed for all users."; | 200 LOG(WARNING) << "Found no Chrome installed for all users."; |
196 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); | |
197 } | 201 } |
198 | 202 |
199 hr = UpdateRegistryAppId(true); | 203 hr = UpdateRegistryAppId(true); |
200 if (FAILED(hr)) | 204 if (FAILED(hr)) |
201 return hr; | 205 return hr; |
202 | 206 |
203 FilePath service_path; | 207 FilePath service_path; |
204 CHECK(PathService::Get(base::FILE_EXE, &service_path)); | 208 CHECK(PathService::Get(base::FILE_EXE, &service_path)); |
205 CommandLine command_line(service_path); | 209 CommandLine command_line(service_path); |
206 command_line.AppendSwitch(kServiceSwitch); | 210 command_line.AppendSwitch(kServiceSwitch); |
207 command_line.AppendSwitchPath(kUserDataDirSwitch, user_data_dir_); | 211 command_line.AppendSwitchPath(kUserDataDirSwitch, user_data_dir_); |
208 | 212 |
| 213 LocalSecurityPolicy local_security_policy; |
| 214 if (local_security_policy.Open()) { |
| 215 if (!local_security_policy.IsPrivilegeSet(user, kSeServiceLogonRight)) { |
| 216 LOG(WARNING) << "Setting " << kSeServiceLogonRight << " for " << user; |
| 217 if (!local_security_policy.SetPrivilege(user, kSeServiceLogonRight)) { |
| 218 LOG(ERROR) << "Failed to set" << kSeServiceLogonRight; |
| 219 LOG(ERROR) << "Make sure you can run the service with this user."; |
| 220 } |
| 221 } |
| 222 } else { |
| 223 LOG(ERROR) << "Failed to open security policy."; |
| 224 } |
| 225 |
209 ServiceHandle scm; | 226 ServiceHandle scm; |
210 hr = OpenServiceManager(&scm); | 227 hr = OpenServiceManager(&scm); |
211 if (FAILED(hr)) | 228 if (FAILED(hr)) |
212 return hr; | 229 return hr; |
213 | 230 |
214 ServiceHandle service( | 231 ServiceHandle service( |
215 ::CreateService( | 232 ::CreateService( |
216 scm, m_szServiceName, m_szServiceName, SERVICE_ALL_ACCESS, | 233 scm, m_szServiceName, m_szServiceName, SERVICE_ALL_ACCESS, |
217 SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, | 234 SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, |
218 command_line.GetCommandLineString().c_str(), NULL, NULL, NULL, | 235 command_line.GetCommandLineString().c_str(), NULL, NULL, NULL, |
219 kUserToRunService, NULL)); | 236 user.empty() ? NULL : user.c_str(), |
| 237 password.empty() ? NULL : password.c_str())); |
220 | 238 |
221 if (!service.IsValid()) | 239 if (!service.IsValid()) |
222 return HResultFromLastError(); | 240 return HResultFromLastError(); |
223 | 241 |
224 return S_OK; | 242 return S_OK; |
225 } | 243 } |
226 | 244 |
227 HRESULT UninstallService() { | 245 HRESULT UninstallService() { |
228 if (!Uninstall()) | 246 if (!Uninstall()) |
229 return E_FAIL; | 247 return E_FAIL; |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
270 *is_service = false; | 288 *is_service = false; |
271 | 289 |
272 user_data_dir_ = command_line.GetSwitchValuePath(kUserDataDirSwitch); | 290 user_data_dir_ = command_line.GetSwitchValuePath(kUserDataDirSwitch); |
273 if (command_line.HasSwitch(kStopSwitch)) | 291 if (command_line.HasSwitch(kStopSwitch)) |
274 return StopService(); | 292 return StopService(); |
275 | 293 |
276 if (command_line.HasSwitch(kUninstallSwitch)) | 294 if (command_line.HasSwitch(kUninstallSwitch)) |
277 return UninstallService(); | 295 return UninstallService(); |
278 | 296 |
279 if (command_line.HasSwitch(kInstallSwitch)) { | 297 if (command_line.HasSwitch(kInstallSwitch)) { |
280 if (!command_line.HasSwitch(kUserDataDirSwitch)) { | 298 if (!command_line.HasSwitch(kUserDataDirSwitch) || |
| 299 !command_line.HasSwitch(kRunAsUser) || |
| 300 !command_line.HasSwitch(kRunAsPassword)) { |
281 InvalidUsage(); | 301 InvalidUsage(); |
282 return S_FALSE; | 302 return S_FALSE; |
283 } | 303 } |
284 | 304 |
285 HRESULT hr = ValidateUserDataDir(); | 305 HRESULT hr = ProcessServiceState(command_line.HasSwitch(kQuietSwitch)); |
286 if (FAILED(hr)) | 306 if (FAILED(hr)) |
287 return hr; | 307 return hr; |
288 | 308 |
289 hr = ProcessServiceState(command_line.HasSwitch(kQuietSwitch)); | 309 CommandLine::StringType run_as_user = |
290 if (FAILED(hr)) | 310 command_line.GetSwitchValueNative(kRunAsUser); |
291 return hr; | 311 CommandLine::StringType run_as_password = |
| 312 command_line.GetSwitchValueNative(kRunAsPassword); |
292 | 313 |
293 hr = InstallService(); | 314 hr = ValidateUserDataDir(run_as_user.c_str()); |
| 315 |
| 316 hr = InstallService(run_as_user.c_str(), run_as_password.c_str()); |
294 if (SUCCEEDED(hr) && command_line.HasSwitch(kStartSwitch)) | 317 if (SUCCEEDED(hr) && command_line.HasSwitch(kStartSwitch)) |
295 return StartService(); | 318 return StartService(); |
296 | 319 |
297 return hr; | 320 return hr; |
298 } | 321 } |
299 | 322 |
300 if (command_line.HasSwitch(kStartSwitch)) | 323 if (command_line.HasSwitch(kStartSwitch)) |
301 return StartService(); | 324 return StartService(); |
302 | 325 |
303 if (command_line.HasSwitch(kServiceSwitch)) { | 326 if (command_line.HasSwitch(kServiceSwitch)) { |
304 *is_service = true; | 327 *is_service = true; |
305 return S_OK; | 328 return S_OK; |
306 } | 329 } |
307 | 330 |
308 if (command_line.HasSwitch(kConsoleSwitch)) { | 331 if (command_line.HasSwitch(kConsoleSwitch)) { |
309 ::SetConsoleCtrlHandler(&ConsoleCtrlHandler, TRUE); | 332 ::SetConsoleCtrlHandler(&ConsoleCtrlHandler, TRUE); |
310 HRESULT hr = Run(); | 333 HRESULT hr = Run(); |
311 ::SetConsoleCtrlHandler(NULL, FALSE); | 334 ::SetConsoleCtrlHandler(NULL, FALSE); |
312 return hr; | 335 return hr; |
313 } | 336 } |
314 | 337 |
315 InvalidUsage(); | 338 InvalidUsage(); |
316 return S_FALSE; | 339 return S_FALSE; |
317 } | 340 } |
318 | 341 |
319 HRESULT ValidateUserDataDir() { | 342 HRESULT ValidateUserDataDir(const string16& user) { |
320 FilePath temp_file; | 343 FilePath temp_file; |
321 const char some_data[] = "1234"; | 344 const char some_data[] = "1234"; |
322 if (!file_util::CreateTemporaryFileInDir(user_data_dir_, &temp_file)) | 345 if (!file_util::CreateTemporaryFileInDir(user_data_dir_, &temp_file)) |
323 return E_FAIL; | 346 return E_FAIL; |
324 HRESULT hr = WriteFileAsUser(temp_file, kUserToRunService, some_data, | 347 HRESULT hr = WriteFileAsUser(temp_file, user, some_data, |
325 sizeof(some_data)); | 348 sizeof(some_data)); |
326 if (FAILED(hr)) { | 349 if (FAILED(hr)) { |
327 LOG(ERROR) << "Failed to write user data. Make sure that account \'" << | 350 LOG(ERROR) << "Failed to write user data. Make sure that account \'" << |
328 kUserToRunService << "\'has full access to \'" << | 351 user << "\' has full access to \'" << |
329 user_data_dir_.value() << "\'."; | 352 user_data_dir_.value() << "\'."; |
330 } | 353 } |
331 file_util::Delete(temp_file, false); | 354 file_util::Delete(temp_file, false); |
332 return hr; | 355 return hr; |
333 } | 356 } |
334 | 357 |
335 HRESULT ProcessServiceState(bool quiet) { | 358 HRESULT ProcessServiceState(bool quiet) { |
336 FilePath file = user_data_dir_.Append(kServiceStateFileName); | 359 FilePath file = user_data_dir_.Append(kServiceStateFileName); |
337 | 360 |
338 for (;;) { | 361 for (;;) { |
339 std::string contents; | 362 std::string contents; |
340 ServiceState service_state; | 363 ServiceState service_state; |
341 | 364 |
342 bool is_valid = file_util::ReadFileToString(file, &contents) && | 365 bool is_valid = file_util::ReadFileToString(file, &contents) && |
343 service_state.FromString(contents); | 366 service_state.FromString(contents); |
344 | 367 |
345 if (!quiet) { | 368 if (!quiet) { |
| 369 std::cout << "\n"; |
346 std::cout << file.value() << ":\n"; | 370 std::cout << file.value() << ":\n"; |
347 std::cout << contents << "\n"; | 371 std::cout << contents << "\n"; |
348 } | 372 } |
349 | 373 |
350 if (!is_valid) | 374 if (!is_valid) |
351 LOG(ERROR) << "Invalid file: " << file.value(); | 375 LOG(ERROR) << "Invalid file: " << file.value(); |
352 | 376 |
353 if (quiet) | 377 if (quiet) |
354 return is_valid ? S_OK : HRESULT_FROM_WIN32(ERROR_FILE_INVALID); | 378 return is_valid ? S_OK : HRESULT_FROM_WIN32(ERROR_FILE_INVALID); |
355 | 379 |
(...skipping 14 matching lines...) Expand all Loading... |
370 | 394 |
371 while (!is_valid) { | 395 while (!is_valid) { |
372 std::string email = GetOption("email", service_state.email(), false); | 396 std::string email = GetOption("email", service_state.email(), false); |
373 std::string password = GetOption("password", "", true); | 397 std::string password = GetOption("password", "", true); |
374 std::string proxy_id = GetOption("connector_id", | 398 std::string proxy_id = GetOption("connector_id", |
375 service_state.proxy_id(), false); | 399 service_state.proxy_id(), false); |
376 is_valid = service_state.Configure(email, password, proxy_id); | 400 is_valid = service_state.Configure(email, password, proxy_id); |
377 if (is_valid) { | 401 if (is_valid) { |
378 std::string new_contents = service_state.ToString(); | 402 std::string new_contents = service_state.ToString(); |
379 if (new_contents != contents) { | 403 if (new_contents != contents) { |
380 HRESULT hr = WriteFileAsUser(file, kUserToRunService, | 404 size_t written = file_util::WriteFile(file, new_contents.c_str(), |
381 new_contents.c_str(), | 405 new_contents.size()); |
382 new_contents.size()); | 406 if (written != new_contents.size()) { |
383 if (FAILED(hr)) | 407 LOG(ERROR) << "Failed to write file " << file.value() << "."; |
384 return hr; | 408 return HResultFromLastError(); |
| 409 } |
385 } | 410 } |
386 } | 411 } |
387 } | 412 } |
388 } | 413 } |
389 | 414 |
390 return S_OK; | 415 return S_OK; |
391 } | 416 } |
392 | 417 |
393 HRESULT OpenServiceManager(ServiceHandle* service_manager) { | 418 HRESULT OpenServiceManager(ServiceHandle* service_manager) { |
394 if (!service_manager) | 419 if (!service_manager) |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
459 | 484 |
460 BOOL CloudPrintServiceModule::ConsoleCtrlHandler(DWORD type) { | 485 BOOL CloudPrintServiceModule::ConsoleCtrlHandler(DWORD type) { |
461 PostThreadMessage(_AtlModule.m_dwThreadID, WM_QUIT, 0, 0); | 486 PostThreadMessage(_AtlModule.m_dwThreadID, WM_QUIT, 0, 0); |
462 return TRUE; | 487 return TRUE; |
463 } | 488 } |
464 | 489 |
465 int main() { | 490 int main() { |
466 base::AtExitManager at_exit; | 491 base::AtExitManager at_exit; |
467 return _AtlModule.WinMain(0); | 492 return _AtlModule.WinMain(0); |
468 } | 493 } |
OLD | NEW |