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, MAX_PATH, 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 append_to_file(TCHAR *path, unsigned long sharing, SECURITY_ATTRIBUTES *attributes, unsigned long disposition, unsigned long flags) {
\r
141 /* Try to append to the file first. */
\r
142 ret = CreateFile(path, FILE_APPEND_DATA, sharing, attributes, disposition, flags, 0);
\r
148 unsigned long error = GetLastError();
\r
149 if (error != ERROR_FILE_NOT_FOUND) {
\r
150 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEFILE_FAILED, path, error_string(error), 0);
\r
154 /* It didn't exist. Create it. */
\r
155 ret = CreateFile(path, FILE_WRITE_DATA, sharing, attributes, disposition, flags, 0);
\r
156 if (! ret) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEFILE_FAILED, path, error_string(GetLastError()), 0);
\r
161 static void rotated_filename(TCHAR *path, TCHAR *rotated, unsigned long rotated_len, SYSTEMTIME *st) {
\r
168 TCHAR buffer[MAX_PATH];
\r
169 memmove(buffer, path, sizeof(buffer));
\r
170 TCHAR *ext = PathFindExtension(buffer);
\r
171 TCHAR extension[MAX_PATH];
\r
172 _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
174 _sntprintf_s(rotated, rotated_len, _TRUNCATE, _T("%s%s"), buffer, extension);
\r
177 void rotate_file(TCHAR *service_name, TCHAR *path, unsigned long seconds, unsigned long low, unsigned long high) {
\r
178 unsigned long error;
\r
182 GetSystemTime(&st);
\r
184 BY_HANDLE_FILE_INFORMATION info;
\r
186 /* Try to open the file to check if it exists and to get attributes. */
\r
187 HANDLE file = CreateFile(path, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
\r
189 /* Get file attributes. */
\r
190 if (! GetFileInformationByHandle(file, &info)) {
\r
191 /* Reuse current time for rotation timestamp. */
\r
192 seconds = low = high = 0;
\r
193 SystemTimeToFileTime(&st, &info.ftLastWriteTime);
\r
199 error = GetLastError();
\r
200 if (error == ERROR_FILE_NOT_FOUND) return;
\r
201 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_ROTATE_FILE_FAILED, service_name, path, _T("CreateFile()"), path, error_string(error), 0);
\r
202 /* Reuse current time for rotation timestamp. */
\r
203 seconds = low = high = 0;
\r
204 SystemTimeToFileTime(&st, &info.ftLastWriteTime);
\r
207 /* Check file age. */
\r
210 SystemTimeToFileTime(&st, &ft);
\r
213 s.LowPart = ft.dwLowDateTime;
\r
214 s.HighPart = ft.dwHighDateTime;
\r
215 s.QuadPart -= seconds * 10000000LL;
\r
216 ft.dwLowDateTime = s.LowPart;
\r
217 ft.dwHighDateTime = s.HighPart;
\r
218 if (CompareFileTime(&info.ftLastWriteTime, &ft) > 0) return;
\r
221 /* Check file size. */
\r
223 if (info.nFileSizeHigh < high) return;
\r
224 if (info.nFileSizeHigh == high && info.nFileSizeLow < low) return;
\r
227 /* Get new filename. */
\r
228 FileTimeToSystemTime(&info.ftLastWriteTime, &st);
\r
230 TCHAR rotated[MAX_PATH];
\r
231 rotated_filename(path, rotated, _countof(rotated), &st);
\r
234 if (MoveFile(path, rotated)) {
\r
235 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ROTATED, service_name, path, rotated, 0);
\r
238 error = GetLastError();
\r
240 if (error == ERROR_FILE_NOT_FOUND) return;
\r
241 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_ROTATE_FILE_FAILED, service_name, path, _T("MoveFile()"), rotated, error_string(error), 0);
\r
245 int get_output_handles(nssm_service_t *service, HKEY key, STARTUPINFO *si) {
\r
246 bool set_flags = false;
\r
248 /* Standard security attributes allowing inheritance. */
\r
249 SECURITY_ATTRIBUTES attributes;
\r
250 ZeroMemory(&attributes, sizeof(attributes));
\r
251 attributes.bInheritHandle = true;
\r
254 if (get_createfile_parameters(key, NSSM_REG_STDIN, service->stdin_path, &service->stdin_sharing, NSSM_STDIN_SHARING, &service->stdin_disposition, NSSM_STDIN_DISPOSITION, &service->stdin_flags, NSSM_STDIN_FLAGS)) {
\r
255 service->stdin_sharing = service->stdin_disposition = service->stdin_flags = 0;
\r
256 ZeroMemory(service->stdin_path, _countof(service->stdin_path) * sizeof(TCHAR));
\r
259 if (si && service->stdin_path[0]) {
\r
260 si->hStdInput = CreateFile(service->stdin_path, FILE_READ_DATA, service->stdin_sharing, &attributes, service->stdin_disposition, service->stdin_flags, 0);
\r
261 if (! si->hStdInput) {
\r
262 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEFILE_FAILED, service->stdin_path, error_string(GetLastError()), 0);
\r
269 if (get_createfile_parameters(key, NSSM_REG_STDOUT, service->stdout_path, &service->stdout_sharing, NSSM_STDOUT_SHARING, &service->stdout_disposition, NSSM_STDOUT_DISPOSITION, &service->stdout_flags, NSSM_STDOUT_FLAGS)) {
\r
270 service->stdout_sharing = service->stdout_disposition = service->stdout_flags = 0;
\r
271 ZeroMemory(service->stdout_path, _countof(service->stdout_path) * sizeof(TCHAR));
\r
274 if (si && service->stdout_path[0]) {
\r
275 if (service->rotate_files) rotate_file(service->name, service->stdout_path, service->rotate_seconds, service->rotate_bytes_low, service->rotate_bytes_high);
\r
276 HANDLE stdout_handle = append_to_file(service->stdout_path, service->stdout_sharing, 0, service->stdout_disposition, service->stdout_flags);
\r
277 if (! stdout_handle) return 4;
\r
279 /* Try online rotation only if a size threshold is set. */
\r
280 if (service->rotate_files && service->rotate_stdout_online) {
\r
281 service->stdout_pipe = si->hStdOutput = 0;
\r
282 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
284 if (! service->stdout_thread) {
\r
285 if (! DuplicateHandle(GetCurrentProcess(), stdout_handle, GetCurrentProcess(), &si->hStdOutput, 0, true, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
\r
286 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_DUPLICATEHANDLE_FAILED, NSSM_REG_STDOUT, error_string(GetLastError()), 0);
\r
289 service->rotate_stdout_online = NSSM_ROTATE_OFFLINE;
\r
297 if (get_createfile_parameters(key, NSSM_REG_STDERR, service->stderr_path, &service->stderr_sharing, NSSM_STDERR_SHARING, &service->stderr_disposition, NSSM_STDERR_DISPOSITION, &service->stderr_flags, NSSM_STDERR_FLAGS)) {
\r
298 service->stderr_sharing = service->stderr_disposition = service->stderr_flags = 0;
\r
299 ZeroMemory(service->stderr_path, _countof(service->stderr_path) * sizeof(TCHAR));
\r
302 if (service->stderr_path[0]) {
\r
303 /* Same as stdout? */
\r
304 if (str_equiv(service->stderr_path, service->stdout_path)) {
\r
305 service->stderr_sharing = service->stdout_sharing;
\r
306 service->stderr_disposition = service->stdout_disposition;
\r
307 service->stderr_flags = service->stdout_flags;
\r
308 service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
311 /* Two handles to the same file will create a race. */
\r
312 if (! DuplicateHandle(GetCurrentProcess(), si->hStdOutput, GetCurrentProcess(), &si->hStdError, 0, true, DUPLICATE_SAME_ACCESS)) {
\r
313 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_DUPLICATEHANDLE_FAILED, NSSM_REG_STDOUT, error_string(GetLastError()), 0);
\r
319 if (service->rotate_files) rotate_file(service->name, service->stderr_path, service->rotate_seconds, service->rotate_bytes_low, service->rotate_bytes_high);
\r
320 HANDLE stderr_handle = append_to_file(service->stderr_path, service->stderr_sharing, 0, service->stderr_disposition, service->stderr_flags);
\r
321 if (! stderr_handle) return 7;
\r
323 if (service->rotate_files && service->rotate_stderr_online) {
\r
324 service->stderr_pipe = si->hStdError = 0;
\r
325 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
327 if (! service->stderr_thread) {
\r
328 if (! DuplicateHandle(GetCurrentProcess(), stderr_handle, GetCurrentProcess(), &si->hStdError, 0, true, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
\r
329 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_DUPLICATEHANDLE_FAILED, NSSM_REG_STDERR, error_string(GetLastError()), 0);
\r
332 service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
340 if (! set_flags) return 0;
\r
343 We need to set the startup_info flags to make the new handles
\r
344 inheritable by the new process.
\r
346 if (si) si->dwFlags |= STARTF_USESTDHANDLES;
\r
351 void close_output_handles(STARTUPINFO *si) {
\r
352 if (si->hStdInput) CloseHandle(si->hStdInput);
\r
353 if (si->hStdOutput) CloseHandle(si->hStdOutput);
\r
354 if (si->hStdError) CloseHandle(si->hStdError);
\r
358 Try multiple times to read from a file.
\r
359 Returns: 0 on success.
\r
360 1 on non-fatal error.
\r
363 static int try_read(logger_t *logger, void *address, unsigned long bufsize, unsigned long *in, int *complained) {
\r
365 unsigned long error;
\r
366 for (int tries = 0; tries < 5; tries++) {
\r
367 if (ReadFile(logger->read_handle, address, bufsize, in, 0)) return 0;
\r
369 error = GetLastError();
\r
371 /* Other end closed the pipe. */
\r
372 case ERROR_BROKEN_PIPE:
\r
374 goto complain_read;
\r
376 /* Couldn't lock the buffer. */
\r
377 case ERROR_NOT_ENOUGH_QUOTA:
\r
378 Sleep(2000 + tries * 3000);
\r
382 /* Write was cancelled by the other end. */
\r
383 case ERROR_OPERATION_ABORTED:
\r
385 goto complain_read;
\r
393 /* Ignore the error if we've been requested to exit for rotation anyway. */
\r
394 if (*logger->rotate_online == NSSM_ROTATE_ONLINE_ASAP) return ret;
\r
395 if (! (*complained & COMPLAINED_READ)) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_READFILE_FAILED, logger->service_name, logger->path, error_string(error), 0);
\r
396 *complained |= COMPLAINED_READ;
\r
401 Try multiple times to write to a file.
\r
402 Returns: 0 on success.
\r
403 1 on non-fatal error.
\r
406 static int try_write(logger_t *logger, void *address, unsigned long bufsize, unsigned long *out, int *complained) {
\r
408 unsigned long error;
\r
409 for (int tries = 0; tries < 5; tries++) {
\r
410 if (WriteFile(logger->write_handle, address, bufsize, out, 0)) return 0;
\r
412 error = GetLastError();
\r
413 if (error == ERROR_IO_PENDING) {
\r
414 /* Operation was successful pending flush to disk. */
\r
419 /* Other end closed the pipe. */
\r
420 case ERROR_BROKEN_PIPE:
\r
422 goto complain_write;
\r
424 /* Couldn't lock the buffer. */
\r
425 case ERROR_NOT_ENOUGH_QUOTA:
\r
426 /* Out of disk space. */
\r
427 case ERROR_DISK_FULL:
\r
428 Sleep(2000 + tries * 3000);
\r
433 /* We'll lose this line but try to read and write subsequent ones. */
\r
439 if (! (*complained & COMPLAINED_WRITE)) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_WRITEFILE_FAILED, logger->service_name, logger->path, error_string(error), 0);
\r
440 *complained |= COMPLAINED_WRITE;
\r
444 /* Wrapper to be called in a new thread for logging. */
\r
445 unsigned long WINAPI log_and_rotate(void *arg) {
\r
446 logger_t *logger = (logger_t *) arg;
\r
447 if (! logger) return 1;
\r
450 BY_HANDLE_FILE_INFORMATION info;
\r
452 /* Find initial file size. */
\r
453 if (! GetFileInformationByHandle(logger->write_handle, &info)) logger->size = 0LL;
\r
456 l.HighPart = info.nFileSizeHigh;
\r
457 l.LowPart = info.nFileSizeLow;
\r
463 unsigned long in, out;
\r
464 unsigned long charsize = 0;
\r
465 unsigned long error;
\r
467 int complained = 0;
\r
470 /* Read data from the pipe. */
\r
472 ret = try_read(logger, address, sizeof(buffer), &in, &complained);
\r
474 CloseHandle(logger->read_handle);
\r
475 CloseHandle(logger->write_handle);
\r
476 HeapFree(GetProcessHeap(), 0, logger);
\r
479 else if (ret) continue;
\r
481 if (*logger->rotate_online == NSSM_ROTATE_ONLINE_ASAP || (logger->size && size + (__int64) in >= logger->size)) {
\r
482 /* Look for newline. */
\r
484 for (i = 0; i < in; i++) {
\r
485 if (buffer[i] == '\n') {
\r
486 if (! charsize) charsize = guess_charsize(address, in);
\r
489 /* Write up to the newline. */
\r
490 ret = try_write(logger, address, i, &out, &complained);
\r
492 HeapFree(GetProcessHeap(), 0, logger);
\r
493 CloseHandle(logger->read_handle);
\r
494 CloseHandle(logger->write_handle);
\r
497 size += (__int64) out;
\r
500 *logger->rotate_online = NSSM_ROTATE_ONLINE;
\r
501 TCHAR rotated[MAX_PATH];
\r
502 rotated_filename(logger->path, rotated, _countof(rotated), 0);
\r
505 Ideally we'd try the rename first then close the handle but
\r
506 MoveFile() will fail if the handle is still open so we must
\r
507 risk losing everything.
\r
509 CloseHandle(logger->write_handle);
\r
510 if (MoveFile(logger->path, rotated)) {
\r
511 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ROTATED, logger->service_name, logger->path, rotated, 0);
\r
515 error = GetLastError();
\r
516 if (error != ERROR_FILE_NOT_FOUND) {
\r
517 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
518 complained |= COMPLAINED_ROTATE;
\r
519 /* We can at least try to re-open the existing file. */
\r
520 logger->disposition = OPEN_ALWAYS;
\r
525 logger->write_handle = append_to_file(logger->path, logger->sharing, 0, logger->disposition, logger->flags);
\r
526 if (! logger->write_handle) {
\r
527 error = GetLastError();
\r
528 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEFILE_FAILED, logger->path, error_string(error), 0);
\r
529 /* Oh dear. Now we can't log anything further. */
\r
530 HeapFree(GetProcessHeap(), 0, logger);
\r
531 CloseHandle(logger->read_handle);
\r
532 CloseHandle(logger->write_handle);
\r
536 /* Resume writing after the newline. */
\r
537 address = (void *) ((char *) address + i);
\r
544 /* Write a BOM to the new file. */
\r
545 if (! charsize) charsize = guess_charsize(address, in);
\r
546 if (charsize == sizeof(wchar_t)) write_bom(logger, &out);
\r
547 size += (__int64) out;
\r
550 /* Write the data, if any. */
\r
551 if (! in) continue;
\r
553 ret = try_write(logger, address, in, &out, &complained);
\r
554 size += (__int64) out;
\r
556 HeapFree(GetProcessHeap(), 0, logger);
\r
557 CloseHandle(logger->read_handle);
\r
558 CloseHandle(logger->write_handle);
\r
563 HeapFree(GetProcessHeap(), 0, logger);
\r
564 CloseHandle(logger->read_handle);
\r
565 CloseHandle(logger->write_handle);
\r