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
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);
|
|
}
|