3 #define COMPLAINED_READ (1 << 0)
\r
4 #define COMPLAINED_WRITE (1 << 1)
\r
5 #define COMPLAINED_ROTATE (1 << 2)
\r
7 static HANDLE create_logging_thread(TCHAR *service_name, TCHAR *path, unsigned long sharing, unsigned long disposition, unsigned long flags, HANDLE *read_handle_ptr, HANDLE *pipe_handle_ptr, HANDLE *write_handle_ptr, unsigned long rotate_bytes_low, unsigned long rotate_bytes_high, unsigned long *tid_ptr, unsigned long *rotate_online) {
\r
10 /* Pipe between application's stdout/stderr and our logging handle. */
\r
11 if (read_handle_ptr && ! *read_handle_ptr) {
\r
12 if (pipe_handle_ptr && ! *pipe_handle_ptr) {
\r
13 if (CreatePipe(read_handle_ptr, pipe_handle_ptr, 0, 0)) {
\r
14 SetHandleInformation(*pipe_handle_ptr, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
\r
17 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPIPE_FAILED, service_name, path, error_string(GetLastError()));
\r
23 logger_t *logger = (logger_t *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(logger_t));
\r
25 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("logger"), _T("create_logging_thread()"), 0);
\r
29 ULARGE_INTEGER size;
\r
30 size.LowPart = rotate_bytes_low;
\r
31 size.HighPart = rotate_bytes_high;
\r
33 logger->service_name = service_name;
\r
34 logger->path = path;
\r
35 logger->sharing = sharing;
\r
36 logger->disposition = disposition;
\r
37 logger->flags = flags;
\r
38 logger->read_handle = *read_handle_ptr;
\r
39 logger->write_handle = *write_handle_ptr;
\r
40 logger->size = (__int64) size.QuadPart;
\r
41 logger->tid_ptr = tid_ptr;
\r
42 logger->rotate_online = rotate_online;
\r
44 HANDLE thread_handle = CreateThread(NULL, 0, log_and_rotate, (void *) logger, 0, logger->tid_ptr);
\r
45 if (! thread_handle) {
\r
46 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
\r
47 HeapFree(GetProcessHeap(), 0, logger);
\r
50 return thread_handle;
\r
53 static inline unsigned long guess_charsize(void *address, unsigned long bufsize) {
\r
54 if (IsTextUnicode(address, bufsize, 0)) return (unsigned long) sizeof(wchar_t);
\r
55 else return (unsigned long) sizeof(char);
\r
58 static inline void write_bom(logger_t *logger, unsigned long *out) {
\r
59 wchar_t bom = L'\ufeff';
\r
60 if (! WriteFile(logger->write_handle, (void *) &bom, sizeof(bom), out, 0)) {
\r
61 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_SOMEBODY_SET_UP_US_THE_BOM, logger->service_name, logger->path, error_string(GetLastError()), 0);
\r
65 /* Get path, share mode, creation disposition and flags for a stream. */
\r
66 int get_createfile_parameters(HKEY key, TCHAR *prefix, TCHAR *path, unsigned long *sharing, unsigned long default_sharing, unsigned long *disposition, unsigned long default_disposition, unsigned long *flags, unsigned long default_flags) {
\r
67 TCHAR value[NSSM_STDIO_LENGTH];
\r
70 if (_sntprintf_s(value, _countof(value), _TRUNCATE, _T("%s"), prefix) < 0) {
\r
71 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, prefix, _T("get_createfile_parameters()"), 0);
\r
74 switch (expand_parameter(key, value, path, PATH_LENGTH, true, false)) {
\r
75 case 0: if (! path[0]) return 0; break; /* OK. */
\r
76 default: return 2; /* Error. */
\r
80 if (_sntprintf_s(value, _countof(value), _TRUNCATE, _T("%s%s"), prefix, NSSM_REG_STDIO_SHARING) < 0) {
\r
81 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REG_STDIO_SHARING, _T("get_createfile_parameters()"), 0);
\r
84 switch (get_number(key, value, sharing, false)) {
\r
85 case 0: *sharing = default_sharing; break; /* Missing. */
\r
86 case 1: break; /* Found. */
\r
87 case -2: return 4; break; /* Error. */
\r
90 /* CreationDisposition. */
\r
91 if (_sntprintf_s(value, _countof(value), _TRUNCATE, _T("%s%s"), prefix, NSSM_REG_STDIO_DISPOSITION) < 0) {
\r
92 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REG_STDIO_DISPOSITION, _T("get_createfile_parameters()"), 0);
\r
95 switch (get_number(key, value, disposition, false)) {
\r
96 case 0: *disposition = default_disposition; break; /* Missing. */
\r
97 case 1: break; /* Found. */
\r
98 case -2: return 6; break; /* Error. */
\r
102 if (_sntprintf_s(value, _countof(value), _TRUNCATE, _T("%s%s"), prefix, NSSM_REG_STDIO_FLAGS) < 0) {
\r
103 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REG_STDIO_FLAGS, _T("get_createfile_parameters()"), 0);
\r
106 switch (get_number(key, value, flags, false)) {
\r
107 case 0: *flags = default_flags; break; /* Missing. */
\r
108 case 1: break; /* Found. */
\r
109 case -2: return 8; break; /* Error. */
\r
115 int set_createfile_parameter(HKEY key, TCHAR *prefix, TCHAR *suffix, unsigned long number) {
\r
116 TCHAR value[NSSM_STDIO_LENGTH];
\r
118 if (_sntprintf_s(value, _countof(value), _TRUNCATE, _T("%s%s"), prefix, suffix) < 0) {
\r
119 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, suffix, _T("set_createfile_parameter()"), 0);
\r
123 return set_number(key, value, number);
\r
126 int delete_createfile_parameter(HKEY key, TCHAR *prefix, TCHAR *suffix) {
\r
127 TCHAR value[NSSM_STDIO_LENGTH];
\r
129 if (_sntprintf_s(value, _countof(value), _TRUNCATE, _T("%s%s"), prefix, suffix) < 0) {
\r
130 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, suffix, _T("delete_createfile_parameter()"), 0);
\r
134 if (RegDeleteValue(key, value)) return 0;
\r
138 HANDLE write_to_file(TCHAR *path, unsigned long sharing, SECURITY_ATTRIBUTES *attributes, unsigned long disposition, unsigned long flags) {
\r
139 HANDLE ret = CreateFile(path, FILE_WRITE_DATA, sharing, attributes, disposition, flags, 0);
\r
141 if (SetFilePointer(ret, 0, 0, FILE_END) != INVALID_SET_FILE_POINTER) SetEndOfFile(ret);
\r
145 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEFILE_FAILED, path, error_string(GetLastError()), 0);
\r
149 static void rotated_filename(TCHAR *path, TCHAR *rotated, unsigned long rotated_len, SYSTEMTIME *st) {
\r
156 TCHAR buffer[PATH_LENGTH];
\r
157 memmove(buffer, path, sizeof(buffer));
\r
158 TCHAR *ext = PathFindExtension(buffer);
\r
159 TCHAR extension[PATH_LENGTH];
\r
160 _sntprintf_s(extension, _countof(extension), _TRUNCATE, _T("-%04u%02u%02uT%02u%02u%02u.%03u%s"), st->wYear, st->wMonth, st->wDay, st->wHour, st->wMinute, st->wSecond, st->wMilliseconds, ext);
\r
162 _sntprintf_s(rotated, rotated_len, _TRUNCATE, _T("%s%s"), buffer, extension);
\r
165 void rotate_file(TCHAR *service_name, TCHAR *path, unsigned long seconds, unsigned long low, unsigned long high) {
\r
166 unsigned long error;
\r
170 GetSystemTime(&st);
\r
172 BY_HANDLE_FILE_INFORMATION info;
\r
174 /* Try to open the file to check if it exists and to get attributes. */
\r
175 HANDLE file = CreateFile(path, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
\r
177 /* Get file attributes. */
\r
178 if (! GetFileInformationByHandle(file, &info)) {
\r
179 /* Reuse current time for rotation timestamp. */
\r
180 seconds = low = high = 0;
\r
181 SystemTimeToFileTime(&st, &info.ftLastWriteTime);
\r
187 error = GetLastError();
\r
188 if (error == ERROR_FILE_NOT_FOUND) return;
\r
189 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_ROTATE_FILE_FAILED, service_name, path, _T("CreateFile()"), path, error_string(error), 0);
\r
190 /* Reuse current time for rotation timestamp. */
\r
191 seconds = low = high = 0;
\r
192 SystemTimeToFileTime(&st, &info.ftLastWriteTime);
\r
195 /* Check file age. */
\r
198 SystemTimeToFileTime(&st, &ft);
\r
201 s.LowPart = ft.dwLowDateTime;
\r
202 s.HighPart = ft.dwHighDateTime;
\r
203 s.QuadPart -= seconds * 10000000LL;
\r
204 ft.dwLowDateTime = s.LowPart;
\r
205 ft.dwHighDateTime = s.HighPart;
\r
206 if (CompareFileTime(&info.ftLastWriteTime, &ft) > 0) return;
\r
209 /* Check file size. */
\r
211 if (info.nFileSizeHigh < high) return;
\r
212 if (info.nFileSizeHigh == high && info.nFileSizeLow < low) return;
\r
215 /* Get new filename. */
\r
216 FileTimeToSystemTime(&info.ftLastWriteTime, &st);
\r
218 TCHAR rotated[PATH_LENGTH];
\r
219 rotated_filename(path, rotated, _countof(rotated), &st);
\r
222 if (MoveFile(path, rotated)) {
\r
223 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ROTATED, service_name, path, rotated, 0);
\r
226 error = GetLastError();
\r
228 if (error == ERROR_FILE_NOT_FOUND) return;
\r
229 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_ROTATE_FILE_FAILED, service_name, path, _T("MoveFile()"), rotated, error_string(error), 0);
\r
233 int get_output_handles(nssm_service_t *service, STARTUPINFO *si) {
\r
234 if (! si) return 1;
\r
236 /* Allocate a new console so we get a fresh stdin, stdout and stderr. */
\r
237 alloc_console(service);
\r
240 if (service->stdin_path[0]) {
\r
241 si->hStdInput = CreateFile(service->stdin_path, FILE_READ_DATA, service->stdin_sharing, 0, service->stdin_disposition, service->stdin_flags, 0);
\r
242 if (! si->hStdInput) {
\r
243 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEFILE_FAILED, service->stdin_path, error_string(GetLastError()), 0);
\r
249 if (service->stdout_path[0]) {
\r
250 if (service->rotate_files) rotate_file(service->name, service->stdout_path, service->rotate_seconds, service->rotate_bytes_low, service->rotate_bytes_high);
\r
251 HANDLE stdout_handle = write_to_file(service->stdout_path, service->stdout_sharing, 0, service->stdout_disposition, service->stdout_flags);
\r
252 if (! stdout_handle) return 4;
\r
254 if (service->rotate_files && service->rotate_stdout_online) {
\r
255 service->stdout_pipe = si->hStdOutput = 0;
\r
256 service->stdout_thread = create_logging_thread(service->name, service->stdout_path, service->stdout_sharing, service->stdout_disposition, service->stdout_flags, &service->stdout_pipe, &si->hStdOutput, &stdout_handle, service->rotate_bytes_low, service->rotate_bytes_high, &service->stdout_tid, &service->rotate_stdout_online);
\r
257 if (! service->stdout_thread) {
\r
258 CloseHandle(service->stdout_pipe);
\r
259 CloseHandle(si->hStdOutput);
\r
262 else service->stdout_thread = 0;
\r
264 if (! service->stdout_thread) {
\r
265 if (! DuplicateHandle(GetCurrentProcess(), stdout_handle, GetCurrentProcess(), &si->hStdOutput, 0, true, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
\r
266 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_DUPLICATEHANDLE_FAILED, NSSM_REG_STDOUT, _T("stdout"), error_string(GetLastError()), 0);
\r
269 service->rotate_stdout_online = NSSM_ROTATE_OFFLINE;
\r
274 if (service->stderr_path[0]) {
\r
275 /* Same as stdout? */
\r
276 if (str_equiv(service->stderr_path, service->stdout_path)) {
\r
277 service->stderr_sharing = service->stdout_sharing;
\r
278 service->stderr_disposition = service->stdout_disposition;
\r
279 service->stderr_flags = service->stdout_flags;
\r
280 service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
282 /* Two handles to the same file will create a race. */
\r
283 if (! DuplicateHandle(GetCurrentProcess(), si->hStdOutput, GetCurrentProcess(), &si->hStdError, 0, true, DUPLICATE_SAME_ACCESS)) {
\r
284 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_DUPLICATEHANDLE_FAILED, NSSM_REG_STDOUT, _T("stderr"), error_string(GetLastError()), 0);
\r
289 if (service->rotate_files) rotate_file(service->name, service->stderr_path, service->rotate_seconds, service->rotate_bytes_low, service->rotate_bytes_high);
\r
290 HANDLE stderr_handle = write_to_file(service->stderr_path, service->stderr_sharing, 0, service->stderr_disposition, service->stderr_flags);
\r
291 if (! stderr_handle) return 7;
\r
293 if (service->rotate_files && service->rotate_stderr_online) {
\r
294 service->stderr_pipe = si->hStdError = 0;
\r
295 service->stderr_thread = create_logging_thread(service->name, service->stderr_path, service->stderr_sharing, service->stderr_disposition, service->stderr_flags, &service->stderr_pipe, &si->hStdError, &stderr_handle, service->rotate_bytes_low, service->rotate_bytes_high, &service->stderr_tid, &service->rotate_stderr_online);
\r
296 if (! service->stderr_thread) {
\r
297 CloseHandle(service->stderr_pipe);
\r
298 CloseHandle(si->hStdError);
\r
301 else service->stderr_thread = 0;
\r
303 if (! service->stderr_thread) {
\r
304 if (! DuplicateHandle(GetCurrentProcess(), stderr_handle, GetCurrentProcess(), &si->hStdError, 0, true, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
\r
305 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_DUPLICATEHANDLE_FAILED, NSSM_REG_STDERR, _T("stderr"), error_string(GetLastError()), 0);
\r
308 service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
314 We need to set the startup_info flags to make the new handles
\r
315 inheritable by the new process.
\r
317 si->dwFlags |= STARTF_USESTDHANDLES;
\r
319 if (service->no_console) return 0;
\r
321 /* Redirect other handles. */
\r
322 if (! si->hStdInput) {
\r
323 if (! DuplicateHandle(GetCurrentProcess(), GetStdHandle(STD_INPUT_HANDLE), GetCurrentProcess(), &si->hStdInput, 0, true, DUPLICATE_SAME_ACCESS)) {
\r
324 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_DUPLICATEHANDLE_FAILED, _T("STD_INPUT_HANDLE"), _T("stdin"), error_string(GetLastError()), 0);
\r
328 if (! si->hStdOutput) {
\r
329 if (! DuplicateHandle(GetCurrentProcess(), GetStdHandle(STD_OUTPUT_HANDLE), GetCurrentProcess(), &si->hStdOutput, 0, true, DUPLICATE_SAME_ACCESS)) {
\r
330 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_DUPLICATEHANDLE_FAILED, _T("STD_OUTPUT_HANDLE"), _T("stdout"), error_string(GetLastError()), 0);
\r
334 if (! si->hStdError) {
\r
335 if (! DuplicateHandle(GetCurrentProcess(), GetStdHandle(STD_ERROR_HANDLE), GetCurrentProcess(), &si->hStdError, 0, true, DUPLICATE_SAME_ACCESS)) {
\r
336 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_DUPLICATEHANDLE_FAILED, _T("STD_ERROR_HANDLE"), _T("stderr"), error_string(GetLastError()), 0);
\r
344 void close_output_handles(STARTUPINFO *si) {
\r
345 if (si->hStdInput) CloseHandle(si->hStdInput);
\r
346 if (si->hStdOutput) CloseHandle(si->hStdOutput);
\r
347 if (si->hStdError) CloseHandle(si->hStdError);
\r
351 Try multiple times to read from a file.
\r
352 Returns: 0 on success.
\r
353 1 on non-fatal error.
\r
356 static int try_read(logger_t *logger, void *address, unsigned long bufsize, unsigned long *in, int *complained) {
\r
358 unsigned long error;
\r
359 for (int tries = 0; tries < 5; tries++) {
\r
360 if (ReadFile(logger->read_handle, address, bufsize, in, 0)) return 0;
\r
362 error = GetLastError();
\r
364 /* Other end closed the pipe. */
\r
365 case ERROR_BROKEN_PIPE:
\r
367 goto complain_read;
\r
369 /* Couldn't lock the buffer. */
\r
370 case ERROR_NOT_ENOUGH_QUOTA:
\r
371 Sleep(2000 + tries * 3000);
\r
375 /* Write was cancelled by the other end. */
\r
376 case ERROR_OPERATION_ABORTED:
\r
378 goto complain_read;
\r
386 /* Ignore the error if we've been requested to exit anyway. */
\r
387 if (*logger->rotate_online != NSSM_ROTATE_ONLINE) return ret;
\r
388 if (! (*complained & COMPLAINED_READ)) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_READFILE_FAILED, logger->service_name, logger->path, error_string(error), 0);
\r
389 *complained |= COMPLAINED_READ;
\r
394 Try multiple times to write to a file.
\r
395 Returns: 0 on success.
\r
396 1 on non-fatal error.
\r
399 static int try_write(logger_t *logger, void *address, unsigned long bufsize, unsigned long *out, int *complained) {
\r
401 unsigned long error;
\r
402 for (int tries = 0; tries < 5; tries++) {
\r
403 if (WriteFile(logger->write_handle, address, bufsize, out, 0)) return 0;
\r
405 error = GetLastError();
\r
406 if (error == ERROR_IO_PENDING) {
\r
407 /* Operation was successful pending flush to disk. */
\r
412 /* Other end closed the pipe. */
\r
413 case ERROR_BROKEN_PIPE:
\r
415 goto complain_write;
\r
417 /* Couldn't lock the buffer. */
\r
418 case ERROR_NOT_ENOUGH_QUOTA:
\r
419 /* Out of disk space. */
\r
420 case ERROR_DISK_FULL:
\r
421 Sleep(2000 + tries * 3000);
\r
426 /* We'll lose this line but try to read and write subsequent ones. */
\r
432 if (! (*complained & COMPLAINED_WRITE)) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_WRITEFILE_FAILED, logger->service_name, logger->path, error_string(error), 0);
\r
433 *complained |= COMPLAINED_WRITE;
\r
437 /* Wrapper to be called in a new thread for logging. */
\r
438 unsigned long WINAPI log_and_rotate(void *arg) {
\r
439 logger_t *logger = (logger_t *) arg;
\r
440 if (! logger) return 1;
\r
443 BY_HANDLE_FILE_INFORMATION info;
\r
445 /* Find initial file size. */
\r
446 if (! GetFileInformationByHandle(logger->write_handle, &info)) logger->size = 0LL;
\r
449 l.HighPart = info.nFileSizeHigh;
\r
450 l.LowPart = info.nFileSizeLow;
\r
456 unsigned long in, out;
\r
457 unsigned long charsize = 0;
\r
458 unsigned long error;
\r
460 int complained = 0;
\r
463 /* Read data from the pipe. */
\r
465 ret = try_read(logger, address, sizeof(buffer), &in, &complained);
\r
467 CloseHandle(logger->read_handle);
\r
468 CloseHandle(logger->write_handle);
\r
469 HeapFree(GetProcessHeap(), 0, logger);
\r
472 else if (ret) continue;
\r
474 if (*logger->rotate_online == NSSM_ROTATE_ONLINE_ASAP || (logger->size && size + (__int64) in >= logger->size)) {
\r
475 /* Look for newline. */
\r
477 for (i = 0; i < in; i++) {
\r
478 if (buffer[i] == '\n') {
\r
479 if (! charsize) charsize = guess_charsize(address, in);
\r
482 /* Write up to the newline. */
\r
483 ret = try_write(logger, address, i, &out, &complained);
\r
485 HeapFree(GetProcessHeap(), 0, logger);
\r
486 CloseHandle(logger->read_handle);
\r
487 CloseHandle(logger->write_handle);
\r
490 size += (__int64) out;
\r
493 *logger->rotate_online = NSSM_ROTATE_ONLINE;
\r
494 TCHAR rotated[PATH_LENGTH];
\r
495 rotated_filename(logger->path, rotated, _countof(rotated), 0);
\r
498 Ideally we'd try the rename first then close the handle but
\r
499 MoveFile() will fail if the handle is still open so we must
\r
500 risk losing everything.
\r
502 CloseHandle(logger->write_handle);
\r
503 if (MoveFile(logger->path, rotated)) {
\r
504 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ROTATED, logger->service_name, logger->path, rotated, 0);
\r
508 error = GetLastError();
\r
509 if (error != ERROR_FILE_NOT_FOUND) {
\r
510 if (! (complained & COMPLAINED_ROTATE)) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_ROTATE_FILE_FAILED, logger->service_name, logger->path, _T("MoveFile()"), rotated, error_string(error), 0);
\r
511 complained |= COMPLAINED_ROTATE;
\r
512 /* We can at least try to re-open the existing file. */
\r
513 logger->disposition = OPEN_ALWAYS;
\r
518 logger->write_handle = write_to_file(logger->path, logger->sharing, 0, logger->disposition, logger->flags);
\r
519 if (! logger->write_handle) {
\r
520 error = GetLastError();
\r
521 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEFILE_FAILED, logger->path, error_string(error), 0);
\r
522 /* Oh dear. Now we can't log anything further. */
\r
523 HeapFree(GetProcessHeap(), 0, logger);
\r
524 CloseHandle(logger->read_handle);
\r
525 CloseHandle(logger->write_handle);
\r
529 /* Resume writing after the newline. */
\r
530 address = (void *) ((char *) address + i);
\r
537 /* Write a BOM to the new file. */
\r
538 if (! charsize) charsize = guess_charsize(address, in);
\r
539 if (charsize == sizeof(wchar_t)) write_bom(logger, &out);
\r
540 size += (__int64) out;
\r
543 /* Write the data, if any. */
\r
544 if (! in) continue;
\r
546 ret = try_write(logger, address, in, &out, &complained);
\r
547 size += (__int64) out;
\r
549 HeapFree(GetProcessHeap(), 0, logger);
\r
550 CloseHandle(logger->read_handle);
\r
551 CloseHandle(logger->write_handle);
\r
556 HeapFree(GetProcessHeap(), 0, logger);
\r
557 CloseHandle(logger->read_handle);
\r
558 CloseHandle(logger->write_handle);
\r