- 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>
390 lines
11 KiB
C++
390 lines
11 KiB
C++
//----------------------------------------------------------------------------
|
|
// ObjectWindows - (C) Copyright 1991, 1993 by Borland International
|
|
//
|
|
//
|
|
// This is a sample application using the OWL library that demonstrats the
|
|
// use of the Windows 3.1 DDEML API in a server application. You should
|
|
// first build this application and run it. Run the client application,
|
|
// DDECLI.EXE, to start a conversation with this Server. Detailed
|
|
// information on DDEML can found in the online help and is suggested
|
|
// reading for anyone interested in writing DDEML applications. Search on
|
|
// the keyword DDEML.
|
|
//----------------------------------------------------------------------------
|
|
#include <owl\owlpch.h>
|
|
#include <owl\applicat.h>
|
|
#include <owl\framewin.h>
|
|
#include <owl\menu.h>
|
|
#include <ddeml.h>
|
|
#include <string.h>
|
|
|
|
#define CM_ABOUT 0x100
|
|
|
|
|
|
class TDMLSrApp : public TApplication {
|
|
public:
|
|
TDMLSrApp() : TApplication(),CallBackProc((FARPROC)CallBack) {
|
|
nCmdShow = SW_SHOWMINIMIZED;
|
|
InstId=0;
|
|
}
|
|
|
|
void InitMainWindow();
|
|
BOOL IdleAction(long);
|
|
|
|
void InitInstance();
|
|
int TermInstance(int status);
|
|
|
|
DWORD InstId;
|
|
static HDDEDATA FAR PASCAL _export CallBack(WORD, WORD, HCONV, HSZ, HSZ,
|
|
HDDEDATA, DWORD, DWORD);
|
|
|
|
TProcInstance CallBackProc;
|
|
|
|
};
|
|
|
|
|
|
class TDMLSrWnd : public TFrameWindow {
|
|
public:
|
|
TDMLSrWnd(TWindow*, const char*);
|
|
~TDMLSrWnd();
|
|
void SetupWindow();
|
|
|
|
void EvSysCommand(UINT cmdType, TPoint&);
|
|
BOOL EvQueryOpen();
|
|
|
|
virtual BOOL MatchTopicAndService(HSZ, HSZ);
|
|
virtual BOOL MatchTopicAndItem(HSZ, HSZ);
|
|
virtual HDDEDATA WildConnect(HSZ, HSZ, WORD);
|
|
virtual HDDEDATA DataRequested(WORD);
|
|
virtual void UpdateData();
|
|
|
|
DWORD InstId() {
|
|
return ((TDMLSrApp*)GetApplication())->InstId;
|
|
}
|
|
|
|
HCONV HConv;
|
|
BOOL Loop;
|
|
HSZ Service;
|
|
HSZ Topic;
|
|
HSZ Item;
|
|
|
|
DECLARE_RESPONSE_TABLE(TDMLSrWnd);
|
|
DECLARE_CASTABLE;
|
|
};
|
|
|
|
DEFINE_RESPONSE_TABLE1(TDMLSrWnd, TFrameWindow)
|
|
EV_WM_SYSCOMMAND,
|
|
EV_WM_QUERYOPEN,
|
|
END_RESPONSE_TABLE;
|
|
|
|
IMPLEMENT_CASTABLE1(TDMLSrWnd, TFrameWindow);
|
|
|
|
|
|
TDMLSrWnd* This = 0;
|
|
|
|
TDMLSrWnd::TDMLSrWnd(TWindow* parent, const char* title)
|
|
: TFrameWindow(parent, title)
|
|
{
|
|
HConv = 0;
|
|
Loop = FALSE;
|
|
}
|
|
|
|
TDMLSrWnd::~TDMLSrWnd()
|
|
{
|
|
// This clean up is required for those resources that were allocated during
|
|
// the DDE conversation.
|
|
//
|
|
if (HConv)
|
|
DdeDisconnect(HConv); // Let the other party know we are leaving
|
|
|
|
if (InstId()) {
|
|
DdeNameService(InstId(), Service, 0, DNS_UNREGISTER);
|
|
if (Service)
|
|
DdeFreeStringHandle(InstId(), Service);
|
|
if (Topic)
|
|
DdeFreeStringHandle(InstId(), Topic);
|
|
if (Item)
|
|
DdeFreeStringHandle(InstId(), Item);
|
|
}
|
|
}
|
|
|
|
void
|
|
TDMLSrWnd::SetupWindow()
|
|
{
|
|
This = this;
|
|
|
|
TFrameWindow::SetupWindow();
|
|
|
|
Service = Topic = Item = 0;
|
|
|
|
// The strings below are the 'Service', 'Topic' and 'Item' identifiers
|
|
// that this application makes available, through DDE, to other
|
|
// applications.
|
|
//
|
|
Service = DdeCreateStringHandle(InstId(), "TDMLSR_Server", CP_WINANSI);
|
|
Topic = DdeCreateStringHandle(InstId(), "Borland", CP_WINANSI);
|
|
Item = DdeCreateStringHandle(InstId(), "Products", CP_WINANSI);
|
|
if (Service && Topic && Item) {
|
|
if (DdeNameService(InstId(), Service, 0, DNS_REGISTER) != 0) {
|
|
// If everything is successful then an About choice is added to the
|
|
// system menu.
|
|
//
|
|
TMenu menu(GetSystemMenu());
|
|
menu.AppendMenu(MF_BYCOMMAND | MF_SEPARATOR, -1, "");
|
|
menu.AppendMenu(MF_BYCOMMAND | MF_STRING, CM_ABOUT, "&About DDESVR");
|
|
} else {
|
|
MessageBox("Registration failed.", Title, MB_ICONSTOP);
|
|
PostQuitMessage(0);
|
|
}
|
|
} else {
|
|
MessageBox("String creation failed.", Title, MB_ICONSTOP);
|
|
PostQuitMessage(0);
|
|
}
|
|
}
|
|
|
|
//
|
|
// The code below is used to trap the About menu choice when it is selected
|
|
// from the system menu.
|
|
//
|
|
void
|
|
TDMLSrWnd::EvSysCommand(UINT cmdType, TPoint&)
|
|
{
|
|
if ((cmdType & 0xFFF0) == CM_ABOUT)
|
|
MessageBox("DDESVR.EXE\nWritten using ObjectWindows\nCopyright (c) 1991, 1993 by Borland International", "About DDESVR", MB_ICONINFORMATION);
|
|
else
|
|
DefaultProcessing();
|
|
}
|
|
|
|
//
|
|
// This seemingly insignificant function is what keeps this program
|
|
// minimized, no matter what the user might try to do.
|
|
//
|
|
BOOL
|
|
TDMLSrWnd::EvQueryOpen()
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// This function is used to compare incoming Topic and Service requests.
|
|
// This example DDE Server only makes one Service and one Topic available
|
|
// so the logic is simple for this case but could be more complex for
|
|
// Servers that offer multiple Services or Topics.
|
|
//
|
|
BOOL
|
|
TDMLSrWnd::MatchTopicAndService(HSZ topic, HSZ service)
|
|
{
|
|
if (DdeCmpStringHandles(Topic, topic) == 0)
|
|
if (DdeCmpStringHandles(Service, service) == 0)
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// This function is used to compare incoming Topic and Item pair requests.
|
|
// This Server only makes one Topic with one Item available so the logic is
|
|
// simple for this case but could be more complex if the Server offered
|
|
// multiple Items for multiple Topics.
|
|
//
|
|
BOOL
|
|
TDMLSrWnd::MatchTopicAndItem(HSZ hsz1, HSZ hsz2)
|
|
{
|
|
if (DdeCmpStringHandles(Topic, hsz1) == 0)
|
|
if (DdeCmpStringHandles(Item, hsz2) == 0)
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// This function responds to 'system wide' polling of any available
|
|
// Services with specific Topics, any Topics with specific Services or any
|
|
// Services with any Topics. It simply replies with a match, if there is
|
|
// one, so that the polling application can open data discussions later, if
|
|
// desired.
|
|
//
|
|
HDDEDATA
|
|
TDMLSrWnd::WildConnect(HSZ hsz1, HSZ hsz2, WORD wFmt)
|
|
{
|
|
HSZPAIR hszpTemp[] = { { Service, Topic }, { 0, 0 } };
|
|
|
|
if (!hsz1 && !hsz2) // Returns all if true
|
|
return DdeCreateDataHandle(InstId(), (LPBYTE)&hszpTemp[0], sizeof(hszpTemp), 0L, 0, wFmt, 0);
|
|
|
|
if (!hsz1 && DdeCmpStringHandles(hsz2, Service) == 0)
|
|
return DdeCreateDataHandle(InstId(), (LPBYTE)&hszpTemp[0], sizeof(hszpTemp), 0L, 0, wFmt, 0);
|
|
|
|
if (DdeCmpStringHandles(hsz1, Topic) == 0 && !hsz2)
|
|
return DdeCreateDataHandle(InstId(), (LPBYTE)&hszpTemp[0], sizeof(hszpTemp), 0L, 0, wFmt, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
HDDEDATA
|
|
TDMLSrWnd::DataRequested(WORD wFmt)
|
|
{
|
|
static char szItems[][42] = {
|
|
"Borland C++ with Object Windows",
|
|
"Turbo C++",
|
|
"Borland Pascal with Objects",
|
|
"Paradox For Windows",
|
|
"dBase",
|
|
"Quattro Pro For Windows"
|
|
};
|
|
|
|
static int iLoop = 0;
|
|
if (wFmt == CF_TEXT) {
|
|
iLoop++;
|
|
iLoop %= sizeof(szItems) / sizeof(szItems[0]);
|
|
return DdeCreateDataHandle(InstId(), (unsigned char *)szItems[iLoop], sizeof(szItems[iLoop]), 0, Item, wFmt, 0);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// This is triggered by the IdleAction() loop above whenever the user
|
|
// enters an advise loop.
|
|
//
|
|
void
|
|
TDMLSrWnd::UpdateData()
|
|
{
|
|
DdePostAdvise(InstId(), Topic, Item);
|
|
}
|
|
|
|
//
|
|
// This call back function is the heart of interaction between this program
|
|
// and DDE. Because Windows doesn't pass C++ 'this' pointers to call
|
|
// back functions, a static 'this' pointer was used. If you wanted to
|
|
// create a Server that would allow for more than one conversation, using a
|
|
// List of conversations and their associated 'this' pointers would be one
|
|
// possible method to try. The XTYP_ constants are described in detail in
|
|
// the online help.
|
|
//
|
|
HDDEDATA FAR PASCAL
|
|
TDMLSrApp::CallBack(WORD wType, WORD wFmt, HCONV hConv, HSZ hsz1, HSZ hsz2,
|
|
HDDEDATA hData, DWORD, DWORD)
|
|
{
|
|
switch (wType) {
|
|
case XTYP_ADVREQ:
|
|
if (This->MatchTopicAndItem(hsz1, hsz2))
|
|
return This->DataRequested(wFmt);
|
|
return 0;
|
|
|
|
case XTYP_ADVSTART:
|
|
if (!This->Loop && This->MatchTopicAndItem(hsz1, hsz2)) {
|
|
This->Loop = TRUE;
|
|
return (HDDEDATA)1;
|
|
}
|
|
return 0;
|
|
|
|
case XTYP_ADVSTOP:
|
|
if (This->Loop && This->MatchTopicAndItem(hsz1, hsz2))
|
|
This->Loop = FALSE;
|
|
break;
|
|
|
|
case XTYP_CONNECT:
|
|
if (!This->HConv)
|
|
if (This->MatchTopicAndService(hsz1, hsz2))
|
|
return (HDDEDATA)1;
|
|
return 0;
|
|
|
|
case XTYP_CONNECT_CONFIRM:
|
|
This->HConv = hConv;
|
|
break;
|
|
|
|
case XTYP_DISCONNECT:
|
|
if (hConv == This->HConv) {
|
|
This->HConv = 0;
|
|
This->Loop = FALSE;
|
|
}
|
|
break;
|
|
|
|
case XTYP_ERROR:
|
|
This->MessageBox("A critical DDE error has occured.", This->Title, MB_ICONINFORMATION);
|
|
break;
|
|
|
|
case XTYP_EXECUTE:
|
|
return DDE_FNOTPROCESSED;
|
|
|
|
case XTYP_POKE: {
|
|
char temp[128];
|
|
strcpy(temp, "The server received: ");
|
|
int size = strlen(temp);
|
|
DdeGetData(hData, (unsigned char *)&temp[size], sizeof(temp) - size, 0);
|
|
::MessageBox(GetFocus(), temp, This->Title, MB_ICONINFORMATION);
|
|
return (HDDEDATA)DDE_FACK;
|
|
}
|
|
|
|
case XTYP_REQUEST:
|
|
if (This->MatchTopicAndItem(hsz1, hsz2))
|
|
return This->DataRequested(wFmt);
|
|
return 0;
|
|
|
|
case XTYP_WILDCONNECT:
|
|
return This->WildConnect(hsz1, hsz2, wFmt);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
//
|
|
// Make server window, and start it off minimized--it will keep itself like
|
|
// that.
|
|
//
|
|
void
|
|
TDMLSrApp::InitMainWindow()
|
|
{
|
|
MainWindow = new TDMLSrWnd(0, "DDESVR (A DDE Server)");
|
|
}
|
|
|
|
//
|
|
// There are 16 available timers in Windows. This little trick uses the
|
|
// OWL IdleAction() member function to save that scarce resource.
|
|
//
|
|
BOOL
|
|
TDMLSrApp::IdleAction(long)
|
|
{
|
|
static DWORD dwTime = GetTickCount();
|
|
TDMLSrWnd* win = TYPESAFE_DOWNCAST(MainWindow, TDMLSrWnd);
|
|
if (MainWindow && win->Loop)
|
|
if (GetTickCount() - dwTime > 1000) {
|
|
dwTime = GetTickCount();
|
|
win->UpdateData();
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
TDMLSrApp::InitInstance()
|
|
{
|
|
// The code below sets up the DDEML call back function that is used by the
|
|
// DDE Management Library to carry out data transfers between
|
|
// applications.
|
|
//
|
|
if (DdeInitialize(&InstId, (PFNCALLBACK)(FARPROC)CallBackProc, 0, 0) != DMLERR_NO_ERROR) {
|
|
::MessageBox(0,"Initialization failed.", "DDEML Server", MB_ICONSTOP|MB_TASKMODAL);
|
|
PostQuitMessage(0);
|
|
}
|
|
|
|
// Must come after we've initialized DDE since InitInstance will trigger
|
|
// SetupWindow
|
|
TApplication::InitInstance();
|
|
}
|
|
|
|
int
|
|
TDMLSrApp::TermInstance(int status)
|
|
{
|
|
if (InstId) {
|
|
DdeUninitialize(InstId);
|
|
}
|
|
return TApplication::TermInstance(status);
|
|
}
|
|
|
|
|
|
int
|
|
OwlMain(int /*argc*/, char* /*argv*/ [])
|
|
{
|
|
return TDMLSrApp().Run();
|
|
}
|