PRB: COleServerDoc Calls CoLockObjectExternal When Displaying a View (297130)



The information in this article applies to:

  • Microsoft Visual Studio, Enterprise Edition 6.0

This article was previously published under Q297130

SYMPTOMS

If a Microsoft Foundation Classes (MFC) document view architecture-based executable file is automated, the view can be displayed to the user through Automation, usually by adding a Visible property that the client can set to TRUE, enabling the server to make its windows visible in response. When this occurs, MFC places the server under user control by calling AfxOleSetUserCtrl(TRUE). When the last reference count is released, MFC will shut down the application only if the application is no longer under user control, so the standard practice is to call AfxOleSetUserCtrl(FALSE) when the window is no longer visible.

When the server's document class is based on COleServerDoc, this technique is ineffective, and the server remains in memory.

CAUSE

A COleServerDoc-based document class makes a call to CoLockObjectExternal when its views are made visible. The syntax of the call is effectively CoLockObjectExternal(lpUnk, TRUE, TRUE). This means that the object is locked in memory independent of external AddRef/Release operations. As a result, the object stays in memory until this call is undone.

RESOLUTION

The way to work around this problem is to call CoLockObjectExternal, passing FALSE as the second parameter when the views are no longer visible. MFC provides a wrapper function called LockExternal to simplify this call. The syntax to unlock the server would be:
LockExternal(FALSE, TRUE);
				

Another approach, which may be simpler but could introduce potential complications, would be to override the OnShowViews function in the document class. The default for this function is to call UpdateVisibleLocks, which eventually calls CoLockObjectExternal only when a view is visible. This is correct for an embedding scenario, which is what COleServerDoc is designed to manage, but in Automation the matching call is made after the view has been hidden, so the corresponding call to undo the lock is never made. One approach would be to simply call UpdateVisibleLocks unconditionally, regardless of whether the view is visible.

Another approach would be to do nothing and leave the OnShowViews overridden function empty. These overrides could present problems in an embedding scenario, however, and are not recommended. Adding a call to LockExternal (or UpdateVisibleLocks) in the Automation method, as described earlier, would be preferable.

STATUS

This behavior is by design. It is standard, recommended practice for object servers.

MORE INFORMATION

For more information, see the Component Object Model/OLE article "Managing Object Lifetimes" Douglas Hodges on MSDN Online:

Steps to Reproduce Behavior

Create an MFC full server project that supports Automation. Add a Get/Set Visible property to the document class by using the Automation tab in the Class Wizard. Add a member variable m_bVisible of type BOOL to the document class. Implement Get and Set methods for this property, as shown in the following code:
void CAutoEXEDoc::SetVisible(BOOL bNewValue) 
{
	if (m_bVisible != bNewValue)
	{
		m_bVisible = bNewValue;

		if (m_bVisible)
		{
			AfxGetMainWnd()->ShowWindow(SW_SHOW);
			AfxGetMainWnd()->UpdateWindow();
		}
		else
		{
			AfxGetMainWnd()->ShowWindow(SW_HIDE);
			LockExternal(FALSE, TRUE);
		}
	}
}

BOOL CAutoEXEDoc::GetVisible() 
{
	return m_bVisible;
}

				

Add a simple method of any kind for test purposes. For example:
void CCoLockDoc::Test() 
{
	AfxMessageBox("Test method in MFC/COM server");
}
				

Next, create a client, such as an MFC dialog application. Add a wrapper class for the MFC server by reading the type library of your server project. Add a button to the dialog box and execute a simple test of the server with code such as the following:
void CAutoClientDlg::OnButton1() 
{
	m_pAutoEXE = new IAutoEXE;
	if (!m_pAutoEXE->CreateDispatch(clsid_AutoEXE))
	{
		AfxMessageBox("CreateDispatch failed for AutoEXE!");
		delete m_pAutoEXE;
		return;
	}

	m_pAutoEXE->SetVisible(TRUE);
	m_pAutoEXE->Test();
	m_pAutoEXE->SetVisible(FALSE);

	delete m_pAutoEXE;
	m_pAutoEXE = NULL;
}
				

If the call to LockExternal is omitted when the Visible property is set to FALSE, the server will remain in memory.

REFERENCES

See the following Visual C++ Programmer's Guide topics:
For sample code to use with generic MFC automation servers, see the following Microsoft Knowledge Base article:

140591 HOWTO: Display an MFC Automation Document Automatically


Modification Type:MinorLast Reviewed:8/15/2005
Keywords:kbAutomation kbDocView kbprb KB297130