C++ Insert text at specific position in append with Windows API - c++

I'm having troubles with insertion of text, in append, in a classical text file. What I want to do is simple : insert a single character in front of some lines. I know the exact offset of each line beginning. I have one restriction, I have to use the Windows API : CreateFile(), WriteFile(), SetFilePointer()...
I can't insert text, whatever I do, the program write to the end, or if it writes at the good offset, it erase the existing text.
Here is my code (I just simplified some checks to be more readable here) :
HANDLE handleFile = CreateFile (filename,
FILE_APPEND_DATA,
FILE_SHARE_READ, //SHARE
NULL, //SecurityAttibute
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (handleFile != INVALID_HANDLE_VALUE) {
if (SetFilePointer (handleFile, 12345, NULL, FILE_BEGIN) != INVALID_SET_FILE_POINTER) {
DWORD written = 0;
WriteFile (handleFile, "$", 1, &written, NULL);
}
}
When I use FILE_APPEND_DATA, the SetFilePointer() doesn't work and my character is written to the end.
When I use GENERIC_WRITE, or even FILE_GENERIC_WRITE, the character is written at the good offset, but it erase the present character :'(
What is the good parameter to really insert please ?
PS : this code is for very large files, so read / write the whole file is not possible, it would be too long.
Thanks a lot !

You cannot insert text into a file in the way you are attempting. You can append data to the end, or you can overwrite existing data. In order to effect an insertion you have to re-write all the contents that follow the point of insertion.

Related

Writing to a memory mapped text file printing NULL till the end of mapped memory

In this code, Im writing some text to a memory mapped text file. Data is written to the file successfully, but when i open it with notepad, after the written data, "NULL" is repeatedly written upto the mapped memory limit which is greater than the text i have written.
What could be the possible reason?
pLogMsg = (char*)calloc(1024,sizeof(char));
printf("[INFO] entering logger writeback thread\n");
log_file = CreateFile (TEXT("one.txt"), // Open one.txt.
GENERIC_READ | GENERIC_WRITE, // Open for reading and writing
FILE_SHARE_WRITE, // file share
NULL, // No security
OPEN_ALWAYS, // Open or create
FILE_ATTRIBUTE_NORMAL, // Normal file
NULL); // No template file
if (log_file == INVALID_HANDLE_VALUE)
{
printf("%d [ERR] cant open file GLE %d\n",GetCurrentThreadId(),GetLastError());
return -1;
}
hMapping = CreateFileMapping( log_file, 0, PAGE_READWRITE, 0,4096 ,0 );
if (hMapping == INVALID_HANDLE_VALUE)
{
printf("%d [ERR] cant create file mapping %d\n",GetCurrentThreadId(),GetLastError());
return -1;
}
pFileData = (CHAR*)MapViewOfFile( hMapping, FILE_MAP_ALL_ACCESS, 0,0, 0 );
if (pFileData == NULL)
{
printf("%d [ERR] cant mapview of file %d\n",GetCurrentThreadId(),GetLastError());
return -1;
}
pLogMsg = LogPrint();//returns a null terminated string
memcpy(pFileData,pLogMsg,strlen(pLogMsg));
pFileData += strlen(pLogMsg);
free(pLogMsg);
first of all CreateFileMapping and MapViewOfFile this is bad solution for log file. you need create/open file with FILE_APPEND_DATA access instead GENERIC_READ | GENERIC_WRITE - so call must look like:
HANDLE log_file = CreateFileW(L"one.txt",
FILE_APPEND_DATA,
FILE_SHARE_WRITE|FILE_SHARE_READ,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
in this case you open file with FILE_APPEND_DATA and SYNCHRONIZE (because no FILE_FLAG_OVERLAPPED):
If the caller sets only the FILE_APPEND_DATA and SYNCHRONIZE flags, it
can write only to the end of the file, and any offset information
about write operations to the file is ignored. The file will
automatically be extended as necessary for this type of operation.
after this you need log via WriteFile - it will be automatically append to the end of file. this is what exactly need for log file.
however, if want use CreateFileMapping and MapViewOfFile - at first CreateFileMapping return 0 on error, so check
if (hMapping == INVALID_HANDLE_VALUE) is wrong
if you want change file size - you need use for this SetFileInformationByHandle with FileEndOfFileInfo (vista+)or NtSetInformationFile with FileEndOfFileInformation (working everywhere, how not hard understand SetFileInformationByHandle is only very thin shell over NtSetInformationFile or ZwSetInformationFile (in user mode this is the same function)). use SetFilePointer followed by SetEndOfFile also possible but bad and not effective choice - you will be have 2 calls in src code instead on single. in binary - you will even more worse situation: first you set file position, that SetEndOfFile read this file position and finally call NtSetInformationFile with this position. so 3 calls instead one. and assume that nobody change file position (on this handle) between SetFilePointer and SetEndOfFile calls
but need clear understand that call SetFileInformationByHandle with FileEndOfFileInfo you can only after you unmap view and close section. otherwise you got error ERROR_USER_MAPPED_FILE (STATUS_USER_MAPPED_FILE )
If CreateFileMapping is called to create a file mapping object for
hFile, UnmapViewOfFile must be called first to unmap all views and
call CloseHandle to close the file mapping object before you can
call SetEndOfFile.
There is no 'end of file' marker within the file. You need to set the file length, so the OS marks it correctly.
See MSDN documentation for SetEndOfFile
Sets the physical file size for the specified file to the current position of the file pointer.
You cannot set the end-of-file marker through a file mapping. The end-of-file marker does not physically exist in a file. It is a flag raised by the OS when reading past the end of a file.
To set the size of a file you will have to call SetFilePointer followed by SetEndOfFile on the file object used to create the file mapping.

How to send wstring buffer to stdin of a child process?

I'm having trouble writing a WString to the STDIN of a child process. If I have only acii character string (eg: #WSX3edc), the code works fine, but if it contains a non ascii character (eg: #WSX3edcß) it fails.
The child process is 7zr.exe (7Zip cmd line version). The input I'm writing to the STDIN is the password to extract the file.
// inject password
wPassword.append(password);
wPassword.append(L"\n"); \\For carriage return
...
DWORD dwBytesToWrite = wPassword.length()*sizeof(wchar_t);
DWORD dwBytesWritten = 0;
char szBuffer[1024] = "\0";
wcstombs(szBuffer, wPassword.c_str(),wcslen(wPassword.c_str())+1);
dwBytesToWrite = strlen(szBuffer);
if (!WriteFile(hInput, szBuffer, dwBytesToWrite, &dwBytesWritten, NULL)) {
std::cout<<"write file failed"<<GetLastError()<<std::endl;
goto Cleanup;
}
The writefile always succeed but some how the file extraction is not successful due to faulty password injecting mechanism.
Createprocess for this looks like : (si object has the STDIN and STDOUT streams set using a CreatePipe earlier)
if(!CreateProcess((LPWSTR)cmd, (LPWSTR)cmdArgs, NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS,
NULL, NULL, &si, &pi)) {
std::cout<<"7zr.exe process creation failed "<<GetLastError()<<std::endl;
goto Cleanup;
}
Note : 7zr.exe works just fine with this particular password, if we run it on command-line and paste this password. The extraction works fine.
If the narrow character set doesn't have the relevant password character, you can't use this approach. Instead find what option 7zr has for specifying the password. I don't have an executable called 7zr but I do have 7z, and the command 7z | find /i "pass" worked nicely.
In other news:
The variable dwBytesToWrite is initialized with one value, only to be reassigned a few lines later, without having been used.
goto Cleanup is generally ungood in C++. If you want guaranteed cleanup use a destructor (the technique called RAII, read up on it).
Microsoft's Hungarian notation, with prefixes such as sz and dw, is generally an abomination. It once, in the 1980's, supported the help system in Microsoft's Programmer's Workbench. AFAIK that product hasn't existed the last 30 years or so.
The C cast in (LPWSTR)cmd can easily introduce a bug. Use const_cast where you want to cast constness. Then it would be more clear that this cast is incorrect: you need a mutable buffer.
Instead of reporting a failure to the standard output stream, via std::cout, consider using the standard error stream, via either std::cerr or std::clog. Better, don't do i/o at the place where a failure is detected, but throw an exception to let the calling code deal with it. The calling code can't remove output that's already, uh, output.
wcslen(wide) returns the number of wide characters in its argument wide (see).
wcstombs(narrow,wide,len) writes no more than len bytes to narrow (see).
Now if we always had one wide character = one narrow character = one byte, it wouldn't have much sense to have two varieties of characters, would it?
As soon as you have a wide character that translates to more than one narrow character, there is undefined behaviour.
Since your szBuffer is of fixed size, you could just as well write
wcstombs(szBuffer, wPassword.c_str(), sizeof(szBuffer));

Find and edit unicode HEX-strings in binary file

I've got a binary file of VS2013 project, compiled using Unicode. Now i have a need to find few data strings in this file and replace them, using my utility. My idea was to search this strings in hex-editor, look their addresses and then simple update this data using winapi CreateFile/SetFilePointer/WriteFile. But there is a problem. First - i can't find this strings in hex-editor (because of unicode) and now i don't sure how to update them, because unicode chars are two bytes long.
So i used WinHex and found strings of data that i need, using Unicode. Then, i found out the offset of each string. Then, just simply wrote data at needed offset:
TCHAR data[MAX_APTH *2];
DWORD dwWritten = 0;
m_sStr = m_sStr.Trim();
offset = 0x00012F12;
wcscpy_s(data, m_sStr);
SetFilePointer(hFile, offset, NULL, 0);
WriteFile(hFile, data, wcslen(data) * sizeof(TCHAR), &dwWritten, NULL);
//WriteFile(hFile, L"\00", 100, &dwWritten, NULL);
Looks like not as hard as i thought.

Writing to a file, giving an incorrect name

I'm creating a file in the following manner:
if ((BmpFile = CreateFile((LPCWSTR)"Test.bmp",
GENERIC_WRITE,
0, NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL)) == INVALID_HANDLE_VALUE)
But the file that gets created has name 敔瑳戮灭.
Clearly not what I'm looking for! I'm in the process of trying to learn the windows API, could anyone tell me what I have to change to make it output what I think it should? I've looked at http://msdn.microsoft.com/en-us/library/windows/desktop/aa363858%28v=vs.85%29.aspx and it hasn't helped me too much-- I think because I don't know what I'm looking for.
Premature edit: everything else works as expected in the function.
You don't create a WSTR by simply casting. If you want your fixed text to be a wide string, apply the L in front of the literal:
... = CreateFile( L"Test.bmp", ...

WriteFile() Function for Win32 Applications

I am facing a problem on the WriteFile(); function using Win32 C++ Application. the second argument asks for a pointer to the buffer that is storing the information. what Syntax do I use to point the input text from the boxes? My information is text from the input of text boxes. What syntax do i use to create a pointer to that?
Here is a snippet of code the code I am using:
case IDC_BUTTON_ONE:
{
HANDLE hFile = CreateFile("C:\\test.txt", GENERIC_READ,
0, NULL, CREATE_NEW, FILE_FLAG_OVERLAPPED, NULL);
}
To write a control's text to a file you'll also need these lines:
char TextBuffer[256]; // Ascii
GetDlgItemTextA(hDlg, IDC_YOUR_CONTROL_ID, TextBuffer, ARRAY_SIZE(TextBuffer));
WriteFile(hFile, TextBuffer, strlen(TextBuffer), &SizeOut, lpOverlapped);
That'll just write plain old ASCII. If you want to use unicode and TCHARs (instead of chars) then you'll need to choose your encoding and write more than "just the bytes" from the text buffer.