/***************************************************************************/ /** Microsoft Windows **/ /** Copyright(c) Microsoft Corp., 1991, 1992 **/ /***************************************************************************/ /**************************************************************************** dde.cpp Aug 92, JimH May 93, JimH chico port Member functions for DDE, DDEServer, and DDEClient are here. ****************************************************************************/ #include "hearts.h" #include "dde.h" #include "debug.h" // declare DDE objects DDEClient *ddeClient; DDEServer *ddeServer; /**************************************************************************** DDE:DDE performs basic DDEML initialization. m_bResult is TRUE if everything works. ****************************************************************************/ DDE::DDE(const TCHAR *server, const TCHAR *topic, DDECALLBACK CallBack, DWORD filters) : m_idInst(0), m_CallBack(NULL) { // Check for basic compatibility, ie protect mode // m_bResult = ( (LOBYTE(GetVersion()) > 2) && (GetWinFlags() & WF_PMODE) ) // ? TRUE : FALSE; m_bResult = TRUE; if (!m_bResult) return; m_data.Empty(); // clear CString object // Set callback function and filters from passed-in parameters if (!SetCallBack(CallBack)) return; SetFilters(filters); if (!Initialize()) { m_idInst = 0; return; } // create CString objects and HSZ handles for server and topic m_server = server; m_topic = topic; m_hServer = CreateStrHandle(m_server); m_hTopic = CreateStrHandle(m_topic); } /**************************************************************************** DDE::~DDE cleans up string handles and DDEML-uninitializes ****************************************************************************/ DDE::~DDE() { if (!m_idInst) return; DestroyStrHandle(m_hServer); DestroyStrHandle(m_hTopic); ::DdeUninitialize(m_idInst); FreeProcInstance((FARPROC)m_CallBack); } /**************************************************************************** DDE:CreateDataHandle converts data to a HDDEDATA handle. ****************************************************************************/ HDDEDATA DDE::CreateDataHandle(void FAR *pdata, DWORD size, HSZ hItem) { return ::DdeCreateDataHandle(m_idInst, // instance ID (LPBYTE)pdata, // data to convert size, // size of data 0, // offset of data hItem, // corresponding string handle CF_OWNERDISPLAY,// clipboard format 0); // creation flags, system owns } /**************************************************************************** DDE:CreateStrHandle converts a string into a HSZ. The codepage defaults to CP_WINANSI. ****************************************************************************/ HSZ DDE::CreateStrHandle(LPCTSTR str, int codepage) { HSZ hsz = NULL; if (m_idInst) hsz = ::DdeCreateStringHandle(m_idInst, str, codepage); if (hsz == NULL) m_bResult = FALSE; return hsz; } /**************************************************************************** DDE::DestroyStrHandle frees HSZ created by CreateStrHandle ****************************************************************************/ void DDE::DestroyStrHandle(HSZ hsz) { if (m_idInst && hsz) ::DdeFreeStringHandle(m_idInst, hsz); } /**************************************************************************** DDE:GetData Like GetDataString, this function retrieves data represented by hData provided in callback function. However, the buffer must be provided by the caller. The len parameter defaults to 0 meaning the caller promises pdata points to a large enough buffer. ****************************************************************************/ PBYTE DDE::GetData(HDDEDATA hData, PBYTE pdata, DWORD len) { DWORD datalen = ::DdeGetData(hData, NULL, 0, 0); if (len == 0) len = datalen; ::DdeGetData(hData, pdata, min(len, datalen), 0); return pdata; } /**************************************************************************** DDE:GetDataString The default value of hData is NULL meaning just return the current m_data string. Otherwise get associated DDE data. The caller does not have to provide a CString buffer. ****************************************************************************/ CString DDE::GetDataString(HDDEDATA hData) { if (hData == NULL) // default paramenter return m_data; DWORD len = ::DdeGetData(hData, NULL, 0, 0); // find length TCHAR *pdata = m_data.GetBuffer((int)len); ::DdeGetData(hData, (LPBYTE)pdata, len, 0); m_data.ReleaseBuffer(); return m_data; } /**************************************************************************** DDE::Initialize performs DDEML initialization ****************************************************************************/ BOOL DDE::Initialize() { m_initerr = (WORD)::DdeInitialize(&m_idInst, (PFNCALLBACK)m_CallBack, m_filters, 0); m_bResult = (m_initerr == DMLERR_NO_ERROR); return m_bResult; } /**************************************************************************** DDE::SetCallBack ****************************************************************************/ BOOL DDE::SetCallBack(DDECALLBACK CallBack) { if (m_CallBack) FreeProcInstance((FARPROC)m_CallBack); m_CallBack = (DDECALLBACK)MakeProcInstance((FARPROC)CallBack, AfxGetInstanceHandle()); m_bResult = (m_CallBack != NULL); return m_bResult; } /**************************************************************************** DDEServer functions ****************************************************************************/ /**************************************************************************** DDEServer::DDEServer registers server name ****************************************************************************/ DDEServer::DDEServer(const TCHAR *server, const TCHAR *topic, DDECALLBACK ServerCallBack, DWORD filters) : DDE(server, topic, ServerCallBack, filters) { if (!m_bResult) return; if (::DdeNameService(m_idInst, m_hServer, NULL, DNS_REGISTER) == 0) m_bResult = FALSE; } /**************************************************************************** DDEServer::~DDEServer unregisters server name ****************************************************************************/ DDEServer::~DDEServer() { ::DdeNameService(m_idInst, NULL, NULL, DNS_UNREGISTER); } /**************************************************************************** DDEServer::PostAdvise notify clients that data has changed ****************************************************************************/ BOOL DDEServer::PostAdvise(HSZ hItem) { return ::DdePostAdvise(m_idInst, m_hTopic, hItem); } /**************************************************************************** DDEClient functions ****************************************************************************/ /**************************************************************************** DDEClient::DDEClient after DDE construction, connect to specified server and topic. m_bResult indicates success or failure. ****************************************************************************/ DDEClient::DDEClient(const TCHAR *server, const TCHAR *topic, DDECALLBACK ClientCallBack, DWORD filters) : DDE(server, topic, ClientCallBack, filters) { if (!m_bResult) // if DDE construction failed return; m_timeout = m_deftimeout = TIMEOUT_ASYNC; // default to asynch trans m_hConv = ::DdeConnect(m_idInst, m_hServer, m_hTopic, NULL); if (m_hConv == NULL) m_bResult = FALSE; } /**************************************************************************** DDEClient::~DDEClient disconnects from server ****************************************************************************/ DDEClient::~DDEClient() { ::DdeDisconnect(m_hConv); } /**************************************************************************** DDEClient:Poke Use this function to send general unsolicited data to the server. String data can be sent more conveniently using string Poke below. ****************************************************************************/ BOOL DDEClient::Poke(HSZ hItem, void FAR *pdata, DWORD len, DWORD uTimeout) { if (uTimeout == NULL) // default m_timeout = m_deftimeout; else m_timeout = uTimeout; ClientTransaction((LPBYTE)pdata, len, hItem, XTYP_POKE, CF_OWNERDISPLAY); return m_bResult; } BOOL DDEClient::Poke(HSZ hItem, const TCHAR *string, DWORD uTimeout) { if (uTimeout == NULL) // default m_timeout = m_deftimeout; else m_timeout = uTimeout; ClientTransaction((void FAR *)string, lstrlen(string)+1, hItem, XTYP_POKE); return m_bResult; } /**************************************************************************** DDEClient::RequestString DDEClient::RequestData These request a synchronous update from server on specified item. RequestString returns a BOOL which says if the request succeeded. Get the result from GetDataString(void). RequestData returns a HDDEDATA. If it is not NULL, pass it to GetData() along with a buffer to copy the result in to. ****************************************************************************/ BOOL DDEClient::RequestString(HSZ hItem, DWORD uTimeout) { if (uTimeout == NULL) // default m_timeout = m_deftimeout; else m_timeout = uTimeout; HDDEDATA hData = ClientTransaction(NULL, 0, hItem, XTYP_REQUEST); if (m_bResult) GetDataString(hData); else m_data.Empty(); return m_bResult; } HDDEDATA DDEClient::RequestData(HSZ hItem, DWORD uTimeout) { if (uTimeout == NULL) // default m_timeout = m_deftimeout; else m_timeout = uTimeout; HDDEDATA hData = ClientTransaction(NULL, 0, hItem, XTYP_REQUEST, CF_OWNERDISPLAY); return hData; } /**************************************************************************** DDEClient::StartAdviseLoop This function sets up a hotlink with the server on the specified item. It returns TRUE if the link was set up successfully. Setting up a warm link would involve changing the XTYP. ****************************************************************************/ BOOL DDEClient::StartAdviseLoop(HSZ hItem) { ClientTransaction(NULL, 0, hItem, XTYP_ADVSTART); return m_bResult; } /**************************************************************************** DDEClient::ClientTransaction an internal wrapper for ::DdeClientTransaction() ****************************************************************************/ HDDEDATA DDEClient::ClientTransaction(void FAR *lpvData, DWORD cbData, HSZ hItem, UINT uType, UINT uFmt) { HDDEDATA hData = ::DdeClientTransaction( (LPBYTE)lpvData, // data to send to server cbData, // size of data in bytes m_hConv, // conversation handle hItem, // handle of item name string uFmt, // clipboard format uType, // XTYP_* type m_timeout, // timeout duration in milliseconds NULL); // transaction result, not used m_bResult = (hData != FALSE); return hData; }