Hossam Houssien
Hossam Houssien

Reputation: 379

How to append data on a file in win32

I've searched to do this but I can't find what am I doing incorrectly.I'm trying to make this function appends data every time it's called but it always do it once. If the file doesn't exist it creates a new one and write on file for ONLY once if the file exist it does nothing (or maybe overwrite)

void WriteToFile (char data[],wchar_t filename[] )
{
    HANDLE hFile;
    DWORD dwBytesToWrite = (DWORD)strlen(data);
    DWORD dwBytesWritten ;
    BOOL bErrorFlag = FALSE;


    hFile = CreateFile((LPCWSTR)filename,            // name of the write
        GENERIC_WRITE,          // open for writing 
        0,                      // do not share
        NULL,                   // default security
        CREATE_NEW,             // create new file only
        FILE_ATTRIBUTE_NORMAL,  // normal file
        NULL);                  // no attr. template

    if (hFile == INVALID_HANDLE_VALUE)
    {
        DisplayError(TEXT("CreateFile"));
        _tprintf(TEXT("Terminal failure: Unable to open file \"%s\" for write.\n"), filename);
        return;
    }


    bErrorFlag = WriteFile(
    hFile,              // open file handle
    data,               // start of data to write
    dwBytesToWrite,     // number of bytes to write
    &dwBytesWritten,    // number of bytes that were written
    NULL);              // no overlapped structure

    if (FALSE == bErrorFlag)
    {
        DisplayError(TEXT("WriteFile"));
        printf("Terminal failure: Unable to write to file.\n");
    }
    else
    {
        if (dwBytesWritten != dwBytesToWrite)
        {
        // This is an error because a synchronous write that results in
        // success (WriteFile returns TRUE) should write all data as
        // requested. This would not necessarily be the case for
        // asynchronous writes.
        printf("Error: dwBytesWritten != dwBytesToWrite\n");
       }
       else
       {
        _tprintf(TEXT("Wrote %d bytes to %s successfully.\n"), dwBytesWritten, filename);
    }
}

CloseHandle(hFile);
}

And this is where I call the function in WM_COMMAND

//When a menu item selected execute this code
case IDM_FILE_SAVE:
        saveBool = true;
        char Str[] = "this is my own data";
        wchar_t filename[] = L"data.txt";
        WriteToFile(Str, filename);
        break;

Upvotes: 5

Views: 7572

Answers (2)

winapiadmin
winapiadmin

Reputation: 149

You didn't specify the location for writing, then that may overwrite the first part. The CreateFileA documentation says for the CREATE_NEW flag:

Creates a new file, only if it does not already exist. If the specified file exists, the function fails and the last-error code is set to ERROR_FILE_EXISTS (80).

The flag OPEN_ALWAYS flag is fit your needs. It also says:

Opens a file, always. If the specified file exists, the function succeeds and the last-error code is set to ERROR_ALREADY_EXISTS (183).

If the specified file does not exist and is a valid path to a writable location, the function creates a file and the last-error code is set to zero.

The WriteFile documentation says:

[in, out, optional] lpOverlapped

...

To write to the end of file, specify both the Offset and OffsetHigh members of the OVERLAPPED structure as 0xFFFFFFFF. This is functionally equivalent to previously calling the CreateFile function to open hFile using FILE_APPEND_DATA access.

Then you don't need to use FILE_APPEND_DATA flag, SetFilePointer, or SetFilePointerEx function to set the pointer to the end.

This is an example:

    LPCWSTR fileName=L"D:\foo\bar.txt";
    LPCWSTR buffer=L"dummy text";
    HANDLE fileHandle=CreateFileW(fileName,
                                  GENERIC_READ | GENERIC_WRITE,
                                  FILE_SHARE_READ | FILE_SHARE_WRITE,
                                  NULL,
                                  CREATE_ALWAYS,
                                  NULL,
                                  FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
                                  NULL);
    if (fileHandle==ERROR_INVALID_HANDLE) CloseHandle(fileHandle);
    OVERLAPPED writeFlag{0};
    writeFlag.DUMMYUNIONNAME.DUMMYSTRUCTNAME.Offset=0xFFFFFFFF;
    writeFlag.DUMMYUNIONNAME.DUMMYSTRUCTNAME.OffsetHigh=0xFFFFFFFF;
    WriteFile(fileHandle,buffer,sizeof(buffer),&writeFlag);
    // do another things here
    CloseHandle(fileHandle);

You can change the buffer to any buffer you need to write at the end of file, and the file name.

Also, there are the SetEndOfFile function, without the OVERLAPPED structure.

Example:

    LPCWSTR fileName=L"D:\foo\bar.txt";
    LPCWSTR buffer=L"dummy text";
    HANDLE fileHandle=CreateFileW(fileName,
                                  GENERIC_READ | GENERIC_WRITE,
                                  FILE_SHARE_READ | FILE_SHARE_WRITE,
                                  NULL,
                                  OPEN_ALWAYS,
                                  NULL,
                                  FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
                                  NULL);
    if (fileHandle==ERROR_INVALID_HANDLE) CloseHandle(fileHandle);
    SetEndOfFile(fileHandle);
    if (GetLastError()==ERROR_ACCESS_DENIED) CloseHandle(fileHandle);
    WriteFile(fileHandle,buffer,sizeof(buffer));
    // do another things here
    CloseHandle(fileHandle);

Upvotes: 0

Remy Lebeau
Remy Lebeau

Reputation: 597205

if the file exist it does nothing

As it should be. Per the CreateFile() documentation:

CREATE_NEW
1
Creates a new file, only if it does not already exist.

If the specified file exists, the function fails and the last-error code is set to ERROR_FILE_EXISTS (80).

If the specified file does not exist and is a valid path to a writable location, a new file is created.

For what you are attempting to do, use OPEN_ALWAYS instead:

OPEN_ALWAYS
4
Opens a file, always.

If the specified file exists, the function succeeds and the last-error code is set to ERROR_ALREADY_EXISTS (183).

If the specified file does not exist and is a valid path to a writable location, the function creates a file and the last-error code is set to zero.

You can use the FILE_APPEND_DATA access specifier to have CreateFile() automatically seek to the end of the file after creating/opening it (otherwise, you have to seek manually using SetFilePointer/Ex()) before you then write new data to the file.

Try this:

void WriteToFile (char *data, wchar_t *filename)
{
    HANDLE hFile;
    DWORD dwBytesToWrite = strlen(data);
    DWORD dwBytesWritten ;
    BOOL bErrorFlag = FALSE;

    hFile = CreateFileW(filename,  // name of the write
        FILE_APPEND_DATA,          // open for appending
        FILE_SHARE_READ,           // share for reading only
        NULL,                      // default security
        OPEN_ALWAYS,               // open existing file or create new file 
        FILE_ATTRIBUTE_NORMAL,     // normal file
        NULL);                     // no attr. template

    if (hFile == INVALID_HANDLE_VALUE)
    {
        DisplayError(TEXT("CreateFile"));
        wprintf(L"Terminal failure: Unable to create/open file \"%s\" for writing.\n", filename);
        return;
    }

    while (dwBytesToWrite > 0)
    {
        bErrorFlag = WriteFile(
            hFile,              // open file handle
            data,               // start of data to write
            dwBytesToWrite,     // number of bytes to write
            &dwBytesWritten,    // number of bytes that were written
            NULL);              // no overlapped structure

        if (!bErrorFlag)
        {
            DisplayError(TEXT("WriteFile"));
            printf("Terminal failure: Unable to write to file.\n");
            break;
        }

        wprintf(L"Wrote %u bytes to \"%s\" successfully.\n", dwBytesWritten, filename);

        data += dwBytesWritten;
        dwBytesToWrite -= dwBytesWritten;
    }

    CloseHandle(hFile);
}

Upvotes: 12

Related Questions