ActiveX plugin causes ASSERT to fail on application exit in VS2008 - c++

My MFC application using the "ESRI MapObjects LT2" ActiveX plugin throws an ASSERT at me when closing it.
The error occurs in cmdtarg.cpp:
CCmdTarget::~CCmdTarget()
{
#ifndef _AFX_NO_OLE_SUPPORT
if (m_xDispatch.m_vtbl != 0)
((COleDispatchImpl*)&m_xDispatch)->Disconnect();
ASSERT(m_dwRef <= 1); //<--- Fails because m_dwRef is 3
#endif
m_pModuleState = NULL;
}
I built the (native C++) application with VC9.
When I compile the application with VC6, it behaves nicely.
What could be the reason for this?

That looks like a reference count. Could this "target" be referenced by something else, something that's not releasing it?

You can trace the Addref and Release calls defining _ATL_DEBUG_INTERFACES
from http://msdn.microsoft.com/en-us/library/sycfy8ec(VS.80).aspx
_ATL_DEBUG_INTERFACES
Define this macro before including any ATL header files to trace all AddRef and Release calls on your components' interfaces to the output window.

Using _ATL_DEBUG_INTERFACES did not yield any additional output...
I defined it on the first line of stdafx.h, directly after #pragma once so I guess this is early enough.
Maybe the reason is how I am using the ActiveX control:
I'm not calling AddRef() or Release() by myself.
The MapObjects Installer comes with sample code with lots of wrapper classes which must have been generated by VC6 or something earlier.
I tried to generate wrapper classes myself with VC9 but there occured errors which I wasn't able to fix.
I use the control by letting one of my windows have a member of type CMap1 (derived from CWnd), which is one of those generated wrapper classes. In CMyWnd::OnCreate() I also call CMap1::Create() and that's it, I'm finished: I can add a layer and the control displays a world map.
I have pretty much no idea what the reference-count stuff is about as I have not added or released any references. At least not knowingly...
The control is pretty old: The .OCX file has the year 2000 in its version information.
It's also not officially supported anymore but I don't have any substitue.

The following solved it for me:
In the window that contains the control, add an OnDestroy() handler:
void CMyWnd::OnDestroy()
{
// Apparently we have to disconnect the (ActiveX) Map control manually
// with this undocumented method.
COleControlSite* pSite = GetOleControlSite(MY_DIALOG_CONTROL_ID);
if(NULL != pSite)
{
pSite->ExternalDisconnect();
}
CWnd::OnDestroy();
}

Related

CWinApp OpenDocumentFile "unsupported operation" error

Problem
I'm trying to open an MFC program that reads a Microsoft Access database (.mdb) and allows the user to view or modify the data. This is an existing program (and source code) given to me by a group in another lab where the program opens and works just fine.
In our lab, I have yet to see it load properly. When run, it pops up a dialog box that says, "Attempted an unsupported operation". Windows then offers me a chance to debug and such before it crashes.
Environment
In the other lab, they use Windows 7 and Microsoft Office 2010, and it works.
In our lab, I've tried Windows 7 with Office 2013 and Windows XP with Office 2010. The latter crashes without giving me the dialog box. I don't know if we have a Win7/MSO2010 machine.
The Function
I have the source code for the program. The solution file implies it was last developed in VS2010, which the computers I tested on had installed as well. Running it out of Visual Studio 2010 or straight from the executable yields the same results.
I have added additional debug dialog boxes to the code that narrow down the problem to this function call, which the code never gets past:
CwinApp:OpenDocumentFile(LPCTSTR lpszPathName)
The single string passed into the function is a path and filename for the MS Access database to be opened. It exists in a temporary directory created by another program. This is on a drive other than C, though I've tested some there, as well. Problems with programs related to this one often stem from files with "read only" status, but I continually check the temporary files created, and they are write-able.
Documentation
I found this information titled "Breaking Changes in Visual C++" for VS2010 through another SO question:
A new virtual function was added to the CDocTemplate class. This new virtual function is CDocTemplate::OpenDocumentFile. The previous version of OpenDocumentFile had two parameters. The new version has three parameters. To support the restart manager, any class derived from CDocTemplate must implement the version that has three parameters. For more information about the function, see CDocTemplate::OpenDocumentFile. The new parameter is bAddToMRU.
Code
I feel this might be the answer! But I don't have a strong idea of exactly what to change to get this to work. Here's where I stopped:
Program.cpp
CDocument* ProgramApp::OpenDocumentFile(LPCTSTR lpszFileName, BOOL bAddToLRU, BOOL bMakeVisible)
{
// Add specialized code here and/or call base class
// Debug messages added
CDocument* tempDoc;
AfxMessageBox(lpszFileName);
tempDoc = CWinApp::OpenDocumentFile(lpszFileName, bAddToMRU);
AfxMessageBox("Opened database!");
return tempDoc;
}
Program.h
class ProgramApp : public CWinApp
{
public:
...
virtual CDocument* OpenDocumentFile(LPCTSTR lpszFileName, BOOL bAddToMRU, BOOL bMakeVisible);
afxwin.h
class CWinApp : public CWinThread
{
...
virtual CDocument* OpenDocumentFile(LPCTSTR lpszFileName);
virtual CDocument* OpenDocumentFile(LPCTSTR lpszFileName, BOOL bAddToMRU);
Changing the Program.cpp call to be from a "Template" class caused errors, but I wouldn't be surprised if that's towards the answer. This solution looks similar, but I'm not sure exactly what to do.
My hope is that this problem has a simple solution that someone more knowledgeable can give me. I would be much appreciative, and additional context would help a lot.
Edit: Debugging
I drilled down into the Windows code to see what was precisely going wrong. It seemed too dense to understand, but a coworker and I may have clues based on it. The failure message happens here:
dlgdata.cpp
// Could be a windowless OCX
pSite = m_pDlgWnd->GetOldControlSite(nIDC);
if (pSite == NULL)
{
TRACE(traceAppMsg, 0, "Error: no data exchange control with ID 0x%04x.\n", nIDC);
ASSERT(FALSE);
AfxThrowNotSupportedException();
}
...Although we are seeing debugger issues here:
occcont.cpp
COleControlSiteOrWnd *pemp = new COleControlSiteOrWnd(hwndCtrl, pOccDlgInfo->m_pItemInfo[i].bAutoRadioButton);
ASSERT(IsWindow(pTemp->m_hWnd));
if (IsWindow(pTemp->m_hWnd))
{
hwndStart = pTemp->m_hWnd;
...
My coworker believes this could have little to do with the opening of this document as I suspected and more to do with objects/controls we don't have on our lab computers trying to be used for the program.
I have faced the same problem in opendocumentfile(), there was a control in CFormView class which i was not using so i commented it out but forget to delete from .rc file. Once i remove the control entry from .rc file the problem disappear.
there should be no control variable uninitialize , please check that also

Does anyone know which relation may exist between registration-free COM and drag/drop functionality?

Does anyone know which relation may exist between registration-free COM and drag/drop functionality?
Specifically, we have a huge C++ CAD/CAM application comprising a number of EXEs and several hundreds DLLs. Many of them serve as COM servers (both in-proc and out-of-proc) and/or clients, and also implement ActiveX controls.
The most of ActiveX controls and the main CMDIFrameWnd-based window of one of EXEs implement drag/drop functionality. ActiveX controls implement the both drop source and drop target, and the main window is only drop target, in particular, for files from Windows Explorer.
The drag/drop implementation is pretty standard and based on two data members derived from COleDataSource and COleDropTarget for drop source and drop target respectively. The COleDropTarget-derived member is registered with respective window in the window's OnCreate method. It also overrides OnDragEnter, OnDragOver and OnDrop methods in a similar way. Namely, the system-supplied COleDataObject parameter is asked for specific format (in particular, CF_HDROP), and in the case of positive answer, the data (e.g., file path) is extracted from the clipboard. The code looks like the following:
static FORMATETC g_FileFmt = {CF_HDROP, 0, DVASPECT_CONTENT, 0, TYMED_HGLOBAL};
....
// Inside OnDragEnter, OnDragOver or OnDrop method
STGMEDIUM stgmedium = {0,0,0};
if (pDataObject->IsDataAvailable(g_FileFmt.cfFormat))
{
HRESULT hr = pDataObject->GetData(g_FileFmt.cfFormat, &stgmedium);
HDROP hdrop = (HDROP)GlobalLock(stgmedium.hGlobal);
if (hdrop != 0)
{
int FilesCount = DragQueryFile(hdrop, (UINT)-1, 0, 0);
if (FilesCount != 0)
{
TCHAR FileName[_MAX_PATH];
DragQueryFile(hdrop, 0, FileName, _MAX_PATH);
// Check file extension and store the file name for farther use.
}
GlobalUnlock(hdrop);
}
}
The drop source implementation is also straightforward and looks like the following:
void CDmDocListCtrl::OnBeginDrag(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
if (pNMListView->iItem != -1 && m_pOleDataSource && prv_BeginDrag())
{
DROPEFFECT DE = m_pOleDataSource->DoDragDrop(
DROPEFFECT_COPY | DROPEFFECT_MOVE | DROPEFFECT_LINK, 0);
}
*pResult = 0;
}
where prv_BeginDrag() function collects dragged data, packs it and puts on the clipboard by calling SetData method from the m_pOleDataSource object's IDataObject interface.
The all this stuff worked perfectly until it was decided to make the whole application registration-free. It took me three months to force the application run isolated (without registration of COM components) by embedding manifests, launching out-of-proc COM servers on demand and altering CLSID of some classes in order to separate instances of the same server launched from different folders. At last it begins to work - but without drag/drop functionality, despite it wasn't even touched by my changes.
On the drop target side, when I drag file from Windows Explorer, depicted above call to COleDataObject::IsDataAvailable returns false, although before my changes returned true. At the same time, if I add a single line of code "DragAcceptFiles();" to the main window's OnCreate method, drag/drop begins working via the standard CFrameWnd's WM_DROPFILE message handler.
On the drop source side, the dragged data are successfully packed and placed on the clipboard, but COleDataSource::DoDragDrop method fails, because a call to ::DoDragDrop API inside MFC implementation returns REGDB_E_CLASSNOTREG "Class not registered" result.
It means, that COM activation changes somehow influence drag/drop behavior. How?
P.S. 1) The EXE, to which I drag files from Windows Explorer, has in its project properties "UAC Execution Level = asInvoker". As far as I understand, it tells that the EXE will run at the same UAC level as Windows Explorer when launched by double-click on the file.
2) Quite surprisingly, although drag/drop stopped working with symptoms described above, Copy/Paste continues work well, despite the both technologies have similar implementation.
3) I believe, that if find out when ::DoDragDrop API returns "Class not registered" error, and which class it is looking for, it would be possible to solve the problem.
Thanks for help,
Ilia.
Following to MartinBa advice, I solved the problem with the help of Process Monitor. The Process Monitor showed me that while I drag an item in the ActiveX control (mentioned in the question), the system unsuccessfully tries get access to a class ID in the Registry. Looking for that ID, I found that it is really not class ID, but IDataObject interface ID. It was referenced in one of my manifest files.
The most of manifests I have written by hand, but a few, especially at the beginning of the project having no experience in the area, I generated automatically by Visual Studio from existing type library. In one of them Studio included the comInterfaceExternalProxyStub statement for a couple of system interfaces, in which proxyStubClsid32 element was (erroneously) equal to the interface ID.
I'm still not sure whether those system interfaces should present in the manifest; for example, the IDataObject is only mentioned as a method's parameter in one of IDL definitions. Anyway, I corrected only the proxyStubClsid32 value, and the problem disappeared...
The moral of this very painful for me story is to always check output of automatic tools...

CMyPrintDialog::OnInitDialog() is not called in IE10

I have a class named CMyPrintDialog derived from CPrintDialog of MFC. It's used as an ActiveX in IE, and the project is linked to MFC statically. It works well in IE8 for many years.
But the same binary of the project doesn't work well on IE10. The print dialog could pop up, but unfortunately its OnInitDialog is never called and causes problems.
The strange thing is that if I attach the VS debugger to IE, OnInitDialog will be called correctly and the customized print dialog works well.
Seems Microsoft has change something and causes the problem.
I found a similar link but it doesn't work.
Thanks a million for any ideas.
The issue is caused by IE's new feature after IE9 - "Hang Resistance". We can avoid the issue by disabling the new feature: Set below value as 0, and close all IE windows. HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Main\ Type: REG_DWORD Name: HangRecovery Value: 0
I am not getting your point exactly you want to say, Consider CPrintDialog crash your application with IE10.What is you need to do is,
Set up the message hook function, _AfxCommDlgProc(), in the constructor of your
CPrintDialog-derived class as shown below:
// CMyPrintDialog is a CPrintDialog-derived class.
CMyPrintDialog::CMyPrintDialog(DWORD dwFlags)
: CPrintDialog(FALSE,dwFlags)
{
//{{AFX_DATA_INIT(CMyPrintDialog)
//}}AFX_DATA_INIT
// MFCBUG: MFC 6.0 doesn't set the message hook!
m_pd.Flags |= PD_ENABLEPRINTHOOK | PD_ENABLESETUPHOOK;
// _AfxCommDlgProc is exported from static MFC libraries
m_pd.lpfnPrintHook = _AfxCommDlgProc;
m_pd.lpfnSetupHook = _AfxCommDlgProc;
}

MFC app Assert fail at CRecentFileList::Add on Command line FileOpen

I'm using VS2010 and Windows 7, and my app is SDI shared DLL, upgraded from VC6. After installing my application, if the user double-clicks the registered file type, the application crashes at the MFC function:
void CRecentFileList::Add(LPCTSTR lpszPathName, LPCTSTR lpszAppID)
{
// ...
#if (WINVER >= 0x0601)
// ...
#ifdef UNICODE
// ...
#endif
ENSURE(SUCCEEDED(hr)); // Crash here: "hr = 0x800401f0 CoInitialize has not been called."
This is called from the InitInstance() function:
// Parse command line for standard shell commands, DDE, file open
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
//CString str = cmdInfo.m_strFileName + '\n';
//MessageBox(NULL,str, "MyApp", MB_OK|MB_ICONWARNING);
// Dispatch commands specified on the command line
if (!ProcessShellCommand(cmdInfo))
return FALSE;
The user's chosen file is correctly passed through (as I checked with the MessageBox).
The hr = 0x800401f0 seems to be a COM problem (here), but I'm not using COM or ATL. The assertion is the same as this, but from a different cause. The Germans had the same problem as me (here), but I can't understand the google translation (here)!! I don't think it's a WINVER issue (here) and I don't want to parse my own stuff (like this), just have the application open when a user double clicks a file.
Thanks for any help you can offer :)
The comment you inserted in your code contains the answer:
// Crash here: "hr = 0x800401f0 CoInitialize has not been called."
The HRESULT value is telling you that you need to call the CoInitialize function in order to initialize the COM library for your application's thread.
Of course, the message is a little bit outdated. As you'll see in the above-linked documentation, all new applications should call the CoInitializeEx function instead. No worries, though: it does essentially the same thing as its older brother.
As the "Remarks" section of the documentation indicates:
CoInitializeEx must be called at least once, and is usually called only once, for each thread that uses the COM library. [. . . ] You need to initialize the COM library on a thread before you call any of the library functions except CoGetMalloc, to get a pointer to the standard allocator, and the memory allocation functions. Otherwise, the COM function will return CO_E_NOTINITIALIZED.
You say that you're not using COM, but this is incorrect. You may not be using it explicitly, but Windows and the MFC framework are definitely using it "behind the scenes". All of the file type registration functions rely on COM. The skeleton code produced by the MFC project wizard in Visual Studio 2010 would have automatically inserted the appropriate COM registration code, but since you upgraded an existing project from VC++ 6, you appear to be missing this vital step.
In MFC, the AfxOleInit function also initializes COM for the current apartment of the calling app, just as the OleInitialize function does internally. Make sure that your overridden InitInstance function contains a call to one of these functions.
For example, in a fresh new MFC project created by the VS 2010 wizard, the InitInstance function looks something like this:
BOOL CTestApp::InitInstance()
{
// InitCommonControlsEx() is required on Windows XP if an application
// manifest specifies use of ComCtl32.dll version 6 or later to enable
// visual styles. Otherwise, any window creation will fail.
INITCOMMONCONTROLSEX InitCtrls;
InitCtrls.dwSize = sizeof(InitCtrls);
// Set this to include all the common control classes you want to use
// in your application.
InitCtrls.dwICC = ICC_WIN95_CLASSES;
InitCommonControlsEx(&InitCtrls);
CWinApp::InitInstance();
// Initialize OLE libraries
if (!AfxOleInit()) // ** MAKE SURE THAT YOU CALL THIS!! **
{
AfxMessageBox(IDP_OLE_INIT_FAILED);
return FALSE;
}
AfxEnableControlContainer();
// . . .
// a bunch more boring initialization stuff...
// The one and only window has been initialized, so show and update it
pFrame->ShowWindow(SW_SHOW);
pFrame->UpdateWindow();
return TRUE;
}

"Debug Assertion" Runtime Error on VS2008?

I'm writing a C++ MFC program on VS2008 and I'm getting this "Debug Assertion Error" when I first run the program sometimes. When I try to debug it, it takes me to this winhand.cpp file which is not part of the program I wrote so I'm not sure how to debug this.
It takes the error to this place in winhand.cpp
CObject* pTemp = LookupTemporary(h);
if (pTemp != NULL)
{
// temporary objects must have correct handle values
HANDLE* ph = (HANDLE*)((BYTE*)pTemp + m_nOffset); // after CObject
ASSERT(ph[0] == h || ph[0] == NULL);
if (m_nHandles == 2)
ASSERT(ph[1] == h);
}
So why does this error happen? Why does it only happen sometimes (50% of the time)? How would I debug this?
I'll provide some code if is needed.
THANKS!
The code that is asserting is part of MFC's CHandleMap class. MFC deals with windows as CWnd objects, but Windows deals with them as HWND handles. the handle map allows MFC to 'convert' an HWND into a pointer to the MFC object representing that object.
What the assertion seems to be doing is checking that when a lookup of the handle finds an MFC object, that the MFC object also thinks it's wrapping the same handle.
If they're different, then you get the assertion.
So it would appear that something is corrupting the handle map or the MFC object for that handle or you're doing something incorrect that gets these 2 data structures out of sync.
Some things you might do to try to debug the problem is to determine:
what MFC object is being found in the lookup (that's what's being pointed to by pObject)
what the MFC object thinks it's wrapping (that's the handle ph[0] and/or ph[1] - I'm not sure why there can be 2 of them)
what the handle is for (that's h)
Do the handles look like handle values or do they look like garbage? Does pObject point to something that looks like an MFC object, or garbage? Do any of these these things seem related?
The answers to these questions may point to what you need to do next (maybe set a debug write breakpoint on the item that looks like it's trashed).
I got this same assertion few days ago, and after some google search,
I found the solution for my case here:
http://forums.codeguru.com/showthread.php?216770-What-would-cause-this-assertion
In my case, change to misused
CDC* dc = GetDC();
CSize spaceSize = dc->GetTextExtent(" ");
dc->DeleteDC();
to
CDC* dc = GetDC();
CSize spaceSize = dc->GetTextExtent(" ");
ReleaseDC(dc);
would fix it.
Look out for code along those lines (from memory from Stroustrup's book):
c1 = (t2+t3).c_str();
(in spirit, could be other commands and expressions of course).Temporary objects are destroyed after their enclosing full expression has been evaluated, or at least the standard allows them to be. That means that what you would like to allocate to c1 may, or may not, still be in memory where it can be assigned to c1. The compiler may alert you to this issue, and the issue may or may not arise depending on what exactly you assign and other circumstances (I am not compiler writer), which would also explain why you get this error message only sometimes.
So in your shoes, I'd scan my code for similar expressions and clean them up.
When the debugger breaks, head up the call stack to the first bit of your code (if there is any - hopefully there is!). Ideally it's as simple as something in your code calling a library function incorrectly, and the library is catching the error with an assert and alerting you to that. (I don't think anyone will be able to tell what's wrong from the library code, we need to see your code.)
Otherwise, you're in for some tricky debugging: you're doing something wrong with the library that is asserting (looks like MFC) so go back and review all your MFC code and make sure everything is correct and according to the documentation.
This looks suspiciously like an error I had this morning. Is this happening in OnIdle()?
I know this is a very old post, but hoping that someone may get a little help from my answer.
I also faced a similar issue recently because of my simple mistake, then I came across this post and got a hint from "pac"'s post.
What I found is that if I use DeleteDC() to release DC returned from GetWindowDC() or GetDC() I will get the above assertion in MFC frame once CPaintDC object instance goes out of scope.
CDC * pDC = GetWindowDC();
...
ReleaseDC(pDC);
You have to use DeleteDC() only in conjunction with CreateDC() API.
CDC * pDC = new CDC();
pDC->CreateDC();
....
pDC->DeleteDC();
We had this problem when some of our project dlls were linking MFC as static library and some as shared library (check "Use of MFC" in Project settings)