How can multiple processes append to a Windows Terminal Server Client file without overwriting?

38 Views Asked by At

I want to run a program in multiple processes and have that program append records to the same file. The append works when running on a non-Windows Server to a local file. It is not working when the program is run on a Windows Server and writing to a file on a shared client drive using the Terminal Server Client syntax. For example, \tsclient\c\temp\myfile-append.txt. If I call CreateFile() with dwDesiredAccess set to GENERIC_WRITE, all data from process 1 gets overwritten, instead of appended to, by process 2. If I replace GENERIC_WRITE with FILE_APPEND_DATA, the file is created but gets ERROR_INVALID_PARAMETER when calling WriteFile().

When I run on a Windows Server I see a WriteFile() error with FILE_APPEND_DATA:

process 1:
Running on a Windows Server and Terminal Services are enabled
CreateFile: \\tsclient\c\temp\myfile-write.txt
WriteFile: TEST-6620
WriteFile: TEST-6620
WriteFile: TEST-6620
WriteFile: TEST-6620
**CreateFile: \\tsclient\c\temp\myfile-append.txt
WriteFile: TEST-6620
WriteFile returned ERROR_INVALID_PARAMETER errno:87**
Press return to exit...

process 2:
Running on a Windows Server and Terminal Services are enabled
CreateFile: \\tsclient\c\temp\myfile-write.txt
WriteFile: TEST-9020
WriteFile: TEST-9020
WriteFile: TEST-9020
WriteFile: TEST-9020
**CreateFile: \\tsclient\c\temp\myfile-append.txt
WriteFile: TEST-9020
WriteFile returned ERROR_INVALID_PARAMETER errno:87**
Press return to exit...

myfile-append was created but is empty because of the error. myfile-write has the expected last 4 records written from process 2:

TEST-4424
TEST-4424
TEST-4424
TEST-4424

When I run on a non-Windows Server, there are no errors:

process 1
Terminal Services are not enabled so using local files
CreateFile: myfile-write.txt
WriteFile: TEST-8320
WriteFile: TEST-8320
WriteFile: TEST-8320
WriteFile: TEST-8320
CreateFile: myfile-append.txt
WriteFile: TEST-8320
WriteFile: TEST-8320
WriteFile: TEST-8320
WriteFile: TEST-8320
Press return to exit...

process 2
Terminal Services are not enabled so using local files
CreateFile: myfile-write.txt
WriteFile: TEST-11992
WriteFile: TEST-11992
WriteFile: TEST-11992
WriteFile: TEST-11992
CreateFile: myfile-append.txt
WriteFile: TEST-11992
WriteFile: TEST-11992
WriteFile: TEST-11992
WriteFile: TEST-11992
Press return to exit...

myfile-append correctly has 8 records, 4 from each process:

TEST-24820
TEST-16324
TEST-24820
TEST-16324
TEST-24820
TEST-16324
TEST-24820
TEST-16324

myfile-write has the expected last 4 records written from process 2:

TEST-16324
TEST-16324
TEST-16324
TEST-16324

Does anyone know how I can get this program to run in multiple processes and have that program append records to the same file when the program is run on a Windows Server writing to a file on a shared client drive using the Terminal Server Client syntax?

Windows batch script to run ConsoleApplication1.cpp in multiple processes

@echo off

del \\tsclient\c\temp\myfile-append.txt
del \\tsclient\c\temp\myfile-write.txt

echo Starting process 1
START "process 1" ConsoleApplication1.exe

timeout /t 1

echo Starting process 2
START "process 2" ConsoleApplication1.exe

echo Press return after both processes have finished...
timeout /t -1

echo myfile-append file, should have 8 records, 4 from each process:
type \\tsclient\c\temp\myfile-append.txt

echo myfile-write file, should have last 4 records written from process 2:
type \\tsclient\c\temp\myfile-write.txt

ConsoleApplication1.cpp

#define COUNT 4
#define SLEEP 2000
#include <Windows.h>
#include <iostream>
#include <stdlib.h>
#include <fileapi.h>
#include <errhandlingapi.h>

wchar_t filename_local_write[512] = L"myfile-write.txt";
wchar_t filename_local_append[512] = L"myfile-append.txt";
wchar_t filename_ts_write[512] = L"\\\\tsclient\\c\\temp\\myfile-write.txt";
wchar_t filename_ts_append[512] = L"\\\\tsclient\\c\\temp\\myfile-append.txt";
wchar_t* filename_write;
wchar_t* filename_append;

void* myExit(int exitVal);

int main()
{
    int ii;
    char buf[512];
    DWORD rval;
    SECURITY_ATTRIBUTES sa;
    HANDLE hFile = 0;
    DWORD pid;

    // See if Terminal Services are enabled
    OSVERSIONINFOEX osVersionInfo;
    ULONGLONG dwlConditionMask = VerSetConditionMask(0, VER_SUITENAME, VER_AND);
    ZeroMemory(&osVersionInfo, sizeof(osVersionInfo));
    osVersionInfo.dwOSVersionInfoSize = sizeof(osVersionInfo);
    osVersionInfo.wSuiteMask = VER_SUITE_TERMINAL;
    int tsEnabled = VerifyVersionInfo((POSVERSIONINFOEX)&osVersionInfo, VER_SUITENAME, dwlConditionMask);
    if (tsEnabled) {
        printf("Running on a Windows Server and Terminal Services are enabled\n");
        filename_write = filename_ts_write;
        filename_append = filename_ts_append;
    } else {
        printf("Terminal Services are not enabled so using local files\n");
        filename_write = filename_local_write;
        filename_append = filename_local_append;
    }

    // Use process id as part of the records written so we
    // know what process the records were written from.
    pid = GetCurrentProcessId();

    // Create a file using GENERIC_WRITE

    memset(&sa, 0, sizeof(sa));
    sa.nLength = sizeof(sa);
    sa.lpSecurityDescriptor = NULL;
    sa.bInheritHandle = FALSE;
    printf("CreateFile: %ws\n", filename_write);
    hFile = CreateFileW(
        filename_write,                     // lpFileName
        GENERIC_READ | GENERIC_WRITE,       // dwDesiredAccess
        FILE_SHARE_READ | FILE_SHARE_WRITE, // dwShareMode
        &sa,                                // lpSecurityAttributes
        OPEN_ALWAYS,                        // dwCreationDisposition
        FILE_ATTRIBUTE_NORMAL,              // dwFlagsAndAttributes
        NULL);                              // hTemplateFile
    if (hFile == INVALID_HANDLE_VALUE) {
        printf("CreateFile returned INVALID_HANDLE_VALUE errno:%d\n", GetLastError());
        myExit(1);
    }

    // write COUNT records to the file that was created using GENERIC_WRITE
    rval = 0;
    for (ii = 0; ii < COUNT; ii++) {
        sprintf_s(buf, "TEST-%d\n", pid);
        printf("WriteFile: %s", buf);
        if (!WriteFile(hFile, buf, strlen(buf), &rval, NULL)) {
            errno = GetLastError();
            if (errno == ERROR_INVALID_PARAMETER) {
                printf("WriteFile returned ERROR_INVALID_PARAMETER errno:%d\n", errno);
            } else {
                printf("WriteFile ERROR: returned errno:%d\n", errno);
            }
            myExit(1);
        }
        Sleep(SLEEP);
    }

    // close the file
    CloseHandle(hFile);


    // Create a file using FILE_APPEND_DATA

    memset(&sa, 0, sizeof(sa));
    sa.nLength = sizeof(sa);
    sa.lpSecurityDescriptor = NULL;
    sa.bInheritHandle = FALSE;
    printf("CreateFile: %ws\n", filename_append);
    hFile = CreateFileW(
        filename_append,                    // lpFileName
        GENERIC_READ | FILE_APPEND_DATA,    // dwDesiredAccess
        FILE_SHARE_READ | FILE_SHARE_WRITE, // dwShareMode
        &sa,                                // lpSecurityAttributes
        OPEN_ALWAYS,                        // dwCreationDisposition
        FILE_ATTRIBUTE_NORMAL,              // dwFlagsAndAttributes
        NULL);                              // hTemplateFile
    if (hFile == INVALID_HANDLE_VALUE) {
        printf("CreateFile returned INVALID_HANDLE_VALUE errno:%d\n", GetLastError());
        myExit(1);
    }

    // write COUNT records to the file that was created using FILE_APPEND_DATA
    rval = 0;
    for (ii = 0; ii < COUNT; ii++) {
        sprintf_s(buf, "TEST-%d\n", pid);
        printf("WriteFile: %s", buf);
        if (!WriteFile(hFile, buf, strlen(buf), &rval, NULL)) {
            errno = GetLastError();
            if (errno == ERROR_INVALID_PARAMETER) {
                printf("WriteFile returned ERROR_INVALID_PARAMETER errno:%d\n", errno);
            }
            else {
                printf("WriteFile ERROR: returned errno:%d\n", errno);
            }
            myExit(1);
        }
        Sleep(SLEEP);
    }

    // close the file
    CloseHandle(hFile);

    myExit(0);

} // main

void *myExit(int exitVal)
{
    printf("Press return to exit...\n");
    int rval = getchar();
    exit(exitVal);
} // myExit
0

There are 0 best solutions below