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 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[PATH_LENGTH];
\r
169 memmove(buffer, path, sizeof(buffer));
\r
170 TCHAR *ext = PathFindExtension(buffer);
\r
171 TCHAR extension[PATH_LENGTH];
\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[PATH_LENGTH];
\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 if (service->rotate_files && service->rotate_stdout_online) {
\r
280 service->stdout_pipe = si->hStdOutput = 0;
\r
281 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
282 if (! service->stdout_thread) {
\r
283 CloseHandle(service->stdout_pipe);
\r
284 CloseHandle(si->hStdOutput);
\r
287 else service->stdout_thread = 0;
\r
289 if (! service->stdout_thread) {
\r
290 if (! DuplicateHandle(GetCurrentProcess(), stdout_handle, GetCurrentProcess(), &si->hStdOutput, 0, true, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
\r
291 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_DUPLICATEHANDLE_FAILED, NSSM_REG_STDOUT, error_string(GetLastError()), 0);
\r
294 service->rotate_stdout_online = NSSM_ROTATE_OFFLINE;
\r
301 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
302 service->stderr_sharing = service->stderr_disposition = service->stderr_flags = 0;
\r
303 ZeroMemory(service->stderr_path, _countof(service->stderr_path) * sizeof(TCHAR));
\r
306 if (service->stderr_path[0]) {
\r
307 /* Same as stdout? */
\r
308 if (str_equiv(service->stderr_path, service->stdout_path)) {
\r
309 service->stderr_sharing = service->stdout_sharing;
\r
310 service->stderr_disposition = service->stdout_disposition;
\r
311 service->stderr_flags = service->stdout_flags;
\r
312 service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
315 /* Two handles to the same file will create a race. */
\r
316 if (! DuplicateHandle(GetCurrentProcess(), si->hStdOutput, GetCurrentProcess(), &si->hStdError, 0, true, DUPLICATE_SAME_ACCESS)) {
\r
317 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_DUPLICATEHANDLE_FAILED, NSSM_REG_STDOUT, error_string(GetLastError()), 0);
\r
323 if (service->rotate_files) rotate_file(service->name, service->stderr_path, service->rotate_seconds, service->rotate_bytes_low, service->rotate_bytes_high);
\r
324 HANDLE stderr_handle = append_to_file(service->stderr_path, service->stderr_sharing, 0, service->stderr_disposition, service->stderr_flags);
\r
325 if (! stderr_handle) return 7;
\r
327 if (service->rotate_files && service->rotate_stderr_online) {
\r
328 service->stderr_pipe = si->hStdError = 0;
\r
329 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
330 if (! service->stderr_thread) {
\r
331 CloseHandle(service->stderr_pipe);
\r
332 CloseHandle(si->hStdError);
\r
335 else service->stderr_thread = 0;
\r
337 if (! service->stderr_thread) {
\r
338 if (! DuplicateHandle(GetCurrentProcess(), stderr_handle, GetCurrentProcess(), &si->hStdError, 0, true, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
\r
339 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_DUPLICATEHANDLE_FAILED, NSSM_REG_STDERR, error_string(GetLastError()), 0);
\r
342 service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
349 if (! set_flags) return 0;
\r
352 We need to set the startup_info flags to make the new handles
\r
353 inheritable by the new process.
\r
355 if (si) si->dwFlags |= STARTF_USESTDHANDLES;
\r
360 void close_output_handles(STARTUPINFO *si) {
\r
361 if (si->hStdInput) CloseHandle(si->hStdInput);
\r
362 if (si->hStdOutput) CloseHandle(si->hStdOutput);
\r
363 if (si->hStdError) CloseHandle(si->hStdError);
\r
367 Try multiple times to read from a file.
\r
368 Returns: 0 on success.
\r
369 1 on non-fatal error.
\r
372 static int try_read(logger_t *logger, void *address, unsigned long bufsize, unsigned long *in, int *complained) {
\r
374 unsigned long error;
\r
375 for (int tries = 0; tries < 5; tries++) {
\r
376 if (ReadFile(logger->read_handle, address, bufsize, in, 0)) return 0;
\r
378 error = GetLastError();
\r
380 /* Other end closed the pipe. */
\r
381 case ERROR_BROKEN_PIPE:
\r
383 goto complain_read;
\r
385 /* Couldn't lock the buffer. */
\r
386 case ERROR_NOT_ENOUGH_QUOTA:
\r
387 Sleep(2000 + tries * 3000);
\r
391 /* Write was cancelled by the other end. */
\r
392 case ERROR_OPERATION_ABORTED:
\r
394 goto complain_read;
\r
402 /* Ignore the error if we've been requested to exit anyway. */
\r
403 if (*logger->rotate_online != NSSM_ROTATE_ONLINE) return ret;
\r
404 if (! (*complained & COMPLAINED_READ)) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_READFILE_FAILED, logger->service_name, logger->path, error_string(error), 0);
\r
405 *complained |= COMPLAINED_READ;
\r
410 Try multiple times to write to a file.
\r
411 Returns: 0 on success.
\r
412 1 on non-fatal error.
\r
415 static int try_write(logger_t *logger, void *address, unsigned long bufsize, unsigned long *out, int *complained) {
\r
417 unsigned long error;
\r
418 for (int tries = 0; tries < 5; tries++) {
\r
419 if (WriteFile(logger->write_handle, address, bufsize, out, 0)) return 0;
\r
421 error = GetLastError();
\r
422 if (error == ERROR_IO_PENDING) {
\r
423 /* Operation was successful pending flush to disk. */
\r
428 /* Other end closed the pipe. */
\r
429 case ERROR_BROKEN_PIPE:
\r
431 goto complain_write;
\r
433 /* Couldn't lock the buffer. */
\r
434 case ERROR_NOT_ENOUGH_QUOTA:
\r
435 /* Out of disk space. */
\r
436 case ERROR_DISK_FULL:
\r
437 Sleep(2000 + tries * 3000);
\r
442 /* We'll lose this line but try to read and write subsequent ones. */
\r
448 if (! (*complained & COMPLAINED_WRITE)) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_WRITEFILE_FAILED, logger->service_name, logger->path, error_string(error), 0);
\r
449 *complained |= COMPLAINED_WRITE;
\r
453 /* Wrapper to be called in a new thread for logging. */
\r
454 unsigned long WINAPI log_and_rotate(void *arg) {
\r
455 logger_t *logger = (logger_t *) arg;
\r
456 if (! logger) return 1;
\r
459 BY_HANDLE_FILE_INFORMATION info;
\r
461 /* Find initial file size. */
\r
462 if (! GetFileInformationByHandle(logger->write_handle, &info)) logger->size = 0LL;
\r
465 l.HighPart = info.nFileSizeHigh;
\r
466 l.LowPart = info.nFileSizeLow;
\r
472 unsigned long in, out;
\r
473 unsigned long charsize = 0;
\r
474 unsigned long error;
\r
476 int complained = 0;
\r
479 /* Read data from the pipe. */
\r
481 ret = try_read(logger, address, sizeof(buffer), &in, &complained);
\r
483 CloseHandle(logger->read_handle);
\r
484 CloseHandle(logger->write_handle);
\r
485 HeapFree(GetProcessHeap(), 0, logger);
\r
488 else if (ret) continue;
\r
490 if (*logger->rotate_online == NSSM_ROTATE_ONLINE_ASAP || (logger->size && size + (__int64) in >= logger->size)) {
\r
491 /* Look for newline. */
\r
493 for (i = 0; i < in; i++) {
\r
494 if (buffer[i] == '\n') {
\r
495 if (! charsize) charsize = guess_charsize(address, in);
\r
498 /* Write up to the newline. */
\r
499 ret = try_write(logger, address, i, &out, &complained);
\r
501 HeapFree(GetProcessHeap(), 0, logger);
\r
502 CloseHandle(logger->read_handle);
\r
503 CloseHandle(logger->write_handle);
\r
506 size += (__int64) out;
\r
509 *logger->rotate_online = NSSM_ROTATE_ONLINE;
\r
510 TCHAR rotated[PATH_LENGTH];
\r
511 rotated_filename(logger->path, rotated, _countof(rotated), 0);
\r
514 Ideally we'd try the rename first then close the handle but
\r
515 MoveFile() will fail if the handle is still open so we must
\r
516 risk losing everything.
\r
518 CloseHandle(logger->write_handle);
\r
519 if (MoveFile(logger->path, rotated)) {
\r
520 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ROTATED, logger->service_name, logger->path, rotated, 0);
\r
524 error = GetLastError();
\r
525 if (error != ERROR_FILE_NOT_FOUND) {
\r
526 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
527 complained |= COMPLAINED_ROTATE;
\r
528 /* We can at least try to re-open the existing file. */
\r
529 logger->disposition = OPEN_ALWAYS;
\r
534 logger->write_handle = append_to_file(logger->path, logger->sharing, 0, logger->disposition, logger->flags);
\r
535 if (! logger->write_handle) {
\r
536 error = GetLastError();
\r
537 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEFILE_FAILED, logger->path, error_string(error), 0);
\r
538 /* Oh dear. Now we can't log anything further. */
\r
539 HeapFree(GetProcessHeap(), 0, logger);
\r
540 CloseHandle(logger->read_handle);
\r
541 CloseHandle(logger->write_handle);
\r
545 /* Resume writing after the newline. */
\r
546 address = (void *) ((char *) address + i);
\r
553 /* Write a BOM to the new file. */
\r
554 if (! charsize) charsize = guess_charsize(address, in);
\r
555 if (charsize == sizeof(wchar_t)) write_bom(logger, &out);
\r
556 size += (__int64) out;
\r
559 /* Write the data, if any. */
\r
560 if (! in) continue;
\r
562 ret = try_write(logger, address, in, &out, &complained);
\r
563 size += (__int64) out;
\r
565 HeapFree(GetProcessHeap(), 0, logger);
\r
566 CloseHandle(logger->read_handle);
\r
567 CloseHandle(logger->write_handle);
\r
572 HeapFree(GetProcessHeap(), 0, logger);
\r
573 CloseHandle(logger->read_handle);
\r
574 CloseHandle(logger->write_handle);
\r