📄 dde_stuf.cpp
字号:
//*************************************************************************************
// DDE_Stuf.cpp
// This file contains the class member functions for DDE handler objects. These
// objects encapsulate a DDE communication interface which allows C++ programs to
// talk to each other (or to other applications) using dynamic data exchange.
// Note that since DDE is a Windows phenomenon, this file can only be compiled
// and run under Windows. Also, it's designed for 32-bit, Windows 95/NT mode.
//
// Classes
// C_DDE_Item - An item of data maintained by one of a server's topics
// C_DDE_Topic - A topic which is to be serviced by a DDE server
// C_DDE_Manager - Base class with common code for client and server objects
// C_DDE_Server - A DDE server which services a bunch of topics and items
// C_DDE_Client - A client which can talk to a DDE server
//
// Nonmember Functions
// DDE_CheckForError - Check for errors from the DDEML library
// DDE_ErrorString - Convert DDE error codes into readable strings
// DDE_Server_WndProc - Processes messages for DDE server window
// Create_DDE_Server_Window - Creates the DDE server window
// DDE_Server_Thread_Function - Function which runs the DDE server's thread
// DdeServerCallback - DDEML callback handles incoming DDE transactions
// DdeClientCallback - A similar callback, but for DDE client objects
//
// Version
// 8-16-97 JRR Original file
// 9-02-97 JRR Added thread protection for data
// 9-07-97 JRR A few changes to make it compatible with client object
// 9-14-97 JRR Merged server and client into one file for convenience
//*************************************************************************************
#ifdef __WIN32__ // This file should only be compiled for Win32 projects
#include <stdio.h>
#include <string.h>
#include <windows.h>
#include <ddeml.h>
#include <DDE_Stuf.hpp>
#include "MT_Debug.hpp"
// We need global pointers to the DDE objects so the callback functions, which are
// not member functions, can access member functions and data of the server object
static C_DDE_Server* The_DDE_Server = NULL;
static C_DDE_Client* The_DDE_Client = NULL;
static HWND hDDE_Server_Window = NULL; // Handles for the server's and client's
static HWND hDDE_Client_Window = NULL; // little GUI windows
static bool DDE_ServerTimeToStop = false; // These flags synchronize the exiting
static bool DDE_ServerThreadDone = false; // of the DDE threads with the server's
static bool DDE_ClientTimeToStop = false; // and the client's destructors
static bool DDE_ClientThreadDone = false;
static int NumConnections = 0; // Number of clients currently connected
const int MAX_TIMEOUT = 1000; // How long to wait for thread to finish
//-------------------------------------------------------------------------------------
// Function: DDE_CheckForError
// This function checks to see if there has been a DDE error. If there has, it
// displays a complaint.
void DDE_CheckForError (DWORD idInstance)
{
UINT ErrorCode; // Save the error (or no error) code here
if ((ErrorCode = DdeGetLastError (idInstance)) != DMLERR_NO_ERROR)
MessageBox (NULL, DDE_ErrorString (ErrorCode), "DDE Error!",
MB_OK | MB_ICONEXCLAMATION);
}
//-------------------------------------------------------------------------------------
// Function: DDE_ErrorString
// This function simply finds the correct string for a given DDE error, puts it
// into the buffer it maintains, and returns a pointer to that buffer.
const char* DDE_ErrorString (UINT TheError)
{
static char DDE_Debug_String[64]; // Buffer holds debugging strings
// Macro which copies DDE error strings into a buffer for output from function
#define ErrorDecode(x,y) case (x): strcpy (DDE_Debug_String, (y)); break
switch (TheError)
{
ErrorDecode (DMLERR_NO_ERROR, "NO ERROR: Oops, there's no error");
ErrorDecode (DMLERR_ADVACKTIMEOUT, "Advise ACK timeout");
ErrorDecode (DMLERR_BUSY, "Somebody's busy");
ErrorDecode (DMLERR_DATAACKTIMEOUT, "Data ACK timeout");
ErrorDecode (DMLERR_DLL_NOT_INITIALIZED, "DLL not initialized");
ErrorDecode (DMLERR_DLL_USAGE, "DLL usage");
ErrorDecode (DMLERR_EXECACKTIMEOUT, "Exec ACK timeout");
ErrorDecode (DMLERR_INVALIDPARAMETER, "Invalid parameter");
ErrorDecode (DMLERR_LOW_MEMORY, "Low memory");
ErrorDecode (DMLERR_MEMORY_ERROR, "Memory error");
ErrorDecode (DMLERR_NOTPROCESSED, "Not processed");
ErrorDecode (DMLERR_NO_CONV_ESTABLISHED, "No conversation established");
ErrorDecode (DMLERR_POKEACKTIMEOUT, "Poke ACK timeout");
ErrorDecode (DMLERR_POSTMSG_FAILED, "Post message failed");
ErrorDecode (DMLERR_REENTRANCY, "Re-entrancy is a no-no");
ErrorDecode (DMLERR_SERVER_DIED, "Server died *sniff*");
ErrorDecode (DMLERR_SYS_ERROR, "System error");
ErrorDecode (DMLERR_UNADVACKTIMEOUT, "Unadvise ACK timeout");
ErrorDecode (DMLERR_UNFOUND_QUEUE_ID, "Queue not found");
default:
sprintf (DDE_Debug_String, "Unknown error code %d", TheError);
break;
}
return DDE_Debug_String;
}
//-------------------------------------------------------------------------------------
// Function: DDE_WndProc
// This is the window procedure for the DDE server window. The DDE server runs
// in its own thread and has its own message loop, and this is the procedure
// which services messages for that window.
LRESULT CALLBACK DDE_WndProc (HWND hWnd, WPARAM message, WPARAM wParam, LPARAM lParam)
{
static LONG TextLineHeight; // Saves height of a line of screen text
static TEXTMETRIC TextMetrics; // Structure for getting GUI text info
PAINTSTRUCT PaintStruct; // Structure for talking to Windows GUI
HDC hDevContext; // Another structure for talking to GUI
RECT ClientRect, DrawRect; // Rectangle structures, also for GUI
char TextBuffer[80]; // Buffer holds a line of displayed text
switch (message)
{
// This message appears when the window is first being created
case WM_CREATE:
// Figure out how big the window text is
hDevContext = GetDC (hWnd);
GetTextMetrics (hDevContext, &TextMetrics);
TextLineHeight = TextMetrics.tmHeight + TextMetrics.tmExternalLeading;
ReleaseDC (hWnd, hDevContext);
return FALSE;
// This message means the window must be repainted. Display text in the
// window showing how many connections to this server are currently active
case WM_PAINT:
BeginPaint (hWnd, &PaintStruct);
SetBkMode (PaintStruct.hdc, TRANSPARENT);
GetClientRect (hWnd, &ClientRect);
ClientRect.bottom = ClientRect.top + TextLineHeight;
wsprintf (TextBuffer, "%d connections active", NumConnections);
DrawRect = ClientRect;
if (IntersectRect (&DrawRect, &ClientRect, &(PaintStruct.rcPaint)))
DrawText (PaintStruct.hdc, TextBuffer, -1, &ClientRect, DT_DDESTYLE);
EndPaint (hWnd, &PaintStruct);
break;
// If the message isn't anything we know about, let Windows deal with it
default:
return DefWindowProc (hWnd, message, wParam, lParam);
}
return FALSE;
}
//-------------------------------------------------------------------------------------
// Function: Create_DDE_Window
// This function creates a window just for the DDE server. The window is just a
// small white popup named "DDE Server" which doesn't display anything much. A
// handle for the window is returned.
HWND Create_DDE_Window (const char* aTitle)
{
WNDCLASS wc;
wc.style = 0; // Class styles
wc.lpfnWndProc = DDE_WndProc; // Name of message loop function
wc.cbClsExtra = 0; // Not using Class Extra data
wc.cbWndExtra = 0; // Not using Window Extra data
wc.hInstance = NULL; // Instance that owns this class
wc.hIcon = LoadIcon (NULL, IDI_APPLICATION); // Use default app. icon
wc.hCursor = LoadCursor (NULL, IDC_ARROW); // Use arrow cursor
wc.hbrBackground = GetStockObject (WHITE_BRUSH); // Use sys. backg. color
wc.lpszMenuName = NULL; // Resource name for menu
wc.lpszClassName = "DDE Window Class"; // Name for this class
if (RegisterClass (&wc) == 0) return NULL; // Register the window class
// Create a window in the default X and Y position, size 240x100, no parent
// window nor menu, and owned by nobody
return (CreateWindow ("DDE Window Class", aTitle,
WS_POPUP | WS_VISIBLE | WS_CAPTION, CW_USEDEFAULT,
CW_USEDEFAULT, 240, 100, 0, 0, NULL, NULL));
}
//-------------------------------------------------------------------------------------
// Function: DDE_ServerThreadFunction
// Because DDEML applications have to run entirely in one thread, the DDE server
// object creates a thread and then runs entirely within it. This is the thread
// function for that thread. It is started up by the C_DDE_Server constructor
// and continues to run until the server object is deleted.
DWORD WINAPI DDE_ServerThreadFunction (LPVOID lpParam)
{
// Set flag which tells other threads this one is still running
DDE_ServerThreadDone = false;
// Create and display a window associated with the DDE system's thread
if ((hDDE_Server_Window = Create_DDE_Window ("DDE Server")) == NULL)
MessageBox (NULL, "Unable to create window", "DDE Error", MB_OK | MB_ICONSTOP);
ShowWindow (hDDE_Server_Window, SW_SHOWNORMAL);
UpdateWindow (hDDE_Server_Window);
// Wait until it's OK to register DDE server, topics, and items. While waiting,
// give up the rest of each timeslice to other processes so they can do stuff
while (The_DDE_Server->ReadyToRegister == false) Sleep (0);
// Initialize the DDE server object
The_DDE_Server->Initialize ();
// Process messages for this window until we're told it's time to exit
MSG msg;
while (DDE_ServerTimeToStop == false)
{
// Tell all items in advise loops to send data if it has been updated
The_DDE_Server->SendAdviseData ();
// Process Windows messages in the ordinary way for GUI windows
if (PeekMessage (&msg, 0, 0, 0, PM_REMOVE))
{
TranslateMessage (&msg);
DispatchMessage (&msg);
}
// If no message was received, give remainder of timeslice to other threads
else Sleep (0);
}
// Uninitialize the server; this must be done within this thread
The_DDE_Server->Uninitialize ();
DDE_ServerThreadDone = true;
return 0;
}
//-------------------------------------------------------------------------------------
// Function: DDE_ClientThreadFunction
// This is the thread function for the DDE client thread. It works the same way
// as the DDE server thread function above works, but it is started up by the
// C_DDE_Client constructor and it runs until the client object is deleted.
DWORD WINAPI DDE_ClientThreadFunction (LPVOID lpParam)
{
// Set flag which tells other threads this one is still running
DDE_ClientThreadDone = false;
// Create and display a window associated with the DDE system's thread
if ((hDDE_Client_Window = Create_DDE_Window ("DDE Client")) == NULL)
MessageBox (NULL, "Unable to create window", "DDE Error", MB_OK | MB_ICONSTOP);
ShowWindow (hDDE_Client_Window, SW_SHOWNORMAL);
UpdateWindow (hDDE_Client_Window);
// Wait until it's OK to register DDE server, topics, and items; then do so
while (The_DDE_Client->IsReadyToRegister () == false) Sleep (0);
The_DDE_Client->Initialize ();
// Process messages for this window until we're told it's time to exit
MSG msg;
while (DDE_ClientTimeToStop == false)
{
// Update the client by checking if the DDE thread has any jobs to do
The_DDE_Client->Update ();
// Check for Windows messages and process them if any have come in
if (PeekMessage (&msg, 0, 0, 0, PM_REMOVE))
{
TranslateMessage (&msg);
DispatchMessage (&msg);
}
else Sleep (0); // If no message, let other threads run
}
// Uninitialize the client, then shut down this thread
The_DDE_Client->Uninitialize ();
DDE_ClientThreadDone = true;
return 0;
}
//=====================================================================================
// Class: C_DDE_DataItem
// This class represents one item of data to be maintained by the DDE server.
// Each item has an item name, a type, and a value.
//=====================================================================================
//-------------------------------------------------------------------------------------
// Constructor: C_DDE_DataItem
// These constructors create new C_DDE_DataItem objects. One constructor is given
// for each of the data types supported by the DDE server package. The construc-
// tor allocates storage space for the local copy (used by the DDE thread) of the
// data and calls the common Construct() function to set everything else up.
C_DDE_Item::C_DDE_Item (const char* aName, int& rData)
{
pTheData = new int[1]; // Allocate space for the data
*(int*)pTheData = rData; // Read the initial value of the data in
TheDataType = DDT_int; // Save the type of the data
Construct (aName); // Call the common constructor code
}
C_DDE_Item::C_DDE_Item (const char* aName, unsigned int& rData)
{
pTheData = new unsigned int[1]; // See comments above
*(unsigned int*)pTheData = rData;
TheDataType = DDT_uint;
Construct (aName);
}
C_DDE_Item::C_DDE_Item (const char* aName, long& rData)
{
pTheData = new long[1]; // Etc.
*(long*)pTheData = rData;
TheDataType = DDT_long;
Construct (aName);
}
C_DDE_Item::C_DDE_Item (const char* aName, unsigned long& rData)
{
pTheData = new unsigned long[1];
*(unsigned long*)pTheData = rData;
TheDataType = DDT_ulong;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -