// This file defines a DLL, which exports procedures for DDE client connections // to server applications. // The base of the procedures are several articles from Mircosoft about DDE and // different examples of code found via internet search. The code examples did // not work in Visual Studio .NET 2003 and had to be revised substantially. // // Author: Robert Matovinovic // Version: 0.03 // Date: 14.10.2006 // // 0.01 // DLL supports only a single connection. For multiple connections the variables // must be localized to procedures. Synchronous and asynchronous transactions are // possible. To access data of asynchronous transactions call // DCAsynchTransactionCompleted // // 0.02 // multiple connections realized by use of individual conversation handle and // separated variables // // 0.03 // bug fixed which caused an infinite loop in DCAsynchTransactionCompleted // DDEClient.cpp : Entry point for DLL application. // #pragma once #define WIN32_LEAN_AND_MEAN // don't bind to seldom used parts of Windows-Header #include #include #include // only for string handling of error messages #include "DCErrors.h" // Error messages for dll // // global data // char szVersion[]="DDEClient.dll, version 0.03, copyright Robert Matovinovic, robert.matovinovic@web.de"; // variable of DDE instance DWORD dwDDEInst = 0; // data handle for execute transaction HDDEDATA hExecData; // string for messages CString csMsg; // Flag for successful return of callback function bool bCbSuccess = true; // DDEClientTransaction data, variables for transactions via DDEML typedef struct { // Pointer to the beginning of the data the client must pass to the server. // can also be a data handle, for execute and poke. LPBYTE pData; // Specifies the length, in bytes, of the data pointed to by the pData parameter DWORD cbData; // Handle to the conversation in which the transaction is to take place. HCONV hConv; // Handle to the data item for which data is being // exchanged during the transaction. HSZ hszItem; // Specifies the standard clipboard format // in which the data item is being submitted or requested. UINT wFmt; // Specifies the transaction type. UINT wType; // Specifies the maximum length of time, in milliseconds, that the client will // wait for a response from the server application in a synchronous transaction. // This parameter should be TIMEOUT_ASYNC for asynchronous transactions. DWORD dwTimeout; // Pointer to a variable that receives the result of the transaction. //LPDWORD pdwResult; // Variable that receives the result of the transaction. DWORD dwResult; // Data handle for transactions HDDEDATA hData; } DDEVARS; // DDE data structure for access data received by DDE transaction typedef struct { // pointer to begin of dde data returned by DdeAccessData BYTE* pData; // length of dde data returned by DdeAccessData DWORD dwLen; // string pointer to dde data, if the data should accessed as string char* pszData; // ID of transaction, used to determine completion of asynchronous transaction DWORD dwTransID; // ID of transaction, returned by callback function with asynch. transaction DWORD dwCbTransID; // string for access type of data char szAccType[6]; } DCDATAACCESS; // maximal number of dde conversations const WORD wCONVMAX = 20; // pointer array to dde conversation variables DDEVARS* DdeV[wCONVMAX]; // pointer array to dde data access variables DCDATAACCESS* DDA[wCONVMAX]; #define DllExport extern "C" _declspec( dllexport ) // // exported variables // // boolean used by DCLastError for appearance of error messages. // false: Messages are shown in a system message box. // true: Messages are passed to the calling program. DllExport bool bDCErrorExport = false; // pointer to an pointer array to dde data access variables DllExport DCDATAACCESS* *DCDA = DDA; // // exported functions // DllExport bool DCRequestString (WORD wC, char szItem[], DWORD dwTimeout); DllExport bool DCRequest(WORD wC, char szItem[], char szFormat[], DWORD dwTimeout ); DllExport bool DCTransaction (WORD wC, char szType[], char szItem[], char szData[], char szFormat[], DWORD dwTimeout, char szAccess[]); DllExport bool DCInit(); DllExport bool DCConnect(WORD* pConvNo, char* szService, char* szTopic); DllExport bool DCDisconnect(WORD wC); DllExport bool DCUninit(); DllExport bool DCFreeDdeMem(WORD wC); DllExport char* DCLastError(); DllExport void DCFinalize(); DllExport bool DCAsynchTransactionCompleted(WORD wC, DWORD dwTransID, bool bWait); DllExport bool DCAbandonTransaction(WORD wC, DWORD dwTransID); DllExport char* DCVersion(); // // forward declarations // bool DdeDataHandling(WORD wC, HDDEDATA hData, char szAccess[]); void FreeData(WORD wC); bool InitDdeVars(HCONV hConv); bool InitDCDataAccess(WORD wC); WORD FindConvNo(HCONV hConv); bool GetConvNo(WORD* pConvNo); void FreeDdeV(WORD wC); void FreeDDA(WORD wC); // // Entry point for application // BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { //hMod = hModule; // save our module handle //InitDdeVars(0L); //InitDCDataAccess(); return true; } // // local functions // void FAR PASCAL WEP(WORD wGarbage) { // // Do absolutely nothing at all here, since // that's all that is safe to do. // } // procedure which retrives the last DDE error and adds it to a CString UINT DCGetLastError(DWORD dwDdeInst, CString csMsgHead) { UINT ui; csMsg=csMsgHead; ui = DdeGetLastError(dwDdeInst); if (ui != DMLERR_NO_ERROR) { switch (ui) { case DMLERR_ADVACKTIMEOUT: csMsg = csMsg + (CString)ccDMLERR01; break; case DMLERR_BUSY: csMsg = csMsg + (CString)ccDMLERR02; break; case DMLERR_DATAACKTIMEOUT: csMsg = csMsg + (CString)ccDMLERR03; break; case DMLERR_DLL_NOT_INITIALIZED: csMsg = csMsg + (CString)ccDMLERR04; break; case DMLERR_DLL_USAGE: csMsg = csMsg + (CString)ccDMLERR05; break; case DMLERR_EXECACKTIMEOUT: csMsg = csMsg + (CString)ccDMLERR06; break; case DMLERR_INVALIDPARAMETER: csMsg = csMsg + (CString)ccDMLERR07; break; case DMLERR_LOW_MEMORY: csMsg = csMsg + (CString)ccDMLERR08; break; case DMLERR_MEMORY_ERROR: csMsg = csMsg + (CString)ccDMLERR09; break; case DMLERR_NO_CONV_ESTABLISHED: csMsg = csMsg + (CString)ccDMLERR10; break; case DMLERR_NOTPROCESSED: csMsg = csMsg + (CString)ccDMLERR11; break; case DMLERR_POKEACKTIMEOUT: csMsg = csMsg + (CString)ccDMLERR12; break; case DMLERR_POSTMSG_FAILED: csMsg = csMsg + (CString)ccDMLERR13; break; case DMLERR_REENTRANCY: csMsg = csMsg + (CString)ccDMLERR14; break; case DMLERR_SERVER_DIED: csMsg = csMsg + (CString)ccDMLERR15; break; case DMLERR_SYS_ERROR: csMsg = csMsg + (CString)ccDMLERR16; break; case DMLERR_UNADVACKTIMEOUT: csMsg = csMsg + (CString)ccDMLERR17; break; case DMLERR_UNFOUND_QUEUE_ID: csMsg = csMsg + (CString)ccDMLERR18; break; } } return ui; } // // Callback function for DDE messages // HDDEDATA CALLBACK DdeCallback(UINT wType, UINT wFmt, HCONV hConv, HSZ hsz1, HSZ hsz2, HDDEDATA hDDEData, DWORD dwData1, DWORD dwData2) { bool bErrorExport; WORD wC; bCbSuccess = true; wC = FindConvNo(hConv); switch ( wType ) { case XTYP_XACT_COMPLETE: // store transaction ID DDA[wC]->dwCbTransID = dwData1; // Return doing nothing, if the transaction IDs are not equal if ( dwData1 != DdeV[wC]->dwResult ) { csMsg = ccMsgCbWrongID; bCbSuccess = false; return 0; } // Check for successful transaction if ( !hDDEData ) { csMsg = ccMsgCbTransactionFailed; bCbSuccess = false; } // access DDE data for request transactions else if ( DdeV[wC]->wType == XTYP_REQUEST ) { if(!DdeDataHandling(wC, hDDEData, DDA[wC]->szAccType)) { FreeData(wC); csMsg = ccMsgCbAsynchRequestFailed; bCbSuccess = false; } } else bCbSuccess = true; // do nothing for execute and poke transactions DdeV[wC]->dwResult = 0; //char szTemp[100]; //sprintf(szTemp,"bCbSuccess = %i CbTransID = %i", bCbSuccess,DDA[wC]->dwCbTransID); //MessageBox((HWND)GetActiveWindow(), szTemp, ccMsgCaptionCb, MB_OK); return 0; case XTYP_DISCONNECT: DdeV[wC]->dwResult = 0; MessageBox((HWND)GetActiveWindow(), ccMsgAppTerminatedConv, ccMsgCaptionCb, MB_OK | MB_ICONERROR); return 0; case XTYP_ERROR: // only valid for one error (low memory) // bDCErrorExport has to be set to false, otherwise the error is not shown. // After that its previous state is restored bErrorExport = bDCErrorExport; bDCErrorExport = false; DCGetLastError(dwDDEInst, csMsg); bDCErrorExport = bErrorExport; return 0; default: return 0; } } // // Extract constant for format of DDE command // UINT DDEFormat(char* szFormat) { // Null-terminated, plain ANSI text in a global memory block. if (0 == strnicmp (szFormat,"CF_TEXT",7)) return CF_TEXT; // A bitmap compatible with Windows 2.x. if (0 == strnicmp (szFormat,"CF_BITMAP",7)) return CF_BITMAP; // A Windows metafile with some additional information about how the // metafile should be displayed. if (0 == strnicmp (szFormat,"CF_METAFILEPICT",7)) return CF_METAFILEPICT; // An ASCII text format used by some older Microsoft products. if (0 == strnicmp (szFormat,"CF_SYLK",7)) return CF_SYLK; // Software Art's data interchange format (DIF). Also an ASCII text format. if (0 == strnicmp (szFormat,"CF_DIF",7)) return CF_DIF; // Tag image file format (TIFF) data in a global memory block. if (0 == strnicmp (szFormat,"CF_TIFF",7)) return CF_TIFF; // Similar to CF_TEXT but using the OEM character set. if (0 == strnicmp (szFormat,"CF_OEMTEXT",7)) return CF_OEMTEXT; // A global memory block containing a Windows device-independent bitmap (DIB) // as a BITMAPINFO structure followed by the bitmap bits. if (0 == strnicmp (szFormat,"CF_DIB",7)) return CF_DIB; // A color-palette handle. (Used in conjunction with CF_DIB.) if (0 == strnicmp (szFormat,"CF_PALETTE",7)) return CF_PALETTE; // Data is for the pen extensions to Windows. if (0 == strnicmp (szFormat,"CF_PENDATA",7)) return CF_PENDATA; // Resource interchange file format (RIFF) data as a global memory block. if (0 == strnicmp (szFormat,"CF_RIFF",7)) return CF_RIFF; // A specific case of RIFF in which the contained data is a waveform (sampled sound). if (0 == strnicmp (szFormat,"CF_WAVE",7)) return CF_WAVE; return 0; } // // Extract constant or time for timeout of DDE command // DWORD DDETimeout(DWORD* pdwTimeout) { if (*pdwTimeout > 0) { return *pdwTimeout; } return TIMEOUT_ASYNC; } // // Set Parameters for DDEClientTransaction considering the type of command // void SetDDEParams(WORD wC, char* szType, char* szItem, char* szData, char* szFormat, DWORD* pdwTimeout) { UINT wCmdLen = lstrlen(szItem)+1; if (0 == strnicmp (szType,"execute",7)) { DdeV[wC]->wType = XTYP_EXECUTE; DdeV[wC]->wFmt = 0; // Create a data handle for the exec string hExecData = DdeCreateDataHandle(dwDDEInst, (LPBYTE)szItem, wCmdLen, 0, NULL, DdeV[wC]->wFmt, 0); DdeV[wC]->pData = (LPBYTE)hExecData; DdeV[wC]->cbData = -1; DdeV[wC]->hszItem = NULL; DdeV[wC]->dwTimeout = DDETimeout(pdwTimeout); //DdeV[wC]->pdwResult = NULL; DdeV[wC]->dwResult = 0; return; } if (0 == strnicmp (szType,"poke",4)) { DdeV[wC]->wType = XTYP_POKE; DdeV[wC]->wFmt = DDEFormat(szFormat); DdeV[wC]->pData = (LPBYTE)szData; DdeV[wC]->cbData = lstrlen(szData)+1; DdeV[wC]->hszItem = DdeCreateStringHandle(dwDDEInst, szItem, CP_WINANSI ); DdeV[wC]->dwTimeout = DDETimeout(pdwTimeout); //DdeV[wC]->pdwResult = NULL; DdeV[wC]->dwResult = 0; return; } if (0 == strnicmp (szType,"request",7)) { DdeV[wC]->wType = XTYP_REQUEST; DdeV[wC]->pData = NULL; DdeV[wC]->cbData = 0; DdeV[wC]->hszItem = DdeCreateStringHandle(dwDDEInst, szItem, CP_WINANSI ); DdeV[wC]->wFmt = DDEFormat(szFormat); DdeV[wC]->dwTimeout = DDETimeout(pdwTimeout); DdeV[wC]->dwResult = 0; return ; } /* if (0 == strnicmp (szType,"advise_start",12)) { return XTYP_ADVSTART; } if (0 == strnicmp (szType,"advise_stop",11)) { return XTYP_ADVSTOP; } */ } // procedure retriving DDE Data as string. bool DdeDataString(WORD wC, HDDEDATA hData) { if (!hData) return false; else { // get the data as string char* pszDdeData = (char *) DdeAccessData(hData, &DDA[wC]->dwLen); if(pszDdeData) { // store data in a new array, otherwise it is lost with // asynchronous transactions DDA[wC]->pszData = (char *)malloc(DDA[wC]->dwLen); char* pszData = DDA[wC]->pszData; for (DWORD i = 0; i < DDA[wC]->dwLen; i++) pszData[i] = *pszDdeData++; DdeUnaccessData( hData ); return true; } else return false; } } // short form of DCTransaction for string request transaction bool DCRequestString(WORD wC, char szItem[], DWORD dwTimeout) { csMsg = ""; DdeV[wC]->wType = XTYP_REQUEST; DdeV[wC]->dwTimeout = DDETimeout(&dwTimeout); // Send the request DdeV[wC]->hData = DdeClientTransaction(NULL, 0, DdeV[wC]->hConv, DdeCreateStringHandle(dwDDEInst, szItem, CP_WINANSI ), CF_TEXT, DdeV[wC]->wType, DdeV[wC]->dwTimeout, &(DdeV[wC]->dwResult)); // store transaction ID DDA[wC]->dwTransID = DdeV[wC]->dwResult; // Check for DDE data if (DdeV[wC]->hData==NULL) { csMsg = (CString)ccMsgRequestFailed + szItem; return 0; } else { if (DdeV[wC]->dwTimeout != TIMEOUT_ASYNC) { // data access for synchronous transactions if(!DdeDataString(wC,DdeV[wC]->hData)) { FreeData(wC); csMsg = (CString)ccMsgRequestFailed + szItem; return false; } else return true; } else { strncpy(DDA[wC]->szAccType,"string",sizeof(DDA[wC]->szAccType)); } } return true; } // procedure retrieving DDE data as byte array bool DdeDataBytes(WORD wC, HDDEDATA hData) { // Check for DDE data if (hData==NULL) { DDA[wC]->pData = NULL; DDA[wC]->dwLen = NULL; return false; } else { // Get data BYTE* pDdeData = DdeAccessData(hData, &DDA[wC]->dwLen); if(pDdeData) { // store data in a new array, otherwise it is lost with // asynchronous transactions DDA[wC]->pData = (BYTE *)malloc(DDA[wC]->dwLen); BYTE* pData = DDA[wC]->pData; for (DWORD i = 0; i < DDA[wC]->dwLen; i++) pData[i] = *pDdeData++; DdeUnaccessData( hData ); return true; } else return false; } } // procedure for DDE request giving back a pointer to the data bool DCRequest(WORD wC, char szItem[], char szFormat[], DWORD dwTimeout ) { csMsg = ""; // Set parameters for a single DDE conversation SetDDEParams(wC, "request", szItem, NULL, szFormat, &dwTimeout); // Send the request DdeV[wC]->hData = DdeClientTransaction(DdeV[wC]->pData, DdeV[wC]->cbData, DdeV[wC]->hConv, DdeV[wC]->hszItem, DdeV[wC]->wFmt, DdeV[wC]->wType, DdeV[wC]->dwTimeout, &(DdeV[wC]->dwResult)); // store transaction ID DDA[wC]->dwTransID = DdeV[wC]->dwResult; if (DdeV[wC]->dwTimeout != TIMEOUT_ASYNC) { // data access for synchronous transaction if(!DdeDataBytes(wC, DdeV[wC]->hData)) { FreeData(wC); csMsg = (CString)ccMsgRequestFailed + szItem; return false; } else return true; } else { strncpy(DDA[wC]->szAccType,"byte",sizeof(DDA[wC]->szAccType)); } return true; } // Initialize data structure for dde variables bool InitDdeVars(HCONV hConv, WORD wC) { // assign memory for conversation variables DdeV[wC] = (DDEVARS *)malloc(sizeof(DDEVARS)); if (DdeV[wC] == NULL) { csMsg = (CString)ccMsgNotEnoughMemory01; return false; } memset(DdeV[wC], 0, sizeof(DdeV[wC])); DdeV[wC]->hConv = hConv; return true; } // Initialize data structure for dde data access bool InitDCDataAccess(WORD wC) { // assign memory for data access variables DDA[wC] = (DCDATAACCESS *)malloc(sizeof(DCDATAACCESS)); if (DDA[wC] == NULL) { csMsg = (CString)ccMsgNotEnoughMemory02; return false; } DDA[wC]->pData = NULL; DDA[wC]->dwLen = 0; DDA[wC]->pszData = NULL; strncpy(DDA[wC]->szAccType,"string",sizeof(DDA[wC]->szAccType)); DDA[wC]->dwTransID = 0; DDA[wC]->dwCbTransID = 0; return true; } // Initiate a conversation with the server application on a topic // If server is not found, ask to launch it. If either server could // not be launched or topic is not found give an error message. bool DCConnect (WORD* pConvNo, char* szService, char* szTopic) { HSZ hszDDEServer; HSZ hszTopic; HCONV hConv; csMsg = ""; // Get server application handle hszDDEServer = DdeCreateStringHandle(dwDDEInst, szService, CP_WINANSI ); // Get topic handle hszTopic = DdeCreateStringHandle(dwDDEInst, szTopic, CP_WINANSI); // Get conversation handle hConv = DdeConnect(dwDDEInst, hszDDEServer, hszTopic, NULL); DdeFreeStringHandle(dwDDEInst, hszDDEServer); DdeFreeStringHandle(dwDDEInst, hszTopic); if(hConv == 0L) { csMsg = (CString)ccMsgConnectFailed01 + szService + (CString)ccMsgConnectFailed02 + szTopic + (CString)ccMsgConnectFailed03; return 0; } // determine conversation number if(!GetConvNo(pConvNo)) return false; // init variables neccessary for conversation if(!InitDdeVars(hConv, *pConvNo)) { FreeDdeV(*pConvNo); return false; } else { if(!InitDCDataAccess(*pConvNo)) { FreeDDA(*pConvNo); return false; } return true; } } // procedure for initializing DDEML.DLL bool DCInit() { UINT ui; // Set message string csMsg = (CString)ccDMLERRInitialize; // Initialize DDEML ui = DdeInitialize(&dwDDEInst, (PFNCALLBACK) DdeCallback, APPCMD_CLIENTONLY, 0L); // Initialize pointer arrays for(int i = 0; i < wCONVMAX; i++) { DdeV[i] = NULL; DDA[i] = NULL; } if (ui != DMLERR_NO_ERROR) { DCGetLastError(dwDDEInst, (CString)ccDMLERRInitialize); return false; } else { return true; } } // procedure which frees handles of the DDE conversation and memory associated // with the last transaction. Has has to be called after each transaction // especially after transactions which give back data. // Important: After data handle is freed no pointer can access the data. // Therefore it has to be processed or copied to another buffer before. bool DCFreeDdeMem(WORD wC) { bool bSuccess = true; CString csMsgLocal = ""; csMsg = ""; // Free string handle now. if (DdeV[wC]->hszItem != NULL) { if(!DdeFreeStringHandle( dwDDEInst, DdeV[wC]->hszItem )) { csMsgLocal = (CString)ccMsgHandleErr01; bSuccess = false; } DdeV[wC]->hszItem = NULL; } // Free data handle now if(DdeV[wC]->hData) { if(!DdeFreeDataHandle(DdeV[wC]->hData)) { csMsgLocal = csMsgLocal + (CString)" " + (CString)ccMsgHandleErr02; bSuccess = false; } DdeV[wC]->hData = 0L; } // Free memory of dde data buffers FreeData(wC); if (!bSuccess) csMsg = csMsgLocal; return bSuccess; } // procedure which disconnects a DDE connection bool DCDisconnect(WORD wC) { int nSuccess = 1; CString csMsgLocal = ""; csMsg = ""; // Free handles and memory associated with the last transaction if(!DCFreeDdeMem(wC)) csMsgLocal = csMsg; // Done with the conversation now. nSuccess = DdeDisconnect(DdeV[wC]->hConv); // Free memory for DDE variables FreeDdeV(wC); // Free memory for DDE data access variables FreeDDA(wC); if (nSuccess == 0) { DCGetLastError(dwDDEInst, csMsgLocal + (CString) " " + (CString)szMsgDMLERRDisconnect); return false; } return true; } // procedure which uninitializes DDEML.DLL bool DCUninit() { if(DdeUninitialize(dwDDEInst))// && AfxFreeLibrary(dwDDEInst)) { // Reset instance handle dwDDEInst = 0; return true; } else { DCGetLastError(dwDDEInst, (CString)szMsgDMLERRUninitialize); return false; } } // handle DDE data bool DdeDataHandling(WORD wC, HDDEDATA hData, char szAccess[]) { bool bSuccess = false; if (0 == strnicmp (szAccess,"string",6)) { // get pointer to the data as string //in pszDCDataString bSuccess = DdeDataString(wC, hData); } else if (0 == strnicmp (szAccess,"byte",4)) // fill c-structure with data for dde data access bSuccess = DdeDataBytes(wC, hData); return bSuccess; } // Do a DDE Transaction bool DCTransaction(WORD wC, char szType[], char szItem[], char szData[], char szFormat[], DWORD dwTimeout, char szAccess[]) { bool bSuccess = true; csMsg = ""; // Set parameters for dde transaction SetDDEParams(wC, szType, szItem, szData, szFormat, &dwTimeout); // execute dde transaction DdeV[wC]->hData = DdeClientTransaction(DdeV[wC]->pData, DdeV[wC]->cbData, DdeV[wC]->hConv, DdeV[wC]->hszItem, DdeV[wC]->wFmt, DdeV[wC]->wType, DdeV[wC]->dwTimeout, &(DdeV[wC]->dwResult)); // store transaction ID DDA[wC]->dwTransID = DdeV[wC]->dwResult; // Error handling if (DdeV[wC]->hData == 0) { // Transaction failed csMsg = (CString)"'" + szType + (CString)" " + szItem + (CString)"' " + (CString)ccMsgTransactionFailed; bSuccess = false; } // data handling for synchronous transactions else if ((DdeV[wC]->wType == XTYP_REQUEST) && (DdeV[wC]->dwTimeout != TIMEOUT_ASYNC)) { bSuccess = DdeDataHandling(wC, DdeV[wC]->hData, szAccess); if (!bSuccess) { FreeData(wC); csMsg = (CString)ccMsgRequestFailed + szItem; } } // store variable for data access format for asynchronous transaction else if (DdeV[wC]->dwTimeout == TIMEOUT_ASYNC) strncpy(DDA[wC]->szAccType,szAccess,sizeof(DDA[wC]->szAccType)); return bSuccess; } // procedure which gives back the message of last error. // By bDCErrorExport is decided whether the message is the return value of // the function or displayed in a message box. // The length of the message is is determined by the size of szErrorMsg. // If the message is longer than sizeof(szErrorMsg) it will be cut off, // and a comment is given. char* DCLastError() { if(bDCErrorExport) { return csMsg.GetBuffer(0); } else { MessageBox((HWND)GetActiveWindow(), csMsg, ccMsgCaption, MB_OK | MB_ICONERROR); return 0; } } // procedure which frees memory of DDE data void FreeData(WORD wC) { // Free memory of dde data if(DDA[wC] != NULL) { if(DDA[wC]->pszData != NULL) { free(DDA[wC]->pszData); DDA[wC]->pszData = NULL; } if(DDA[wC]->pData != NULL) { free(DDA[wC]->pData); DDA[wC]->pData = NULL; } } } // procedure which frees memory, to be called before ending dll access. void DCFinalize() { for (WORD i = 0; i < wCONVMAX; i++) { FreeData(i); FreeDdeV(i); FreeDDA(i); } _heapmin(); } // helper procedure for DCAsynchTransactionCompleted which determines // if a certain asynchronous transaction has completed. bool AsynchTransactionCompleted(WORD wC, DWORD dwTransID) { MSG msg; bool bSuccess = true; csMsg = ""; if(dwTransID == DDA[wC]->dwCbTransID) { // Transaction IDs are equal, the desired transaction // was already handled by the callback function DDA[wC]->dwCbTransID = 0; bSuccess = bCbSuccess; //char szTemp[100]; //sprintf(szTemp,"Entry TransID = CbTransID bSuccess: %i",bSuccess); //MessageBox((HWND)GetActiveWindow(), szTemp, "AsynchTransactionCompleted", MB_OK); } else { // look for message in message queue if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) == 0) { // no message in queue csMsg = (CString)ccMsgNoWindowsMessage; bSuccess = false; } else { //char szTemp[100]; //sprintf(szTemp,"Before dispatch Message: %i TransID = %i CbTransID = %i",msg.message,dwTransID,DDA[wC]->dwCbTransID); //MessageBox((HWND)GetActiveWindow(), szTemp, "AsynchTransactionCompleted", MB_OK); TranslateMessage(&msg); DispatchMessage(&msg); // a message was in the queue, is dispatched // and may have invoked the callback function //sprintf(szTemp,"After dispatch TransID = %i CbTransID = %i",dwTransID,DDA[wC]->dwCbTransID); //MessageBox((HWND)GetActiveWindow(), szTemp, "AsynchTransactionCompleted", MB_OK); if(dwTransID == DDA[wC]->dwCbTransID) { // Transaction IDs are equal, the desired transaction was handled // by the callback function //sprintf(szTemp,"TransID = CbTransID Message: %i",msg.message); //MessageBox((HWND)GetActiveWindow(), szTemp, "AsynchTransactionCompleted", MB_OK); DDA[wC]->dwCbTransID = 0; switch (msg.message) { case 996: // = WM_DDE_ACK if((DdeV[wC]->wType == XTYP_EXECUTE) | (DdeV[wC]->wType == XTYP_POKE)) // execute and poke return only an ACK bSuccess = bCbSuccess; if(DdeV[wC]->wType == XTYP_REQUEST) { // when command cannot be processes (applies not to all servers) csMsg = (CString)ccMsgAsynchRequestNotCompleted; bSuccess = false; } break; case 997: // = WM_DDE_DATA if(DdeV[wC]->wType == XTYP_REQUEST) { // request return data when finished bSuccess = bCbSuccess; //sprintf(szTemp,"inside WM_DDE_DATA bSuccess: %i",bSuccess); //MessageBox((HWND)GetActiveWindow(), szTemp, "AsynchTransactionCompleted", MB_OK); } break; default: //char szTemp[100]; //sprintf(szTemp,"inside default branch"); //MessageBox((HWND)GetActiveWindow(), szTemp, "AsynchTransactionCompleted", MB_OK); csMsg = (CString)ccMsgAsynchTransNotCompleted; bSuccess = false; } } else { csMsg = (CString)ccMsgAsynchTransNotCompleted; bSuccess = false; } } } //char szTemp[100]; //sprintf(szTemp,"Before exit bSuccess: %i",bSuccess); //MessageBox((HWND)GetActiveWindow(), szTemp, "AsynchTransactionCompleted", MB_OK); return bSuccess; } // procedure which determines if a certain asynchronous transaction // has completed. Only to use for one transaction at a time, because // messages from other transactions will be ignored. Procedure enables // quasi synchronous behavior for asynchronous request, if called // after transaction call. To use if in between a transaction is processed // by a server some other calculations shall be done in the program without // waiting for the server to finish the transaction. After the calculations // this procedure can be called. If the server has finished it returns true, // if not false. // bWait: controls, if the procedure shall wait until the transaction is // finished (true) or only check once for completion (false). bool DCAsynchTransactionCompleted(WORD wC, DWORD dwTransID, bool bWait) { bool bSuccess; do { bSuccess = AsynchTransactionCompleted(wC, dwTransID); // transaction completed with error if(!bCbSuccess) return false; // transaction completed successful else if(bSuccess && bCbSuccess) return true; // }while(bWait && !bSuccess && bCbSuccess); // no message available return false; } // procedure which retrives the conversation number from a given // conversation handle WORD FindConvNo(HCONV hConv) { for(WORD i = 0; i < wCONVMAX; i++) if(DdeV[i]->hConv == hConv) return i; return wCONVMAX + 1; } // procedure which determines the next conversation number bool GetConvNo(WORD* pConvNo) { WORD i = 0; while((i < wCONVMAX) && (DdeV[i] != NULL)) i++; if (i >= wCONVMAX) { // too many conversations csMsg = (CString)ccMsgConvMaxError; return false; } else { *pConvNo = i; return true; } } // Free memory for DDE variables void FreeDdeV(WORD wC) { if(DdeV[wC] != NULL) { free(DdeV[wC]); DdeV[wC] = NULL; } return; } // Free memory for DDE data access variables void FreeDDA(WORD wC) { if(DDA[wC] != NULL) { free(DDA[wC]); DDA[wC] = NULL; } return; } // procedure to abandon an asynchronous transaction // takes the conversation number and the transaction id bool DCAbandonTransaction(WORD wC, DWORD dwTransID) { bool bSuccess; bSuccess = DdeAbandonTransaction(dwDDEInst, DdeV[wC]->hConv, dwTransID); FreeData(wC); if(!bSuccess) { DCGetLastError(dwDDEInst, (CString)ccMsgAbandonTransaction); bSuccess = false; } else { bSuccess = true; } return bSuccess; } // procedure which gives back the version of this dll char* DCVersion() { return szVersion; }