- BORLAND/: Borland C++ 4.52 (chosen over 4.5 by byte-match: CODE/RP/CW32.LIB
is identical to 4.52's install lib). BCC32/TLINK32/TLIB/MAKE run natively on
Win11; CODE/BT/OPT.MAK is the shipped BTL4OPT.EXE's exact flag recipe
(extender = Borland PowerPack DPMI32, not Phar Lap TNT).
- restoration/source410/: the literal 1995-form reconstruction of the missing
BT game source (never mixed into CODE/). Round 1-3 state:
* 6 of 10 surviving original TUs COMPILE CLEAN under the period toolchain
(BTMSSN, BTCNSL, BTSCNRL, BTTEAM, BTL4MODE, BTL4ARND) - first builds
since 1996.
* BT_L4/BTL4APP.CPP pilot reconstruction: 12/12 functions, Fail() lands on
its binary-recorded line 400 exactly.
* BT/BTCNSL.HPP: console wire IDs recovered from the binary's ctors
(Killed=9, Damaged=10, ScoreUpdate=13, DeathWithoutHonor=15 [T1];
TeamScore=12 flagged [T4]).
* MUNGA/: 8 engine-header backfills back-dated from the BT412 WinTesla tree
(VDATA numbering decomp-verified; AUDREND's OpenAL-era virtual removed -
the period compiler is the drift detector).
* Tooling: backdate.py (WinTesla->1995 header transform), compile410.sh
(per-TU verification sweep under authentic OPT.MAK flags).
* README: corrected roadmap - MECH.HPP is the capstone grown with the mech
TU reconstructions; BTREG.CPP green = the header-family milestone.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
515 lines
16 KiB
C++
515 lines
16 KiB
C++
// Borland C++ - (C) Copyright 1991, 1992 by Borland International
|
|
|
|
//*******************************************************************
|
|
//
|
|
// program - DDEClnt.c
|
|
// purpose - dde client interface routines. This file is part of the
|
|
// TSTAPP project.
|
|
//
|
|
//*******************************************************************
|
|
|
|
#define STRICT
|
|
#include <windows.h>
|
|
#pragma hdrstop
|
|
#include <dde.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <windowsx.h>
|
|
|
|
#include "ddepack.h"
|
|
#include "ddeclnt.h"
|
|
|
|
#define WW_SERVER 0
|
|
#define ACK 0x8000
|
|
#define BUSY 0x4000
|
|
|
|
#if defined(__FLAT__)
|
|
#define _fstrlen strlen
|
|
#define _fstrcpy strcpy
|
|
#endif
|
|
|
|
#if defined(__WIN32__)
|
|
#define SetWindowServer(hWnd,hWndServer)\
|
|
SetWindowLong (hWnd, WW_SERVER, (LONG) (hWndServer))
|
|
#define GetWindowServer(hWnd)\
|
|
((HWND) GetWindowLong (hWnd, WW_SERVER))
|
|
#define DDECommandHandle(hCommand)\
|
|
((LPARAM)(hCommand))
|
|
#else
|
|
#define SetWindowServer(hWnd,hWndServer)\
|
|
SetWindowWord (hWnd, WW_SERVER, (WORD) (hWndServer))
|
|
#define GetWindowServer(hWnd)\
|
|
((HWND) GetWindowWord (hWnd, WW_SERVER))
|
|
#define DDECommandHandle(hCommand)\
|
|
((LPARAM)(MAKELONG(0,(hCommand))))
|
|
#endif
|
|
|
|
static int nClientClassRegistered;
|
|
static char szClientClass[] = "ddeclient";
|
|
static WORD wLastDDEClientMsg;
|
|
static int nAckFlg;
|
|
static int nBusyFlg;
|
|
static HANDLE hData;
|
|
|
|
//*******************************************************************
|
|
// DDEClientOpen - open a dde conversation with a server
|
|
//
|
|
// paramaters:
|
|
// hParentWnd - The handle of the window trying to start
|
|
// this dde conversation.
|
|
// szApp - The name of the application to talk to.
|
|
// szTopic - The name of the topic to talk about.
|
|
//
|
|
// returns:
|
|
// The handle of the channel to use to continue the conversation
|
|
// or
|
|
// NULL if conversation could not be opened.
|
|
//
|
|
//*******************************************************************
|
|
HWND DDEClientOpen(HWND hParentWnd, char *szApp, char *szTopic)
|
|
{
|
|
HINSTANCE hInst;
|
|
HWND hChannel;
|
|
ATOM aApp;
|
|
ATOM aTopic;
|
|
WNDCLASS wclass;
|
|
|
|
// Get parents instance to use in creating window to carry on
|
|
// dde conversation.
|
|
hInst = (HINSTANCE)GetWindowInstance(hParentWnd);
|
|
if (!hInst)
|
|
return(0);
|
|
|
|
// If we have not already done it, register the window class to
|
|
// carry on this conversation.
|
|
if (!nClientClassRegistered)
|
|
{
|
|
wclass.style = CS_HREDRAW | CS_VREDRAW;
|
|
wclass.lpfnWndProc = DDEClientWndProc;
|
|
wclass.cbClsExtra = 0;
|
|
wclass.cbWndExtra = sizeof (HWND);
|
|
wclass.hInstance = hInst;
|
|
wclass.hIcon = 0;
|
|
wclass.hCursor = 0;
|
|
wclass.hbrBackground = 0;
|
|
wclass.lpszMenuName = 0;
|
|
wclass.lpszClassName = szClientClass;
|
|
|
|
// Do not fail if register fails, because another app may have
|
|
// already registered this class.
|
|
RegisterClass(&wclass);
|
|
|
|
// So we won't try to register again.
|
|
nClientClassRegistered = 1;
|
|
}
|
|
|
|
// Create the window that will remain hidden but will carry on the
|
|
// conversation wih the dde server.
|
|
hChannel = CreateWindow(szClientClass, // window class name
|
|
NULL, // window title
|
|
WS_CHILD, // window style
|
|
CW_USEDEFAULT, // window origin
|
|
0,
|
|
CW_USEDEFAULT, // window size
|
|
0,
|
|
hParentWnd, // window parent
|
|
NULL, // window id
|
|
hInst, // window instance
|
|
NULL); // no parms
|
|
|
|
// If create of window failed we cannot continue
|
|
if (!hChannel)
|
|
return(0);
|
|
|
|
// Create atoms representing application and topic we want to talk about
|
|
aApp = GlobalAddAtom(szApp);
|
|
if (!aApp)
|
|
return(0);
|
|
|
|
aTopic = GlobalAddAtom(szTopic);
|
|
if (!aTopic)
|
|
{
|
|
GlobalDeleteAtom(aApp);
|
|
return(0);
|
|
}
|
|
|
|
// Remember the last dde message we sent, because the window proc we
|
|
// just created must know the last message sent by this application
|
|
// to properly respond to a dde response.
|
|
wLastDDEClientMsg = WM_DDE_INITIATE;
|
|
|
|
// Broadcast a dde initiate to all apps on this system to see if any
|
|
// will talk to us about the requested application and topic.
|
|
SendMessage((HWND)-1, wLastDDEClientMsg, (WPARAM)hChannel, (LPARAM)MAKELONG(aApp, aTopic));
|
|
|
|
// Get rid of the atoms because we don't need them any more.
|
|
GlobalDeleteAtom(aApp);
|
|
GlobalDeleteAtom(aTopic);
|
|
|
|
// If no acknowledgement from any apps on the system to our initiate
|
|
// message, no server will talk on the application and topic we requested.
|
|
if (!nAckFlg)
|
|
{
|
|
DestroyWindow(hChannel);
|
|
hChannel = 0;
|
|
}
|
|
|
|
nAckFlg = 0;
|
|
|
|
return(hChannel);
|
|
}
|
|
|
|
//*******************************************************************
|
|
// DDEClientClose - close the dde conversation with a server
|
|
//
|
|
// paramaters:
|
|
// hChannel - The handle of the window conducting
|
|
// the dde conversation.
|
|
//
|
|
//*******************************************************************
|
|
void DDEClientClose(HWND hChannel)
|
|
{
|
|
HWND hServerWnd;
|
|
|
|
// If the channel is still active.
|
|
if (IsWindow(hChannel))
|
|
{
|
|
wLastDDEClientMsg = WM_DDE_TERMINATE;
|
|
|
|
// Get handle of server we were conversing with.
|
|
hServerWnd = GetWindowServer(hChannel);
|
|
|
|
// If he is still active.
|
|
if (IsWindow(hServerWnd))
|
|
SendMessage(hServerWnd, wLastDDEClientMsg, (WPARAM)hChannel, (LPARAM)0L);
|
|
}
|
|
}
|
|
|
|
//*******************************************************************
|
|
// DDEExecute - request an action from server
|
|
//
|
|
// paramaters:
|
|
// hChannel - The handle of the window conducting
|
|
// the dde conversation.
|
|
// lpszCommand - The command string to send to the
|
|
// dde server.
|
|
// nWait - The time to wait for a response to the
|
|
// command. If zero, will return immediatly.
|
|
//
|
|
// returns:
|
|
// 1 - successful transmission.
|
|
// 0 - error.
|
|
//
|
|
//*******************************************************************
|
|
int DDEExecute(HWND hChannel, LPSTR lpszCommand, int nWait)
|
|
{
|
|
int len;
|
|
HANDLE hCommand;
|
|
HWND hServerWnd;
|
|
LPSTR lpCommand;
|
|
int nRc;
|
|
|
|
nRc = 0;
|
|
|
|
// Get the handle of server we were talking to.
|
|
hServerWnd = GetWindowServer(hChannel);
|
|
|
|
// If he is no longer active we can't talk to him.
|
|
if (!IsWindow(hServerWnd))
|
|
return(nRc);
|
|
|
|
// Get shared memory to put our request to server in.
|
|
len = _fstrlen(lpszCommand);
|
|
hCommand = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT | GMEM_DDESHARE,
|
|
(DWORD) (len + 1));
|
|
if (hCommand)
|
|
{
|
|
// Lock it down to get address of memory.
|
|
lpCommand = GlobalLock(hCommand);
|
|
if (lpCommand)
|
|
{
|
|
// Copy the command into the shared memory.
|
|
_fstrcpy(lpCommand, lpszCommand);
|
|
GlobalUnlock(hCommand);
|
|
|
|
// Post the message to the server.
|
|
wLastDDEClientMsg = WM_DDE_EXECUTE;
|
|
|
|
if (!PostMessage(hServerWnd, wLastDDEClientMsg, (WPARAM)hChannel,
|
|
DDECommandHandle(hCommand)))
|
|
{
|
|
// If post failed.
|
|
GlobalFree(hCommand);
|
|
}
|
|
else
|
|
{
|
|
nRc = 1;
|
|
nAckFlg = 0;
|
|
|
|
// Wait if caller told us to.
|
|
if (nWait)
|
|
nRc = DDEWait(hChannel, nWait);
|
|
}
|
|
}
|
|
}
|
|
|
|
nAckFlg = 0;
|
|
|
|
return(nRc);
|
|
}
|
|
|
|
//*******************************************************************
|
|
// DDERead - read some data from the server
|
|
//
|
|
// paramaters:
|
|
// hChannel - The handle of the window conducting
|
|
// the dde conversation.
|
|
// szItem - The item to get from server.
|
|
//
|
|
// returns:
|
|
// Global Handle - of item requested if successful.
|
|
// or
|
|
// 0 - if error.
|
|
//
|
|
//*******************************************************************
|
|
HANDLE DDERead(HWND hChannel, char *szItem, int nWait)
|
|
{
|
|
HWND hServerWnd;
|
|
DDEDATA FAR *lpData;
|
|
int nReleaseFlg;
|
|
HANDLE hOurData;
|
|
LPSTR lpOurData;
|
|
ATOM aItem;
|
|
int len;
|
|
|
|
hOurData = 0;
|
|
|
|
// Get the handle of server we were talking to.
|
|
hServerWnd = GetWindowServer(hChannel);
|
|
|
|
// If he is no longer active we can't talk to him.
|
|
if (!IsWindow(hServerWnd))
|
|
return(hOurData);
|
|
|
|
// Create an atom representing the data item we want read.
|
|
aItem = GlobalAddAtom(szItem);
|
|
hData = 0;
|
|
|
|
// Post the request to the server.
|
|
wLastDDEClientMsg = WM_DDE_REQUEST;
|
|
PostMessage(hServerWnd, wLastDDEClientMsg, (WPARAM)hChannel,
|
|
(LPARAM)MAKELONG(CF_TEXT, aItem));
|
|
|
|
// Wait for a dde message in response or time to expire.
|
|
DDEWait(hChannel, nWait);
|
|
|
|
// If we have a handle to returned data.
|
|
if (hData)
|
|
{
|
|
// Lock it down to get address.
|
|
lpData = (DDEDATA FAR *) GlobalLock(hData);
|
|
if (lpData)
|
|
{
|
|
// Remember release flag to see if we release or server releases.
|
|
nReleaseFlg = lpData->fRelease;
|
|
|
|
// Get memory for our local copy of data.
|
|
len = _fstrlen((char far *)lpData->Value);
|
|
hOurData = GlobalAlloc(GMEM_MOVEABLE, (DWORD) (len + 1));
|
|
if (hOurData)
|
|
{
|
|
// Lock it down to get address.
|
|
lpOurData = GlobalLock(hOurData);
|
|
if (lpOurData)
|
|
{
|
|
// Copy it in.
|
|
_fstrcpy(lpOurData, (char far *)lpData->Value);
|
|
GlobalUnlock(hOurData);
|
|
}
|
|
else
|
|
hOurData = 0;
|
|
}
|
|
|
|
GlobalUnlock(hData);
|
|
}
|
|
|
|
// If server requested that we free shared memory.
|
|
if (nReleaseFlg)
|
|
GlobalFree(hData);
|
|
}
|
|
|
|
// Return handle to our copy of data.
|
|
return(hOurData);
|
|
}
|
|
|
|
//*******************************************************************
|
|
// DDEWait - wait up to time for a dde messag
|
|
//
|
|
// paramaters:
|
|
// hChannel - The handle of the window conducting
|
|
// the dde conversation.
|
|
// nWait - Length of time to wait for a dde msesage
|
|
// in seconds.
|
|
//
|
|
// returns:
|
|
// 1 - dde message recieved while waiting.
|
|
// or
|
|
// 0 - time expired and no dde messages recieved.
|
|
//
|
|
//*******************************************************************
|
|
int DDEWait(HWND hChannel, int nWait)
|
|
{
|
|
time_t t1;
|
|
time_t t2;
|
|
int nGotDDEMsg;
|
|
MSG msg;
|
|
|
|
time(&t1);
|
|
nGotDDEMsg = 0;
|
|
|
|
do
|
|
{
|
|
if (PeekMessage(&msg, hChannel, WM_DDE_FIRST, WM_DDE_LAST, PM_REMOVE))
|
|
{
|
|
DispatchMessage(&msg);
|
|
nGotDDEMsg = 1;
|
|
}
|
|
}
|
|
while (!nGotDDEMsg && ((time(&t2) - t1) < nWait));
|
|
|
|
return(nGotDDEMsg);
|
|
}
|
|
|
|
//*******************************************************************
|
|
// DDESleep - wait for a time
|
|
//
|
|
// paramaters:
|
|
// nWait - Length of time to wait in seconds
|
|
//
|
|
//*******************************************************************
|
|
void DDESleep(int nWait)
|
|
{
|
|
time_t t1;
|
|
time_t t2;
|
|
MSG msg;
|
|
|
|
time(&t1);
|
|
|
|
do
|
|
{
|
|
PeekMessage(&msg, 0, 0, 0, PM_REMOVE);
|
|
}
|
|
while ((time(&t2) - t1) < nWait);
|
|
}
|
|
|
|
//*******************************************************************
|
|
// DDEClientWndProc - window proc to manage interface with server
|
|
//
|
|
// paramaters:
|
|
// hWnd - The window handle for this message
|
|
// message - The message number
|
|
// wParam - The WPARAM parameter for this message
|
|
// lParam - The LPARAM parameter for this message
|
|
//
|
|
// returns:
|
|
// depends on message.
|
|
//
|
|
//*******************************************************************
|
|
LRESULT CALLBACK _export DDEClientWndProc(HWND hWnd, UINT message,
|
|
WPARAM wParam, LPARAM lParam)
|
|
{
|
|
HWND hServerWnd;
|
|
WORD wStatus;
|
|
HANDLE hMem;
|
|
ATOM aItem;
|
|
|
|
switch (message)
|
|
{
|
|
case WM_CREATE: // create this window
|
|
// We will remember the handle of the server we are conversing
|
|
// with in the first extra window word, but for now we do not
|
|
// have a server.
|
|
SetWindowServer(hWnd, 0);
|
|
break;
|
|
|
|
case WM_DDE_ACK: // an ack from a server
|
|
switch (wLastDDEClientMsg)
|
|
{
|
|
case WM_DDE_INITIATE: // ack in response to initiate
|
|
// Get rid of application and topic atoms used in
|
|
// initiate request.
|
|
GlobalDeleteAtom(LOWORD(lParam));
|
|
GlobalDeleteAtom(HIWORD(lParam));
|
|
|
|
// Now we have a server for this conversation, so remember
|
|
// who it is.
|
|
SetWindowServer(hWnd, wParam);
|
|
nAckFlg = 1;
|
|
break;
|
|
|
|
case WM_DDE_EXECUTE: // ack in response to execute
|
|
// Server acknowledged our execute command.
|
|
DDEUnpack (WM_DDE_ACK, lParam, (UINT *)&wStatus, (UINT *)&hMem);
|
|
nAckFlg = wStatus & ACK;
|
|
if (!nAckFlg)
|
|
nBusyFlg = wStatus & BUSY;
|
|
GlobalFree((HGLOBAL)hMem);
|
|
break;
|
|
|
|
case WM_DDE_REQUEST: // ack in response to request
|
|
// Get rid of item atom used in request.
|
|
GlobalDeleteAtom(HIWORD(lParam));
|
|
wStatus = LOWORD(lParam);
|
|
nAckFlg = wStatus & ACK;
|
|
if (!nAckFlg)
|
|
nBusyFlg = wStatus & BUSY;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case WM_DDE_DATA: // data from a server per request
|
|
|
|
// Unpack handle to global data item and atom item
|
|
DDEUnpack (WM_DDE_DATA, lParam, (UINT *)&hData, (UINT *)&aItem);
|
|
|
|
// Get rid of item atom used in request.
|
|
GlobalDeleteAtom(aItem);
|
|
break;
|
|
|
|
case WM_DDE_TERMINATE: // server says terminate conversation
|
|
// Get handle of server we are dealing with.
|
|
hServerWnd = GetWindowServer(hWnd);
|
|
|
|
// Ignore if this terminate is not from that server.
|
|
if (wParam != (WPARAM)hServerWnd)
|
|
return(0L);
|
|
|
|
// Forget who server is.
|
|
SetWindowServer(hWnd, 0);
|
|
|
|
// Tell our parent conversation with server is closed.
|
|
SendMessage(GetParent(hWnd), WMU_DDE_CLOSED, (WPARAM)hWnd, (LPARAM)0L);
|
|
|
|
// Send terminate to server to acknowledge we recieved
|
|
// terminate, if we did not initiate the termination ourselves.
|
|
if ((wLastDDEClientMsg != WM_DDE_TERMINATE) && IsWindow(hServerWnd))
|
|
PostMessage(hServerWnd, WM_DDE_TERMINATE, (WPARAM)hWnd, (LPARAM)0L);
|
|
|
|
// Terminate this child window.
|
|
DestroyWindow(hWnd);
|
|
break;
|
|
|
|
case WM_DESTROY: // destroy this window
|
|
// Terminate any ongoing conversation on our way out.
|
|
DDEClientClose(hWnd);
|
|
break;
|
|
|
|
default:
|
|
return(DefWindowProc(hWnd, message, wParam, lParam));
|
|
}
|
|
|
|
return(0L);
|
|
}
|
|
|
|
//*******************************************************************
|