Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2345 lines
70 KiB

/****************************** Module Header ******************************\
* Module Name: ddeml.C
*
* DDE Manager main module - Contains all exported ddeml functions.
*
* Created: 12/12/88 Sanford Staab
*
* Copyright (c) 1988, 1989 Microsoft Corporation
* 4/5/89 sanfords removed need for hwndFrame registration parameter
* 6/5/90 sanfords Fixed callbacks so they are blocked during
* timeouts.
* Fixed SendDDEInit allocation bug.
* Added hApp to ConvInfo structure.
* Allowed QueryConvInfo() to work on server hConvs.
* 11/29/90 sanfords eliminated SendDDEInit()
*
\***************************************************************************/
#include "ddemlp.h"
#include "verddeml.h"
/****** Globals *******/
HANDLE hInstance = 0; // initialized by LoadProc
HANDLE hheapDmg = 0; // main DLL heap
PAPPINFO pAppInfoList = NULL; // registered app/thread data list
PPILE pDataInfoPile = NULL; // Data handle tracking pile
PPILE pLostAckPile = NULL; // Ack tracking pile
WORD hwInst = 1; // used to validate stuff.
CONVCONTEXT CCDef = { sizeof(CONVCONTEXT), 0, 0, CP_WINANSI, 0L, 0L }; // default context.
char szNull[] = "";
char szT[20];
WORD cMonitor = 0; // number of registered monitors
FARPROC prevMsgHook = NULL; // used for hook links
FARPROC prevCallHook = NULL; // used for hook links
ATOM gatomDDEMLMom = 0;
ATOM gatomDMGClass = 0;
DWORD ShutdownTimeout;
DWORD ShutdownRetryTimeout;
LPMQL gMessageQueueList = NULL; // see PostDdeMessage();
#ifdef DEBUG
int bDbgFlags = 0;
#endif
/****** class strings ******/
char SZFRAMECLASS[] = "DMGFrame";
char SZDMGCLASS[] = "DMGClass";
char SZCLIENTCLASS[] = "DMGClientClass";
char SZSERVERCLASS[] = "DMGServerClass";
char SZMONITORCLASS[] = "DMGMonitorClass";
char SZCONVLISTCLASS[] = "DMGHoldingClass";
char SZHEAPWATCHCLASS[] = "DMGHeapWatchClass";
#ifdef DEBUG
WORD cAtoms = 0; // for debugging hszs!
#endif
// PROGMAN HACK!!!!
// This is here so DDEML works properly with PROGMAN 3.0 which incorrectly
// deletes its initiate-ack atoms after sending its ack.
ATOM aProgmanHack = 0;
/*
* maps XTYP_CONSTANTS to filter flags
*/
DWORD aulmapType[] = {
0L, // nothing
0L, // XTYP_ADVDATA
0L, // XTYP_ADVREQ
CBF_FAIL_ADVISES, // XTYP_ADVSTART
0L, // XTYP_ADVSTOP
CBF_FAIL_EXECUTES, // XTYP_EXECUTE
CBF_FAIL_CONNECTIONS, // XTYP_CONNECT
CBF_SKIP_CONNECT_CONFIRMS, // XTYP_CONNECT_CONFIRM
0L, // XTYP_MONITOR
CBF_FAIL_POKES, // XTYP_POKE
CBF_SKIP_REGISTRATIONS, // XTYP_REGISTER
CBF_FAIL_REQUESTS, // XTYP_REQUEST
CBF_SKIP_DISCONNECTS, // XTYP_DISCONNECT
CBF_SKIP_UNREGISTRATIONS, // XTYP_UNREGISTER
CBF_FAIL_CONNECTIONS, // XTYP_WILDCONNECT
0L, // XTYP_XACT_COMPLETE
};
UINT EXPENTRY DdeInitialize(
LPDWORD pidInst,
PFNCALLBACK pfnCallback,
DWORD afCmd,
DWORD ulRes)
{
WORD wRet;
#ifdef DEBUG
if (!hheapDmg) {
bDbgFlags = GetProfileInt("DDEML", "DebugFlags", 0);
}
#endif
TRACEAPIIN((szT, "DdeInitialize(%lx(->%lx), %lx, %lx, %lx)\n",
pidInst, *pidInst, pfnCallback, afCmd, ulRes));
if (ulRes != 0L) {
wRet = DMLERR_INVALIDPARAMETER;
} else {
wRet = Register(pidInst, pfnCallback, afCmd);
}
TRACEAPIOUT((szT, "DdeInitialize:%x\n", wRet));
return(wRet);
}
DWORD Myatodw(LPCSTR psz)
{
DWORD dwRet = 0;
if (psz == NULL) {
return(0);
}
while (*psz) {
dwRet = (dwRet << 1) + (dwRet << 3) + (*psz - '0');
psz++;
}
return(dwRet);
}
WORD Register(
LPDWORD pidInst,
PFNCALLBACK pfnCallback,
DWORD afCmd)
{
PAPPINFO pai = 0L;
SEMENTER();
if (afCmd & APPCLASS_MONITOR) {
if (cMonitor == MAX_MONITORS) {
return(DMLERR_DLL_USAGE);
}
// ensure monitors only get monitor callbacks.
afCmd |= CBF_MONMASK;
}
if ((pai = (PAPPINFO)(*pidInst)) != NULL) {
if (pai->instCheck != HIWORD(*pidInst)) {
return(DMLERR_INVALIDPARAMETER);
}
/*
* re-registration - only allow CBF_ and MF_ flags to be altered
*/
pai->afCmd = (pai->afCmd & ~(CBF_MASK | MF_MASK)) |
(afCmd & (CBF_MASK | MF_MASK));
return(DMLERR_NO_ERROR);
}
if (!hheapDmg) {
// Read in any alterations to the zombie terminate timeouts
GetProfileString("DDEML", "ShutdownTimeout", "3000", szT, 20);
ShutdownTimeout = Myatodw(szT);
if (!ShutdownTimeout) {
ShutdownTimeout = 3000;
}
GetProfileString("DDEML", "ShutdownRetryTimeout", "30000", szT, 20);
ShutdownRetryTimeout = Myatodw(szT);
if (!ShutdownRetryTimeout) {
ShutdownRetryTimeout = 30000;
}
// PROGMAN HACK!!!!
aProgmanHack = GlobalAddAtom("Progman");
/* UTTER GREASE to fool the pile routines into making a local pile */
hheapDmg = HIWORD((LPVOID)(&pDataInfoPile));
RegisterClasses();
}
if (!pDataInfoPile) {
if (!(pDataInfoPile = CreatePile(hheapDmg, sizeof(DIP), 8))) {
goto Abort;
}
}
if (!pLostAckPile) {
if (!(pLostAckPile = CreatePile(hheapDmg, sizeof(LAP), 8))) {
goto Abort;
}
}
pai = (PAPPINFO)(DWORD)FarAllocMem(hheapDmg, sizeof(APPINFO));
if (pai == NULL) {
goto Abort;
}
if (!(pai->hheapApp = DmgCreateHeap(4096))) {
FarFreeMem((LPSTR)pai);
pai = 0L;
goto Abort;
}
/*
* We NEVER expect a memory allocation failure here because we just
* allocated the heap.
*/
pai->next = pAppInfoList;
pai->pfnCallback = pfnCallback;
// pai->pAppNamePile = NULL; LMEM_ZEROINIT
pai->pHDataPile = CreatePile(pai->hheapApp, sizeof(HDDEDATA), 32);
pai->pHszPile = CreatePile(pai->hheapApp, sizeof(ATOM), 16);
// pai->plstCBExceptions = NULL; LMEM_ZEROINIT
// pai->hwndSvrRoot = 0; may never need it LMEM_ZEROINIT
pai->plstCB = CreateLst(pai->hheapApp, sizeof(CBLI));
pai->afCmd = afCmd | APPCMD_FILTERINITS;
pai->hTask = GetCurrentTask();
// pai->hwndDmg = LMEM_ZEROINIT
// pai->hwndFrame = LMEM_ZEROINIT
// pai->hwndMonitor = LMEM_ZEROINIT
// pai->hwndTimer = 0; LMEM_ZEROINIT
// pai->LastError = DMLERR_NO_ERROR; LMEM_ZEROINIT
// pai->wFlags = 0;
// pai->fEnableOneCB = FALSE; LMEM_ZEROINIT
// pai->cZombies = 0; LMEM_ZEROINIT
// pai->cInProcess = 0; LMEM_ZEROINIT
pai->instCheck = ++hwInst;
pai->pServerAdvList = CreateLst(pai->hheapApp, sizeof(ADVLI));
pai->lpMemReserve = FarAllocMem(pai->hheapApp, CB_RESERVE);
pAppInfoList = pai;
*pidInst = (DWORD)MAKELONG((WORD)pai, pai->instCheck);
// NB We pass a pointer to pai in this CreateWindow because
// 32bit MFC has a habit of subclassing our dde windows so this
// param ends up getting thunked and since it's not really
// a pointer things get a bit broken by the thunks.
if ((pai->hwndDmg = CreateWindow(
SZDMGCLASS,
szNull,
WS_OVERLAPPED,
0, 0, 0, 0,
(HWND)NULL,
(HMENU)NULL,
hInstance,
&pai)) == 0L) {
goto Abort;
}
if (pai->afCmd & APPCLASS_MONITOR) {
pai->afCmd |= CBF_MONMASK; // monitors only get MONITOR and REGISTER callbacks!
if ((pai->hwndMonitor = CreateWindow(
SZMONITORCLASS,
szNull,
WS_OVERLAPPED,
0, 0, 0, 0,
(HWND)NULL,
(HMENU)NULL,
hInstance,
&pai)) == 0L) {
goto Abort;
}
if (++cMonitor == 1) {
prevMsgHook = SetWindowsHook(WH_GETMESSAGE, (FARPROC)DdePostHookProc);
prevCallHook = SetWindowsHook(WH_CALLWNDPROC, (FARPROC)DdeSendHookProc);
}
} else if (afCmd & APPCMD_CLIENTONLY) {
/*
* create an invisible top-level frame for initiates. (if server ok)
*/
afCmd |= CBF_FAIL_ALLSVRXACTIONS;
} else {
if ((pai->hwndFrame = CreateWindow(
SZFRAMECLASS,
szNull,
WS_POPUP,
0, 0, 0, 0,
(HWND)NULL,
(HMENU)NULL,
hInstance,
&pai)) == 0L) {
goto Abort;
}
}
// SetMessageQueue(200);
SEMLEAVE();
return(DMLERR_NO_ERROR);
Abort:
SEMLEAVE();
if (pai) {
DdeUninitialize((DWORD)(LPSTR)pai);
}
return(DMLERR_SYS_ERROR);
}
LRESULT FAR PASCAL TermDlgProc(
HWND hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam)
{
switch (msg) {
case WM_INITDIALOG:
return(TRUE);
case WM_COMMAND:
switch (wParam) {
case IDABORT:
case IDRETRY:
case IDIGNORE:
EndDialog(hwnd, wParam);
return(0);
}
break;
}
return(0);
}
/***************************** Public Function ****************************\
* PUBDOC START
* BOOL EXPENTRY DdeUninitialize(void);
* This unregisters the application from the DDEMGR. All DLL resources
* associated with the application are destroyed.
*
* PUBDOC END
*
* History:
* Created 12/14/88 Sanfords
\***************************************************************************/
BOOL EXPENTRY DdeUninitialize(
DWORD idInst)
{
register PAPPINFO pai;
PAPPINFO paiT;
ATOM a;
DWORD hData;
MSG msg;
extern VOID DumpGlobalLogs(VOID);
TRACEAPIIN((szT, "DdeUninitialize(%lx)\n", idInst));
pai = (PAPPINFO)LOWORD(idInst);
if (pai == NULL || pai->instCheck != HIWORD(idInst)) {
TRACEAPIOUT((szT, "DdeUninitialize:0\n"));
return(FALSE);
}
pai->LastError = DMLERR_NO_ERROR;
/*
* This is a hack to catch apps that call DdeUninitialize while within
* a synchronous transaction modal loop.
*/
pai->wFlags |= AWF_UNINITCALLED;
if (pai->wFlags & AWF_INSYNCTRANSACTION) {
TRACEAPIOUT((szT, "DdeUninitialize:1\n"));
return(TRUE);
}
/*
* inform others of DeRegistration
*/
if (pai->pAppNamePile != NULL) {
DdeNameService(idInst, (HSZ)NULL, (HSZ)NULL, DNS_UNREGISTER);
}
/*
* Let any lagging dde activity die down.
*/
while (EmptyDDEPostQ()) {
Yield();
while (PeekMessage((MSG FAR *)&msg, (HWND)NULL,
WM_DDE_FIRST, WM_DDE_LAST, PM_REMOVE)) {
DispatchMessage((MSG FAR *)&msg);
Yield();
}
for (paiT = pAppInfoList; paiT != NULL; paiT = paiT->next) {
if (paiT->hTask == pai->hTask) {
CheckCBQ(paiT);
}
}
}
// Let all windows left begin to self destruct.
ChildMsg(pai->hwndDmg, UM_DISCONNECT, ST_PERM2DIE, 0L, FALSE);
if (ShutdownTimeout && pai->cZombies) {
WORD wRet;
WORD hiTimeout;
/*
* This ugly mess is here to prevent DDEML from closing down and
* destroying windows that are not properly terminated. Any
* windows waiting on WM_DDE_TERMINATE messages set the cZombies
* count. If there are any left we go into a modal loop till
* things clean up. This should, in most cases happen fairly
* quickly.
*/
hiTimeout = HIWORD(ShutdownTimeout);
SetTimer(pai->hwndDmg, TID_SHUTDOWN, LOWORD(ShutdownTimeout), NULL);
TRACETERM((szT, "DdeUninitialize: Entering terminate modal loop. cZombies=%d[%x:%x]\n",
((LPAPPINFO)pai)->cZombies,
HIWORD(&((LPAPPINFO)pai)->cZombies),
LOWORD(&((LPAPPINFO)pai)->cZombies)));
while (pai->cZombies > 0) {
Yield(); // give other apps a chance to post terminates.
GetMessage(&msg, (HWND)NULL, 0, 0xffff);
if (msg.message == WM_TIMER && msg.wParam == TID_SHUTDOWN &&
msg.hwnd == pai->hwndDmg) {
if (hiTimeout--) {
SetTimer(pai->hwndDmg, TID_SHUTDOWN, 0xFFFF, NULL);
} else {
FARPROC lpfn;
KillTimer(pai->hwndDmg, TID_SHUTDOWN);
if (!pai->cZombies) {
break;
}
TRACETERM((szT,
"DdeUninitialize Zombie hangup: pai=%x:%x\n",
HIWORD((LPAPPINFO)pai), (WORD)(pai)));
/*
* If the partner window died in any remaining zombie
* windows, get them shut down.
*/
ChildMsg(pai->hwndDmg, UM_DISCONNECT, ST_CHECKPARTNER, 0L, FALSE);
if (pai->cZombies > 0) {
lpfn = MakeProcInstance((FARPROC)TermDlgProc, hInstance);
wRet = DialogBox(hInstance, "TermDialog", (HWND)NULL, lpfn);
FreeProcInstance(lpfn);
if (wRet == IDABORT || wRet == -1) {
pai->cZombies = 0;
break; // ignore zombies!
}
if (wRet == IDRETRY) {
hiTimeout = HIWORD(ShutdownRetryTimeout);
SetTimer(pai->hwndDmg, TID_SHUTDOWN,
LOWORD(ShutdownRetryTimeout), NULL);
}
// IDIGNORE - loop forever!
}
}
}
// app should already be shut-down so we don't bother with
// accelerator or menu translations.
DispatchMessage(&msg);
/*
* tell all instances in this task to process their
* callbacks so we can clear our queue.
*/
EmptyDDEPostQ();
for (paiT = pAppInfoList; paiT != NULL; paiT = paiT->next) {
if (paiT->hTask == pai->hTask) {
CheckCBQ(paiT);
}
}
}
}
#if 0 // don't need this anymore
if (pai->hwndTimer) {
pai->wTimeoutStatus |= TOS_ABORT;
PostMessage(pai->hwndTimer, WM_TIMER, TID_TIMEOUT, 0);
// if this fails, no big deal because it means the queue is full
// and the modal loop will catch our TOS_ABORT quickly.
// We need to do this in case no activity is happening in the
// modal loop.
}
#endif
if (pai->hwndMonitor) {
DmgDestroyWindow(pai->hwndMonitor);
if (!--cMonitor) {
UnhookWindowsHook(WH_GETMESSAGE, (FARPROC)DdePostHookProc);
UnhookWindowsHook(WH_CALLWNDPROC, (FARPROC)DdeSendHookProc);
}
}
UnlinkAppInfo(pai);
DmgDestroyWindow(pai->hwndDmg);
DmgDestroyWindow(pai->hwndFrame);
while (PopPileSubitem(pai->pHDataPile, (LPBYTE)&hData))
FreeDataHandle(pai, hData, FALSE);
DestroyPile(pai->pHDataPile);
while (PopPileSubitem(pai->pHszPile, (LPBYTE)&a)) {
MONHSZ(a, MH_CLEANUP, pai->hTask);
FreeHsz(a);
}
DestroyPile(pai->pHszPile);
DestroyPile(pai->pAppNamePile);
DestroyLst(pai->pServerAdvList);
DmgDestroyHeap(pai->hheapApp);
pai->instCheck--; // make invalid on later attempts to reinit.
FarFreeMem((LPSTR)pai);
/* last one out.... trash the data info heap */
if (!pAppInfoList) {
#ifdef DEBUG
DIP dip;
AssertF(!PopPileSubitem(pDataInfoPile, (LPBYTE)&dip),
"leftover APPOWNED handles");
#endif
DestroyPile(pDataInfoPile);
DestroyPile(pLostAckPile);
pDataInfoPile = NULL;
pLostAckPile = NULL;
AssertFW(cAtoms == 0, "DdeUninitialize() - leftover atoms");
// PROGMAN HACK!!!!
GlobalDeleteAtom(aProgmanHack);
// CLOSEHEAPWATCH();
}
#ifdef DEBUG
DumpGlobalLogs();
#endif
TRACEAPIOUT((szT, "DdeUninitialize:1\n"));
return(TRUE);
}
HCONVLIST EXPENTRY DdeConnectList(
DWORD idInst,
HSZ hszSvcName,
HSZ hszTopic,
HCONVLIST hConvList,
PCONVCONTEXT pCC)
{
PAPPINFO pai;
HWND hConv, hConvNext, hConvNew, hConvLast;
HWND hConvListNew;
PCLIENTINFO pciOld, pciNew;
TRACEAPIIN((szT, "DdeConnectList(%lx, %lx, %lx, %lx, %lx)\n",
idInst, hszSvcName, hszTopic, hConvList, pCC));
pai = (PAPPINFO)idInst;
if (pai == NULL || pai->instCheck != HIWORD(idInst)) {
TRACEAPIOUT((szT, "DdeConnectList:0\n"));
return(0L);
}
pai->LastError = DMLERR_NO_ERROR;
if (hConvList && !ValidateHConv(hConvList)) {
SETLASTERROR(pai, DMLERR_INVALIDPARAMETER);
TRACEAPIOUT((szT, "DdeConnectList:0\n"));
return(0L);
}
/*
* destroy any dead old clients
*/
if ((HWND)hConvList && (hConv = GetWindow((HWND)hConvList, GW_CHILD))) {
do {
hConvNext = GetWindow((HWND)hConv, GW_HWNDNEXT);
pciOld = (PCLIENTINFO)GetWindowLong(hConv, GWL_PCI);
if (!(pciOld->ci.fs & ST_CONNECTED)) {
SetParent(hConv, pai->hwndDmg);
Disconnect(hConv, ST_PERM2DIE, pciOld);
}
} while (hConv = hConvNext);
}
// create a new list window
if ((hConvListNew = CreateWindow(
SZCONVLISTCLASS,
szNull,
WS_CHILD,
0, 0, 0, 0,
pai->hwndDmg,
(HMENU)NULL,
hInstance,
&pai)) == NULL) {
SETLASTERROR(pai, DMLERR_SYS_ERROR);
TRACEAPIOUT((szT, "DdeConnectList:0\n"));
return(0L);
}
// Make all possible connections to new list window
hConvNew = GetDDEClientWindow(pai, hConvListNew, HIWORD(hszSvcName), LOWORD(hszSvcName), LOWORD(hszTopic), pCC);
/*
* If no new hConvs created, return old list.
*/
if (hConvNew == NULL) {
// if no old hConvs as well, destroy all and return NULL
if ((HWND)hConvList && GetWindow((HWND)hConvList, GW_CHILD) == NULL) {
SendMessage((HWND)hConvList, UM_DISCONNECT,
ST_PERM2DIE, 0L);
SETLASTERROR(pai, DMLERR_NO_CONV_ESTABLISHED);
TRACEAPIOUT((szT, "DdeConnectList:0\n"));
return(NULL);
}
// else just return old list (- dead convs)
if (hConvList == NULL) {
DestroyWindow(hConvListNew);
SETLASTERROR(pai, DMLERR_NO_CONV_ESTABLISHED);
}
TRACEAPIOUT((szT, "DdeConnectList:%lx\n", hConvList));
return(hConvList);
}
/*
* remove duplicates from the new list
*/
if ((HWND)hConvList && (hConv = GetWindow((HWND)hConvList, GW_CHILD))) {
// go throuch old list...
do {
pciOld = (PCLIENTINFO)GetWindowLong(hConv, GWL_PCI);
/*
* destroy any new clients that are duplicates of the old ones.
*/
hConvNew = GetWindow(hConvListNew, GW_CHILD);
hConvLast = GetWindow(hConvNew, GW_HWNDLAST);
while (hConvNew) {
if (hConvNew == hConvLast) {
hConvNext = NULL;
} else {
hConvNext = GetWindow(hConvNew, GW_HWNDNEXT);
}
pciNew = (PCLIENTINFO)GetWindowLong(hConvNew, GWL_PCI);
if (pciOld->ci.aServerApp == pciNew->ci.aServerApp &&
pciOld->ci.aTopic == pciNew->ci.aTopic &&
pciOld->ci.hwndFrame == pciNew->ci.hwndFrame) {
/*
* assume same app, same topic, same hwndFrame is a duplicate.
*
* Move dieing window out of the list since it
* dies asynchronously and will still be around
* after this API exits.
*/
SetParent(hConvNew, pai->hwndDmg);
Disconnect(hConvNew, ST_PERM2DIE,
(PCLIENTINFO)GetWindowLong(hConvNew, GWL_PCI));
}
hConvNew = hConvNext;
}
hConvNext = GetWindow(hConv, GW_HWNDNEXT);
if (hConvNext && (GetParent(hConvNext) != (HWND)hConvList)) {
hConvNext = NULL;
}
/*
* move the unique old client to the new list
*/
SetParent(hConv, hConvListNew);
} while (hConv = hConvNext);
// get rid of the old list
SendMessage((HWND)hConvList, UM_DISCONNECT, ST_PERM2DIE, 0L);
}
/*
* If none are left, fail because no conversations were established.
*/
if (GetWindow(hConvListNew, GW_CHILD) == NULL) {
SendMessage(hConvListNew, UM_DISCONNECT, ST_PERM2DIE, 0L);
SETLASTERROR(pai, DMLERR_NO_CONV_ESTABLISHED);
TRACEAPIOUT((szT, "DdeConnectList:0\n"));
return(NULL);
} else {
TRACEAPIOUT((szT, "DdeConnectList:%lx\n", MAKEHCONV(hConvListNew)));
return(MAKEHCONV(hConvListNew));
}
}
HCONV EXPENTRY DdeQueryNextServer(
HCONVLIST hConvList,
HCONV hConvPrev)
{
HWND hwndMaybe;
PAPPINFO pai;
TRACEAPIIN((szT, "DdeQueryNextServer(%lx, %lx)\n",
hConvList, hConvPrev));
if (!ValidateHConv(hConvList)) {
pai = NULL;
while (pai = GetCurrentAppInfo(pai)) {
SETLASTERROR(pai, DMLERR_INVALIDPARAMETER);
}
TRACEAPIOUT((szT, "DdeQueryNextServer:0\n"));
return NULL;
}
pai = EXTRACTHCONVLISTPAI(hConvList);
pai->LastError = DMLERR_NO_ERROR;
if (hConvPrev == NULL) {
TRACEAPIOUT((szT, "DdeQueryNextServer:%lx\n",
MAKEHCONV(GetWindow((HWND)hConvList, GW_CHILD))));
return MAKEHCONV(GetWindow((HWND)hConvList, GW_CHILD));
} else {
if (!ValidateHConv(hConvPrev)) {
SETLASTERROR(pai, DMLERR_INVALIDPARAMETER);
TRACEAPIOUT((szT, "DdeQueryNextServer:0\n"));
return NULL;
}
hwndMaybe = GetWindow((HWND)hConvPrev, GW_HWNDNEXT);
if (!hwndMaybe) {
TRACEAPIOUT((szT, "DdeQueryNextServer:0\n"));
return NULL;
}
// make sure it's got the same parent and isn't the first child
// ### maybe this code can go - I'm not sure how GW_HWNDNEXT acts. SS
if (GetParent(hwndMaybe) == (HWND)hConvList &&
hwndMaybe != GetWindow((HWND)hConvList, GW_CHILD)) {
TRACEAPIOUT((szT, "DdeQueryNextServer:%lx\n", MAKEHCONV(hwndMaybe)));
return MAKEHCONV(hwndMaybe);
}
TRACEAPIOUT((szT, "DdeQueryNextServer:0\n"));
return NULL;
}
}
BOOL EXPENTRY DdeDisconnectList(
HCONVLIST hConvList)
{
PAPPINFO pai;
TRACEAPIIN((szT, "DdeDisconnectList(%lx)\n", hConvList));
if (!ValidateHConv(hConvList)) {
pai = NULL;
while (pai = GetCurrentAppInfo(pai)) {
SETLASTERROR(pai, DMLERR_INVALIDPARAMETER);
}
TRACEAPIOUT((szT, "DdeDisconnectList:0\n"));
return(FALSE);
}
pai = EXTRACTHCONVLISTPAI(hConvList);
pai->LastError = DMLERR_NO_ERROR;
SendMessage((HWND)hConvList, UM_DISCONNECT, ST_PERM2DIE, 0L);
TRACEAPIOUT((szT, "DdeDisconnectList:1\n"));
return(TRUE);
}
HCONV EXPENTRY DdeConnect(
DWORD idInst,
HSZ hszSvcName,
HSZ hszTopic,
PCONVCONTEXT pCC)
{
PAPPINFO pai;
HWND hwnd;
TRACEAPIIN((szT, "DdeConnect(%lx, %lx, %lx, %lx)\n",
idInst, hszSvcName, hszTopic, pCC));
pai = (PAPPINFO)idInst;
if (pai == NULL || pai->instCheck != HIWORD(idInst)) {
TRACEAPIOUT((szT, "DdeConnect:0\n"));
return(FALSE);
}
pai->LastError = DMLERR_NO_ERROR;
if (pCC && pCC->cb != sizeof(CONVCONTEXT)) {
SETLASTERROR(pai, DMLERR_INVALIDPARAMETER);
TRACEAPIOUT((szT, "DdeConnect:0\n"));
return(0);
}
hwnd = GetDDEClientWindow(pai, pai->hwndDmg, (HWND)HIWORD(hszSvcName),
LOWORD(hszSvcName), LOWORD(hszTopic), pCC);
if (hwnd == 0) {
SETLASTERROR(pai, DMLERR_NO_CONV_ESTABLISHED);
}
TRACEAPIOUT((szT, "DdeConnect:%lx\n", MAKEHCONV(hwnd)));
return(MAKEHCONV(hwnd));
}
BOOL EXPENTRY DdeDisconnect(
HCONV hConv)
{
PAPPINFO pai;
PCLIENTINFO pci;
TRACEAPIIN((szT, "DdeDisconnect(%lx)\n", hConv));
if (!ValidateHConv(hConv)) {
pai = NULL;
while (pai = GetCurrentAppInfo(pai)) {
SETLASTERROR(pai, DMLERR_NO_CONV_ESTABLISHED);
}
TRACEAPIOUT((szT, "DdeDisconnect:0\n"));
return(FALSE);
}
pai = EXTRACTHCONVPAI(hConv);
pci = (PCLIENTINFO)GetWindowLong((HWND)hConv, GWL_PCI);
if (pai->cInProcess) {
// do asynchronously if this is called within a callback
if (!PostMessage((HWND)hConv, UM_DISCONNECT, ST_PERM2DIE, (LONG)pci)) {
SETLASTERROR(pai, DMLERR_SYS_ERROR);
TRACEAPIOUT((szT, "DdeDisconnect:0\n"));
return(FALSE);
}
} else {
Disconnect((HWND)hConv, ST_PERM2DIE, pci);
}
TRACEAPIOUT((szT, "DdeDisconnect:1\n"));
return(TRUE);
}
HCONV EXPENTRY DdeReconnect(
HCONV hConv)
{
HWND hwnd;
PAPPINFO pai;
PCLIENTINFO pci;
TRACEAPIIN((szT, "DdeReconnect(%lx)\n", hConv));
if (!ValidateHConv(hConv)) {
pai = NULL;
while (pai = GetCurrentAppInfo(pai)) {
SETLASTERROR(pai, DMLERR_NO_CONV_ESTABLISHED);
}
TRACEAPIOUT((szT, "DdeReconnect:0\n"));
return(FALSE);
}
pai = EXTRACTHCONVPAI(hConv);
pai->LastError = DMLERR_NO_ERROR;
pci = (PCLIENTINFO)GetWindowLong((HWND)hConv, GWL_PCI);
// The dyeing window MUST be a client to reconnect.
if (!(pci->ci.fs & ST_CLIENT)) {
SETLASTERROR(pai, DMLERR_INVALIDPARAMETER);
TRACEAPIOUT((szT, "DdeReconnect:0\n"));
return(FALSE);
}
hwnd = GetDDEClientWindow(pai, pai->hwndDmg, pci->ci.hwndFrame,
pci->ci.aServerApp, pci->ci.aTopic, &pci->ci.CC);
if (hwnd == 0) {
SETLASTERROR(pai, DMLERR_NO_CONV_ESTABLISHED);
TRACEAPIOUT((szT, "DdeReconnect:0\n"));
return(FALSE);
}
if (pci->ci.fs & ST_INLIST) {
SetParent(hwnd, GetParent((HWND)hConv));
}
if (pci->ci.fs & ST_ADVISE) {
DWORD result;
PADVLI pali, paliNext;
// recover advise loops here
for (pali = (PADVLI)pci->pClientAdvList->pItemFirst; pali; pali = paliNext) {
paliNext = (PADVLI)pali->next;
if (pali->hwnd == (HWND)hConv) {
XFERINFO xi;
xi.pulResult = &result;
xi.ulTimeout = (DWORD)TIMEOUT_ASYNC;
xi.wType = XTYP_ADVSTART |
(pali->fsStatus & (XTYPF_NODATA | XTYPF_ACKREQ));
xi.wFmt = pali->wFmt;
xi.hszItem = (HSZ)pali->aItem;
xi.hConvClient = MAKEHCONV(hwnd);
xi.cbData = 0;
xi.hDataClient = NULL;
ClientXferReq(&xi, hwnd,
(PCLIENTINFO)GetWindowLong(hwnd, GWL_PCI));
}
}
}
TRACEAPIOUT((szT, "DdeReconnect:%lx\n", MAKEHCONV(hwnd)));
return(MAKEHCONV(hwnd));
}
UINT EXPENTRY DdeQueryConvInfo(
HCONV hConv,
DWORD idTransaction,
PCONVINFO pConvInfo)
{
PCLIENTINFO pci;
PAPPINFO pai;
PXADATA pxad;
PCQDATA pqd;
BOOL fClient;
WORD cb;
CONVINFO ci;
SEMCHECKOUT();
TRACEAPIIN((szT, "DdeQueryConvInfo(%lx, %lx, %lx(->cb=%lx))\n",
hConv, idTransaction, pConvInfo, pConvInfo->cb));
if (!ValidateHConv(hConv) ||
!(pci = (PCLIENTINFO)GetWindowLong((HWND)hConv, GWL_PCI))) {
pai = NULL;
while (pai = GetCurrentAppInfo(pai)) {
SETLASTERROR(pai, DMLERR_NO_CONV_ESTABLISHED);
}
TRACEAPIOUT((szT, "DdeQueryConvInfo:0\n"));
return(FALSE);
}
pai = pci->ci.pai;
pai->LastError = DMLERR_NO_ERROR;
/*
* This check attempts to prevent improperly coded apps from
* crashing due to having not initialized the cb field.
*/
if (pConvInfo->cb > sizeof(CONVINFO) || pConvInfo->cb == 0) {
pConvInfo->cb = sizeof(CONVINFO) -
sizeof(HWND) - // for new hwnd field
sizeof(HWND); // for new hwndPartner field
}
fClient = (BOOL)SendMessage((HWND)hConv, UM_QUERY, Q_CLIENT, 0L);
if (idTransaction == QID_SYNC || !fClient) {
pxad = &pci->ci.xad;
} else {
if (pci->pQ != NULL && (pqd = (PCQDATA)Findqi(pci->pQ, idTransaction))) {
pxad = &pqd->xad;
} else {
SETLASTERROR(pai, DMLERR_UNFOUND_QUEUE_ID);
TRACEAPIOUT((szT, "DdeQueryConvInfo:0\n"));
return(FALSE);
}
}
SEMENTER();
ci.cb = sizeof(CONVINFO);
ci.hConvPartner = (IsWindow((HWND)pci->ci.hConvPartner) &&
((pci->ci.fs & (ST_ISLOCAL | ST_CONNECTED)) == (ST_ISLOCAL | ST_CONNECTED)))
? pci->ci.hConvPartner : NULL;
ci.hszSvcPartner = fClient ? pci->ci.aServerApp : 0;
ci.hszServiceReq = pci->ci.hszSvcReq;
ci.hszTopic = pci->ci.aTopic;
ci.wStatus = pci->ci.fs;
ci.ConvCtxt = pci->ci.CC;
if (fClient) {
ci.hUser = pxad->hUser;
ci.hszItem = pxad->pXferInfo->hszItem;
ci.wFmt = pxad->pXferInfo->wFmt;
ci.wType = pxad->pXferInfo->wType;
ci.wConvst = pxad->state;
ci.wLastError = pxad->LastError;
} else {
ci.hUser = pci->ci.xad.hUser;
ci.hszItem = NULL;
ci.wFmt = 0;
ci.wType = 0;
ci.wConvst = pci->ci.xad.state;
ci.wLastError = pci->ci.pai->LastError;
}
ci.hConvList = (pci->ci.fs & ST_INLIST) ?
MAKEHCONV(GetParent((HWND)hConv)) : 0;
cb = min(sizeof(CONVINFO), (WORD)pConvInfo->cb);
ci.hwnd = (HWND)hConv;
ci.hwndPartner = (HWND)pci->ci.hConvPartner;
hmemcpy((LPBYTE)pConvInfo, (LPBYTE)&ci, cb);
pConvInfo->cb = cb;
SEMLEAVE();
TRACEAPIOUT((szT, "DdeQueryConvInfo:%x\n", cb));
return(cb);
}
BOOL EXPENTRY DdeSetUserHandle(
HCONV hConv,
DWORD id,
DWORD hUser)
{
PAPPINFO pai;
PCLIENTINFO pci;
PXADATA pxad;
PCQDATA pqd;
TRACEAPIIN((szT, "DdeSetUserHandle(%lx, %lx, %lx)\n",
hConv, id, hUser));
if (!ValidateHConv(hConv)) {
pai = NULL;
while (pai = GetCurrentAppInfo(pai)) {
SETLASTERROR(pai, DMLERR_INVALIDPARAMETER);
}
TRACEAPIOUT((szT, "DdeSetUserHandle:0\n"));
return(FALSE);
}
pai = EXTRACTHCONVPAI(hConv);
pai->LastError = DMLERR_NO_ERROR;
SEMCHECKOUT();
pci = (PCLIENTINFO)GetWindowLong((HWND)hConv, GWL_PCI);
if (!pci) {
Error:
SETLASTERROR(pai, DMLERR_INVALIDPARAMETER);
TRACEAPIOUT((szT, "DdeSetUserHandle:0\n"));
return(FALSE);
}
pxad = &pci->ci.xad;
if (id != QID_SYNC) {
if (!SendMessage((HWND)hConv, UM_QUERY, Q_CLIENT, 0)) {
goto Error;
}
if (pci->pQ != NULL && (pqd = (PCQDATA)Findqi(pci->pQ, id))) {
pxad = &pqd->xad;
} else {
SETLASTERROR(pai, DMLERR_UNFOUND_QUEUE_ID);
TRACEAPIOUT((szT, "DdeSetUserHandle:0\n"));
return(FALSE);
}
}
pxad->hUser = hUser;
TRACEAPIOUT((szT, "DdeSetUserHandle:1\n"));
return(TRUE);
}
BOOL EXPENTRY DdePostAdvise(
DWORD idInst,
HSZ hszTopic,
HSZ hszItem)
{
PAPPINFO pai;
PSERVERINFO psi = NULL;
register PADVLI pali;
PADVLI paliPrev, paliEnd, paliMove;
TRACEAPIIN((szT, "DdePostAdvise(%lx, %lx, %lx)\n",
idInst, hszTopic, hszItem));
pai = (PAPPINFO)idInst;
if (pai == NULL || pai->instCheck != HIWORD(idInst)) {
TRACEAPIOUT((szT, "DdePostAdvise:0\n"));
return(FALSE);
}
pai->LastError = DMLERR_NO_ERROR;
if (pai->afCmd & APPCMD_CLIENTONLY) {
SETLASTERROR(pai, DMLERR_DLL_USAGE);
TRACEAPIOUT((szT, "DdePostAdvise:0\n"));
return(FALSE);
}
paliPrev = NULL;
paliEnd = NULL;
paliMove = NULL;
pali = (PADVLI)pai->pServerAdvList->pItemFirst;
while (pali && pali != paliMove) {
if ((!hszItem || pali->aItem == (ATOM)hszItem) &&
(!hszTopic || pali->aTopic == (ATOM)hszTopic)) {
/*
* Advise loops are tricky because of the desireable FACKREQ feature
* of DDE. The advise loop list holds information in its fsStatus
* field to maintain the state of the advise loop.
*
* if the ADVST_WAITING bit is set, the server is still waiting for
* the client to give it the go-ahead for more data with an
* ACK message on this item. (FACKREQ is set) Without a go-ahead,
* the server will not send any more advise data to the client but
* will instead set the ADVST_CHANGED bit which will cause another
* WM_DDE_DATA message to be sent to the client as soon as the
* go-ahead ACK is received. This keeps the client up to date
* but never overloads it.
*/
if (pali->fsStatus & ADVST_WAITING) {
/*
* if the client has not yet finished with the last data
* we gave him, just update the advise loop status
* instead of sending data now.
*/
pali->fsStatus |= ADVST_CHANGED;
goto NextLink;
}
psi = (PSERVERINFO)GetWindowLong(pali->hwnd, GWL_PCI);
if (pali->fsStatus & DDE_FDEFERUPD) {
/*
* In the nodata case, we don't bother the server. Just
* pass the client an apropriate DATA message.
*/
IncHszCount(pali->aItem); // message copy
#ifdef DEBUG
cAtoms--; // don't count this add
#endif
PostDdeMessage(&psi->ci, WM_DDE_DATA, pali->hwnd,
MAKELONG(0, pali->aItem), 0, 0);
} else {
PostServerAdvise(pali->hwnd, psi, pali, CountAdvReqLeft(pali));
}
if (pali->fsStatus & DDE_FACKREQ && pali->next) {
/*
* In order to know what ack goes with what data sent out, we
* place any updated advise loops at the end of the list so
* that acks associated with them are found last. ie First ack
* back goes with oldest data out.
*/
// Unlink
if (paliPrev) {
paliPrev->next = pali->next;
} else {
pai->pServerAdvList->pItemFirst = (PLITEM)pali->next;
}
// put on the end
if (paliEnd) {
paliEnd->next = (PLITEM)pali;
paliEnd = pali;
} else {
for (paliEnd = pali;
paliEnd->next;
paliEnd = (PADVLI)paliEnd->next) {
}
paliEnd->next = (PLITEM)pali;
paliMove = paliEnd = pali;
}
pali->next = NULL;
if (paliPrev) {
pali = (PADVLI)paliPrev->next;
} else {
pali = (PADVLI)pai->pServerAdvList->pItemFirst;
}
continue;
}
}
NextLink:
paliPrev = pali;
pali = (PADVLI)pali->next;
}
TRACEAPIOUT((szT, "DdePostAdvise:1\n"));
return(TRUE);
}
/*
* History: 4/18/91 sanfords - now always frees any incomming data handle
* thats not APPOWNED regardless of error case.
*/
HDDEDATA EXPENTRY DdeClientTransaction(
LPBYTE pData,
DWORD cbData,
HCONV hConv,
HSZ hszItem,
UINT wFmt,
UINT wType,
DWORD ulTimeout,
LPDWORD pulResult)
{
PAPPINFO pai;
PCLIENTINFO pci;
HDDEDATA hData, hDataBack, hRet = 0;
SEMCHECKOUT();
TRACEAPIIN((szT, "DdeClientTransaction(%lx, %lx, %lx, %lx, %x, %x, %lx, %lx)\n",
pData, cbData, hConv, hszItem, wFmt, wType, ulTimeout, pulResult));
if (!ValidateHConv(hConv)) {
pai = NULL;
while (pai = GetCurrentAppInfo(pai)) {
SETLASTERROR(pai, DMLERR_INVALIDPARAMETER);
}
goto FreeErrExit;
}
pci = (PCLIENTINFO)GetWindowLong((HWND)hConv, GWL_PCI);
pai = pci->ci.pai;
/*
* Don't let transactions happen if we are shutting down
* or are already doing a sync transaction.
*/
if ((ulTimeout != TIMEOUT_ASYNC && pai->wFlags & AWF_INSYNCTRANSACTION) ||
pai->wFlags & AWF_UNINITCALLED) {
SETLASTERROR(pai, DMLERR_REENTRANCY);
goto FreeErrExit;
}
pci->ci.pai->LastError = DMLERR_NO_ERROR;
if (!(pci->ci.fs & ST_CONNECTED)) {
SETLASTERROR(pai, DMLERR_NO_CONV_ESTABLISHED);
goto FreeErrExit;
}
// If local, check filters first
if (pci->ci.fs & ST_ISLOCAL) {
PAPPINFO paiServer;
PSERVERINFO psi;
// we can do this because the app heaps are in global shared memory
psi = (PSERVERINFO)GetWindowLong((HWND)pci->ci.hConvPartner, GWL_PCI);
if (!psi) {
// SERVER DIED! - simulate a terminate received.
Terminate((HWND)hConv, (HWND)pci->ci.hConvPartner, pci);
SETLASTERROR(pai, DMLERR_NO_CONV_ESTABLISHED);
goto FreeErrExit;
}
paiServer = psi->ci.pai;
if (paiServer->afCmd & aulmapType[(wType & XTYP_MASK) >> XTYP_SHIFT]) {
SETLASTERROR(pai, DMLERR_NOTPROCESSED);
FreeErrExit:
if ((wType == XTYP_POKE || wType == XTYP_EXECUTE) && cbData == -1 &&
!(LOWORD((DWORD)pData) & HDATA_APPOWNED)) {
FREEEXTHDATA(pData);
}
TRACEAPIOUT((szT, "DdeClientTransaction:0\n"));
return(0);
}
}
pai = pci->ci.pai;
switch (wType) {
case XTYP_POKE:
case XTYP_EXECUTE:
// prepair the outgoing handle
if (cbData == -1L) { // handle given, not pointer
hData = ((LPEXTDATAINFO)pData)->hData;
if (!(LOWORD(hData) & HDATA_APPOWNED)) {
FREEEXTHDATA(pData);
}
if (!(hData = DllEntry(&pci->ci, hData))) {
TRACEAPIOUT((szT, "DdeClientTransaction:0\n"));
return(0);
}
pData = (LPBYTE)hData; // place onto stack for pass on to ClientXferReq.
} else { // pointer given, create handle from it.
if (!(pData = (LPBYTE)PutData(pData, cbData, 0, LOWORD(hszItem), wFmt, 0, pai))) {
SETLASTERROR(pai, DMLERR_MEMORY_ERROR);
TRACEAPIOUT((szT, "DdeClientTransaction:0\n"));
return(0);
}
}
hData = (HDDEDATA)pData; // used to prevent compiler over-optimization.
case XTYP_REQUEST:
case XTYP_ADVSTART:
case XTYP_ADVSTART | XTYPF_NODATA:
case XTYP_ADVSTART | XTYPF_ACKREQ:
if (wType != XTYP_EXECUTE && !hszItem) {
SETLASTERROR(pai, DMLERR_INVALIDPARAMETER);
TRACEAPIOUT((szT, "DdeClientTransaction:0\n"));
return(0);
}
case XTYP_ADVSTART | XTYPF_NODATA | XTYPF_ACKREQ:
if (wType != XTYP_EXECUTE && !wFmt) {
SETLASTERROR(pai, DMLERR_INVALIDPARAMETER);
TRACEAPIOUT((szT, "DdeClientTransaction:0\n"));
return(0);
}
case XTYP_ADVSTOP:
pai->LastError = DMLERR_NO_ERROR; // reset before start.
if (ulTimeout == TIMEOUT_ASYNC) {
hRet = (HDDEDATA)ClientXferReq((PXFERINFO)&pulResult, (HWND)hConv, pci);
} else {
pai->wFlags |= AWF_INSYNCTRANSACTION;
hDataBack = (HDDEDATA)ClientXferReq((PXFERINFO)&pulResult, (HWND)hConv, pci);
pai->wFlags &= ~AWF_INSYNCTRANSACTION;
if ((wType & XCLASS_DATA) && hDataBack) {
LPEXTDATAINFO pedi;
//if (AddPileItem(pai->pHDataPile, (LPBYTE)&hDataBack, CmpHIWORD) == API_ERROR) {
// SETLASTERROR(pai, DMLERR_MEMORY_ERROR);
// goto ReturnPoint;
//}
// use app heap so any leftovers at Uninitialize time go away.
pedi = (LPEXTDATAINFO)FarAllocMem(pai->hheapApp, sizeof(EXTDATAINFO));
if (pedi) {
pedi->pai = pai;
pedi->hData = hDataBack;
} else {
SETLASTERROR(pai, DMLERR_MEMORY_ERROR);
}
hRet = (HDDEDATA)pedi;
goto ReturnPoint;
} else if (hDataBack) {
hRet = TRUE;
}
}
goto ReturnPoint;
}
SETLASTERROR(pai, DMLERR_INVALIDPARAMETER);
ReturnPoint:
if (pai->wFlags & AWF_UNINITCALLED) {
pai->wFlags &= ~AWF_UNINITCALLED;
DdeUninitialize(MAKELONG((WORD)pai, pai->instCheck));
}
TRACEAPIOUT((szT, "DdeClientTransaction:%lx\n", hRet));
return(hRet);
}
/***************************** Public Function ****************************\
* PUBDOC START
* WORD EXPENTRY DdeGetLastError(void)
*
* This API returns the most recent error registered by the DDE manager for
* the current thread. This should be called anytime a DDE manager API
* returns in a failed state.
*
* returns an error code which corresponds to a DMLERR_ constant found in
* ddeml.h. This error code may be passed on to DdePostError() to
* show the user the reason for the error.
*
* PUBDOC END
*
* History:
* Created 12/14/88 Sanfords
\***************************************************************************/
UINT EXPENTRY DdeGetLastError(
DWORD idInst)
{
register PAPPINFO pai;
register WORD err = DMLERR_DLL_NOT_INITIALIZED;
TRACEAPIIN((szT, "DdeGetLastError(%lx)\n", idInst));
pai = (PAPPINFO)idInst;
if (pai) {
if (pai->instCheck != HIWORD(idInst)) {
TRACEAPIOUT((szT, "DdeGetLastError:%x [bad instance]\n",
DMLERR_INVALIDPARAMETER));
return(DMLERR_INVALIDPARAMETER);
}
err = pai->LastError;
pai->LastError = DMLERR_NO_ERROR;
}
TRACEAPIOUT((szT, "DdeGetLastError:%x\n", err));
return(err);
}
/*\
* Data Handles:
*
* Control flags:
*
* HDCF_APPOWNED
* Only the app can free this in the apps PID/TID context.
* SET - when DdeCreateDataHandle is called with this flag given.
* The hData is Logged at this time.
*
* HDCF_READONLY - set by ClientXfer and callback return.
* The app cannot add data to handles in this state.
* SET - when ClientXfer is entered
* SET - when callback is left
*
* The DLL can free:
* any hData EXCEPT those hDatas which are
* APPOWNED where PIDcurrent == PIDowner.
*
* any unfreed logged hDatas are freed at unregistration time.
*
* The APP can free:
* any logged hData.
*
* Logging points: ClientXfer return, CheckQueue return, PutData(APPOWNED).
*
* WARNING:
*
* Apps with multiple thread registration that talk to themselves
* must not free hDatas until all threads are done with them.
*
\*/
/***************************** Public Function ****************************\
* PUBDOC START
* HDDEDATA EXPENTRY DdeCreateDataHandle(pSrc, cb, cbOff, hszItem, wFmt, afCmd)
* LPBYTE pSrc;
* DWORD cb;
* DWORD cbOff;
* HSZ hszItem;
* WORD wFmt;
* WORD afCmd;
*
* This api allows a server application to create a hData apropriate
* for return from its call-back function.
* The passed in data is stored into the hData which is
* returned on success. Any portions of the data handle not filled are
* undefined. afCmd contains any of the HDATA_ constants described below:
*
* HDATA_APPOWNED
* This declares the created data handle to be the responsability of
* the application to free it. Application owned data handles may
* be returned from the callback function multiple times. This allows
* a server app to be able to support many clients without having to
* recopy the data for each request.
*
* NOTES:
* If an application expects this data handle to hold >64K of data via
* DdeAddData(), it should specify a cb + cbOff to be as large as
* the object is expected to get to avoid unnecessary data copying
* or reallocation by the DLL.
*
* if psrc==NULL, no actual data copying takes place.
*
* Data handles given to an application via the DdeMgrClientXfer() or
* DdeMgrCheckQueue() functions are the responsability of the client
* application to free and MUST NOT be returned from the callback
* function as server data!
*
* PUBDOC END
*
* History:
* Created 12/14/88 Sanfords
\***************************************************************************/
HDDEDATA EXPENTRY DdeCreateDataHandle(
DWORD idInst,
LPBYTE pSrc,
DWORD cb,
DWORD cbOff,
HSZ hszItem,
UINT wFmt,
UINT afCmd)
{
PAPPINFO pai;
HDDEDATA hData;
TRACEAPIIN((szT, "DdeCreateDataHandle(%lx, %lx, %lx, %lx, %lx, %x, %x)\n",
idInst, pSrc, cb, cbOff, hszItem, wFmt, afCmd));
pai = (PAPPINFO)idInst;
if (pai == NULL || pai->instCheck != HIWORD(idInst)) {
TRACEAPIOUT((szT, "DdeCreateDataHandle:0\n"));
return(0);
}
pai->LastError = DMLERR_NO_ERROR;
if (afCmd & ~(HDATA_APPOWNED)) {
SETLASTERROR(pai, DMLERR_INVALIDPARAMETER);
TRACEAPIOUT((szT, "DdeCreateDataHandle:0\n"));
return(0L);
}
hData = PutData(pSrc, cb, cbOff, LOWORD(hszItem), wFmt, afCmd, pai);
if (hData) {
LPEXTDATAINFO pedi;
// use app heap so any leftovers at Uninitialize time go away.
pedi = (LPEXTDATAINFO)FarAllocMem(pai->hheapApp, sizeof(EXTDATAINFO));
if (pedi) {
pedi->pai = pai;
pedi->hData = hData;
}
hData = (HDDEDATA)(DWORD)pedi;
}
TRACEAPIOUT((szT, "DdeCreateDataHandle:%lx\n", hData));
return(hData);
}
HDDEDATA EXPENTRY DdeAddData(
HDDEDATA hData,
LPBYTE pSrc,
DWORD cb,
DWORD cbOff)
{
PAPPINFO pai;
HDDEDATA FAR * phData;
DIP newDip;
HANDLE hd, hNewData;
LPEXTDATAINFO pedi;
TRACEAPIIN((szT, "DdeAddData(%lx, %lx, %lx, %lx)\n",
hData, pSrc, cb, cbOff));
if (!hData)
goto DdeAddDataError;
pedi = (LPEXTDATAINFO)hData;
pai = pedi->pai;
pai->LastError = DMLERR_NO_ERROR;
hData = pedi->hData;
/* if the datahandle is bogus, abort */
hd = hNewData = HIWORD(hData);
if (!hd || (LOWORD(hData) & HDATA_READONLY)) {
DdeAddDataError:
SETLASTERROR(pai, DMLERR_INVALIDPARAMETER);
TRACEAPIOUT((szT, "DdeAddData:0\n"));
return(0L);
}
/*
* we need this check in case the owning app is trying to reallocate
* after giving the hData away. (his copy of the handle would not have
* the READONLY flag set)
*/
phData = (HDDEDATA FAR *)FindPileItem(pai->pHDataPile, CmpHIWORD, (LPBYTE)&hData, 0);
if (!phData || LOWORD(*phData) & HDATA_READONLY) {
SETLASTERROR(pai, DMLERR_INVALIDPARAMETER);
TRACEAPIOUT((szT, "DdeAddData:0\n"));
return(0L);
}
/* HACK ALERT!
* make sure the first two words req'd by windows dde is there,
* that is if the data isn't from an execute
*/
if (!(LOWORD(hData) & HDATA_EXEC)) {
cbOff += 4L;
}
if (GlobalSize(hd) < cb + cbOff) {
/*
* need to grow the block before putting new data in...
*/
if (!(hNewData = GLOBALREALLOC(hd, cb + cbOff, GMEM_MOVEABLE))) {
/*
* We can't grow the seg. Try allocating a new one.
*/
if (!(hNewData = GLOBALALLOC(GMEM_MOVEABLE | GMEM_DDESHARE,
cb + cbOff))) {
/* failed.... die */
SETLASTERROR(pai, DMLERR_MEMORY_ERROR);
TRACEAPIOUT((szT, "DdeAddData:0\n"));
return(0);
} else {
/*
* got a new block, now copy data and trash old one
*/
CopyHugeBlock(GLOBALPTR(hd), GLOBALPTR(hNewData), GlobalSize(hd));
GLOBALFREE(hd); // objects flow through - no need to free.
}
}
if (hNewData != hd) {
/* if the handle is different and in piles, update data piles */
if (FindPileItem(pai->pHDataPile, CmpHIWORD, (LPBYTE)&hData, FPI_DELETE)) {
DIP *pDip;
HDDEDATA hdT;
// replace entry in global data info pile.
if (pDip = (DIP *)(DWORD)FindPileItem(pDataInfoPile, CmpWORD, (LPBYTE)&hd, 0)) {
newDip.hData = hNewData;
newDip.hTask = pDip->hTask;
newDip.cCount = pDip->cCount;
newDip.fFlags = pDip->fFlags;
FindPileItem(pDataInfoPile, CmpWORD, (LPBYTE)&hd, FPI_DELETE);
/* following assumes addpileitem will not fail...!!! */
AddPileItem(pDataInfoPile, (LPBYTE)&newDip, CmpWORD);
}
hdT = (HDDEDATA)MAKELONG(newDip.fFlags, hNewData);
AddPileItem(pai->pHDataPile, (LPBYTE)&hdT, CmpHIWORD);
}
hData = MAKELONG(LOWORD(hData), hNewData);
}
}
if (pSrc) {
CopyHugeBlock(pSrc, HugeOffset(GLOBALLOCK(HIWORD(hData)), cbOff), cb);
}
pedi->hData = hData;
TRACEAPIOUT((szT, "DdeAddData:%lx\n", pedi));
return((HDDEDATA)pedi);
}
DWORD EXPENTRY DdeGetData(hData, pDst, cbMax, cbOff)
HDDEDATA hData;
LPBYTE pDst;
DWORD cbMax;
DWORD cbOff;
{
PAPPINFO pai;
DWORD cbSize;
BOOL fExec = TRUE;
TRACEAPIIN((szT, "DdeGetData(%lx, %lx, %lx, %lx)\n",
hData, pDst, cbMax, cbOff));
//
// Check for NULL.
// Packard Bell Navigator passes NULL at startup. In 3.1 we'd
// maybe trash our local heap using ds:0. But now touching pai will
// fault since it's a far pointer and 0:0 is bad.
//
// Also makes your system stabler.
//
if (!hData)
goto DdeGetDataError;
pai = EXTRACTHDATAPAI(hData);
pai->LastError = DMLERR_NO_ERROR;
hData = ((LPEXTDATAINFO)hData)->hData;
cbSize = GlobalSize(HIWORD(hData));
/* HACK ALERT!
* make sure the first two words req'd by windows dde is there,
* as long as it's not execute data
*/
if (!(LOWORD(hData) & HDATA_EXEC)) {
cbOff += 4;
fExec = FALSE;
}
if (cbOff >= cbSize)
{
DdeGetDataError:
SETLASTERROR(pai, DMLERR_INVALIDPARAMETER);
TRACEAPIOUT((szT, "DdeGetData:0\n"));
return(0L);
}
cbMax = min(cbMax, cbSize - cbOff);
if (pDst == NULL) {
TRACEAPIOUT((szT, "DdeGetData:%lx\n", fExec ? cbSize : cbSize - 4));
return(fExec ? cbSize : cbSize - 4);
} else {
CopyHugeBlock(HugeOffset(GLOBALLOCK(HIWORD(hData)), cbOff),
pDst, cbMax);
TRACEAPIOUT((szT, "DdeGetData:%lx\n", cbMax));
return(cbMax);
}
}
LPBYTE EXPENTRY DdeAccessData(
HDDEDATA hData,
LPDWORD pcbDataSize)
{
PAPPINFO pai;
DWORD offset;
LPBYTE lpRet;
TRACEAPIIN((szT, "DdeAccessData(%lx, %lx)\n",
hData, pcbDataSize));
if (!hData)
goto DdeAccessDataError;
pai = EXTRACTHDATAPAI(hData);
pai->LastError = DMLERR_NO_ERROR;
hData = ((LPEXTDATAINFO)hData)->hData;
if (HIWORD(hData) && (HIWORD(hData) != 0xFFFF) ) {
/* messed around here getting past the first two words, which
* aren't even there if this is execute data
*/
offset = (LOWORD(hData) & HDATA_EXEC) ? 0L : 4L;
if (pcbDataSize) {
*pcbDataSize = GlobalSize(HIWORD(hData)) - offset;
}
lpRet = (LPBYTE)GLOBALLOCK(HIWORD(hData)) + offset;
TRACEAPIOUT((szT, "DdeAccessData:%lx\n", lpRet));
return(lpRet);
}
DdeAccessDataError:
SETLASTERROR(pai, DMLERR_INVALIDPARAMETER);
TRACEAPIOUT((szT, "DdeAccessData:0\n"));
return(0L);
}
BOOL EXPENTRY DdeUnaccessData(
HDDEDATA hData)
{
PAPPINFO pai;
TRACEAPIIN((szT, "DdeUnaccessData(%lx)\n", hData));
//
// BOGUS -- we should set last error and RIP also.
//
if (hData)
{
pai = EXTRACTHDATAPAI(hData);
pai->LastError = DMLERR_NO_ERROR;
}
TRACEAPIOUT((szT, "DdeUnaccessData:1\n"));
return(TRUE);
}
// Diamond Multimedia Kit 5000 creates a non-app-owned data handle,
// uses it in a client transaction (which free's it) and then
// calls DDEFreeDataHandle which can fault (depending on what junk
// gets left behind). To handle this we validate the data handle
// before doing anything else.
BOOL HDdeData_Validate(HDDEDATA hData)
{
WORD wSaveDS;
UINT nRet;
// we better check HIWORD(hData) before we try to stuff it into ds
if(IsBadReadPtr((LPCSTR)hData, 1)) {
#ifdef DEBUG
OutputDebugString("DDEML: Invalid HDDEDATA.\n\r");
#endif
return(FALSE);
}
wSaveDS = SwitchDS(HIWORD(hData));
// Use the validation layer to check the handle
// We can call LocalSize with the near ptr as the handle because:
// 1. The HDDEDATA was allocated with LPTR (LMEM_FIXED | LMEM_ZEROINIT)
// 2. Local mem that is alloc'd LMEM_FIXED, the offset is the handle
// 3. We don't want to call LocalHandle to get the handle because it has
// no parameter vailidation & blows up for bad handles
nRet = LocalSize((HANDLE)LOWORD(hData));
SwitchDS(wSaveDS);
#ifdef DEBUG
if (!nRet) {
OutputDebugString("DDEML: Invalid HDDEDATA.\n\r");
}
#endif
return nRet;
}
BOOL EXPENTRY DdeFreeDataHandle(
HDDEDATA hData)
{
PAPPINFO pai;
LPEXTDATAINFO pedi;
TRACEAPIIN((szT, "DdeFreeDataHandle(%lx)\n", hData));
pedi = (LPEXTDATAINFO)hData;
if ( !pedi || !HDdeData_Validate(hData) ) {
TRACEAPIOUT((szT, "DdeFreeDataHandle:1\n"));
return(TRUE);
}
pai = EXTRACTHDATAPAI(hData);
pai->LastError = DMLERR_NO_ERROR;
if (!(LOWORD(pedi->hData) & HDATA_NOAPPFREE)) {
FreeDataHandle(pedi->pai, pedi->hData, FALSE);
FarFreeMem((LPSTR)pedi);
}
TRACEAPIOUT((szT, "DdeFreeDataHandle:2\n"));
return(TRUE);
}
/***************************************************************************\
* PUBDOC START
* HSZ management notes:
*
* HSZs are used in this DLL to simplify string handling for applications
* and for inter-process communication. Since many applications use a
* fixed set of Application/Topic/Item names, it is convenient to convert
* them to HSZs and allow quick comparisons for lookups. This also frees
* the DLL up from having to constantly provide string buffers for copying
* strings between itself and its clients.
*
* HSZs are the same as atoms except they have no restrictions on length or
* number and are 32 bit values. They are case preserving and can be
* compared directly for case sensitive comparisons or via DdeCmpStringHandles()
* for case insensitive comparisons.
*
* When an application creates an HSZ via DdeCreateStringHandle() or increments its
* count via DdeKeepStringHandle() it is essentially claiming the HSZ for
* its own use. On the other hand, when an application is given an
* HSZ from the DLL via a callback, it is using another application's HSZ
* and should not free that HSZ via DdeFreeStringHandle().
*
* The DLL insures that during the callback any HSZs given will remain
* valid for the duration of the callback.
*
* If an application wishes to keep that HSZ to use for itself as a
* standard for future comparisons, it should increment its count so that,
* should the owning application free it, the HSZ will not become invalid.
* This also prevents an HSZ from changing its value. (ie, app A frees it
* and then app B creates a new one that happens to use the same HSZ code,
* then app C, which had the HSZ stored all along (but forgot to increment
* its count) now is holding a handle to a different string.)
*
* Applications may free HSZs they have created or incremented at any time
* by calling DdeFreeStringHandle().
*
* The DLL internally increments HSZ counts while in use so that they will
* not be destroyed until both the DLL and all applications concerned are
* through with them.
*
* IT IS THE APPLICATIONS RESPONSIBILITY TO PROPERLY CREATE AND FREE HSZs!!
*
* PUBDOC END
\***************************************************************************/
HSZ EXPENTRY DdeCreateStringHandle(
DWORD idInst,
LPCSTR psz,
int iCodePage)
{
#define pai ((PAPPINFO)idInst)
ATOM a;
TRACEAPIIN((szT, "DdeCreateStringHandle(%lx, %s, %x)\n",
idInst, psz, iCodePage));
if (pai == NULL | pai->instCheck != HIWORD(idInst)) {
TRACEAPIOUT((szT, "DdeCreateStringHandle:0\n"));
return(0);
}
pai->LastError = DMLERR_NO_ERROR;
if (psz == NULL || *psz == '\0') {
TRACEAPIOUT((szT, "DdeCreateStringHandle:0\n"));
return(0);
}
if (iCodePage == 0 || iCodePage == CP_WINANSI || iCodePage == GetKBCodePage()) {
SEMENTER();
a = FindAddHsz((LPSTR)psz, TRUE);
SEMLEAVE();
MONHSZ(a, MH_CREATE, pai->hTask);
if (AddPileItem(pai->pHszPile, (LPBYTE)&a, NULL) == API_ERROR) {
SETLASTERROR(pai, DMLERR_MEMORY_ERROR);
a = 0;
}
TRACEAPIOUT((szT, "DdeCreateStringHandle:%x\n", a));
return((HSZ)a);
} else {
SETLASTERROR(pai, DMLERR_INVALIDPARAMETER);
TRACEAPIOUT((szT, "DdeCreateStringHandle:0\n"));
return(0);
}
#undef pai
}
BOOL EXPENTRY DdeFreeStringHandle(
DWORD idInst,
HSZ hsz)
{
PAPPINFO pai;
ATOM a = LOWORD(hsz);
BOOL fRet;
TRACEAPIIN((szT, "DdeFreeStringHandle(%lx, %lx)\n",
idInst, hsz));
pai = (PAPPINFO)idInst;
if (pai == NULL || pai->instCheck != HIWORD(idInst)) {
TRACEAPIOUT((szT, "DdeFreeStringHandle:0\n"));
return(FALSE);
}
pai->LastError = DMLERR_NO_ERROR;
MONHSZ(a, MH_DELETE, pai->hTask);
FindPileItem(pai->pHszPile, CmpWORD, (LPBYTE)&a, FPI_DELETE);
fRet = FreeHsz(a);
TRACEAPIOUT((szT, "DdeFreeStringHandle:%x\n", fRet));
return(fRet);
}
BOOL EXPENTRY DdeKeepStringHandle(
DWORD idInst,
HSZ hsz)
{
PAPPINFO pai;
ATOM a = LOWORD(hsz);
BOOL fRet;
TRACEAPIIN((szT, "DdeKeepStringHandle(%lx, %lx)\n",
idInst, hsz));
pai = (PAPPINFO)idInst;
if (pai == NULL || pai->instCheck != HIWORD(idInst)) {
TRACEAPIOUT((szT, "DdeKeepStringHandle:0\n"));
return(FALSE);
}
pai->LastError = DMLERR_NO_ERROR;
MONHSZ(a, MH_KEEP, pai->hTask);
AddPileItem(pai->pHszPile, (LPBYTE)&a, NULL);
fRet = IncHszCount(a);
TRACEAPIOUT((szT, "DdeKeepStringHandle:%x\n", fRet));
return(fRet);
}
DWORD EXPENTRY DdeQueryString(
DWORD idInst,
HSZ hsz,
LPSTR psz,
DWORD cchMax,
int iCodePage)
{
PAPPINFO pai;
DWORD dwRet;
TRACEAPIIN((szT, "DdeQueryString(%lx, %lx, %lx, %lx, %x)\n",
idInst, hsz, psz, cchMax, iCodePage));
pai = (PAPPINFO)idInst;
if (pai == NULL || pai->instCheck != HIWORD(idInst)) {
TRACEAPIOUT((szT, "DdeQueryString:0\n"));
return(FALSE);
}
pai->LastError = DMLERR_NO_ERROR;
if (iCodePage == 0 || iCodePage == CP_WINANSI || iCodePage == GetKBCodePage()) {
if (psz) {
if (hsz) {
dwRet = QueryHszName(hsz, psz, (WORD)cchMax);
TRACEAPIOUT((szT, "DdeQueryString:%lx(%s)\n", dwRet, psz));
return(dwRet);
} else {
*psz = '\0';
TRACEAPIOUT((szT, "DdeQueryString:0\n"));
return(0);
}
} else if (hsz) {
dwRet = QueryHszLength(hsz);
TRACEAPIOUT((szT, "DdeQueryString:%lx\n", dwRet));
return(dwRet);
} else {
TRACEAPIOUT((szT, "DdeQueryString:0\n"));
return(0);
}
} else {
SETLASTERROR(pai, DMLERR_INVALIDPARAMETER);
TRACEAPIOUT((szT, "DdeQueryString:0\n"));
return(0);
}
}
int EXPENTRY DdeCmpStringHandles(
HSZ hsz1,
HSZ hsz2)
{
int iRet;
TRACEAPIIN((szT, "DdeCmpStringHandles(%lx, %lx)\n",
hsz1, hsz2));
if (hsz2 > hsz1) {
iRet = -1;
} else if (hsz2 < hsz1) {
iRet = 1;
} else {
iRet = 0;
}
TRACEAPIOUT((szT, "DdeCmpStringHandles:%x\n", iRet));
return(iRet);
}
BOOL EXPENTRY DdeAbandonTransaction(
DWORD idInst,
HCONV hConv,
DWORD idTransaction)
{
PAPPINFO pai;
TRACEAPIIN((szT, "DdeAbandonTransaction(%lx, %lx, %lx)\n",
idInst, hConv, idTransaction));
pai = (PAPPINFO)idInst;
if (pai == NULL || pai->instCheck != HIWORD(idInst)) {
TRACEAPIOUT((szT, "DdeAbandonTransaction:0\n"));
return(FALSE);
}
pai->LastError = DMLERR_NO_ERROR;
if ((hConv && !ValidateHConv(hConv)) || idTransaction == QID_SYNC) {
SETLASTERROR(pai, DMLERR_INVALIDPARAMETER);
TRACEAPIOUT((szT, "DdeAbandonTransaction:0\n"));
return(FALSE);
}
if (hConv == NULL) {
// do all conversations!
register HWND hwnd;
register HWND hwndLast;
if (!(hwnd = GetWindow(pai->hwndDmg, GW_CHILD))) {
TRACEAPIOUT((szT, "DdeAbandonTransaction:1\n"));
return(TRUE);
}
hwndLast = GetWindow(hwnd, GW_HWNDLAST);
do {
AbandonTransaction(hwnd, pai, idTransaction, TRUE);
if (hwnd == hwndLast) {
break;
}
hwnd = GetWindow(hwnd, GW_HWNDNEXT);
} while (TRUE);
} else {
BOOL fRet;
fRet = AbandonTransaction((HWND)hConv, pai, idTransaction, TRUE);
TRACEAPIOUT((szT, "DdeAbandonTransaction:%x\n", fRet));
return(fRet);
}
TRACEAPIOUT((szT, "DdeAbandonTransaction:1\n"));
return(TRUE);
}
BOOL AbandonTransaction(
HWND hwnd,
PAPPINFO pai,
DWORD id,
BOOL fMarkOnly)
{
PCLIENTINFO pci;
PCQDATA pcqd;
WORD err;
SEMCHECKOUT();
SEMENTER();
pci = (PCLIENTINFO)GetWindowLong(hwnd, GWL_PCI);
if (!pci->ci.fs & ST_CLIENT) {
err = DMLERR_INVALIDPARAMETER;
failExit:
SETLASTERROR(pai, err);
SEMLEAVE();
SEMCHECKOUT();
return(FALSE);
}
do {
/*
* HACK: id == 0 -> all ids so we cycle
*/
pcqd = (PCQDATA)Findqi(pci->pQ, id);
if (!pcqd) {
if (id) {
err = DMLERR_UNFOUND_QUEUE_ID;
goto failExit;
}
break;
}
if (fMarkOnly) {
pcqd->xad.fAbandoned = TRUE;
if (!id) {
while (pcqd = (PCQDATA)FindNextQi(pci->pQ, (PQUEUEITEM)pcqd,
FALSE)) {
pcqd->xad.fAbandoned = TRUE;
}
break;
}
} else {
if (pcqd->xad.pdata && pcqd->xad.pdata != 1 &&
!FindPileItem(pai->pHDataPile, CmpHIWORD,
(LPBYTE)&pcqd->xad.pdata, 0)) {
FreeDDEData(LOWORD(pcqd->xad.pdata), pcqd->xad.pXferInfo->wFmt);
}
/*
* Decrement the use count we incremented when the client started
* this transaction.
*/
FreeHsz(LOWORD(pcqd->XferInfo.hszItem));
Deleteqi(pci->pQ, MAKEID(pcqd));
}
} while (!id);
SEMLEAVE();
SEMCHECKOUT();
return(TRUE);
}
BOOL EXPENTRY DdeEnableCallback(
DWORD idInst,
HCONV hConv,
UINT wCmd)
{
PAPPINFO pai;
BOOL fRet;
TRACEAPIIN((szT, "DdeEnableCallback(%lx, %lx, %x)\n",
idInst, hConv, wCmd));
pai = (PAPPINFO)idInst;
if (pai == NULL || pai->instCheck != HIWORD(idInst)) {
TRACEAPIOUT((szT, "DdeEnableCallback:0\n"));
return(FALSE);
}
pai->LastError = DMLERR_NO_ERROR;
if ((hConv && !ValidateHConv(hConv)) ||
(wCmd & ~(EC_ENABLEONE | EC_ENABLEALL |
EC_DISABLE | EC_QUERYWAITING))) {
SETLASTERROR(pai, DMLERR_INVALIDPARAMETER);
TRACEAPIOUT((szT, "DdeEnableCallback:0\n"));
return(FALSE);
}
SEMCHECKOUT();
if (wCmd & EC_QUERYWAITING) {
PCBLI pli;
int cWaiting = 0;
SEMENTER();
for (pli = (PCBLI)pai->plstCB->pItemFirst;
pli && cWaiting < 2;
pli = (PCBLI)pli->next) {
if (hConv || pli->hConv == hConv) {
cWaiting++;
}
}
SEMLEAVE();
fRet = cWaiting > 1 || (cWaiting == 1 && pai->cInProcess == 0);
TRACEAPIOUT((szT, "DdeEnableCallback:%x\n", fRet));
return(fRet);
}
/*
* We depend on the fact that EC_ constants relate to ST_ constants.
*/
if (hConv == NULL) {
if (wCmd & EC_DISABLE) {
pai->wFlags |= AWF_DEFCREATESTATE;
} else {
pai->wFlags &= ~AWF_DEFCREATESTATE;
}
ChildMsg(pai->hwndDmg, UM_SETBLOCK, wCmd, 0, FALSE);
} else {
SendMessage((HWND)hConv, UM_SETBLOCK, wCmd, 0);
}
if (!(wCmd & EC_DISABLE)) {
// This is synchronous! Fail if we made this from within a callback.
if (pai->cInProcess) {
SETLASTERROR(pai, DMLERR_REENTRANCY);
TRACEAPIOUT((szT, "DdeEnableCallback:0\n"));
return(FALSE);
}
SendMessage(pai->hwndDmg, UM_CHECKCBQ, 0, (DWORD)(LPSTR)pai);
}
TRACEAPIOUT((szT, "DdeEnableCallback:1\n"));
return(TRUE); // TRUE implies the callback queue is free of unblocked calls.
}
HDDEDATA EXPENTRY DdeNameService(
DWORD idInst,
HSZ hsz1,
HSZ hsz2,
UINT afCmd)
{
PAPPINFO pai;
PPILE panp;
TRACEAPIIN((szT, "DdeNameService(%lx, %lx, %lx, %x)\n",
idInst, hsz1, hsz2, afCmd));
pai = (PAPPINFO)idInst;
if (pai == NULL || pai->instCheck != HIWORD(idInst)) {
TRACEAPIOUT((szT, "DdeNameService:0\n"));
return(FALSE);
}
pai->LastError = DMLERR_NO_ERROR;
if (afCmd & DNS_FILTERON) {
pai->afCmd |= APPCMD_FILTERINITS;
}
if (afCmd & DNS_FILTEROFF) {
pai->afCmd &= ~APPCMD_FILTERINITS;
}
if (afCmd & (DNS_REGISTER | DNS_UNREGISTER)) {
if (pai->afCmd & APPCMD_CLIENTONLY) {
SETLASTERROR(pai, DMLERR_DLL_USAGE);
TRACEAPIOUT((szT, "DdeNameService:0\n"));
return(FALSE);
}
panp = pai->pAppNamePile;
if (hsz1 == NULL) {
if (afCmd & DNS_REGISTER) {
/*
* registering NULL is not allowed!
*/
SETLASTERROR(pai, DMLERR_INVALIDPARAMETER);
TRACEAPIOUT((szT, "DdeNameService:0\n"));
return(FALSE);
}
/*
* unregistering NULL is just like unregistering each
* registered name.
*
* 10/19/90 - made this a synchronous event so that hsz
* can be freed by calling app after this call completes
* without us having to keep a copy around forever.
*/
while (PopPileSubitem(panp, (LPBYTE)&hsz1)) {
RegisterService(FALSE, (GATOM)hsz1, pai->hwndFrame);
FreeHsz(LOWORD(hsz1));
}
TRACEAPIOUT((szT, "DdeNameService:1\n"));
return(TRUE);
}
if (afCmd & DNS_REGISTER) {
if (panp == NULL) {
panp = pai->pAppNamePile =
CreatePile(pai->hheapApp, sizeof(HSZ), 8);
}
IncHszCount(LOWORD(hsz1));
AddPileItem(panp, (LPBYTE)&hsz1, NULL);
} else { // DNS_UNREGISTER
FindPileItem(panp, CmpDWORD, (LPBYTE)&hsz1, FPI_DELETE);
}
// see 10/19/90 note above.
RegisterService(afCmd & DNS_REGISTER ? TRUE : FALSE, (GATOM)hsz1,
pai->hwndFrame);
if (afCmd & DNS_UNREGISTER) {
FreeHsz(LOWORD(hsz1));
}
TRACEAPIOUT((szT, "DdeNameService:1\n"));
return(TRUE);
}
TRACEAPIOUT((szT, "DdeNameService:0\n"));
return(0L);
}