www.pudn.com > pueblo.zip > ChDDESrv.cpp


/*---------------------------------------------------------------------------- 
                        _                              _ _        
        /\             | |                            | (_)       
       /  \   _ __   __| |_ __ ___  _ __ ___   ___  __| |_  __ _  
      / /\ \ | '_ \ / _` | '__/ _ \| '_ ` _ \ / _ \/ _` | |/ _` | 
     / ____ \| | | | (_| | | | (_) | | | | | |  __/ (_| | | (_| | 
    /_/    \_\_| |_|\__,_|_|  \___/|_| |_| |_|\___|\__,_|_|\__,_| 
 
    The contents of this file are subject to the Andromedia Public 
	License Version 1.0 (the "License"); you may not use this file 
	except in compliance with the License. You may obtain a copy of 
	the License at http://www.andromedia.com/APL/ 
 
    Software distributed under the License is distributed on an 
	"AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or 
	implied. See the License for the specific language governing 
	rights and limitations under the License. 
 
    The Original Code is Pueblo client code, released November 4, 1998. 
 
    The Initial Developer of the Original Code is Andromedia Incorporated. 
	Portions created by Andromedia are Copyright (C) 1998 Andromedia 
	Incorporated.  All Rights Reserved. 
 
	Andromedia Incorporated                         415.365.6700 
	818 Mission Street - 2nd Floor                  415.365.6701 fax 
	San Francisco, CA 94103 
 
    Contributor(s): 
	-------------------------------------------------------------------------- 
	   Chaco team:  Dan Greening, Glenn Crocker, Jim Doubek, 
	                Coyote Lussier, Pritham Shetty. 
 
					Wrote and designed original codebase. 
 
------------------------------------------------------------------------------ 
 
	This file consists of the Chaco DDE server interface to WEB clients. 
 
----------------------------------------------------------------------------*/ 
 
#include "grheader.h" 
 
#include  
#include  
#include  
#include  
#include "ChDDEPrv.h" 
 
//	Serialization, runtime info, dynamic creation. 
IMPLEMENT_SERIAL( CDDEConversation, CObject, 1 ) 
 
//	Static initialization. 
CObList *CDDEConversation::m_pcolConversations = 0; 
 
 
///////////////////////////////////////////////////////////////////////////// 
// CDDEConversation  
//	Construction 
CDDEConversation::CDDEConversation()	 
{ 
	m_hConv = NULL; 
	CommonConstruction(); 
} 
 
CDDEConversation::CDDEConversation(HCONV hConv)	 
{ 
	m_hConv = hConv; 
	CommonConstruction(); 
} 
 
///////////////////////////////////////////////////////////////////////////// 
// CDDEConversation  
void CDDEConversation::CommonConstruction()	 
{ 
	//	Initialize everything but the conversation handle. 
	if ( m_pcolConversations == 0 ) 
	{ 
		m_pcolConversations = new CObList( 20 ); 
		ASSERT( m_pcolConversations ); 
	} 
	m_rIndex = m_pcolConversations->AddTail(this); 
	 
	//TRACE("Creating Covnersation %p\n", this); 
} 
 
///////////////////////////////////////////////////////////////////////////// 
// CDDEConversation  
//	Destruction. 
CDDEConversation::~CDDEConversation()	 
{ 
	//	Remove ourselves from the list of running conversations. 
	m_pcolConversations->RemoveAt(m_rIndex); 
 
	if ( m_pcolConversations->GetCount() == 0 ) 
	{ 
		delete m_pcolConversations; 
		m_pcolConversations = 0; 
	} 
	 
	//TRACE("Destroying Conversation %p\n", this); 
} 
 
///////////////////////////////////////////////////////////////////////////// 
// CDDEConversation  
ChHTTPDDE *CDDEConversation::GetDDEConn()	 
{ 
	CDDEObject *pDDE = CDDEObject::ResolveConversation(this); 
	if(pDDE == NULL)	 
	{ 
		return(NULL); 
	} 
	return pDDE->GetDDEConn(); 
} 
 
///////////////////////////////////////////////////////////////////////////// 
// CDDEConversation  
//	Proactive disconnection. 
void CDDEConversation::DoDisconnect()	 
{ 
	//	Just disconnect from the current HCONV. 
	DdeDisconnect(m_hConv);	 
	GetDDEConn()->Display(ExplainError()); 
	CDDEObject::DDEDisconnect(this); 
	delete this; 
} 
 
///////////////////////////////////////////////////////////////////////////// 
// CDDEConversation  
//	Informaitonal Lookup. 
CDDEConversation *CDDEConversation::ResolveConversation(HCONV hConv)	 
{ 
	//	Loop through all conversations, and return the obect handling the 
	//		conversation. 
	POSITION rTraverse = m_pcolConversations->GetHeadPosition(); 
	CDDEConversation *pRetVal = NULL; 
	while(rTraverse != NULL)	 
	{ 
		pRetVal = (CDDEConversation *)m_pcolConversations->GetNext(rTraverse); 
		if(pRetVal->m_hConv == hConv)	 
		{ 
			break; 
		} 
		else	 
		{ 
			pRetVal = NULL; 
		} 
	} 
	 
	return(pRetVal); 
} 
 
//	Utility 
void CDDEConversation::ClientPassed(HSZ hszArgs, const char *pFormat, ...)	 
{ 
	//	Initialize variable number of argumetns. 
	va_list VarList; 
	va_start(VarList, pFormat);	 
	 
	int i_ArgNum = 0; 
	char *pScan = (char *)pFormat; 
	char *pExtract; 
	 
	//	Loop through the arguments we are going to parse. 
	while(pScan && *pScan)	 
	{ 
		//	What argument are we currently looking for? 
		i_ArgNum++; 
		pExtract = ExtractArgument(hszArgs, i_ArgNum); 
	 
		if(0 == strncmp(pScan, "DW", 2))	 
		{ 
			//	DWORD. 
			DWORD *pWord; 
			pWord = va_arg(VarList, DWORD *); 
			 
			if(pExtract == NULL)	 
			{ 
				*pWord = 0x0; 
			} 
			else	{ 
				sscanf(pExtract, "%lu", pWord); 
			} 
		} 
		else if(0 == strncmp(pScan, "QCS", 3))	 
		{ 
			//	A quoted CString 
			CString *pCS = va_arg(VarList, CString *); 
			 
			if(pExtract == NULL)	 
			{ 
				pCS->Empty(); 
			} 
			else	 
			{ 
				//	Extractions took off the leading and ending quotes. 
				char *pCopy = pExtract; 
				while(*pCopy)	{ 
					if(*pCopy == '\\' && *(pCopy + 1) == '\"')	 
					{ 
						pCopy++; 
					} 
					 
					*pCS += *pCopy; 
					pCopy++; 
				} 
			} 
		} 
		else if(0 == strncmp(pScan, "CS", 2))	 
		{ 
			//	A CString 
			CString *pCS = va_arg(VarList, CString *); 
 
			if(pExtract == NULL)	 
			{ 
				pCS->Empty(); 
			} 
			else	{ 
				*pCS = pExtract; 
			} 
		} 
		else if(0 == strncmp(pScan, "BL", 2))	 
		{ 
			//	A boolean 
			TwoByteBool *pBool = va_arg(VarList, TwoByteBool *); 
			 
			if(pExtract == NULL)	 
			{ 
				*pBool = FALSE; 
			} 
			else	 
			{ 
				//	Compare for a TRUE or a FALSE 
				if(0 == stricmp(pExtract, "TRUE"))	 
				{ 
					*pBool = TRUE; 
				} 
				else	{ 
					*pBool = FALSE; 
				} 
			} 
		} 
		 
		//	Go on to the next argument in our format string. 
		pScan = SkipToNextArgument(pScan); 
		 
		//	Free the memory that was used during extraction. 
		if(pExtract != NULL)	 
		{ 
			delete pExtract; 
		} 
	} 
	 
	//	Done with variable number of arguments 
	va_end(VarList); 
} 
 
HDDEDATA CDDEConversation::ServerReturns( HSZ hszItem, const char *pFormat, ...)	 
{ 
	va_list VarList; 
	va_start(VarList, pFormat); 
 
	char *pTraverse = (char *)pFormat; 
	char caNumpad[64]; 
	CString csBuffer; 
	CString csRetval; 
	 
	while(*pTraverse)	 
	{ 
		//	Erase temp data from our last pass. 
		caNumpad[0] = '\0'; 
		csBuffer.Empty(); 
	 
		//	Compare our current format to the known formats 
		if(0 == strncmp(pTraverse, "DW", 2))	 
		{ 
			//	A DWORD. 
			DWORD *pWord = va_arg(VarList, DWORD *); 
			 
			if(pWord != NULL)	 
			{ 
                //  See if we're to use hex or not. 
                if(FALSE)   
                { 
                    sprintf(caNumpad, "%#lx", *pWord); 
                } 
                else     
                { 
				    sprintf(caNumpad, "%lu", *pWord); 
                } 
				csRetval += caNumpad; 
			} 
		} 
		else if(0 == strncmp(pTraverse, "CS", 2))	 
		{ 
			//	A CString, not quoted 
			CString *pCS = va_arg(VarList, CString *); 
			 
			if(pCS != NULL)	{ 
				csRetval += *pCS; 
			} 
		} 
		else if(0 == strncmp(pTraverse, "QCS", 3))	 
		{ 
			//	A quoted CString 
			CString *pQCS = va_arg(VarList, CString *); 
			 
			if(pQCS != NULL)	 
			{ 
				csRetval += '\"'; 
				 
				//	Need to escape any '"' to '\"', literally. 
				char *pConvert = (char *)(const char *)*pQCS; 
				while(*pConvert != '\0')	 
				{ 
					if(*pConvert == '\"')	 
					{ 
						csRetval += '\\'; 
					} 
					csRetval += *pConvert; 
					pConvert++; 
				} 
				csRetval += '\"'; 
			} 
		} 
		else if(0 == strncmp(pTraverse, "BL", 2))	 
		{ 
			//	A boolean 
			TwoByteBool *pBool = va_arg(VarList, TwoByteBool *); 
			 
			if(pBool != NULL)	{ 
				if(*pBool != FALSE)	{ 
					csRetval += "TRUE"; 
				} 
				else	{ 
					csRetval += "FALSE"; 
				} 
			} 
		} 
 
		 
		pTraverse = SkipToNextArgument(pTraverse); 
		 
		if(*pTraverse != '\0')	 
		{ 
			csRetval += ','; 
		} 
	} 
 
	//	Done with varargs. 
	va_end(VarList); 
	 
	if(csRetval.IsEmpty())	 
	{ 
		return(NULL); 
	} 
 
	HDDEDATA Final; 
	Final = DdeCreateDataHandle(CDDEObject::m_dwidInst, 
		#ifdef _WIN32 
        (unsigned char *)(const char *)csRetval, 
		#else 
		(void *)(const char *)csRetval,  
		#endif // _WIN32 
 		csRetval.GetLength() + 1, 0, 
 		hszItem, 
		CF_TEXT, 0); 
	GetDDEConn()->Display(ExplainError()); 
 
	//	THIS IS A HACK. 
	if(strchr(pFormat, ',') == NULL)	 
	{ 
		if(strcmp(pFormat, "BL") == 0)	 
		{ 
			TwoByteBool bData = FALSE; 
			if(csRetval == "TRUE")	 
			{ 
				bData = TRUE; 
			} 
			DdeFreeDataHandle(Final); 
			GetDDEConn()->Display(ExplainError()); 
			 
			Final = DdeCreateDataHandle(CDDEObject::m_dwidInst, 
				(unsigned char *)&bData, sizeof(TwoByteBool), 0, 
				hszItem, CF_TEXT, 0); 
			GetDDEConn()->Display(ExplainError()); 
		} 
		else if(strcmp(pFormat, "DW") == 0)	 
		{ 
			DWORD dwData; 
			dwData = strtoul(csRetval, NULL, 0); 
			DdeFreeDataHandle(Final); 
			GetDDEConn()->Display(ExplainError()); 
			 
			Final = DdeCreateDataHandle(CDDEObject::m_dwidInst, 
				(unsigned char *)&dwData, sizeof(DWORD), 0, 
				hszItem, CF_TEXT, 0); 
			GetDDEConn()->Display(ExplainError()); 
		} 
	} 
 
	return(Final); 
} 
 
//	Handle DDE Pokes 
HDDEDATA CDDEConversation::ServerPoke(HSZ hszTopic, HSZ hszItem, HDDEDATA hDataPoke)	 
{ 
	//	Switch on the topic we're handling. 
	int iTopic = CDDEObject::EnumTopic(hszTopic); 
	switch(iTopic)	 
	{ 
		case CDDEObject::m_EndProgress: 
			return(WWW_EndProgress(hszItem)); 
		case CDDEObject::m_SetProgressRange: 
			return(WWW_SetProgressRange(hszItem)); 
//		case CDDEObject::m_URLEcho: 
//			return(WWW_URLEcho(hszItem)); 
		case CDDEObject::m_ViewDocFile: 
			return(WWW_ViewDocFile(hszItem)); 
//		case CDDEObject::m_WindowChange: 
//			return(WWW_WindowChange(hszItem)); 
		case CDDEObject::m_OpenURLResult: 
			return(WWW_OpenURLResult(hszItem)); 
		case CDDEObject::m_BeginProgress: 
			return(WWW_BeginProgress(hszItem)); 
 
	} 
 
	TRACE("Unknown poke received\n"); 
	return(DDE_FNOTPROCESSED); 
} 
 
HDDEDATA CDDEConversation::WWW_EndProgress(HSZ hszItem)	 
{ 
	DWORD dwTransactionID; 
	 
	ClientPassed(hszItem, "DW", &dwTransactionID); 
	 
	GetDDEConn()->EndProgress( dwTransactionID ); 
 
	return(DDEFACK); 
} 
 
HDDEDATA CDDEConversation::WWW_SetProgressRange(HSZ hszItem)	 
{ 
	DWORD dwTransactionID; 
	DWORD dwMaximum; 
	 
	ClientPassed(hszItem, "DW,DW", &dwTransactionID, &dwMaximum); 
	 
	GetDDEConn()->SetProgressRange( dwTransactionID, dwMaximum ); 
	 
	return(DDEFACK); 
} 
 
#if 0 
HDDEDATA CDDEConversation::WWW_URLEcho(HSZ hszItem)	 
{ 
	CString csURL; 
	CString csMIMEType; 
	DWORD dwWindowID; 
	CString csReferrer; 
	 
	ClientPassed(hszItem, "QCS,QCS,DW,QCS", &csURL, &csMIMEType, &dwWindowID, 
		&csReferrer); 
		 
	char aBuf[1024]; 
	sprintf(aBuf, "%s WWW_URLEcho(%s,%s,%lu,%s);", (const char *)ExplainPoke(DDEFACK), 
		(const char *)csURL, (const char *)csMIMEType, dwWindowID, (const char *)csReferrer); 
	GetDDEConn()->Display(aBuf); 
		 
	return(DDEFACK); 
} 
#endif 
 
HDDEDATA CDDEConversation::WWW_ViewDocFile(HSZ hszItem)	 
{ 
	CString csFileName; 
	CString csURL; 
	CString csMIMEType; 
	DWORD dwWindowID; 
	 
	ClientPassed(hszItem, "QCS,QCS,QCS,DW", &csFileName, &csURL, &csMIMEType, &dwWindowID); 
	 
	#ifdef _DEBUG 
	CString strMsg; 
	strMsg.Format( "%s WWW_ViewDocFile(%s,%s,%s,%lu);\n", (const char *)ExplainPoke(DDEFACK), 
		(const char *)csFileName, (const char *)csURL, (const char *)csMIMEType, 
		dwWindowID); 
 
	TRACE( strMsg );  
	#endif 
 
	GetDDEConn()->ViewDocFile( csFileName, csURL, csMIMEType ); 
	 
	return(DDEFACK); 
} 
 
#if 0 
HDDEDATA CDDEConversation::WWW_WindowChange(HSZ hszItem)	 
{ 
	DWORD dwWindowID; 
	DWORD dwWindowFlags; 
	DWORD dwX; 
	DWORD dwY; 
	DWORD dwCX; 
	DWORD dwCY; 
	 
	ClientPassed(hszItem, "DW,DW,DW,DW,DW,DW", &dwWindowID, &dwWindowFlags, &dwX, 
		&dwY, &dwCX, &dwCY); 
		 
	char aBuf[512]; 
	sprintf(aBuf, "%s WWW_WindowChange(%lu,%lu,%lu,%lu,%lu,%lu);", (const char *)ExplainPoke(DDEFACK), 
		dwWindowID, dwWindowFlags, dwX, dwY, dwCX, dwCY); 
	GetDDEConn()->Display(aBuf); 
		 
	return(DDEFACK); 
} 
#endif 
 
HDDEDATA CDDEConversation::WWW_OpenURLResult(HSZ hszItem)	 
{ 
	DWORD dwTransID; 
	DWORD dwWindowID; 
	 
	ClientPassed(hszItem, "DW,DW", &dwTransID, &dwWindowID ); 
		 
	GetDDEConn()->OpenURLResult( dwTransID, dwWindowID ); 
		 
	return(DDEFACK); 
} 
 
 
//	Handle DDE Requests 
HDDEDATA CDDEConversation::ServerRequest(HSZ hszTopic, HSZ hszItem)	 
{ 
	//	Switch on the topic we're handling. 
	int iTopic = CDDEObject::EnumTopic(hszTopic); 
	switch(iTopic)	 
	{ 
		case CDDEObject::m_Alert: 
			return(WWW_Alert(hszItem)); 
		case CDDEObject::m_BeginProgress: 
			return(WWW_BeginProgress(hszItem)); 
		case CDDEObject::m_MakingProgress: 
			return(WWW_MakingProgress(hszItem)); 
		case CDDEObject::m_OpenURL: 
			return(WWW_OpenURL(hszItem)); 
		case CDDEObject::m_QueryViewer: 
			return(WWW_QueryViewer(hszItem)); 
		//case CDDEObject::m_ViewDocCache: 
		//	return( WWW_ViewDocCache(hszItem)); 
		case CDDEObject::m_RegisterNow: 
			return( WWW_RegisterNow(hszItem)); 
	} 
 
	TRACE("Unknown request received\n"); 
	return(NULL); 
} 
 
HDDEDATA CDDEConversation::WWW_Alert(HSZ hszItem)	 
{ 
	CString csMessage; 
	DWORD dwType; 
	DWORD dwButtons; 
	DWORD dwAnswer = 0; 
		 
	ClientPassed(hszItem, "QCS,DW,DW", &csMessage, &dwType, &dwButtons); 
	 
	dwAnswer = GetDDEConn()->Alert( csMessage, dwType, dwButtons ); 
	 
	return(ServerReturns( hszItem, "DW", &dwAnswer)); 
 
} 
HDDEDATA CDDEConversation::WWW_BeginProgress(HSZ hszItem)	 
{ 
	DWORD dwWindowID; 
	CString csInitialMessage; 
	DWORD dwTransactionID = (DWORD)time(NULL);	 
	 
	ClientPassed(hszItem, "DW,QCS", &dwWindowID, &csInitialMessage); 
	 
	GetDDEConn()->BeginProcess( dwTransactionID,  csInitialMessage ); 
 
	CDDEObject *pDDE = CDDEObject::ResolveConversation(this); 
 
	if ( pDDE->GetServerType() == CDDEObject::srvMosaic ) 
	{ 
		TwoByteBool bTrue = TRUE; 
		return(ServerReturns( hszItem, "BL", &bTrue)); 
	} 
	else 
	{ 
		return(ServerReturns( hszItem, "DW", &dwTransactionID)); 
	} 
} 
HDDEDATA CDDEConversation::WWW_MakingProgress(HSZ hszItem)	 
{ 
	DWORD dwTransactionID; 
	CString csMessage; 
	DWORD dwProgress; 
	TwoByteBool bStop; 
	 
	ClientPassed(hszItem, "DW,QCS,DW", &dwTransactionID, &csMessage, &dwProgress); 
	 
	bStop = GetDDEConn()->MakingProgress( dwTransactionID, csMessage, dwProgress ); 
	 
	return(ServerReturns( hszItem, "BL", &bStop)); 
} 
 
HDDEDATA CDDEConversation::WWW_OpenURL(HSZ hszItem)	 
{ 
	CString csURL; 
	CString csSaveAs; 
	DWORD dwWindowID; 
	DWORD dwFlags; 
	CString csPostFormData; 
	CString csPostMIMEType; 
	CString csProgressServer; 
	DWORD dwServicingID = (DWORD)time(NULL); 
	 
	ClientPassed(hszItem, "QCS,QCS,DW,DW,QCS,QCS,CS", &csURL, &csSaveAs, 
		&dwWindowID, &dwFlags, &csPostFormData, &csPostMIMEType, &csProgressServer); 
	 
	#if 0	 
	char aBuf[2048]; 
	sprintf(aBuf, "%lu WWW_OpenURL(%s,%s,%lu,%lu,%s,%s,%s);", dwServicingID, 
		(const char *)csURL, (const char *)csSaveAs, dwWindowID, dwFlags, 
		(const char *)csPostFormData, (const char *)csPostMIMEType, 
		(const char *)csProgressServer); 
	GetDDEConn()->Display(aBuf); 
	#endif 
 
	if ( !csSaveAs.IsEmpty() ) 
	{ 
		string csMIMEType( "x-world/x-vrml"); 
		GetDDEConn()->ViewDocFile( csSaveAs, csURL,  csMIMEType ); 
	} 
	else 
	{ 
		GetDDEConn()->GetURL( csURL, 0 ); 
	} 
 
		 
	return(ServerReturns( hszItem, "DW", &dwServicingID)); 
} 
 
#if 0 
HDDEDATA CDDEConversation::WWW_ViewDocCache(HSZ hszItem)	 
{ 
	TwoByteBool bNotInCache = FALSE; 
	return(ServerReturns( hszItem, "BL", &bNotInCache)); 
} 
#endif 
 
HDDEDATA CDDEConversation::WWW_RegisterNow(HSZ hszItem)	 
{ 
	DWORD dwTimeOut = 0xFFFFF; 
	CDDEObject *pDDE = CDDEObject::ResolveConversation(this); 
	CString csBrowser; 
	DWORD   dwID; 
	ClientPassed(hszItem, "QCS,DW", &csBrowser, &dwID); 
	pDDE->SetRegNowID( dwID); 
	// Call to register viewer 
	GetDDEConn()->RegisterNow(); 
	return(ServerReturns( hszItem, "DW", &dwTimeOut)); 
} 
 
HDDEDATA CDDEConversation::WWW_QueryViewer(HSZ hszItem)	 
{ 
	CString csSaveIn; 
	ChUtil::GetTempFileName( csSaveIn, 0, 0, 0 ); 
	ChUtil::AddFileToTempList( csSaveIn ); 
 
	#if 0 
	CString csMIMEType; 
	char *pTemp = _tempnam("c:\\temp", "nstest"); 
	CString csSaveIn = (pTemp == NULL) ? "\\delme.nst" : pTemp; 
	 
	ClientPassed(hszItem, "QCS,QCS", &csURL, &csMIMEType); 
	 
	char aBuf[2048]; 
	sprintf(aBuf, "%s WWW_QueryViewer(%s,%s);", (const char *)csSaveIn, 
		(const char *)csURL, (const char *)csMIMEType); 
	GetDDEConn()->Display(aBuf); 
	#endif 
	 
	return(ServerReturns( hszItem, "QCS", &csSaveIn )); 
} 
 
 
//	Client Requests and pokes. 
HDDEDATA CDDEConversation::ClientRequest(HSZ hszItem)	 
{ 
	//	Send along the item to the server, as an XTYP_REQUEST. 
	//	Return the data received from the server. 
	//	Proactively delete the hszItem string. 
	HDDEDATA hRetVal = DdeClientTransaction(NULL, 0ul, m_hConv, hszItem,  
						CF_TEXT, XTYP_REQUEST, DDETIMEOUT, NULL); 
	GetDDEConn()->Display(ExplainError());	 
	if(hszItem != NULL)	 
	{ 
		DdeFreeStringHandle(CDDEObject::m_dwidInst, hszItem); 
		GetDDEConn()->Display(ExplainError()); 
	} 
	return(hRetVal); 
} 
HDDEDATA CDDEConversation::ClientPoke(HSZ hszItem)	 
{ 
	//	Send along the item to the server, as an XTYP_POKE. 
	//	Return the data received from the server. 
	//	Proactively delete the hszItem string. 
	HDDEDATA hRetVal = DdeClientTransaction(NULL, 0ul, m_hConv, hszItem,  
					CF_TEXT, XTYP_POKE, DDETIMEOUT, NULL); 
	GetDDEConn()->Display(ExplainError()); 
	if(hszItem != NULL)	 
	{ 
		DdeFreeStringHandle(CDDEObject::m_dwidInst, hszItem); 
		GetDDEConn()->Display(ExplainError()); 
	} 
	return(hRetVal); 
} 
 
//	Persistance 
void CDDEConversation::Serialize(CArchive& ar) 
{ 
	if(ar.IsStoring())	 
	{ 
		//	Add storing code 
	} 
	else	 
	{ 
		//	Add loading code 
	} 
	 
	//	Serialize the base. 
	CObject::Serialize(ar); 
} 
 
#ifdef _DEBUG 
 
void CDDEConversation::AssertValid() const	 
{ 
	//	Call base to assert valid. 
	CObject::AssertValid(); 
	 
	//	Check our instance specific members. 
	ASSERT(m_hConv);	//	Better have a conversation! 
	ASSERT(m_rIndex);	//	Position in list. 
} 
 
void CDDEConversation::Dump(CDumpContext& dc) const	 
{ 
	//	Have the base dump 
	CObject::Dump(dc); 
	 
	//	Now we dump any relevant information.... 
} 
 
#endif // _DEBUG