memory leak in c++ console program calling Windows APIs - c++

I have copied some code I found on-line here. I have successfully run it but if I run it in a loop, there is a bad memory leak. I program mostly in C#, and this example goes way over my head. Could somebody point me in the right direction to fixing the memory leak? Here is my C++ console application in it's entirety. Any help is appreciated. Thanks.
// ConsoleApplication1.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream>
#include "Netlistmgr.h"
bool checkForCaptivePortalMode()
{
bool fCaptivePortalDetected = false;
// Initialize COM.
if (SUCCEEDED(CoInitializeEx(NULL, COINIT_MULTITHREADED)))
{
// Declare a pointer to INetworkListManager
INetworkListManager* pNetworkListManager;
// Create instance of the CLSID_NetworkListManger COM object
if (SUCCEEDED(CoCreateInstance(CLSID_NetworkListManager, NULL,
CLSCTX_ALL, IID_INetworkListManager,
(LPVOID*)&pNetworkListManager)))
{
// Declare pointer to an IEnumNetworkConnections
IEnumNetworks* pEnum;
// Call to GetNetworks from INetworkListManager interface
if (SUCCEEDED(pNetworkListManager->GetNetworks
(NLM_ENUM_NETWORK_CONNECTED, &pEnum)) && pEnum != NULL)
{
INetwork *pINetwork;
HRESULT hr = pEnum->Next(1, &pINetwork, nullptr);
while (hr == S_OK)
{
if (pINetwork != NULL)
{
IPropertyBag *pNetworkPropertyBag;
HRESULT hrQueryInterface = pINetwork->QueryInterface
(IID_IPropertyBag, (LPVOID*)&pNetworkPropertyBag);
if (SUCCEEDED(hrQueryInterface) && pNetworkPropertyBag != nullptr)
{
NLM_CONNECTIVITY networkConnectivity;
VARIANT variantConnectivity;
if (SUCCEEDED(pINetwork->GetConnectivity(&networkConnectivity)))
{
if ((networkConnectivity &
NLM_CONNECTIVITY_IPV4_INTERNET) == NLM_CONNECTIVITY_IPV4_INTERNET)
{
VariantInit(&variantConnectivity);
if (SUCCEEDED(pNetworkPropertyBag->Read
(NA_InternetConnectivityV4, &variantConnectivity, nullptr))
&& (V_UINT(&variantConnectivity) &
NLM_INTERNET_CONNECTIVITY_WEBHIJACK) ==
NLM_INTERNET_CONNECTIVITY_WEBHIJACK)
{
fCaptivePortalDetected = true;
}
auto t = V_UINT(&variantConnectivity);
VariantClear(&variantConnectivity);
}
if (!fCaptivePortalDetected && (networkConnectivity
& NLM_CONNECTIVITY_IPV6_INTERNET) == NLM_CONNECTIVITY_IPV6_INTERNET)
{
VariantInit(&variantConnectivity);
if (SUCCEEDED(pNetworkPropertyBag->Read(NA_InternetConnectivityV6,
&variantConnectivity, nullptr)) &&
(V_UINT(&variantConnectivity) &
NLM_INTERNET_CONNECTIVITY_WEBHIJACK) ==
NLM_INTERNET_CONNECTIVITY_WEBHIJACK)
{
fCaptivePortalDetected = true;
}
VariantClear(&variantConnectivity);
}
}
}
pINetwork->Release();
}
if (fCaptivePortalDetected)
break;
hr = hr = pEnum->Next(1, &pINetwork, nullptr);
}
}
}
}
// Uninitialize COM.
// (This should be called on application shutdown.)
CoUninitialize();
return fCaptivePortalDetected;
}
int main()
{
for (;;)
{
bool check = checkForCaptivePortalMode();
std::cout << "\n" << check;
//char c = std::getchar();
}
return 0;
}

For one thing you never call pNetworkListManager->Release(). More generally, in C++ if you are not using smart pointers you must make sure each and every resource allocation has a matching deallocation. It would be easier and probably make your code simpler if you used smart pointers throughout.

Related

WriteFile with an overlapped occasionally gives me ERROR_INVALID_HANDLE. Am I doing memset correct?

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 ?

Why is my Qt application crashing when accessing the IWMPMedia interface?

I have built a Qt (5.7) application that interfaces with the Windows Media Player COM API to parse track meta data. For some in-explicit reason my application is crashing when calling into IWMPMedia3::getAttributeCountByType.
The line where the crash keeps occurring is:
if (pMedia3Item && pMedia3Item->getAttributeCountByType(L"TrackingID", 0, &l) == S_OK) //Will eventually crash here
It does not crash on the first instance, it takes a couple of hundred loops and seems to be connected to when the call repeatedly returns 0. If I switch to an attribute that I know exists then it runs fine.
What is posted below is the object stripped right back, but it still crashes. This object is designed to run in its own QThread and all the COM symbols are defined and contained within the QThread.
The code forms part of a larger app that makes use of many other Qt modules, GUI, web engine being two of the biggest.
#include "WMPMLImport.h"
#include <QDebug>
#include "Wininet.h"
#define WMP_CLSID L"{6BF52A52-394A-11d3-B153-00C04F79FAA6}"
#define WMP_REFIID L"{D84CCA99-CCE2-11d2-9ECC-0000F8085981}"
#define WMP_PL_ALL_MUSIC L"All Music"
#define SAFE_RELEASE(ptr) if(NULL!=(ptr)){(ptr)->Release();ptr=NULL;}
#define BSTR_RELEASE(bstr) {SysFreeString(bstr);(bstr)=NULL;}
class CCoInitialize
{
public:
CCoInitialize() :
m_hr(CoInitialize(NULL))
{}
~CCoInitialize()
{
if(SUCCEEDED(m_hr)) {
qDebug() << "CCoInitialize: DTOR";
CoUninitialize();
}
}
HRESULT m_hr;
};
/**
Worker class that will step through the WMP COM interface
and extact the audio meta data contained within it,
*/
class WMPMLComHandler : public QObject
{
public:
WMPMLComHandler(WMPMLImport* t) :
m_thread(t)
{
qDebug() << "WMPMLComHandler::CTOR" << QThread::currentThreadId();
}
~WMPMLComHandler()
{
qDebug() << "WMPMLComHandler::DTOR" << QThread::currentThreadId();
}
/**
Method responsible for walking through the COM interface and extracting
all the track and playlist metadata including the artwork.
#returns Return false if the COM API was not parsed correctly.
*/
bool parse()
{
bool b = true;
CCoInitialize cCoInit;
IWMPCore* pIWMPCore = NULL;
IWMPCore3* pIWMPCore3 = NULL;
IWMPPlaylistCollection *pPlaylistCollection;
IWMPPlaylist *pMainLibplaylist;
CLSID clsID;
CLSID refID;
CLSIDFromString(WMP_CLSID, &clsID);
CLSIDFromString(WMP_REFIID, &refID);
if(SUCCEEDED(CoCreateInstance(clsID, NULL, CLSCTX_ALL, refID, (void**)&pIWMPCore)))
{
if(SUCCEEDED(pIWMPCore->get_playlistCollection(&pPlaylistCollection)))
{
if(SUCCEEDED(pIWMPCore->QueryInterface(__uuidof(IWMPCore3), reinterpret_cast<void**>(&pIWMPCore3))))
{
IWMPPlaylistArray* pPlaylistArray = NULL;
if(SUCCEEDED(pPlaylistCollection->getAll(&pPlaylistArray)))
{
long playlistCount = 0;
if(SUCCEEDED(pPlaylistArray->get_count(&playlistCount)))
{
IWMPPlaylist* pPlaylist = NULL;
for(int playlistIndex=0; playlistIndex < playlistCount; playlistIndex++)
{
if (SUCCEEDED(pPlaylistArray->item(playlistIndex, &pPlaylist)))
{
long lMediaCount = 0;
if (SUCCEEDED(pPlaylist->get_count(&lMediaCount)))
{
fetchPlaylist(pPlaylist, NULL, playlistIndex, lMediaCount);
}
SAFE_RELEASE(pPlaylist);
}
}
}
SAFE_RELEASE(pPlaylistArray);
}
SAFE_RELEASE(pIWMPCore3);
}
SAFE_RELEASE(pPlaylistCollection);
}
SAFE_RELEASE(pIWMPCore);
}
SAFE_RELEASE(pMainLibplaylist);
return b;
}
private:
void fetchPlaylist(IWMPPlaylist* pPlaylist, BSTR bstrName, unsigned int playlistIndex, long count)
{
//get the playlist items
for(long mediaIndex=0; mediaIndex<count-1; mediaIndex++)
{
IWMPMedia* pMediaItem = NULL;
if(SUCCEEDED(pPlaylist->get_item(mediaIndex, &pMediaItem)))
{
IWMPMedia3 *pMedia3Item = NULL;
if (pMediaItem->QueryInterface(__uuidof(IWMPMedia3), reinterpret_cast<void **>(&pMedia3Item)) == S_OK)
{
long l = 0;
qDebug() << "About to call method for" << mediaIndex << "time";
if (pMedia3Item && pMedia3Item->getAttributeCountByType(L"TrackingID", 0, &l) == S_OK) //Will eventually crash here
{
qDebug() << "Exited method for" << mediaIndex << "time";
}
}
SAFE_RELEASE(pMedia3Item);
SAFE_RELEASE(pMediaItem);
}
}
qDebug() << "*********COMPLETE*********";
}
WMPMLImport* m_thread;
};
//==
WMPMLImport::WMPMLImport() :
m_comHandler(NULL)
{
qDebug() << "WMPMLImporter CTOR" << QThread::currentThreadId();
}
WMPMLImport::~WMPMLImport()
{
}
/**
Reimplemented function that runs the function contents in a new thread context.
The parsing of the COM is all ran through this thread. Returning out of this
thread will end its execution.
*/
void WMPMLImport::run()
{
QMutexLocker g(&m_lock);
m_comHandler = new WMPMLComHandler(this);
g.unlock();
bool parseOk = m_comHandler->parse();
g.relock();
delete m_comHandler;
m_comHandler = NULL;
}

C++ - Access violoation when calling .clear() of std::vector

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

Converting to ctypes but I don't know what these functions are doing

I was wondering what this function here does:
ralloc_t(HWND hwnd) : proc_(0)
{
DWORD pid = 0;
if (!GetWindowThreadProcessId(hwnd, &pid)) {
throw exception("dang, no dice");
}
proc_ = OpenProcess(
PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION, FALSE, pid);
if (!proc_) {
throw exception("no open for me!");
}
}
~ralloc_t()
{
buffers_t::reverse_iterator i = buffers_.rbegin(), e = buffers_.rend();
for (; i != e; ++i) {
free(i->first);
}
}
I honestly don't know where function starts and where it ends and if it's returning anything.
The full code is below. I got a decent start on it, in that I have all the winapi functions in use below converted already to js-ctypes. This is what I have so far: https://gist.github.com/Noitidart/f691ab9a750f24be346f
#include <windows.h>
#include <commctrl.h>
#include <iostream>
#include <cstdio>
#include <stdexcept>
#include <map>
using namespace std;
/**
* Allocate/read/write remote process memory.
* The implementation is pretty crappy, as it isn't intelligent at all:
* It always allocates at least a full page per allocation :(
* Do something more clever in production!
* Also, type safety and convenience are pretty lacking.
* But again, this is test code, so it sucks!
*/
class ralloc_t
{
public:
ralloc_t(HWND hwnd) : proc_(0)
{
DWORD pid = 0;
if (!GetWindowThreadProcessId(hwnd, &pid)) {
throw exception("dang, no dice");
}
proc_ = OpenProcess(
PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION, FALSE, pid);
if (!proc_) {
throw exception("no open for me!");
}
}
~ralloc_t()
{
buffers_t::reverse_iterator i = buffers_.rbegin(), e = buffers_.rend();
for (; i != e; ++i) {
free(i->first);
}
}
void* alloc(size_t size)
{
void* rv = VirtualAllocEx(
proc_, 0, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (!rv) {
throw bad_alloc();
}
buffers_.insert(make_pair(rv, size));
return rv;
}
template <typename T>
T* create(size_t elems = 1)
{
return (T*)alloc(elems * sizeof(T));
}
void free(void* p)
{
buffers_t::iterator i = buffers_.find(p);
if (i == buffers_.end()) {
throw exception("invalid buffer");
}
VirtualFreeEx(proc_, i->first, i->second, MEM_RELEASE);
buffers_.erase(i);
}
void read(void* remote, void* local)
{
buffers_t::iterator i = buffers_.find(remote);
if (i == buffers_.end()) {
throw exception("invalid remote read buffer");
}
if (!ReadProcessMemory(proc_, i->first, local, i->second, 0)) {
throw exception("failed to read remote buffer");
}
}
void write(void* remote, const void* local)
{
buffers_t::iterator i = buffers_.find(remote);
if (i == buffers_.end()) {
throw exception("invalid remote write buffer");
}
if (!WriteProcessMemory(proc_, i->first, local, i->second, 0)) {
throw exception("failed to write remote buffer");
}
}
private:
typedef map<void*, size_t> buffers_t;
buffers_t buffers_;
HANDLE proc_;
};
int main()
{
typedef HWND(WINAPI * GetTaskmanWindowPtr)();
try
{
HMODULE user32 = LoadLibrary(L"user32");
GetTaskmanWindowPtr GetTaskmanWindow =
(GetTaskmanWindowPtr)GetProcAddress(user32, "GetTaskmanWindow");
if (!GetTaskmanWindow) {
throw exception("Failed to get GetTaskmanWindow!");
}
HWND htm = GetTaskmanWindow();
if (!htm) {
throw exception("Failed to get taskman window");
}
HWND htb = FindWindowEx(htm, 0, L"ToolbarWindow32", 0);
if (!htb) {
throw exception("Failed to get toolbar window");
}
ralloc_t ralloc(htb);
int count = SendMessage(htb, TB_BUTTONCOUNT, 0, 0);
cout << count << endl;
for (int i = 0; i < count; ++i) {
TBBUTTON tbb;
TBBUTTON* rtbb = ralloc.create<TBBUTTON>();
BOOL rv = SendMessage(htb, TB_GETBUTTON, i, (LPARAM)rtbb);
ralloc.read(rtbb, &tbb);
ralloc.free(rtbb);
cout << rv << " " << sizeof(tbb) << " " << tbb.idCommand << " "
<< tbb.iString << endl << flush;
int chars = SendMessage(htb, TB_GETBUTTONTEXT, tbb.idCommand, (LPARAM)0);
if (chars <= 0) {
continue;
}
chars++;
wchar_t* rbuf = ralloc.create<wchar_t>(chars);
if (SendMessage(htb, TB_GETBUTTONTEXT, tbb.idCommand, (LPARAM)rbuf) > 0) {
wchar_t* buf = new wchar_t[chars];
ralloc.read(rbuf, buf);
wcout << buf << endl << flush;
delete[] buf;
}
}
}
catch (const exception& ex)
{
cerr << "Error: " << ex.what() << endl;
}
// Sleep
getchar();
return 0;
}
It seems you need to learn about classes in C++. What you are looking at is two functions. The first is called ralloc_t and is the class's constructor. The second is called ~ralloc_t and is the class's destructor.
// This is the constructor
ralloc_t(HWND hwnd) : proc_(0)
{
DWORD pid = 0;
if (!GetWindowThreadProcessId(hwnd, &pid)) {
throw exception("dang, no dice");
}
proc_ = OpenProcess(
PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION, FALSE, pid);
if (!proc_) {
throw exception("no open for me!");
}
}
// This is the destructor
~ralloc_t()
{
buffers_t::reverse_iterator i = buffers_.rbegin(), e = buffers_.rend();
for (; i != e; ++i) {
free(i->first);
}
}
Note that the constructor has the same name as the class (see the line class ralloc_t), and the destructor has the same name but prefixed with a tilde (~).
These functions, by their very nature, do not return anything. The purpose of the constructor is to initialise objects of type ralloc_t when they are constructed, and the purpose of the destructor is to clean up when they are destroyed.
For example, you might have a block of code that looks something like this:
{
ralloc_t my_ralloc(some_hwnd);
// ...
}
The constructor gets called as a result of the declaration of my_ralloc (passing some_hwnd as the constructor's argument). The destructor will get called at the end of the block, which is when the variable goes out of scope and is destroyed.

Memory leaks in Webbrowser (COM)

This snippet of code generate huge memory leaks. Could you help me to find out where it happens?
This code does the following thing:
1) It gets IHTMLDocuments2 interface
2) Requests for all tags collection
3) Iterates over whole collection
4) and adds some of the tags' data to list
IDispatch* pDisp;
pDisp = this->GetHtmlDocument();
if (pDisp != NULL )
{
IHTMLDocument2* pHTMLDocument2;
HRESULT hr;
hr = pDisp->QueryInterface( IID_IHTMLDocument2,(void**)&pHTMLDocument2 );
if (hr == S_OK)
{
// I know that I could use IHTMLDocument3 interface to get collection by ID
// but it didn't worked and returned NULL on each call.
IHTMLElementCollection* pColl = NULL;
// get all tags
hr = pHTMLDocument2->get_all( &pColl );
if (hr == S_OK && pColl != NULL)
{
LONG celem;
hr = pColl->get_length( &celem );
if ( hr == S_OK )
{
//iterate through all tags
// if I iterate this block of code in cycle, it
// uses memory available upto 2GBs and then
// app crashes
for ( int i=0; i< celem; i++ )
{
VARIANT varIndex;
varIndex.vt = VT_UINT;
varIndex.lVal = i;
VARIANT var2;
VariantInit( &var2 );
IDispatch* pElemDisp = NULL;
hr = pColl->item( varIndex, var2, &pElemDisp );
if ( hr == S_OK && pElemDisp != NULL)
{
IHTMLElement* pElem;
hr = pElemDisp->QueryInterface(IID_IHTMLElement,(void **)&pElem);
if ( hr == S_OK)
{
// check INPUT tags only
BSTR tagNameStr = L"";
pElem->get_tagName(&tagNameStr);
CString tagname(tagNameStr);
SysFreeString(tagNameStr);
tagname.MakeLower();
if (tagname != "input")
{
continue;
}
//get ID attribute
BSTR bstr = L"";
pElem->get_id(&bstr);
CString idStr(bstr);
SysFreeString(bstr);
if (RequiredTag(pElem))
{
AddTagToList(pElem);
}
//release all objects
pElem->Release();
}
pElemDisp->Release();
}
}
}
// I looked over this code snippet many times and couldn't find what I'm missing here...
pColl->Release();
}
pHTMLDocument2->Release();
}
pDisp->Release();
}
Inside your loop, for each retreived element that does not have a tagname of "input" (which will be most elements), you are not calling pElem->Release() when calling continue, so you are leaking them:
if (tagname != "input")
{
pElem->Release(); // <-- add this
continue;
}
With that said, you should re-write your code to use ATL's smart pointer classes (CComPtr, CComQIPtr, CComBSTR, etc) to manage the memory for you so you do not have to manually release everything yourself anymore, eg:
CComPtr<IDispatch> pDisp;
pDisp.Attach(this->GetHtmlDocument());
if (pDisp.p != NULL)
{
CComQIPtr<IHTMLDocument2> pHTMLDocument2(pDisp);
if (pHTMLDocument2.p != NULL)
{
CComPtr<IHTMLElementCollection> pColl;
pHTMLDocument2->get_all(&pColl);
if (pColl.p != NULL)
{
LONG celem;
if (SUCCEEDED(pColl->get_length(&celem)))
{
for (LONG i = 0; i < celem; ++i)
{
VARIANT varIndex;
varIndex.vt = VT_UINT;
varIndex.lVal = i;
VARIANT var2;
VariantInit( &var2 );
CComPtr<IDispatch> pElemDisp;
pColl->item( varIndex, var2, &pElemDisp );
if (pElemDisp.p != NULL)
{
CComQIPtr<IHTMLElement> pElem(pElemDisp);
if (pElem.p != NULL)
{
CComBSTR tagNameStr;
pElem->get_tagName(&tagNameStr);
if (lstrcmpiW(tagNameStr.m_str, L"input") != 0)
continue;
CComBSTR idStr;
pElem->get_id(&idStr);
if (RequiredTag(pElem))
AddTagToList(pElem);
}
}
}
}
}
}
}