Debug Assertion failed!!! error using DoModal - c++

I'm new to C++ and MFC programming (using VS2005). I'm trying to use CFileDialog. Here is my code:
void CsampleDlg::StopRfWrite()
{
if (pFile != NULL)
fclose(pFile);
pFile = NULL;
char dir[512];
GetCurrentDirectoryA(512, dir);
CString connected=CString(dir) + _T("\\");
CString conn = connected + fn;
TCHAR szFilters[]= _T("RF data file (*.bin)|*.bin||");
CFileDialog fileDlg(FALSE, _T("bin"), fn,
OFN_FILEMUSTEXIST | OFN_HIDEREADONLY, szFilters);
// Display the file dialog. When user clicks OK, fileDlg.DoModal()
// returns IDOK.
if(fileDlg.DoModal() == IDOK)
{
CString pathName = fileDlg.GetPathName();
CString fileName = fileDlg.GetFileTitle();
SetWindowText(fileName);
MoveFile(conn,pathName);
}
else
{
DeleteFile(conn);
}
}
and receive
Debug Assertion failed error; (Line 384 in wincore.cpp) it is invoked by fileDlg.DoModal() call. It is random, sometimes program run fine.
Call Stack indicates the following row:
ASSERT(pWnd != NULL);

Related

Detect which window has focus in other instance and post a message with a CString parameter to it

I have code that executes in InitInstance that checks if my application is already running, and if so brings it to the foreground. Standard code:
if (m_hMutex != nullptr)
{ // indicates running instance
if (::GetLastError() == ERROR_ALREADY_EXISTS)
{
EnumWindows(searcher, (LPARAM)&hOther);
if (hOther != nullptr)
{
::SetForegroundWindow(::GetLastActivePopup(hOther));
if (IsIconic(hOther))
{
::ShowWindow(hOther, SW_RESTORE);
}
}
return FALSE; // terminates the creation
}
}
The above I have no issues with. Recently I added support for double-clicking a file from File Explorer and it open in my software CDialog derived). So I do the following in InitInstance:
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
if (PathFileExists(cmdInfo.m_strFileName))
{
m_bOpenFileFromFileExplorer = true;
m_strFileToOpenFromFileExplorerPath = cmdInfo.m_strFileName;
}
Then, in my main dialog OnInitDialog I do this:
if (theApp.OpenFileFromFileExplorer())
{
CString strFileToOpen = theApp.GetFileToOpenFromFileExplorerPath();
CString strFileExtension = PathFindExtension(strFileToOpen);
strFileExtension.MakeLower();
if (strFileExtension == _T(".srr"))
PostMessage(WM_COMMAND, ID_FILE_OPENREPORT);
else if (strFileExtension == _T(".mwb"))
PostMessage(WM_COMMAND, ID_FILE_OPEN_CHRISTIAN_LIFE_AND_MINISTRY_REPORT);
}
Finally, each of my respective handlers for each editor does something like this):
void CMeetingScheduleAssistantDlg::OnFileOpenReport()
{
CCreateReportDlg dlgReport(this);
CString strFilePath, strFileName;
if (theApp.OpenFileFromFileExplorer())
{
strFilePath = theApp.GetFileToOpenFromFileExplorerPath();
strFileName = PathFindFileName(strFilePath);
if (strFilePath == _T("") || strFileName == _T(""))
{
// Error!
return;
}
}
else
{
CString strTitle, strFilter;
strTitle.LoadString(IDS_STR_SELECT_SRR_FILE);
strFilter.LoadString(IDS_STR_SRR_FILTER);
CFileDialog dlgOpen(TRUE, _T(".SRR"),
nullptr, OFN_PATHMUSTEXIST | OFN_HIDEREADONLY, strFilter, this);
dlgOpen.m_ofn.lpstrTitle = strTitle;
// get a file to open from user
if (dlgOpen.DoModal() != IDOK)
return;
strFilePath = dlgOpen.GetPathName();
strFileName = dlgOpen.GetFileName();
}
// AJT V9.1.0 - Most Recent File List support
theApp.AddToRecentFileList(strFilePath);
// tell report we want to open it
dlgReport.SetFileToOpen(strFilePath, strFileName);
// display it
dlgReport.DoModal();
// AJT V9.1.0 Bug Fix
SetDayStates(m_calStart);
SetDayStates(m_calEnd);
}
The other handler works in a similar way. There are no issues with the code implemented and users can double-click a file and it will open in the software with the right editor.
Ofcourse, if my software is already running (but only on the primary dialog) and the user double clicks a file, the duplicate instance will trigger and it will simply bring that window to the foreground.
What I would like to do is this:
Is the duplicate instance on the primary window?
Bring it to the foreground.
Trigger to open this file the user has double in this attempted instance.
Shutdown this instance.
Else
Bring it to the foreground.
Not much else we can do since a modal window is open in the other instance.
So just shut down.
End if
So how do I do that bit:
Is the duplicate instance on the primary window?
Bring it to the foreground.
Trigger to open this file the user has double in this attempted instance.
Shutdown this instance.
Else
?
Update
The problem is that:
HWND hOther = nullptr;
if (DetectRunningInstance(hOther))
{
DetectFileToOpenFromFileExplorer(); // AJT v20.1.6
CString strFile = GetFileToOpenFromFileExplorerPath();
LPCTSTR lpszString = strFile.GetBufferSetLength(_MAX_PATH);
COPYDATASTRUCT cds;
cds.dwData = 1;
cds.cbData = _MAX_PATH;
cds.lpData = (LPVOID)lpszString;
DWORD dwResult;
SendMessageTimeout(hOther, WM_COPYDATA,
NULL, (LPARAM)(LPVOID)&cds, SMTO_BLOCK, 2000, &dwResult);
strFile.ReleaseBuffer();
return FALSE; // Terminates the creation
}
...
...
BOOL CMeetingScheduleAssistantDlg::OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct)
{
if (pCopyDataStruct->dwData == nnn)
{
LPCTSTR lpszString = (LPCTSTR)(pCopyDataStruct->lpData);
AfxMessageBox(lpszString);
}
return TRUE;
}
The above passes the string, even if the other instance of MSA has a modal window up. So SendMessageTimeout never actual times out.
Got!
if (GetLastActivePopup() != this)
This is what I have so far...
Once I have determined that another instance is already running, and that a file was provided on the command line, I then run this method before I cancel the duplicate instance:
void CMeetingScheduleAssistantApp::TryToOpenFileInOtherInstance(HWND hOtherInstance)
{
CString strFile = GetFileToOpenFromFileExplorerPath();
LPCTSTR lpszString = strFile.GetBufferSetLength(_MAX_PATH);
COPYDATASTRUCT cds;
cds.dwData = xxxxx;
cds.cbData = _MAX_PATH;
cds.lpData = (LPVOID)lpszString;
DWORD_PTR dwResult;
if (SendMessageTimeout(hOtherInstance, WM_COPYDATA,
NULL, (LPARAM)(LPVOID)&cds, SMTO_BLOCK, 2000, &dwResult) != 0)
{
// The message was sent and processed
if (dwResult == FALSE)
{
// The other instance returned FALSE. This is probably because it
// has a pop-up window open so can't open the file
::OutputDebugString(_T("InitInstance::SendMessageTimeout [dwResult was FALSE].\n"));
}
}
else
{
DWORD dwError = ::GetLastError();
if (dwError == ERROR_TIMEOUT)
{
// The message timed out for some reason
::OutputDebugString(_T("InitInstance::SendMessageTimeout [ERROR_TIMEOUT].\n"));
}
else
{
// Another unknown error
}
CString strError = _T("");
strError.Format(_T("InitInstance::SendMessageTimeout [%d: %s]\n"), dwError, GetLastErrorAsStringEx(dwError));
::OutputDebugString(strError);
}
strFile.ReleaseBuffer();
}
In the WM_COPYDATA message handler:
BOOL CMeetingScheduleAssistantDlg::OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct)
{
if (pCopyDataStruct->dwData == xxx)
{
LPCTSTR lpszString = (LPCTSTR)(pCopyDataStruct->lpData);
{
if (GetLastActivePopup() != this) // Popup windows!
{
// TODO: Tell user?
return FALSE;
}
theApp.SetFileToOpenFromFileExplorer(lpszString);
OpenFileFromFileExplorer();
}
}
return TRUE;
}
I was trying to adapt the CopyData approach as described here but could not get it to work — Got it to work and code is shown here.

CHtmlView::Navigate2 and locked files

I am finding that if I navigate to a file and then I try to delete the file the system tells me it is in use.
How can I stopped the files from being "in use" so I can delete it and recreate it to update the html display?
If I create a NEW XML data file every time and navigate to that then I get no problems. This is because there is no file to delete.
But the moment I use the same file I get the file in use issue.
I added code in my dialog OnDestroy method and added an array of temporary files that i create. Then I try to delete them:
for (auto i = 0; i < m_aryStrTempFiles.GetCount(); i++)
{
if (PathFileExists(m_aryStrTempFiles[i]))
{
if (!::DeleteFile(m_aryStrTempFiles[i]))
{
AfxMessageBox(theApp.GetLastErrorAsString(), MB_OK | MB_ICONERROR);
}
}
}
I find that ALL of the files are considered still in use.
The code that creates the temporary file names is not the issue:
CString CMeetingScheduleAssistantApp::GetFolderTempFilenameEx(CString strFolder, CString strToken, CString strSuffix /*_T("htm")*/)
{
CString strFile;
int i;
::GetTempFileName(strFolder, strToken, 0, strFile.GetBuffer(_MAX_PATH));
strFile.ReleaseBuffer();
// Because we will rename to .HTM we must delete old file
::DeleteFile(strFile);
// I can't be sure the suffix is .tmp so I manually
// replace the suffix, whatever it is, with .htm"
i = strFile.ReverseFind(_T('.'));
strFile = strFile.Left(i + 1);
strFile += strSuffix;
return strFile;
}
And this is the code that saves my XML files:
bool CMeetingScheduleAssistantApp::SaveToXML(CString strFileXML, tinyxml2::XMLDocument& rDocXML)
{
FILE *fStream = nullptr;
CString strError, strErrorCode;
errno_t eResult;
bool bDisplayError = false;
int iErrorNo = -1;
using namespace tinyxml2;
// Does the file already exist?
if (PathFileExists(strFileXML))
{
// It does, so try to delete it
if (!::DeleteFile(strFileXML))
{
// Unable to delete!
AfxMessageBox(theApp.GetLastErrorAsString(), MB_OK | MB_ICONINFORMATION);
return false;
}
}
// Now try to create a FILE buffer (allows UNICODE filenames)
eResult = _tfopen_s(&fStream, strFileXML, _T("w"));
if (eResult != 0 || fStream == nullptr) // Error
{
bDisplayError = true;
_tcserror_s(strErrorCode.GetBufferSetLength(_MAX_PATH), _MAX_PATH, errno);
strErrorCode.ReleaseBuffer();
}
else // Success
{
// Now try to save the XML file
XMLError eXML = rDocXML.SaveFile(fStream);
int fileCloseResult = fclose(fStream);
if (eXML != XMLError::XML_SUCCESS)
{
// Error saving
bDisplayError = true;
strErrorCode = rDocXML.ErrorName();
iErrorNo = rDocXML.GetErrorLineNum();
}
if (!bDisplayError)
{
if (fileCloseResult != 0)
{
// There was a problem closing the stream. We should tell the user
bDisplayError = true;
_tcserror_s(strErrorCode.GetBufferSetLength(_MAX_PATH), _MAX_PATH, errno);
strErrorCode.ReleaseBuffer();
}
}
}
if (bDisplayError)
{
if (iErrorNo == -1)
iErrorNo = errno;
strError.Format(IDS_TPL_ERROR_SAVE_XML, strFileXML, strErrorCode, iErrorNo);
AfxMessageBox(strError, MB_OK | MB_ICONINFORMATION);
return false;
}
return true;
}
As you can see, they all close the stream. Yet, even though in OnDestroy I delete the html view first the temporary files still can't be deleted. Why?
The issue was how I was testing for a file still being open:
bool CMeetingScheduleAssistantApp::WaitForFileToBeReady(CString strFile)
{
HANDLE hFile;
int delay = 10;
while ((hFile = CreateFile(strFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE)
{
if (GetLastError() == ERROR_SHARING_VIOLATION) {
Sleep(delay);
if (delay < 5120) // max delay approx 5.Sec
delay *= 2;
}
else
{
AfxMessageBox(theApp.GetLastErrorAsString(), MB_OK | MB_ICONINFORMATION);
return false; // some other error occurred
}
}
if (hFile != INVALID_HANDLE_VALUE)
CloseHandle(hFile);
return true;
}
I was missing the CloseHandle lines of code.

Visual Leak Detector + CFileDialog = app hang

I'm using VLD 2.4.0 to search for memory leaks in my MFC x64 app.
When I'm trying to open CFileDialog my application just hangs in the way that it just waiting for the CFileDialog to appear (which never happens).
When I don't include VLD header in my code CFileDialog works as expected.
Here is my code:
void CMainFrame::OnBtnOpen()
{
// TODO: Add your command handler code here
if (theApp.xAM->GetApplicationState() != idle)
{
return;
}
bool isFirst = true; //czy aktualnie wczytana chmura byla pierwsza(potrzebne przy wczytywaniu wielu chmur na raz)
CString csFilter = CMsg(ID_IMPORT_CLOUDS_OPEN_DIALOG_FILTER);
CFileDialog OpenDialog(TRUE, NULL, NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_ALLOWMULTISELECT, csFilter, this, 0, TRUE);
UINT maxFiles = 50;
UINT buffSize = maxFiles*(MAX_PATH + 1) + 1;
CString buffer;
OpenDialog.GetOFN().lpstrFile = buffer.GetBuffer(buffSize);
OpenDialog.GetOFN().nMaxFile = buffSize;
if (OpenDialog.DoModal() == IDOK) // HANGS ON THIS CALL
{
// set import path in data structure
POSITION POS = OpenDialog.GetStartPosition();
while (POS)
{
CString strPath = OpenDialog.GetNextPathName(POS);
std::wstring v_wsPath(strPath);
theApp.xAM->SetImportPath(v_wsPath);
DWORD thrdExitCode;
WThreadParams_ImportCloud threadParams;
if (isFirst)
{
//tworzy nowa grupe
threadParams.iCloudIndex = -1;
threadParams.iGroupIndex = -1;
isFirst = false;
}
else
{
//dopisuje chmure do ostatniej grupy
threadParams.iCloudIndex = -1;
threadParams.iGroupIndex = theApp.xAM->GetGroupsCount() - 1;
}
theApp.StartWorkerThread(ImportPointCloudsThread, (WThreadParams*)(&threadParams), &thrdExitCode);
reinterpret_cast<CSideDockablePane*>(theApp.GetSideDockablePane())->RepaintTree();
theApp.xAM->FitDataToViewport(true);
theApp.xAM->RenderScene(OpenGLRenderingCtx::eRM_STATIC);
glFinish();
}
}
reinterpret_cast<CSideDockablePane*>(theApp.GetSideDockablePane())->RepaintTree();
}
For me this is pretty big problem because that is how I input data into my app so I can test other algorithms for memory leaks.
Is there any solution so I can use VLD together with CFileDialog?

Get file type from windows registry in c++

I am trying to display file type of given filename (based on extension) using AssocQueryKey() API function.
The problem is thar return wrong HKEY value sometimes. For example the following function works correctly on win 7 ultimate x64, but fails for some extensions like ".mp3" on my win xp x86 machine (other extensions works though).
Even when "succeeded" and returns S_OK, GetLastError() is 1008 ALWAYS after AssocQueryKey() call:
// Return STL string representation of file type from windows registry
stlstring GetFileTypeFromRegistry(const stlstring& m_filename)
{
CRegKey reg;
HKEY key = {0};
stlstring s;
//Get file extension
LPCTSTR fExt = PathFindExtension(m_filename.c_str());
if(AssocQueryKey(NULL, ASSOCKEY_CLASS, fExt, TEXT(""), &key) != S_OK)
DisplayError(_T("AssocQueryKey != S_OK"), GetLastError());
else
DisplayError(_T("AssocQueryKey == S_OK"), GetLastError());
if(reg.Open ( key, NULL, KEY_QUERY_VALUE) != ERROR_SUCCESS){
reg.Close();
DisplayError((LPTSTR)fExt);
return s;
}
//DWORD out = 0;
/*WCHAR *h = new WCHAR[1024];
ZeroMemory(h, sizeof(h));
AssocQueryStringByKey(0, ASSOCSTR_EXECUTABLE, HKEY_CLASSES_ROOT, NULL, h, &out);
//MessageBox(0,_T("gbtbb"),h,MB_OK);
delete[] h;*/
ULONG m_sz = 256;
//if( reg.QueryStringValue(NULL, NULL, &m_sz) == ERROR_SUCCESS){
TCHAR *m_regstring = new TCHAR[m_sz + 1];
if(reg.QueryStringValue(NULL, m_regstring, &m_sz) == ERROR_SUCCESS){
//DisplayError(_T(""));
s += m_regstring;
/*delete[] m_regstring; m_regstring = NULL;
reg.Close();
return s;*/
} else {
DisplayError(_T("CRegKey::QueryStringValue()"), GetLastError());
}
s += m_regstring;
delete[] m_regstring; m_regstring = NULL;
reg.Close();
return s;
/*}
reg.Close();
return s;*/
}
Any ideas on this ?? This function is from a DLL which is loaded by windows explorer, implementing IQueryInfo::GetInfoTip() if that matters.
You shouldn't use GetLastError for functions that return the error code directly. The MSDN page for AssocQueryKey says "Returns S_OK if successful, or a COM error value otherwise.", which means you already get the error code in the return value.
If you just want to get the file type information, there's a much simpler solution: SHGetFileInfo. It's really simple to use, like this:
SHFILEINFO shfi;
SHGetFileInfo(filename, 0, &shfi, sizeof(shfi), SHGFI_TYPENAME | SHGFI_USEFILEATTRIBUTES);
// shfi.szTypeName now contains the file type string of the given filename

Creating a MiniDump of a running process

Im trying to make a tool for my end users that can create a MiniDump of my application if it hangs (i.e. external to the app). Im using the same code as the internal MiniDumper but with the handle and processid of the app but i keep getting error code 0xD0000024 when calling MiniDumpWriteDump. Any ideas?
void produceDump( const char* exe )
{
DWORD processId = 0;
HANDLE process = findProcess(exe, processId);
if (!process || processId == 0)
{
printf("Unable to find exe %s to produce dump.\n", exe);
return;
}
LONG retval = EXCEPTION_CONTINUE_SEARCH;
HWND hParent = NULL; // find a better value for your app
// firstly see if dbghelp.dll is around and has the function we need
// look next to the EXE first, as the one in System32 might be old
// (e.g. Windows 2000)
HMODULE hDll = NULL;
char szDbgHelpPath[_MAX_PATH];
if (GetModuleFileName( NULL, szDbgHelpPath, _MAX_PATH ))
{
char *pSlash = _tcsrchr( szDbgHelpPath, '\\' );
if (pSlash)
{
_tcscpy( pSlash+1, "DBGHELP.DLL" );
hDll = ::LoadLibrary( szDbgHelpPath );
}
}
if (hDll==NULL)
{
// load any version we can
hDll = ::LoadLibrary( "DBGHELP.DLL" );
}
LPCTSTR szResult = NULL;
int err = 0;
if (hDll)
{
MINIDUMPWRITEDUMP pDump = (MINIDUMPWRITEDUMP)::GetProcAddress( hDll, "MiniDumpWriteDump" );
if (pDump)
{
char szDumpPath[_MAX_PATH];
char szScratch [_MAX_PATH];
time_t rawtime;
struct tm * timeinfo;
time ( &rawtime );
timeinfo = localtime ( &rawtime );
char comAppPath[MAX_PATH];
SHGetFolderPath(NULL, CSIDL_COMMON_APPDATA , NULL, SHGFP_TYPE_CURRENT, comAppPath );
//COMMONAPP_PATH
_snprintf(szDumpPath, _MAX_PATH, "%s\\DN", comAppPath);
CreateDirectory(szDumpPath, NULL);
_snprintf(szDumpPath, _MAX_PATH, "%s\\DN\\D", comAppPath);
CreateDirectory(szDumpPath, NULL);
_snprintf(szDumpPath, _MAX_PATH, "%s\\DN\\D\\dumps", comAppPath);
CreateDirectory(szDumpPath, NULL);
char fileName[_MAX_PATH];
_snprintf(fileName, _MAX_PATH, "%s_Dump_%04d%02d%02d_%02d%02d%02d.dmp", exe, timeinfo->tm_year+1900, timeinfo->tm_mon, timeinfo->tm_mday, timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec );
_snprintf(szDumpPath, _MAX_PATH, "%s\\DN\\D\\dumps\\%s", comAppPath, fileName);
// create the file
HANDLE hFile = ::CreateFile( szDumpPath, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
if (hFile!=INVALID_HANDLE_VALUE)
{
MINIDUMP_CALLBACK_INFORMATION mci;
mci.CallbackRoutine = (MINIDUMP_CALLBACK_ROUTINE)MyMiniDumpCallback;
mci.CallbackParam = 0;
MINIDUMP_TYPE mdt = (MINIDUMP_TYPE)(MiniDumpWithPrivateReadWriteMemory |
MiniDumpWithDataSegs |
MiniDumpWithHandleData |
//MiniDumpWithFullMemoryInfo |
//MiniDumpWithThreadInfo |
MiniDumpWithProcessThreadData |
MiniDumpWithUnloadedModules );
// write the dump
BOOL bOK = pDump( process, processId, hFile, mdt, NULL, NULL, &mci );
DWORD lastErr = GetLastError();
if (bOK)
{
printf("Crash dump saved to: %s\n", szDumpPath);
return;
}
else
{
_snprintf( szScratch, _MAX_PATH, "Failed to save dump file to '%s' (error %u)", szDumpPath, lastErr);
szResult = szScratch;
err = ERR_CANTSAVEFILE;
}
::CloseHandle(hFile);
}
else
{
_snprintf( szScratch, _MAX_PATH, "Failed to create dump file '%s' (error %u)", szDumpPath, GetLastError());
szResult = szScratch;
err = ERR_CANTMAKEFILE;
}
}
else
{
szResult = "DBGHELP.DLL too old";
err = ERR_DBGHELP_TOOLD;
}
}
else
{
szResult = "DBGHELP.DLL not found";
err = ERR_DBGHELP_NOTFOUND;
}
printf("Could not produce a crash dump of %s.\n\n[error: %u %s].\n", exe, err, szResult);
return;
}
this code works 100% when its internal to the process (i.e. with SetUnhandledExceptionFilter)
Are you opening the process with the necessary access rights? MiniDumpWriteDump() needs the process handle to be opened using PROCESS_QUERY_INFORMATION and PROCESS_VM_READ access rights. When using GetCurrentProcess(), I think these are granted automatically, but when using OpenProcess() to open another process, you have to request these rights.
To do so, you might also have to enable SeDebugPrivilege, which would cause problems for users whose accounts don't have that privilege. But the documentation doesn't seem to be clear on whether SeDebugPrivilege is necessary for PROCESS_QUERY_INFORMATION and PROCESS_VM_READ rights specifically (as opposed to all process access rights), particularly when opening a process that is running as the same user account.
I see that you are explicitly casting MyMiniDumpCallback to be a PMINIDUMP_CALLBACK_INFORMATION type. That looks fishy, as if you had a compiler error that you were getting around because the types didn't match. That, and PMINIDUMP_CALLBACK_INFORMATION is a struct, not a function pointer.
The direct cast of function pointer to PMINIDUMP_CALLBACK_INFORMATION might be valid since the first parameter of that struct is the callback function. But again, it looks real fishy. Perhaps you misdeclared your callback function (like forgetting the CALLBACK/__stdcall modifier). Get your code to compile without casting those formal params first, then I'll be more inclined to help you.
Also, did you even check that your callback function is even getting called at all?