| Index: cloud_print/virtual_driver/win/port_monitor/port_monitor.cc
|
| ===================================================================
|
| --- cloud_print/virtual_driver/win/port_monitor/port_monitor.cc (revision 80684)
|
| +++ cloud_print/virtual_driver/win/port_monitor/port_monitor.cc (working copy)
|
| @@ -1,600 +0,0 @@
|
| -// Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
| -// Use of this source code is governed by a BSD-style license that can be
|
| -// found in the LICENSE file.
|
| -
|
| -#include "cloud_print/virtual_driver/win/port_monitor/port_monitor.h"
|
| -
|
| -#include <lmcons.h>
|
| -#include <shlobj.h>
|
| -#include <strsafe.h>
|
| -#include <userenv.h>
|
| -#include <windows.h>
|
| -#include <winspool.h>
|
| -
|
| -#include "base/at_exit.h"
|
| -#include "base/command_line.h"
|
| -#include "base/file_util.h"
|
| -#include "base/logging.h"
|
| -#include "base/path_service.h"
|
| -#include "base/process.h"
|
| -#include "base/process_util.h"
|
| -#include "base/string16.h"
|
| -#include "base/win/registry.h"
|
| -#include "base/win/scoped_handle.h"
|
| -#include "chrome/common/chrome_constants.h"
|
| -#include "chrome/common/chrome_switches.h"
|
| -#include "cloud_print/virtual_driver/win/port_monitor/spooler_win.h"
|
| -
|
| -namespace cloud_print {
|
| -
|
| -#ifndef UNIT_TEST
|
| -const wchar_t kChromeExePath[] = L"google\\chrome\\application\\chrome.exe";
|
| -const wchar_t kChromePathRegValue[] = L"PathToChromeExe";
|
| -#endif
|
| -
|
| -const wchar_t kChromePathRegKey[] = L"Software\\Google\\CloudPrint";
|
| -
|
| -namespace {
|
| -#ifdef _WIN64
|
| -const wchar_t kPortMonitorDllName[] = L"gcp_portmon64.dll";
|
| -#else
|
| -const wchar_t kPortMonitorDllName[] = L"gcp_portmon.dll";
|
| -#endif
|
| -
|
| -const wchar_t kPortName[] = L"GCP:";
|
| -
|
| -const wchar_t kXpsMimeType[] = L"application/vnd.ms-xpsdocument";
|
| -
|
| -const size_t kMaxCommandLineLen = 0x7FFF;
|
| -
|
| -const size_t kMaxMessageLen = 100;
|
| -
|
| -struct MonitorData {
|
| - base::AtExitManager* at_exit_manager;
|
| -};
|
| -
|
| -struct PortData {
|
| - DWORD job_id;
|
| - HANDLE printer_handle;
|
| - FILE* file;
|
| - FilePath* file_path;
|
| -};
|
| -
|
| -typedef struct {
|
| - ACCESS_MASK granted_access;
|
| -} XcvUiData;
|
| -
|
| -
|
| -MONITORUI g_monitor_ui = {
|
| - sizeof(MONITORUI),
|
| - MonitorUiAddPortUi,
|
| - MonitorUiConfigureOrDeletePortUI,
|
| - MonitorUiConfigureOrDeletePortUI
|
| -};
|
| -
|
| -MONITOR2 g_monitor_2 = {
|
| - sizeof(MONITOR2),
|
| - Monitor2EnumPorts,
|
| - Monitor2OpenPort,
|
| - NULL, // OpenPortEx is not supported.
|
| - Monitor2StartDocPort,
|
| - Monitor2WritePort,
|
| - Monitor2ReadPort,
|
| - Monitor2EndDocPort,
|
| - Monitor2ClosePort,
|
| - NULL, // AddPort is not supported.
|
| - NULL, // AddPortEx is not supported.
|
| - NULL, // ConfigurePort is not supported.
|
| - NULL, // DeletePort is not supported.
|
| - NULL,
|
| - NULL, // SetPortTimeOuts is not supported.
|
| - Monitor2XcvOpenPort,
|
| - Monitor2XcvDataPort,
|
| - Monitor2XcvClosePort,
|
| - Monitor2Shutdown
|
| -};
|
| -
|
| -// Frees any objects referenced by port_data and sets pointers to NULL.
|
| -void CleanupPortData(PortData* port_data) {
|
| - delete port_data->file_path;
|
| - port_data->file_path = NULL;
|
| - if (port_data->printer_handle != NULL) {
|
| - ClosePrinter(port_data->printer_handle);
|
| - port_data->printer_handle = NULL;
|
| - }
|
| - if (port_data->file != NULL) {
|
| - file_util::CloseFile(port_data->file);
|
| - port_data->file = NULL;
|
| - }
|
| -}
|
| -
|
| -// Attempts to retrieve the title of the specified print job.
|
| -// On success returns TRUE and the first title_chars characters of the job title
|
| -// are copied into title.
|
| -// On failure returns FALSE and title is unmodified.
|
| -bool GetJobTitle(HANDLE printer_handle,
|
| - DWORD job_id,
|
| - string16 *title) {
|
| - DCHECK(printer_handle != NULL);
|
| - DCHECK(title != NULL);
|
| - DWORD bytes_needed = 0;
|
| - GetJob(printer_handle, job_id, 1, NULL, 0, &bytes_needed);
|
| - if (bytes_needed == 0) {
|
| - LOG(ERROR) << "Unable to get bytes needed for job info.";
|
| - return false;
|
| - }
|
| - scoped_ptr<BYTE> buffer(new BYTE[bytes_needed]);
|
| - if (!GetJob(printer_handle,
|
| - job_id,
|
| - 1,
|
| - buffer.get(),
|
| - bytes_needed,
|
| - &bytes_needed)) {
|
| - LOG(ERROR) << "Unable to get job info.";
|
| - return false;
|
| - }
|
| - JOB_INFO_1* job_info = reinterpret_cast<JOB_INFO_1*>(buffer.get());
|
| - *title = job_info->pDocument;
|
| - return true;
|
| -}
|
| -
|
| -// Handler for the UI functions exported by the port monitor.
|
| -// Verifies that a valid parent Window exists and then just displays an
|
| -// error message to let the user know that there is no interactive
|
| -// configuration.
|
| -void HandlePortUi(HWND hwnd, const string16& caption) {
|
| - if (hwnd != NULL && IsWindow(hwnd)) {
|
| - wchar_t message_text[kMaxMessageLen + 1] = L"";
|
| -
|
| - ::FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
|
| - NULL,
|
| - CO_E_NOT_SUPPORTED,
|
| - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
| - message_text,
|
| - kMaxMessageLen,
|
| - NULL);
|
| - ::MessageBox(hwnd, message_text, caption.c_str(), MB_OK);
|
| - }
|
| -}
|
| -
|
| -// Launches the Cloud Print dialog in Chrome.
|
| -// xps_path references a file to print.
|
| -// job_title is the title to be used for the resulting print job.
|
| -// process_handle is set to the handle of the resulting process.
|
| -bool LaunchPrintDialog(const string16& xps_path,
|
| - const string16& job_title,
|
| - base::ProcessHandle* process_handle) {
|
| - DCHECK(process_handle != NULL);
|
| - HANDLE token = NULL;
|
| - if (!OpenThreadToken(GetCurrentThread(),
|
| - TOKEN_QUERY|TOKEN_DUPLICATE|TOKEN_ASSIGN_PRIMARY,
|
| - FALSE,
|
| - &token)) {
|
| - LOG(ERROR) << "Unable to get thread token.";
|
| - return false;
|
| - }
|
| - base::win::ScopedHandle token_scoped(token);
|
| - if (!DuplicateTokenEx(token,
|
| - TOKEN_QUERY|TOKEN_DUPLICATE|TOKEN_ASSIGN_PRIMARY,
|
| - NULL,
|
| - SecurityImpersonation,
|
| - TokenPrimary,
|
| - &token)) {
|
| - LOG(ERROR) << "Unable to get primary thread token.";
|
| - return false;
|
| - }
|
| - base::win::ScopedHandle primary_token_scoped(token);
|
| - FilePath chrome_path;
|
| - if (!GetChromeExePath(&chrome_path)) {
|
| - LOG(ERROR) << "Unable to get chrome exe path.";
|
| - return false;
|
| - }
|
| - CommandLine command_line(chrome_path);
|
| - command_line.AppendSwitchPath(switches::kCloudPrintFile,
|
| - FilePath(xps_path));
|
| - command_line.AppendSwitchNative(switches::kCloudPrintFileType,
|
| - kXpsMimeType);
|
| - command_line.AppendSwitchNative(switches::kCloudPrintJobTitle,
|
| - job_title);
|
| - base::LaunchAppAsUser(primary_token_scoped,
|
| - command_line.command_line_string(),
|
| - false,
|
| - process_handle);
|
| - return true;
|
| -}
|
| -
|
| -// Returns false if the print job is being run in a context
|
| -// that shouldn't be launching Chrome.
|
| -bool ValidateCurrentUser() {
|
| - wchar_t user_name[UNLEN + 1] = L"";
|
| - DWORD name_size = sizeof(user_name);
|
| - GetUserName(user_name, &name_size);
|
| - LOG(INFO) << "Username is " << user_name << ".";
|
| - // TODO(abodenha@chromium.org) Return false if running as session 0 or
|
| - // as local system.
|
| - return true;
|
| -}
|
| -} // namespace
|
| -
|
| -bool GetChromeExePath(FilePath* chrome_path) {
|
| - base::win::RegKey app_path_key(HKEY_CURRENT_USER,
|
| - kChromePathRegKey,
|
| - KEY_READ);
|
| - DCHECK(chrome_path != NULL);
|
| - std::wstring reg_data;
|
| - if (SUCCEEDED(app_path_key.ReadValue(kChromePathRegValue,
|
| - ®_data))) {
|
| - if (!reg_data.empty() && file_util::PathExists(FilePath(reg_data))) {
|
| - *chrome_path = FilePath(reg_data);
|
| - return true;
|
| - }
|
| - }
|
| - // First check %localappdata%\google\chrome\application\chrome.exe
|
| - FilePath path;
|
| - PathService::Get(base::DIR_LOCAL_APP_DATA, &path);
|
| - path = path.Append(kChromeExePath);
|
| - if (file_util::PathExists(path)) {
|
| - *chrome_path = FilePath(path.value());
|
| - return true;
|
| - }
|
| -
|
| - // Chrome doesn't appear to be installed per user.
|
| - // Now check %programfiles(x86)%\google\chrome\application
|
| - // TODO(abodenha@chromium.org) Extend PathService::Get to be able to
|
| - // return the X86 program files path. At a minimum, use SHGetKnownFolderPath
|
| - // instead.
|
| - wchar_t system_buffer[MAX_PATH] = L"";
|
| - SHGetFolderPath(NULL,
|
| - CSIDL_PROGRAM_FILESX86,
|
| - NULL,
|
| - SHGFP_TYPE_CURRENT,
|
| - system_buffer);
|
| - path = FilePath(system_buffer);
|
| - path = path.Append(kChromeExePath);
|
| - if (file_util::PathExists(path)) {
|
| - *chrome_path = FilePath(path.value());
|
| - return true;
|
| - }
|
| - LOG(WARNING) << kChromeExePath << " not found.";
|
| - return false;
|
| -}
|
| -
|
| -BOOL WINAPI Monitor2EnumPorts(HANDLE,
|
| - wchar_t*,
|
| - DWORD level,
|
| - BYTE* ports,
|
| - DWORD ports_size,
|
| - DWORD* needed_bytes,
|
| - DWORD* returned) {
|
| - LOG(INFO) << "Monitor2EnumPorts";
|
| -
|
| - if (needed_bytes == NULL) {
|
| - LOG(ERROR) << "needed_bytes should not be NULL.";
|
| - SetLastError(ERROR_INVALID_PARAMETER);
|
| - return FALSE;
|
| - }
|
| - if (level == 1) {
|
| - *needed_bytes = sizeof(PORT_INFO_1);
|
| - } else if (level == 2) {
|
| - *needed_bytes = sizeof(PORT_INFO_2);
|
| - } else {
|
| - LOG(ERROR) << "Level " << level << "is not supported.";
|
| - SetLastError(ERROR_INVALID_LEVEL);
|
| - return FALSE;
|
| - }
|
| - *needed_bytes += sizeof(kPortName);
|
| - if (ports_size < *needed_bytes) {
|
| - LOG(WARNING) << *needed_bytes << " bytes are required. Only "
|
| - << ports_size << " were allocated.";
|
| - SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
| - return FALSE;
|
| - }
|
| - if (ports == NULL) {
|
| - LOG(ERROR) << "ports should not be NULL.";
|
| - SetLastError(ERROR_INVALID_PARAMETER);
|
| - return FALSE;
|
| - }
|
| - if (returned == NULL) {
|
| - LOG(ERROR) << "returned should not be NULL.";
|
| - SetLastError(ERROR_INVALID_PARAMETER);
|
| - return FALSE;
|
| - }
|
| -
|
| - // Windows expects any strings refernced by PORT_INFO_X structures to
|
| - // appear at the END of the buffer referenced by ports. Placing
|
| - // strings immediately after the PORT_INFO_X structure will cause
|
| - // EnumPorts to fail until the spooler is restarted.
|
| - // This is NOT mentioned in the documentation.
|
| - wchar_t* string_target =
|
| - reinterpret_cast<wchar_t*>(ports + ports_size - sizeof(kPortName));
|
| - if (level == 1) {
|
| - PORT_INFO_1* port_info = reinterpret_cast<PORT_INFO_1*>(ports);
|
| - port_info->pName = string_target;
|
| - StringCbCopy(port_info->pName, sizeof(kPortName), kPortName);
|
| - } else {
|
| - PORT_INFO_2* port_info = reinterpret_cast<PORT_INFO_2*>(ports);
|
| - port_info->pPortName = string_target;
|
| - StringCbCopy(port_info->pPortName, sizeof(kPortName), kPortName);
|
| - port_info->pMonitorName = NULL;
|
| - port_info->pDescription = NULL;
|
| - port_info->fPortType = PORT_TYPE_WRITE;
|
| - port_info->Reserved = 0;
|
| - }
|
| - *returned = 1;
|
| - return TRUE;
|
| -}
|
| -
|
| -BOOL WINAPI Monitor2OpenPort(HANDLE, wchar_t*, HANDLE* handle) {
|
| - LOG(INFO) << "Monitor2OpenPort";
|
| -
|
| - PortData* port_data =
|
| - reinterpret_cast<PortData*>(GlobalAlloc(GMEM_FIXED|GMEM_ZEROINIT,
|
| - sizeof(PortData)));
|
| - if (port_data == NULL) {
|
| - LOG(ERROR) << "Unable to allocate memory for internal structures.";
|
| - SetLastError(E_OUTOFMEMORY);
|
| - return FALSE;
|
| - }
|
| - if (handle == NULL) {
|
| - LOG(ERROR) << "handle should not be NULL.";
|
| - SetLastError(ERROR_INVALID_PARAMETER);
|
| - return FALSE;
|
| - }
|
| - *handle = (HANDLE)port_data;
|
| - return TRUE;
|
| -}
|
| -
|
| -BOOL WINAPI Monitor2StartDocPort(HANDLE port_handle,
|
| - wchar_t* printer_name,
|
| - DWORD job_id,
|
| - DWORD,
|
| - BYTE*) {
|
| - LOG(INFO) << "Monitor2StartDocPort";
|
| - if (port_handle == NULL) {
|
| - LOG(ERROR) << "port_handle should not be NULL.";
|
| - SetLastError(ERROR_INVALID_PARAMETER);
|
| - return FALSE;
|
| - }
|
| - if (printer_name == NULL) {
|
| - LOG(ERROR) << "printer_name should not be NULL.";
|
| - SetLastError(ERROR_INVALID_PARAMETER);
|
| - return FALSE;
|
| - }
|
| - if (!ValidateCurrentUser()) {
|
| - // TODO(abodenha@chromium.org) Abort the print job.
|
| - return FALSE;
|
| - }
|
| - PortData* port_data = reinterpret_cast<PortData*>(port_handle);
|
| - port_data->job_id = job_id;
|
| - if (!OpenPrinter(printer_name, &(port_data->printer_handle), NULL)) {
|
| - LOG(WARNING) << "Unable to open printer " << printer_name << ".";
|
| - // We can continue without a handle to the printer.
|
| - // It just means we can't get the job title or tell the spooler that
|
| - // the print job is complete.
|
| - // This is the normal flow during a unit test.
|
| - port_data->printer_handle = NULL;
|
| - }
|
| - FilePath app_data;
|
| - port_data->file_path = new FilePath();
|
| - if (port_data->file_path == NULL) {
|
| - LOG(ERROR) << "Unable to allocate memory for internal structures.";
|
| - SetLastError(E_OUTOFMEMORY);
|
| - return FALSE;
|
| - }
|
| - bool result = PathService::Get(base::DIR_LOCAL_APP_DATA_LOW, &app_data);
|
| - file_util::CreateTemporaryFileInDir(app_data, port_data->file_path);
|
| - port_data->file = file_util::OpenFile(*(port_data->file_path), "wb+");
|
| - if (port_data->file == NULL) {
|
| - LOG(ERROR) << "Error opening file " << port_data->file_path << ".";
|
| - return FALSE;
|
| - }
|
| -
|
| - return TRUE;
|
| -}
|
| -
|
| -BOOL WINAPI Monitor2WritePort(HANDLE port_handle,
|
| - BYTE* buffer,
|
| - DWORD buffer_size,
|
| - DWORD* bytes_written) {
|
| - LOG(INFO) << "Monitor2WritePort";
|
| - PortData* port_data = reinterpret_cast<PortData*>(port_handle);
|
| - if (!ValidateCurrentUser()) {
|
| - // TODO(abodenha@chromium.org) Abort the print job.
|
| - return FALSE;
|
| - }
|
| - *bytes_written =
|
| - static_cast<DWORD>(fwrite(buffer, 1, buffer_size, port_data->file));
|
| - if (*bytes_written > 0) {
|
| - return TRUE;
|
| - } else {
|
| - return FALSE;
|
| - }
|
| -}
|
| -
|
| -BOOL WINAPI Monitor2ReadPort(HANDLE, BYTE*, DWORD, DWORD* read_bytes) {
|
| - LOG(INFO) << "Monitor2ReadPort";
|
| - LOG(ERROR) << "Read is not supported.";
|
| - *read_bytes = 0;
|
| - SetLastError(ERROR_NOT_SUPPORTED);
|
| - return FALSE;
|
| -}
|
| -
|
| -BOOL WINAPI Monitor2EndDocPort(HANDLE port_handle) {
|
| - LOG(INFO) << "Monitor2EndDocPort";
|
| - HANDLE process_handle = NULL;
|
| - if (!ValidateCurrentUser()) {
|
| - // TODO(abodenha@chromium.org) Abort the print job.
|
| - return FALSE;
|
| - }
|
| - PortData* port_data = reinterpret_cast<PortData*>(port_handle);
|
| - if (port_data == NULL) {
|
| - SetLastError(ERROR_INVALID_PARAMETER);
|
| - return FALSE;
|
| - }
|
| -
|
| - if (port_data->file != NULL) {
|
| - file_util::CloseFile(port_data->file);
|
| - port_data->file = NULL;
|
| - string16 job_title;
|
| - if (port_data->printer_handle != NULL) {
|
| - GetJobTitle(port_data->printer_handle,
|
| - port_data->job_id,
|
| - &job_title);
|
| - }
|
| - LaunchPrintDialog(port_data->file_path->value().c_str(),
|
| - job_title,
|
| - &process_handle);
|
| -
|
| - // Wait for the print dialog process to exit and then delete the file.
|
| - // TODO(abodenha@chromium.org) Consider launching a thread to handle the
|
| - // deletion.
|
| - if (process_handle != NULL) {
|
| - WaitForSingleObject(process_handle, INFINITE);
|
| - }
|
| - file_util::Delete(*(port_data->file_path), false);
|
| - }
|
| - if (port_data->printer_handle != NULL) {
|
| - // Tell the spooler that the job is complete.
|
| - SetJob(port_data->printer_handle,
|
| - port_data->job_id,
|
| - 0,
|
| - NULL,
|
| - JOB_CONTROL_SENT_TO_PRINTER);
|
| - }
|
| - CleanupPortData(port_data);
|
| - // Return success even if we can't display the dialog.
|
| - // TODO(abodenha@chromium.org) Come up with a better way of handling
|
| - // this situation.
|
| - return TRUE;
|
| -}
|
| -
|
| -BOOL WINAPI Monitor2ClosePort(HANDLE port_handle) {
|
| - LOG(INFO) << "Monitor2ClosePort";
|
| - if (port_handle == NULL) {
|
| - LOG(ERROR) << "port_handle should not be NULL.";
|
| - SetLastError(ERROR_INVALID_PARAMETER);
|
| - return FALSE;
|
| - }
|
| - PortData* port_data = reinterpret_cast<PortData*>(port_handle);
|
| - CleanupPortData(port_data);
|
| - GlobalFree(port_handle);
|
| - return TRUE;
|
| -}
|
| -
|
| -VOID WINAPI Monitor2Shutdown(HANDLE monitor_handle) {
|
| - LOG(INFO) << "Monitor2Shutdown";
|
| - if (monitor_handle != NULL) {
|
| - MonitorData* monitor_data =
|
| - reinterpret_cast<MonitorData*>(monitor_handle);
|
| - delete monitor_data->at_exit_manager;
|
| - GlobalFree(monitor_handle);
|
| - }
|
| -}
|
| -
|
| -BOOL WINAPI Monitor2XcvOpenPort(HANDLE,
|
| - const wchar_t*,
|
| - ACCESS_MASK granted_access,
|
| - HANDLE* handle) {
|
| - LOG(INFO) << "Monitor2XcvOpenPort";
|
| - if (handle == NULL) {
|
| - LOG(ERROR) << "handle should not be NULL.";
|
| - SetLastError(ERROR_INVALID_PARAMETER);
|
| - return FALSE;
|
| - }
|
| - XcvUiData* xcv_data;
|
| - xcv_data = reinterpret_cast<XcvUiData*>(GlobalAlloc(GMEM_FIXED|GMEM_ZEROINIT,
|
| - sizeof(XcvUiData)));
|
| - if (xcv_data == NULL) {
|
| - LOG(ERROR) << "Unable to allocate memory for internal structures.";
|
| - SetLastError(E_OUTOFMEMORY);
|
| - return FALSE;
|
| - }
|
| - xcv_data->granted_access = granted_access;
|
| - *handle = (HANDLE)xcv_data;
|
| - return TRUE;
|
| -}
|
| -
|
| -DWORD WINAPI Monitor2XcvDataPort(HANDLE xcv_handle,
|
| - const wchar_t* data_name,
|
| - BYTE*,
|
| - DWORD,
|
| - BYTE* output_data,
|
| - DWORD output_data_bytes,
|
| - DWORD* output_data_bytes_needed) {
|
| - LOG(INFO) << "Monitor2XcvDataPort";
|
| - XcvUiData* xcv_data = reinterpret_cast<XcvUiData*>(xcv_handle);
|
| - DWORD ret_val = ERROR_SUCCESS;
|
| - if ((xcv_data->granted_access & SERVER_ACCESS_ADMINISTER) == 0) {
|
| - return ERROR_ACCESS_DENIED;
|
| - }
|
| - if (output_data == NULL || output_data_bytes == 0) {
|
| - return ERROR_INVALID_PARAMETER;
|
| - }
|
| - // We don't handle AddPort or DeletePort since we don't support
|
| - // dynamic creation of ports.
|
| - if (lstrcmp(L"MonitorUI", data_name) == 0) {
|
| - if (output_data_bytes_needed != NULL) {
|
| - *output_data_bytes_needed = sizeof(kPortMonitorDllName);
|
| - }
|
| - if (output_data_bytes < sizeof(kPortMonitorDllName)) {
|
| - return ERROR_INSUFFICIENT_BUFFER;
|
| - } else {
|
| - ret_val = StringCbCopy(reinterpret_cast<wchar_t*>(output_data),
|
| - output_data_bytes,
|
| - kPortMonitorDllName);
|
| - }
|
| - } else {
|
| - return ERROR_INVALID_PARAMETER;
|
| - }
|
| - return ret_val;
|
| -}
|
| -
|
| -BOOL WINAPI Monitor2XcvClosePort(HANDLE handle) {
|
| - GlobalFree(handle);
|
| - return TRUE;
|
| -}
|
| -
|
| -BOOL WINAPI MonitorUiAddPortUi(const wchar_t*,
|
| - HWND hwnd,
|
| - const wchar_t* monitor_name,
|
| - wchar_t**) {
|
| - HandlePortUi(hwnd, monitor_name);
|
| - return TRUE;
|
| -}
|
| -
|
| -BOOL WINAPI MonitorUiConfigureOrDeletePortUI(const wchar_t*,
|
| - HWND hwnd,
|
| - const wchar_t* port_name) {
|
| - HandlePortUi(hwnd, port_name);
|
| - return TRUE;
|
| -}
|
| -
|
| -} // namespace cloud_print
|
| -
|
| -MONITOR2* WINAPI InitializePrintMonitor2(MONITORINIT*,
|
| - HANDLE* handle) {
|
| - LOG(INFO) << "InitializePrintMonitor2";
|
| - cloud_print::MonitorData* monitor_data =
|
| - reinterpret_cast<cloud_print::MonitorData*>
|
| - (GlobalAlloc(GMEM_FIXED|GMEM_ZEROINIT, sizeof(cloud_print::MonitorData)));
|
| - if (monitor_data == NULL) {
|
| - return NULL;
|
| - }
|
| - if (handle != NULL) {
|
| - *handle = (HANDLE)monitor_data;
|
| - #ifndef UNIT_TEST
|
| - // Unit tests set up their own AtExitManager
|
| - monitor_data->at_exit_manager = new base::AtExitManager();
|
| - #endif
|
| - } else {
|
| - SetLastError(ERROR_INVALID_PARAMETER);
|
| - return NULL;
|
| - }
|
| - return &cloud_print::g_monitor_2;
|
| -}
|
| -
|
| -MONITORUI* WINAPI InitializePrintMonitorUI(void) {
|
| - LOG(INFO) << "InitializePrintMonitorUI";
|
| - return &cloud_print::g_monitor_ui;
|
| -}
|
| -
|
|
|