Visual C++ RegGetValue() fails in program where it shouldn't - c++

I'm making a C++ program using Visual C++ 2008 Express that gets the paths to specific apps from the registry, display a list of those that are installed, allows the user to pick one to configure, then launch the selected app.
This program is supposed to retrieve the paths to (currently) three apps by using RegGetValue (from windows.h).
While it works with Apps n°1 and 3, it fails with app n°2.
The part of the source that handles the registry is available on Pastebin: http://pastebin.com/9X2hjGqh.
I get error n°234 (ERROR_MORE_DATA) when I add a cout to get the function's return.
RegGetValue syntax:
LONG WINAPI RegGetValue(
_In_ HKEY hkey,
_In_opt_ LPCTSTR lpSubKey,
_In_opt_ LPCTSTR lpValue,
_In_opt_ DWORD dwFlags,
_Out_opt_ LPDWORD pdwType,
_Out_opt_ PVOID pvData,
_Inout_opt_ LPDWORD pcbData
);
Full reference here: http://msdn.microsoft.com/en-us/library/ms724875(v=VS.85).aspx

One thing that stands out are all the calls where you do this:
RegGetValue(hKey, NULL, "PATH", RRF_RT_ANY, NULL, (PVOID)&ValueBuffer, &BufferSize);
Notice the last parameter is &BufferSize. This is an [in, out] parameter. You set it to the size of your buffer before going in and it changes the value to the number of characters read into the buffer on the way out. This is what the docs say about RegGetValue (and other similar Reg) functions:
pcbData [in, out, optional]
A pointer to a variable that specifies the size of the buffer pointed to by the pvData parameter, in bytes. When the function returns, this variable contains the size of the data copied to pvData.
So when you call RegGetValue it started at 8192 (BUFFER) but after the first call it was overwritten by the the number of characters read.
Before each RegGetValue call where you pass a &BufferSize you should do this:
BufferSize = BUFFER
As well I notice you have:
#define BUFFER 8192
char ValueBuffer[255];
DWORD BufferSize = BUFFER;
Shouldn't you be setting ValueBuffer to be at least ValueBuffer[BUFFER] instead of ValueBuffer[255]?
As well your code as it is only supports an 8192 byte buffer. If the Value key from the registry is longer than that then it would return ERROR_MORE_DATA. I assume that your code is not anticipating anything beyond 8192. You can determine the size up front for a Value key by using RegQueryInfoKey and dynamically allocating enough space ahead of time.

ERROR_MORE_DATA means that one of the buffers you passed to RegGetValue is not large enough to store the data you asked RegGetValue to give you. You need to loop and reallocate the buffers supplied here when you get this exit code.
For example:
LONG result = ERROR_MORE_DATA;
DWORD dataLength = /* some reasonable default size */ 255;
unique_ptr<char[]> buffer;
while (result == ERROR_MORE_DATA)
{
buffer.reset(new char[dataLength]);
result = RegGetValueW(
hKey,
L"Subkey",
L"Value",
0,
nullptr,
buffer.get(),
&dataLength
);
}
if (result != ERROR_SUCCESS)
{
// Handle the error
}

Related

Difference between QueryVirtualMemoryInformation and VirtualQueryEx

While looking for ways to query specific information about a range of pages in windows I came across two solutions that were used commonly. However these two alternatives seem to return overlapping information.
VirtualQueryEx
Found on MSDN we see that it takes the parameters hProcess, lpAddress, lpBuffer and dwLength to query information for that range of pages. It returns this struct which tells us something about page state, protection and type. Oh well, so a good choice for querying page information right? But wait there is more!
QueryVirtualMemoryInformation
Also found on MSDN and does nearly the same thing. The difference is that it uses a DUMMYSTRUCTNAME and returns a memory structure that overlaps quite perfectly with the struct returned by VirtualQueryEx.
It seems like this could be an oversight and it doesn't matter which one to use. Maybe MS themselves don't even know why there are two overlapping variants inside a single OS. But for someone that does know: What's the difference here?
VirtualQuery and VirtualQueryEx have existed since forever (NT v3.51).
QueryVirtualMemoryInformation is much newer (10 1607) and adds the information class parameter often seen in the NT API. This allows for future expansion. There is currently only one documented class value and the returned information is pretty similar to VirtualQueryEx but not exactly the same (BaseAddress, CommitSize etc).
DUMMYSTRUCTNAME is a compiler thing related to members without a name and has nothing to do with the actual data returned.
exist next NT api - NtQueryVirtualMemory
__kernel_entry NTSYSCALLAPI NTSTATUS NtQueryVirtualMemory(
[in] HANDLE ProcessHandle,
[in, optional] PVOID BaseAddress,
[in] MEMORY_INFORMATION_CLASS MemoryInformationClass,
[out] PVOID MemoryInformation,
[in] SIZE_T MemoryInformationLength,
[out, optional] PSIZE_T ReturnLength
);
where MEMORY_INFORMATION_CLASS can have next values:
enum MEMORY_INFORMATION_CLASS
{
MemoryBasicInformation, // MEMORY_BASIC_INFORMATION
MemoryWorkingSetInformation, // MEMORY_WORKING_SET_INFORMATION
MemoryMappedFilenameInformation, // UNICODE_STRING
MemoryRegionInformation, // MEMORY_REGION_INFORMATION
MemoryWorkingSetExInformation, // MEMORY_WORKING_SET_EX_INFORMATION // since VISTA
MemorySharedCommitInformation, // MEMORY_SHARED_COMMIT_INFORMATION // since WIN8
MemoryImageInformation, // MEMORY_IMAGE_INFORMATION
MemoryRegionInformationEx, // MEMORY_REGION_INFORMATION
MemoryPrivilegedBasicInformation,
MemoryEnclaveImageInformation, // MEMORY_ENCLAVE_IMAGE_INFORMATION // since REDSTONE3
MemoryBasicInformationCapped, // 10
MemoryPhysicalContiguityInformation, // MEMORY_PHYSICAL_CONTIGUITY_INFORMATION // since 20H1
MemoryBadInformation, // since WIN11
MemoryBadInformationAllProcesses, // since 22H1
MaxMemoryInfoClass
} ;
both VirtualQueryEx and QueryVirtualMemoryInformation is thin shell over NtQueryVirtualMemory.
the VirtualQueryEx call NtQueryVirtualMemory with MemoryInformationClass = MemoryBasicInformation
SIZE_T
WINAPI
VirtualQueryEx(
_In_ HANDLE hProcess,
_In_opt_ LPCVOID VirtualAddress,
_Out_writes_bytes_to_(dwLength,return) PMEMORY_BASIC_INFORMATION lpBuffer,
_In_ SIZE_T dwLength
)
{
NTSTATUS status = NtQueryVirtualMemory(hProcess, VirtualAddress, MemoryBasicInformation, dwLength, &dwLength);
if (0 > status)
{
RtlNtStatusToDosError(status);
return 0;
}
return dwLength;
}
the QueryVirtualMemoryInformation call NtQueryVirtualMemory with MemoryInformationClass = MemoryRegionInformationEx. despite MemoryInformationClass parameter still exist, it accept only single value.
BOOL
WINAPI
QueryVirtualMemoryInformation(
_In_ HANDLE Process,
_In_ const VOID* VirtualAddress,
_In_ WIN32_MEMORY_INFORMATION_CLASS MemoryInformationClass,
_Out_writes_bytes_(MemoryInformationSize) PVOID MemoryInformation,
_In_ SIZE_T MemoryInformationSize,
_Out_opt_ PSIZE_T ReturnSize
)
{
NTSTATUS status = MemoryRegionInfo != MemoryInformationClass ? STATUS_INVALID_INFO_CLASS :
NtQueryVirtualMemory(hProcess, VirtualAddress, MemoryRegionInformationEx, MemoryInformationSize, ReturnSize);
if (0 > status)
{
RtlNtStatusToDosError(status);
return FALSE;
}
return TRUE;
}
so because in both case the same NT api called internal - this 2 win32 api very similar.

use SetupGetInfDriverStoreLocation function

BOOL SetupGetInfDriverStoreLocation(
_In_ PCTSTR FileName,
_In_opt_ PSP_ALTPLATFORM_INFO AlternatePlatformInfo,
_In_opt_ PCTSTR LocaleName,
_Out_ PTSTR ReturnBuffer,
_In_ DWORD ReturnBufferSize,
_Out_opt_ PDWORD RequiredSize
);
How can I call this function if I have FileName as:
TCHAR FileName[MAX_VALUE_NAME];
where #define MAX_VALUE_NAME 16383
This is a very common pattern in the Windows API; you give the function a buffer and the size of the buffer (often as a character count) and the call will fail if the buffer is too small.
#define MAX_VALUE_NAME 16383
TCHAR fullpath[MAX_VALUE_NAME];
BOOL result = SetupGetInfDriverStoreLocation(TEXT("NameOfMyFile.inf"), 0, 0, fullpath, MAX_VALUE_NAME, 0);
if (result) MessageBox(0, fullpath, 0, 0);
Your buffer is very large (MSDN says "The maximum supported path size is MAX_PATH") so it should be able to hold any .inf path but the function is designed so you can first call it with a NULL buffer:
To determine the size of the return buffer that is required to contain
the fully qualified file name of the specified INF file in the driver
store, call SetupGetInfDriverStoreLocation and set ReturnBuffer to
NULL, ReturnBufferSize to zero, and supply RequiredSize.
SetupGetInfDriverStoreLocation will return the required buffer size in
RequiredSize.
I'd call this the "peek, allocate, get" pattern. If there is a chance the data might actually change then you should call it in a loop until the "get" call succeeds.

WriteFile throws Access Violation when 4th (optional) parameter is NULL

Thought that it may be helpful to someone because it was a kind of surprise for me.
WriteFile function tries to write into its 4th (optional) parameter, and if it is NULL it causes Access Violation exception... But not on Windows 8(.1).
This is the function definition from msdn:
BOOL WINAPI WriteFile(
_In_ HANDLE hFile,
_In_ LPCVOID lpBuffer,
_In_ DWORD nNumberOfBytesToWrite,
_Out_opt_ LPDWORD lpNumberOfBytesWritten, // Optional
_Inout_opt_ LPOVERLAPPED lpOverlapped
);
lpNumberOfBytesWritten [out, optional]
...
This parameter can be NULL only when the lpOverlapped parameter is not NULL.
I created short example that reproduces the error. The code is Win32 console application created in Visual Studio 2008 SP1:
int _tmain(int argc, _TCHAR* argv[])
{
HANDLE hFile = CreateFile(L"N:\\Test\\Test.tmp", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, NULL);
if (INVALID_HANDLE_VALUE == hFile) {
return -1;
}
unsigned int testValue = 32;
//-----------------------------------------------
// Next line generates AV exception on Windows 7,
// On Windows 8 it works fine:
//-----------------------------------------------
WriteFile(hFile, &testValue, sizeof(unsigned int), NULL, NULL);
CloseHandle(hFile);
return 0;
}
And, finally, if I change the call to WriteFile() by the following two lines, this solves the problem and works on all platforms:
// Now it does not generate AV:
DWORD written = 0;
WriteFile(hFile, &testValue, sizeof(unsigned int), &written, NULL);
The code generates Access Violation on Windows 7 and on Windows XP SP3 (did not test it on Vista). On Windows 8(.1) it works, even when I pass NULL in 4th parameter (lpNumberOfBytesWritten).
The actual problem was that I developed a module that writes into a temporary file, but I ignored the 4th parameter (I read "optional" but misread the rest and thought that it may be ignored). I developed and tested it on Windows 8.1, so the code worked fine, but the client machine was on Windows 7 and code failed.
The lessons I learned: I should be more attentive (to details) and don't be smug (and test carefully).
The documentation for lpNumberOfBytesWritten says:
This parameter can be NULL only when the lpOverlapped parameter is not NULL.
In other words, the lpNumberOfBytesWritten parameter is only optional when you are using overlapped IO. You are passing NULL for lpOverlapped, and therefore not using overlapped IO, the lpNumberOfBytesWritten is not optional and cannot be NULL.

How to copy a file but touch the timestamp on the new copy?

I am using ::CopyFile() to make a copy of a file. It appears the original file's timestamp is being preserved and I would like the copy to set the current timestamp on the copy, i.e. 'touching' it.
Is there a WinAPI way to do this easily?
If you read the MSDN documentation for CopyFile(), there are comments at the bottom that say the following:
File times semantics
This article should document semantics with respect to file creation/modification/access times.
Creation time: if the target file already exists, its' creation time is preserved, otherwise it is set to the current system time.
Last Modification time: always copied from modification time of the source file.
Last Access time: always set to the current system time.
and
Mod-time not always preserved
The modification time is not guaranteed to be set. CopyFileEx does try to set the modification time, but it does NO error checking on it. This means if setting modification time fails internally in CopyFileEx (e.g. with access denied), latter will still returns successful!
So if modification time is important for your scenario (it is for my synchronization program), you have to explicitly call SetFileTime() and check it's return value to be sure.
You should use SetFileTime() to update the copied file's timestamp(s) yourself to make sure they are set to what you want them to be set to. There is an example on MSDN:
Changing a File Time to the Current Time
#include <windows.h>
// SetFileToCurrentTime - sets last write time to current system time
// Return value - TRUE if successful, FALSE otherwise
// hFile - must be a valid file handle
BOOL SetFileToCurrentTime(HANDLE hFile)
{
FILETIME ft;
SYSTEMTIME st;
BOOL f;
GetSystemTime(&st); // Gets the current system time
SystemTimeToFileTime(&st, &ft); // Converts the current system time to file time format
f = SetFileTime(hFile, // Sets last-write time of the file
(LPFILETIME) NULL, // to the converted current system time
(LPFILETIME) NULL,
&ft);
return f;
}
::CopyFile is actually part of windows:
http://msdn.microsoft.com/en-us/library/windows/desktop/aa363851%28v=vs.85%29.aspx
It is a convenience function that does copy metadata. There is another way, though less convenient, to copy files using windows. Check out these functions:
ReadFile:
http://msdn.microsoft.com/en-us/library/windows/desktop/aa365467%28v=vs.85%29.aspx
CreateFile:
http://msdn.microsoft.com/en-us/library/windows/desktop/aa363858%28v=vs.85%29.aspx
WriteFile:
http://msdn.microsoft.com/en-us/library/windows/desktop/aa365747%28v=vs.85%29.aspx
HANDLE WINAPI CreateFile(
_In_ LPCTSTR lpFileName,
_In_ DWORD dwDesiredAccess,
_In_ DWORD dwShareMode,
_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
_In_ DWORD dwCreationDisposition,
_In_ DWORD dwFlagsAndAttributes,
_In_opt_ HANDLE hTemplateFile
);
BOOL WINAPI ReadFile(
_In_ HANDLE hFile,
_Out_ LPVOID lpBuffer,
_In_ DWORD nNumberOfBytesToRead,
_Out_opt_ LPDWORD lpNumberOfBytesRead,
_Inout_opt_ LPOVERLAPPED lpOverlapped
);
BOOL WINAPI WriteFile(
_In_ HANDLE hFile,
_In_ LPCVOID lpBuffer,
_In_ DWORD nNumberOfBytesToWrite,
_Out_opt_ LPDWORD lpNumberOfBytesWritten,
_Inout_opt_ LPOVERLAPPED lpOverlapped
);
So you would open the file with something like this:
CreateFile(<File>, GENERIC_READ, FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL)
Check for a invalid return handle,
Create the new file with something like this:
CreateFile(<File>, GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL)
Check for a invalid return handle,
read the file into a buffer:
ReadFile(handle for file 1, buffer, Buffer size, Destination file,OUT for Bytes Read, NULL)
Then write out the Buffer:
WriteFile(outfile handle,buffer,number of bytes,NULL)
Close your handles.
Make sure to read CreateFiles details, it can be used to open files too.

WriteFile function C++

Im trying to use the WriteFile function. Ive been working off this example
http://msdn.microsoft.com/en-us/library/ms900134.aspx
Here the buffer that is passed to WriteFile is filled from ReadFile. But I dont want to do it that way. I just want to write a string like "Example text testing WriteFile" or something. But im not sure what values the parameters should have. Ive tried looking around on google but couldnt find anything. Anyone know how i do this?
From MSDN:
BOOL WINAPI WriteFile(
__in HANDLE hFile,
__in LPCVOID lpBuffer,
__in DWORD nNumberOfBytesToWrite,
__out_opt LPDWORD lpNumberOfBytesWritten,
__inout_opt LPOVERLAPPED lpOverlapped
);
The first argument is the handle to the file.
The second argument is a pointer to the data you want to write. In your case it's the string.
The third argument is the length of the data you want to write. In your case it will be something like strlen(str).
The fourth argument is a pointer to a DWORD variable that will receive the number of bytes actually written.
The fifth and last parameter can be NULL for now.
You use it like this:
char str[] = "Example text testing WriteFile";
DWORD bytesWritten;
WriteFile(fileHandle, str, strlen(str), &bytesWritten, NULL);
If WriteFile returns FALSE then there was an error. Use the GetLastError function to find out the error code.
A simple example of writing a string:
(hOutFile here is an open file handle from a call to CreateFile):
{
DWORD dwBytesWritten = 0;
char Str[] = "Example text testing WriteFile";
WriteFile( hOutFile, Str, strlen(Str), &dwBytesWritten, NULL );
}
EDIT: Check the MSDN function definition for what each parameter does.