PRB: COleServerDoc Calls CoLockObjectExternal When Displaying a View

This article was previously published under Q297130
This article has been archived. It is offered "as is" and will no longer be updated.
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.
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.
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.
This behavior is by design. It is standard, recommended practice for object servers.
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.
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
CoLockObjectExternal AfxOleSetUserCtrl

Article ID: 297130 - Last Review: 01/05/2015 13:05:59 - Revision: 1.2

Microsoft Visual Studio 6.0 Enterprise Edition

  • kbnosurvey kbarchive kbautomation kbdocview kbprb KB297130