I am implementing an XLSX spreadsheet Reader in Visual Studio C++ MFC app and am getting an access violation error when executing it multiple times:
First-chance exception at 0x7720e39e in VCT.exe: 0xC0000005: Access violation reading location 0x02bdddab.
Unhandled exception at 0x7720e39e in VCT.exe: 0xC0000005: Access violation reading location 0x02bdddab.
The program '[1756] VCT.exe: Native' has exited with code -1073741819 (0xc0000005).
The weird thing is that depending on the rest of my code, this can occur after the function being called twice, three times, or more... This makes me think that it has something to do with timing, but I only have the single thread running. Another one of my (more realistic) hypotheses is that it is an undefined behavior. This makes it especially difficult to debug. What confuses me is why this access violation would happen after multiple calls to the function.
Added to question:
I call the function getVectorAllIP every time a button is clicked. After a few clicks (calls to getVectorAllIP), the I get the access violation error on the first call to mm_XLSXReader.xlsxGetCellOffset.
vector<CString> CVCTDlg::getVectorAllIP(string ipName){
CString CSIP1;
vector<CString> VCSIPAddresses;
XLSXReader mm_XLSXReader;
mm_XLSXReader.reloadFile();
mm_XLSXReader.setFilePath(XLSX_FILE_PATH);
for(int i = 0; i < m_vectorStrHostname.size(); i++)
{
CSIP1="";
for(int iOffset = 0; iOffset < 4; iOffset++)
{
CSIP1.Append(mm_XLSXReader.xlsxGetCellOffset(ipName, i, iOffset).c_str());
if(iOffset != 3)
{
CSIP1.Append(".");
}
}
if(CSIP1 != "...")
{
VCSIPAddresses.push_back(CSIP1);
}else{
VCSIPAddresses.push_back("");
}
}
return VCSIPAddresses;
}
Within xlsxGetCellOffset, the access violation error occurs within readSheetXml.
string XLSXReader::xlsxGetCellOffset(string columnName, int id, int offset)
{
string contentToReturn;
id++;
if(!m_bFileInMemory)
{
if(openm_XLSXReader())
{
readSharedStringsXml();
readSheetXml();
closem_XLSXReaders();
m_bFileInMemory = true;
}
}
for(int i = 0; i < m_header.size(); i++)
{
if(m_header.at(i) == columnName && m_header.size() > i + offset)
{
if(m_sheetContent.size() > id)
{
if(m_sheetContent.at(id).size() > i)
{
contentToReturn = m_sheetContent.at(id).at(i+offset);
}
}
}
}
return contentToReturn;
}
The access violation occurs during the clearing sequence at the end. Specifically at the columnContent.clear(). If I remove columnContent.clear() it occurs at the next line tParameterColumn.clear().
void XLSXReader::readSheetXml()
{
if(m_m_XLSXReader)
{
int error = unzLocateFile(m_m_XLSXReader, SHEET_NAME, 0);
if(error == UNZ_OK)
{
error = unzOpenCurrentFile(m_m_XLSXReader);
if(error == UNZ_OK)
{
int readingStatus = 0;
char readBuffer[BUFFERSIZE];
string file;
int indexValue;
//Reading File
do
{
readingStatus = unzReadCurrentFile(m_m_XLSXReader, readBuffer, BUFFERSIZE);
file.append(readBuffer, readingStatus);
}while(readingStatus > 0);
//Sort Data
vector<string> rowContent;
rowContent = findXmlTagsContent(file, "row");
unsigned int iHdrSize;
m_header.clear();
vector<string> columnContent;
vector<string> tParameterColumn;
vector<string> rParameterColumn;
vector<string> tmpRow;
for(int i = 0 ; i < rowContent.size(); i++)
{
columnContent=findXmlTagsContent( rowContent.at(i), "c");
rParameterColumn=findXmlParameterInTag(rowContent.at(i), "c", "r");
tParameterColumn=findXmlParameterInTag(rowContent.at(i), "c", "t");
if(i==0){
iHdrSize = columnContent.size();
}
//Should be equal
if(columnContent.size() == tParameterColumn.size())
{
unsigned int iFilledColumns = 0;
for(int j = 0 ; j < columnContent.size(); j++)
{
int columnNumber = 0;
if(!rParameterColumn.at(j).empty())
{
columnNumber = columnLetter2Int(rParameterColumn.at(j));
}
vector<string> value;
value = findXmlTagsContent(columnContent.at(j), "v");
if(value.size()>1){
value.clear();
value.push_back("");
}
//Header Reading
if( i == 0)
{
//Fill empty spaces in excel sheet with ""
for(int a = 1; a < columnNumber-iFilledColumns; a++)
{
m_header.push_back("");
}
iFilledColumns=m_header.size();
//link to sharedString
if(tParameterColumn.at(j) == "s")
{
indexValue = atoi(value.at(0).c_str());
string tmpStr = m_sharedString.at(indexValue);
m_header.push_back(tmpStr);
}
//Value
else
{
m_header.push_back(value.at(0));
}
}
// Content Reading
else
{
////Fill empty spaces in excel sheet with ""
for(int a = 1; a < columnNumber-iFilledColumns; a++)
{
tmpRow.push_back("");
}
iFilledColumns=tmpRow.size();
//link to sharedString
if(tParameterColumn.at(j) == "s")
{
indexValue = atoi(value.at(0).c_str());
tmpRow.push_back(m_sharedString.at(indexValue));
}
//Value
else
{
if(value.size() != 0)
{
tmpRow.push_back(value.at(value.size()-1));
}
else
{
tmpRow.push_back("");
}
}
}
iFilledColumns++;
}
for(int k=0;k<iHdrSize-iFilledColumns;k++){
tmpRow.push_back("");
}
m_sheetContent.push_back(tmpRow);
tmpRow.clear();
columnContent.clear();
tParameterColumn.clear();
rParameterColumn.clear();
}
}
}
}
}
}
And just FYI, the m_m_XLSXReader is instantiated on a call to openm_XLSXReader called within xlsxGetCellOffset. Here it is for your reference:
bool XLSXReader::openm_XLSXReader()
{
//Uncompress .xlsx
m_m_XLSXReader = unzOpen(m_strXLSXPath.c_str());
if(m_m_XLSXReader){
return true;
}
return false;
}
Hope someone can point out some glaring obvious mistake, because I am starting to question my sanity :) Thanks.
This loop:
do
{
readingStatus = unzReadCurrentFile(m_m_XLSXReader, readBuffer, BUFFERSIZE);
file.append(readBuffer, readingStatus);
} while (readingStatus > 0);
will append the last read block twice, most likely producing invalid XML.
I don't know which library you use to read XML (can't find any references to findXmlTagsContent other than this question), and how resilient it is, but suspect it may not behave well being fed garbage. Plus, you are not checking for any possible errors...
Bottom line is: try reading your file like this:
while ((readingStatus = unzReadCurrentFile(m_m_XLSXReader, readBuffer, BUFFERSIZE)) > 0)
file.append(readBuffer, readingStatus);
Also, what are you going to do if the return is negative (an error code)?
OK, So thank you all for your help and suggestions.
Vlad, your suggestions really got me thinking in a more logical way and then I found the problem which was completely unrelated to the actual XLSX reading.
The simple explanation is that when I click the button that sets off the XLSX reader, I modify the windows registry before that. In the process of recursively removing some registry keys, some sort of memory corruption occurs, that was only reflected when I got the access violation. I fixed my registry code, and now the issue is has been resolved.
If anyone is interested about the actual issue regarding recursively deleting registry keys, keep reading...
I was using the code to recursively delete a registry key and its subkeys:
Deleting a Key with Subkeys
#include <windows.h>
#include <stdio.h>
#include <strsafe.h>
//*************************************************************
//
// RegDelnodeRecurse()
//
// Purpose: Deletes a registry key and all its subkeys / values.
//
// Parameters: hKeyRoot - Root key
// lpSubKey - SubKey to delete
//
// Return: TRUE if successful.
// FALSE if an error occurs.
//
//*************************************************************
BOOL RegDelnodeRecurse (HKEY hKeyRoot, LPTSTR lpSubKey)
{
LPTSTR lpEnd;
LONG lResult;
DWORD dwSize;
TCHAR szName[MAX_PATH];
HKEY hKey;
FILETIME ftWrite;
// First, see if we can delete the key without having
// to recurse.
lResult = RegDeleteKey(hKeyRoot, lpSubKey);
if (lResult == ERROR_SUCCESS)
return TRUE;
lResult = RegOpenKeyEx (hKeyRoot, lpSubKey, 0, KEY_READ, &hKey);
if (lResult != ERROR_SUCCESS)
{
if (lResult == ERROR_FILE_NOT_FOUND) {
printf("Key not found.\n");
return TRUE;
}
else {
printf("Error opening key.\n");
return FALSE;
}
}
// Check for an ending slash and add one if it is missing.
lpEnd = lpSubKey + lstrlen(lpSubKey);
if (*(lpEnd - 1) != TEXT('\\'))
{
*lpEnd = TEXT('\\');
lpEnd++;
*lpEnd = TEXT('\0');
}
// Enumerate the keys
dwSize = MAX_PATH;
lResult = RegEnumKeyEx(hKey, 0, szName, &dwSize, NULL,
NULL, NULL, &ftWrite);
if (lResult == ERROR_SUCCESS)
{
do {
StringCchCopy (lpEnd, MAX_PATH*2, szName);
if (!RegDelnodeRecurse(hKeyRoot, lpSubKey)) {
break;
}
dwSize = MAX_PATH;
lResult = RegEnumKeyEx(hKey, 0, szName, &dwSize, NULL,
NULL, NULL, &ftWrite);
} while (lResult == ERROR_SUCCESS);
}
lpEnd--;
*lpEnd = TEXT('\0');
RegCloseKey (hKey);
// Try again to delete the key.
lResult = RegDeleteKey(hKeyRoot, lpSubKey);
if (lResult == ERROR_SUCCESS)
return TRUE;
return FALSE;
}
//*************************************************************
//
// RegDelnode()
//
// Purpose: Deletes a registry key and all its subkeys / values.
//
// Parameters: hKeyRoot - Root key
// lpSubKey - SubKey to delete
//
// Return: TRUE if successful.
// FALSE if an error occurs.
//
//*************************************************************
BOOL RegDelnode (HKEY hKeyRoot, LPTSTR lpSubKey)
{
TCHAR szDelKey[MAX_PATH*2];
StringCchCopy (szDelKey, MAX_PATH*2, lpSubKey);
return RegDelnodeRecurse(hKeyRoot, szDelKey);
}
void __cdecl main()
{
BOOL bSuccess;
bSuccess = RegDelnode(HKEY_CURRENT_USER, TEXT("Software\\TestDir"));
if(bSuccess)
printf("Success!\n");
else printf("Failure.\n");
}
If I had values in the key I was trying to delete, then everything worked as expected. In the case where I had a key with a subkey inside of it, they were deleted, but I began have the issues of my question above.
So for example this was deleted without causing me a life of pain,
[KeyName1]
--> SOME_DWORD
--> SOME_STRING
And this one caused me much pain indeed,
[KeyName2]
--> SOME_DWORD
--> SOME_STRING
--> [SubKeyName]
-----> SOME_DWORD
-----> SOME_STRING
Related
I'm trying to make a program that deletes a list of files one by one, using system().
The reason I'm using system() instead of remove() is because remove() doesn't support environment variables.
I've tried checking the return value, but my code seems to just always output that it's been deleted, even when it hasn't.
Code:
void fileLoop() {
std::vector<std::string> fileList = { "\"%userprofile%\\test.txt\"" };
while (!inter) {
for (int count = 0; count <= 0; count++) {
std::string moddedstring = "del /F " + fileList[count];
DWORD checker = GetFileAttributes((LPCWSTR)fileList[count].c_str());
if (checker == INVALID_FILE_ATTRIBUTES) {
goto next;
}
else {
system(moddedstring.c_str());
MessageBoxW(NULL, L"File found, successfully deleted", L"File Deleted", MB_OK | MB_ICONWARNING);
}
next:
std::cout << "\n";
}
Sleep(500);
}
}
I thought there is some easy way to find this out. I haven't found a way like that yet.
I will be adding more paths later.
Update:
I've tried using OpenProcessToken() with ExpandEnvironmentStringsForUserW() to add ENVs
But it complains that my buffer is of type LPCSTR* even when I set it to LPCSTR
Thanks!
A better way to get the user's profile path is to simply ask the OS directly, via SHGetFolderPath(CSIDL_PROFILE) or SHGetKnownFolderPath(FOLDERID_Profile).
Also, you should use DeleteFileA() instead of system("del"). There is no need to spawn an external console process when you can do the deletion directly. Also, because you are interested in error checking, which is much easier if you use the Win32 API directly.
Also, (LPCWSTR)fileList[count].c_str() will not work like you think. You can't convert a const char* to a const wchar_t* using a simple typecast. Drop the typecast and use GetFileAttributesA() instead.
Or, simply don't bother checking attributes at all. You can call DeleteFileA() unconditionally and let it tell you if it actually succeeded or failed.
With that said, try something more like this:
#include <shlobj.h>
std::string getUserProfilePath() {
CHAR szPath[MAX_PATH];
if (FAILED(SHGetFolderPathA(NULL, CSIDL_PROFILE, NULL, SHGFP_TYPE_CURRENT, szPath))) {
// error handling...
throw ...;
}
int len = lstrlenA(szPath);
szPath[len] = '\\';
return std::string(szPath, len + 1);
/*
PWSTR pszPath;
if (FAILED(SHGetKnownFolderPath(FOLDERID_Profile, KF_FLAG_DEFAULT, NULL, &pszPath))) {
// error handling...
throw ...;
}
int wlen = lstrlenW(pszPath);
int len = WideCharToMultiByte(0, 0, pszPath, wlen, NULL, 0, NULL, NULL);
if (len == 0) {
// error handling...
throw ...;
}
std::vector<CHAR> buffer(len + 1);
len = WideCharToMultiByte(CP_ACP, 0, pszPath, wlen, buffer.data(), len, NULL, NULL);
if (len == 0) {
// error handling...
throw ...;
}
buffer[len] = '\\';
CoTaskMemFree(pszPath);
return std::wstring(buffer.data(), buffer.size());
*/
}
void fileLoop() {
std::vector<std::string> fileList = { getUserProfilePath() + "test.txt" };
while (!inter) {
for (size_t count = 0; count < fileList.size(); ++count) {
if (DeleteFileA(fileList[count].c_str())) {
MessageBoxW(NULL, L"File found, successfully deleted", L"File Deleted", MB_OK | MB_ICONWARNING);
} else {
// error handling...
}
std::cout << "\n";
}
Sleep(500);
}
}
Alternatively, using Unicode strings instead (which you really should use when interacting with the filesystem):
#include <shlobj.h>
std::wstring getUserProfilePath() {
WCHAR szPath[MAX_PATH];
if (FAILED(SHGetFolderPathW(NULL, CSIDL_PROFILE, NULL, SHGFP_TYPE_CURRENT, szPath))) {
// error handling...
throw ...;
}
int len = lstrlenW(szPath);
szPath[len] = '\\';
return std::wstring(szPath, len + 1);
/*
PWSTR pszPath;
if (FAILED(SHGetKnownFolderPath(FOLDERID_Profile, KF_FLAG_DEFAULT, NULL, &pszPath))) {
// error handling...
throw ...;
}
int len = lstrlenW(pszPath);
std::wstring sPath(len + 1, '\0');
std::copy(pszPath, pszPath + len, sPath.begin());
sPath[len] = '\\';
CoTaskMemFree(pszPath);
return sPath;
*/
}
void fileLoop() {
std::vector<std::wstring> fileList = { getUserProfilePath() + L"test.txt" };
while (!inter) {
for (size_t count = 0; count < fileList.size(); ++count) {
if (DeleteFileW(fileList[count].c_str())) {
MessageBoxW(NULL, L"File found, successfully deleted", L"File Deleted", MB_OK | MB_ICONWARNING);
} else {
// error handling...
}
std::cout << "\n";
}
Sleep(500);
}
}
Even better, if you are using C++17 or later, consider using the <filesystem> library instead:
#include <shlobj.h>
#include <filesystem>
using fs = std::filesystem;
fs::path getUserProfilePath() {
WCHAR szPath[MAX_PATH];
if (FAILED(SHGetFolderPathW(NULL, CSIDL_PROFILE, NULL, SHGFP_TYPE_CURRENT, szPath))) {
// error handling...
throw ...;
}
return szPath;
/*
PWSTR pszPath;
if (FAILED(SHGetKnownFolderPath(FOLDERID_Profile, KF_FLAG_DEFAULT, NULL, &pszPath))) {
// error handling...
throw ...;
}
fs::path pth(pszPath);
CoTaskMemFree(pszPath);
return pth;
*/
}
void fileLoop() {
std::vector<fs::path> fileList = { getUserProfilePath() / L"test.txt" };
while (!inter) {
for (auto &pth : fileList) {
std::error_code ec;
if (fs::remove(pth, ec)) {
MessageBoxW(NULL, L"File found, successfully deleted", L"File Deleted", MB_OK | MB_ICONWARNING);
} else {
// error handling...
}
std::cout << "\n";
}
Sleep(500);
}
}
I am using WriteFile to write to a file and I am getting an error of ERROR_INVALID_HANDLE sometimes. I read that this could be because the value of HANDLE in Overlapped is invalid.
But I am just having a hard time figuring this out. I wonder if something is going out of scope. Would appreciate it if someone can take a look. I can always add more code here
const LPOVERLAPPED lpOverlapped = GetOverlapped(true, hFile, ulBufSize, &ullFileOffset,volumeName);
if (lpOverlapped == nullptr)
{
CloseHandle(hFile);
return false;
}
if (!WriteFile(hFile,(const PVOID)(((UINT64) s_pMemoryBuffer[volumeName]) + iBufferOffset),ulBufSize,&dwBytesWritten,lpOverlapped))
{
DWORD errCode = GetLastError(); //Error here
//Why do I get an error code 6 here every now and then
}
Now this is the method that returns the overlapped structure
LPOVERLAPPED foo::GetOverlapped(bool useOverlappedIo, HANDLE hFile, UINT32 ulBufSize, UINT64* ullFileOffset,const std::string& volumeName)
{
if (useOverlappedIo)
{
while (true)
{
int index = 0;
while (index < cMaxOverlappedIOS)
{
if (!OverlappedInUse[volumeName][index])
{
OverlappedInUse[volumeName][index] = true;
LPOVERLAPPED overlapped = &(OverlappedArray[volumeName][index]);
if (overlapped->hEvent == nullptr) // Need Event
{
overlapped->hEvent = CreateEvent(
nullptr,
TRUE,
TRUE,
nullptr);
if (overlapped->hEvent == nullptr)
{
printf("Error creating event (error code: %u)\n", GetLastError());
return nullptr;
}
}
overlapped->Offset = (UINT32)(*ullFileOffset & 0xFFFFFFFF); // Low order 32 bits
overlapped->OffsetHigh = (UINT32)(*ullFileOffset >> 32); // High order 32 bits
*ullFileOffset += ulBufSize; // Update pointer to next record
return overlapped;
}
// Else Keep looking
index++;
}
// None available, wait for at least one to free up
if (WaitForPendingIOs(hFile, FALSE,volumeName) != ERROR_SUCCESS)
{
return nullptr;
}
} // Now start loop over again
}
else
{
return nullptr;
}
}
This is how I am initializing the array before this code gets called
for(auto vol : readVolumes)
{
OVERLAPPED* oarray = new OVERLAPPED[cMaxOverlappedIOS];
memset(oarray, 0, sizeof(oarray));
OverlappedArray[vol] = oarray;
bool* boolinuse = new bool[cMaxOverlappedIOS]{false};
OverlappedInUse[vol] = boolinuse;
s_pMemoryBuffer[vol] = nullptr;
s_uDataBufferSize[vol] = 0;
}
Any suggestions on why I would get that error ?
Sometimes GetModuleFileName returns the correct string, however 99% of the time currently with current code I am using the string returns as ÀÙáøÛáws\system32\HID.DLL instead of E:\Windows\system32\HID.DLL that should be the correct value. With this in mind I can't compare the string with an list of all modules that should be loaded to see if that string is in the list, if not someone injected that DLL.
This code below might not be the best, however it is the code I attempted to use for this. I did try all sorts of code changes to try to figure it out like not using TCHAR and investigating the returns from EnumProcessModules.
void _scan_dll_data(VBTrpSetup_t &setup, VBTrp_DetectData_t &_ret, VBTrp_InjectData_t &_dlllists) {
bool _detected_injected_dll = false;
std::vector<std::string> _ModuleContainer;
std::string _ModuleName;
HMODULE hMods[1024]; /* Hopefully enough for this. */
DWORD cbNeeded;
if (EnumProcessModules(setup.GameProcHandle, hMods, sizeof(hMods), &cbNeeded)) {
for (unsigned int i = 0; i < (cbNeeded / sizeof(HMODULE)); i++ ) {
char szModName[MAX_PATH];
if (GetModuleFileName(hMods[i], szModName, sizeof(szModName) / sizeof(char))) {
_ModuleName = szModName;
for (unsigned int i = 0; i < _dlllists.ModuleExcludeList.size(); i++) {
// item must not be in the ModuleExcludeList!!!
if (!_dlllists.ModuleExcludeList[i].compare(_ModuleName)) {
_ModuleContainer.push_back(_ModuleName);
}
}
}
}
}
if (_dlllists.ModuleList != _ModuleContainer) {
_detected_injected_dll = true;
_ret.DLLName = reinterpret_cast<LPCSTR>(_ModuleName.c_str());
}
if (_detected_injected_dll) {
_ret.value = TRUE;
}
else {
_ret.value = FALSE;
}
if (_ret.value == TRUE) {
_ret.int_value = -1;
} else {
_ret.int_value = NULL;
}
}
Hopefully the answer is something simple that I must have missed. I did do some parts of this according to MSDN examples. Maybe those examples was wrong. I am not so sure.
Does anyone know how to fix this string issue it returns?
The fix was to definately use the Unicode versions and to make the whole function use wide unicode strings. And the reason for this is because of a struct (related to PEB) that is internal and undocumented to ntdll.dll.
So, basically changing everything to the GetModuleBaseNameW function because of the fact I was going to basename them anyway later, the wstring, noticing a second loop using i causing an overwrite of the i on the outer for loop and removing it, and adding checks on GetLastError for when the Handle is invalidated and toreturn the error code to the end user to handle cleanup.
The result is then this code:
void _scan_dll_data(VBTrpSetup_t &setup, VBTrp_DetectData_t &_ret, VBTrp_InjectData_t &_dlllists) {
BOOL _detected_injected_dll = FALSE;
std::vector<std::wstring> _ModuleContainer;
HANDLE hProcess;
std::vector<HMODULE> hMods;
DWORD cbNeeded;
hProcess = GetCurrentProcess();
_ret.int_value = 0;
if (EnumProcessModulesEx(hProcess, hMods.data(), setup.NumOfModules, &cbNeeded, LIST_MODULES_ALL)) {
for (unsigned int i = 0; i < (cbNeeded / sizeof(HMODULE)); i++) {
wchar_t *szModName = L'\x0';
std::wstring _ModuleName;
if (GetModuleBaseNameW(hProcess, hMods[i], reinterpret_cast<LPWSTR>(szModName), MAX_PATH)) {
_ModuleName = szModName;
// item must not be in the ModuleExcludeList!!!
if (!_dlllists.ModuleExcludeList[i].compare(_ModuleName)) {
_ModuleContainer.push_back(_ModuleName);
}
} else {
_ret.error_code = GetLastError();
}
}
} else {
_ret.error_code = GetLastError();
}
if (_ret.error_code != ERROR_INVALID_HANDLE) {
for (unsigned int j = 0; j < _dlllists.ModuleList.size(); j++) {
if (_dlllists.ModuleList[j] != _ModuleContainer[j]) {
_detected_injected_dll = TRUE;
_ret.int_value = -1;
_ret.DLLName = (LPWSTR)_ModuleContainer[j].c_str();
// to avoid overwriting the first one.
break;
}
}
_ret.value = _detected_injected_dll;
}
}
I want to search a file which may be present in any drives such as C:\, D:\ etc. Using GetLogicalDriveStrings I can able to get the list of drives but when I add anything extra for the output, I am getting a null in the output prompt. Here is my code:
#include "StdAfx.h"
#include <windows.h>
#include <stdio.h>
#include <conio.h>
// Buffer length
DWORD mydrives = 100;
// Buffer for drive string storage
char lpBuffer[100];
const char *extFile = "text.ext";
// You may want to try the wmain() version
int main(void)
{
DWORD test;
int i;
test = GetLogicalDriveStrings(mydrives, (LPWSTR)lpBuffer);
if(test != 0)
{
printf("GetLogicalDriveStrings() return value: %d, Error (if any): %d \n", test, GetLastError());
printf("The logical drives of this machine are:\n");
// Check up to 100 drives...
for(i = 0; i<100; i++)
printf("%c%s", lpBuffer[i],extFile);
printf("\n");
}
else
printf("GetLogicalDriveStrings() is failed lor!!! Error code: %d\n", GetLastError());
_getch();
return 0;
}
I want above output as C:\text.ext D:\text.ext ... rather I am getting text.ext only. I am using Microsoft Visual C++ 2010 Express
GetLogicalDriveStrings() returns a double-null terminated list of null-terminated strings. E.g., say you had drives A, B and C in your machine. The returned string would look like this:
A:\<nul>B:\<nul>C:\<nul><nul>
You can use the following code to iterate through the strings in the returned buffer and print each one in turn:
DWORD dwSize = MAX_PATH;
char szLogicalDrives[MAX_PATH] = {0};
DWORD dwResult = GetLogicalDriveStrings(dwSize,szLogicalDrives);
if (dwResult > 0 && dwResult <= MAX_PATH)
{
char* szSingleDrive = szLogicalDrives;
while(*szSingleDrive)
{
printf("Drive: %s\n", szSingleDrive);
// get the next drive
szSingleDrive += strlen(szSingleDrive) + 1;
}
}
Note that the details of how the function works, including the example code that I shamelessly copied and pasted, can be found by reading the docs.
Did you mean to put the printf in the loop?
Currently, you set extFile 100 times (just to be sure?!)
for(i = 0; i<100; i++)
extFile = "text.ext";
You meant to show all the drive letters in a loop:
for(i = 0; i<100; i++)
{
extFile = "text.ext";
printf("%c%s", lpBuffer[i], extFile); //I guess you mean extFile here?
}
DWORD dwSize = MAX_PATH;
WCHAR szLogicalDrives[MAX_PATH] = { 0 };
DWORD dwResult = GetLogicalDriveStrings(dwSize, szLogicalDrives);
CStringArray m_Drives;
m_Drives.RemoveAll();
if (dwResult > 0 && dwResult <= MAX_PATH)
{
WCHAR* szSingleDrive = szLogicalDrives;
while (*szSingleDrive)
{
UINT nDriveType = GetDriveType(szSingleDrive);
m_Drives.Add(CString(szSingleDrive, 2));
// get the next drive
szSingleDrive += wcslen(szSingleDrive) + 1;
}
}
return m_Drives;
class DriveList {
protected:
LPTSTR m_driveList;
DWORD m_driveCount;
DWORD m_bufSize = 32 * sizeof(TCHAR);
public:
virtual ~DriveList() {
free(m_driveList);
}
DriveList() {
m_driveList = (LPTSTR)malloc(m_bufSize);
}
int getDriveCount() const {
return m_driveCount;
}
TCHAR operator[] (const int index) const {
return m_driveList[index];
}
void loadDriveList() {
DWORD mask;
if((mask = GetLogicalDrives()) == 0) {
throw;
}
m_driveCount = 0;
for(int x = 0; x <= 25; x++ ) {
if(mask & 1) {
m_driveList[m_driveCount] = TCHAR(65 + x);
m_driveCount += 1;
}
mask >>= 1;
}
}
};
I am having problems with reading the registry.
This function finds the number of entries in a registry path. It works perfectly, I have tested it:
void findNumberEntries(registryTest &INSTALLKEY) {
char buffer[50];
char size = sizeof(buffer);
int index = 0;
if(RegOpenKeyEx(INSTALLKEY.hKey,(LPTSTR)(INSTALLKEY.regpath.c_str()),0,KEY_ALL_ACCESS,&INSTALLKEY.hKey) == ERROR_SUCCESS) {
DWORD readEntry;
do {
readEntry = RegEnumValue(INSTALLKEY.hKey,index,(LPTSTR)buffer,(LPDWORD)&size,NULL,NULL,NULL,NULL);
index++;
}
while(readEntry != ERROR_NO_MORE_ITEMS);
}
INSTALLKEY.number = index;
RegCloseKey(INSTALLKEY.hKey);
}
now, the main function:
std::string regpath32 = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run\\";
struct registryTest {
HKEY hKey;
std::string regpath;
int number;
};
registryTest INSTALLKEY = {HKEY_LOCAL_MACHINE, regpath32};
findNumberEntries(INSTALLKEY);
printf("%d\n",INSTALLKEY.number);
system("PAUSE");
//until here everything works as it should
HKEY hKey = INSTALLKEY.hKey;
std::string regpath = INSTALLKEY.regpath;
char buffer[50];
char size = sizeof(buffer);
std::string bufferString;
DWORD regOpen = RegOpenKeyEx(INSTALLKEY.hKey,(LPTSTR)INSTALLKEY.regpath.c_str(),0,KEY_READ,&INSTALLKEY.hKey);
if(regOpen == ERROR_SUCCESS) //this is the part that fails.
{
printf("Registry Key was successfully opened\n");
}
else
{
printf("Unable to open registry key\n");
LPVOID message;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, GetLastError(), NULL,(LPTSTR) &message, 0, NULL );
MessageBox(NULL,(LPCTSTR)message,"ERROR",MB_OK|MB_ICONINFORMATION);
}
...rest of the code
I always get "Unable to open registry" and the error message I get is "There are no more files". What is the problem??
your problem is that when you first open the registry key ,you assign it to hkey-member of your struct. So the second time this hkey doesn't contain the original basekey anymore.
change :
DWORD regOpen =
RegOpenKeyEx(INSTALLKEY.hKey,(LPTSTR)INSTALLKEY.regpath.c_str(),0,KEY_READ,&INSTALLKEY.hKey);
into
DWORD regOpen = RegOpenKeyEx(
HKEY_LOCAL_MACHINE
,(LPTSTR)INSTALLKEY.regpath.c_str(),0,KEY_READ,&INSTALLKEY.hKey);
or change this:
void findNumberEntries( registryTest &INSTALLKEY)
{
char buffer[50];
char size = sizeof(buffer);
int index = 0;
HKEY hkOpen = 0; // can't use INVALID_HANDLE_VALUE for HKEY's;
if (RegOpenKeyEx( INSTALLKEY.hKey ,(LPTSTR)(INSTALLKEY.regpath.c_str())
,0,&hkOpen ) == ERROR_SUCCESS)
{
// You should use RegQueryInfoKey for below code !
DWORD readEntry;
do {
readEntry = RegEnumValue( hkOpen ,index,(LPTSTR)buffer
,(LPDWORD size,NULL,NULL,NULL,NULL);
index++;
}
while(readEntry != ERROR_NO_MORE_ITEMS); }
INSTALLKEY.number = index;
RegCloseKey( hkOpen );
}
You may need to specify KEY_ALL_ACCESS in the second call as well, rather than just in the first. And on Win7 64-bit you may be running into the registry redirect craziness (http://msdn.microsoft.com/en-us/library/aa384232%28VS.85%29.aspx).
EDIT: ah, you might just be getting an ERROR_CANTWRITE back (error code number 5). You might be able to ignore that and see if it still works.
It's very likely that on Windows 7 64-bit that you are being redirected via Registry Virtualization. You can determine what keys are being redirected by calling RegQueryReflectionKey.
If you modify your code to output the actual integer value that is returned rather than a generic "Unable to open key", then it would be helpful. For example,
long n = RegOpenKeyEx(HKEY_LOCAL_MACHINE,TEXT("\\SOFTWARE"),
0,KEY_QUERY_VALUE, &hk );
if ( n == ERROR_SUCCESS ) {
cout << "OK" << endl;
}
else {
cout << "Failed with value " << n << endl;
}