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.
543 lines
16 KiB
543 lines
16 KiB
/****************************** Module Header ******************************\
|
|
* Module Name: callback.c
|
|
*
|
|
* Copyright (c) 1985 - 1999, Microsoft Corporation
|
|
*
|
|
* DDE Manager callback related functions
|
|
*
|
|
* Created: 11/11/91 Sanford Staab
|
|
*
|
|
\***************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
/***************************************************************************\
|
|
* DoCallback
|
|
*
|
|
* Description:
|
|
* Performs a synchronous callback to the given instance's callback proc.
|
|
*
|
|
* History:
|
|
* 11-12-91 sanfords Created.
|
|
\***************************************************************************/
|
|
HDDEDATA DoCallback(
|
|
PCL_INSTANCE_INFO pcii,
|
|
WORD wType,
|
|
WORD wFmt,
|
|
HCONV hConv,
|
|
HSZ hsz1,
|
|
HSZ hsz2,
|
|
HDDEDATA hData,
|
|
ULONG_PTR dw1,
|
|
ULONG_PTR dw2)
|
|
{
|
|
HDDEDATA hDataRet;
|
|
PCLIENTINFO pci;
|
|
|
|
CheckDDECritIn;
|
|
|
|
|
|
/*
|
|
* Zombie conversations don't generate callbacks!
|
|
*/
|
|
if (hConv && TypeFromHandle(hConv) == HTYPE_ZOMBIE_CONVERSATION) {
|
|
return(0);
|
|
}
|
|
|
|
pci = GetClientInfo();
|
|
pci->cInDDEMLCallback++;
|
|
|
|
pcii->cInDDEMLCallback++;
|
|
LeaveDDECrit;
|
|
CheckDDECritOut;
|
|
|
|
/*
|
|
* Bug 246472 - joejo
|
|
* fixup all DDE Callbacks since some apps make their callbacks
|
|
* C-Style instead of PASCAL.
|
|
*/
|
|
hDataRet = UserCallDDECallback(*pcii->pfnCallback, (UINT)wType, (UINT)wFmt, hConv, hsz1, hsz2,
|
|
hData, dw1, dw2);
|
|
|
|
EnterDDECrit;
|
|
pcii->cInDDEMLCallback--;
|
|
pci->cInDDEMLCallback--;
|
|
|
|
if (!(pcii->afCmd & APPCLASS_MONITOR) && pcii->MonitorFlags & MF_CALLBACKS) {
|
|
PEVENT_PACKET pep;
|
|
|
|
pep = (PEVENT_PACKET)DDEMLAlloc(sizeof(EVENT_PACKET) - sizeof(DWORD) +
|
|
sizeof(MONCBSTRUCT));
|
|
if (pep != NULL) {
|
|
|
|
pep->EventType = MF_CALLBACKS;
|
|
pep->fSense = TRUE;
|
|
pep->cbEventData = sizeof(MONCBSTRUCT);
|
|
|
|
#define pcbs ((MONCBSTRUCT *)&pep->Data)
|
|
pcbs->cb = sizeof(MONCBSTRUCT);
|
|
pcbs->dwTime = NtGetTickCount();
|
|
pcbs->hTask = (HANDLE)LongToHandle( pcii->tid );
|
|
pcbs->dwRet = HandleToUlong(hDataRet);
|
|
pcbs->wType = wType;
|
|
pcbs->wFmt = wFmt;
|
|
pcbs->hConv = hConv;
|
|
pcbs->hsz1 = (HSZ)LocalToGlobalAtom(LATOM_FROM_HSZ(hsz1));
|
|
pcbs->hsz2 = (HSZ)LocalToGlobalAtom(LATOM_FROM_HSZ(hsz2));
|
|
pcbs->hData = hData;
|
|
pcbs->dwData1 = dw1;
|
|
pcbs->dwData2 = dw2;
|
|
if (((wType == XTYP_CONNECT) || (wType == XTYP_WILDCONNECT)) && dw1) {
|
|
RtlCopyMemory(&pcbs->cc, (PVOID)dw1, sizeof(CONVCONTEXT));
|
|
}
|
|
|
|
LeaveDDECrit;
|
|
|
|
if (wType & XCLASS_DATA) {
|
|
if (hDataRet && hDataRet != CBR_BLOCK) {
|
|
pcbs->cbData = DdeGetData(hDataRet, (LPBYTE)pcbs->Data, 32, 0);
|
|
}
|
|
} else if (hData) {
|
|
pcbs->cbData = DdeGetData(hData, (LPBYTE)pcbs->Data, 32, 0);
|
|
}
|
|
|
|
Event(pep);
|
|
|
|
EnterDDECrit;
|
|
|
|
GlobalDeleteAtom(LATOM_FROM_HSZ(pcbs->hsz1));
|
|
GlobalDeleteAtom(LATOM_FROM_HSZ(pcbs->hsz2));
|
|
DDEMLFree(pep);
|
|
#undef pcbs
|
|
}
|
|
}
|
|
return (hDataRet);
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************\
|
|
* _ClientEventCallback
|
|
*
|
|
* Description:
|
|
* Called from the server side to perform event callbacks.
|
|
*
|
|
* History:
|
|
* 11-12-91 sanfords Created.
|
|
\***************************************************************************/
|
|
DWORD _ClientEventCallback(
|
|
PCL_INSTANCE_INFO pcii,
|
|
PEVENT_PACKET pep)
|
|
{
|
|
HDDEDATA hData;
|
|
|
|
EnterDDECrit;
|
|
|
|
switch (pep->EventType) {
|
|
case 0: // MonitorFlags change event - everybody gets it
|
|
pcii->MonitorFlags = pep->Data;
|
|
break;
|
|
|
|
case MF_CALLBACKS:
|
|
{
|
|
MONCBSTRUCT mcb;
|
|
|
|
mcb = *((MONCBSTRUCT *)&pep->Data);
|
|
mcb.hsz1 = NORMAL_HSZ_FROM_LATOM(GlobalToLocalAtom((GATOM)(ULONG_PTR)mcb.hsz1));
|
|
mcb.hsz2 = NORMAL_HSZ_FROM_LATOM(GlobalToLocalAtom((GATOM)(ULONG_PTR)mcb.hsz2));
|
|
if ( mcb.wType == XTYP_REGISTER ||
|
|
mcb.wType == XTYP_UNREGISTER) {
|
|
mcb.hsz2 = INST_SPECIFIC_HSZ_FROM_LATOM((LATOM)(ULONG_PTR)mcb.hsz2);
|
|
}
|
|
hData = InternalCreateDataHandle(pcii, (LPSTR)&mcb,
|
|
pep->cbEventData, 0,
|
|
HDATA_NOAPPFREE | HDATA_READONLY | HDATA_EXECUTE, 0, 0);
|
|
if (hData) {
|
|
DoCallback(pcii, (WORD)XTYP_MONITOR, 0, 0, 0, 0, hData, 0L,
|
|
pep->EventType);
|
|
InternalFreeDataHandle((HDDEDATA)hData, TRUE);
|
|
DeleteAtom(LATOM_FROM_HSZ(mcb.hsz1));
|
|
DeleteAtom(LATOM_FROM_HSZ(mcb.hsz2));
|
|
}
|
|
}
|
|
break;
|
|
|
|
case MF_LINKS:
|
|
{
|
|
MONLINKSTRUCT ml;
|
|
|
|
ml = *((MONLINKSTRUCT *)&pep->Data);
|
|
ml.hszSvc = NORMAL_HSZ_FROM_LATOM(GlobalToLocalAtom((GATOM)(ULONG_PTR)ml.hszSvc));
|
|
ml.hszTopic = NORMAL_HSZ_FROM_LATOM(GlobalToLocalAtom((GATOM)(ULONG_PTR)ml.hszTopic));
|
|
ml.hszItem = NORMAL_HSZ_FROM_LATOM(GlobalToLocalAtom((GATOM)(ULONG_PTR)ml.hszItem));
|
|
hData = InternalCreateDataHandle(pcii, (LPSTR)&ml,
|
|
pep->cbEventData, 0,
|
|
HDATA_NOAPPFREE | HDATA_READONLY | HDATA_EXECUTE, 0, 0);
|
|
if (hData) {
|
|
DoCallback(pcii, (WORD)XTYP_MONITOR, 0, 0, 0, 0, hData, 0L,
|
|
pep->EventType);
|
|
InternalFreeDataHandle((HDDEDATA)hData, TRUE);
|
|
DeleteAtom(LATOM_FROM_HSZ(ml.hszSvc));
|
|
DeleteAtom(LATOM_FROM_HSZ(ml.hszTopic));
|
|
DeleteAtom(LATOM_FROM_HSZ(ml.hszItem));
|
|
}
|
|
}
|
|
break;
|
|
|
|
case MF_CONV:
|
|
{
|
|
MONCONVSTRUCT mc;
|
|
|
|
mc = *((MONCONVSTRUCT *)&pep->Data);
|
|
mc.hszSvc = NORMAL_HSZ_FROM_LATOM(GlobalToLocalAtom((GATOM)(ULONG_PTR)mc.hszSvc));
|
|
mc.hszTopic = NORMAL_HSZ_FROM_LATOM(GlobalToLocalAtom((GATOM)(ULONG_PTR)mc.hszTopic));
|
|
hData = InternalCreateDataHandle(pcii, (LPSTR)&mc,
|
|
pep->cbEventData, 0,
|
|
HDATA_NOAPPFREE | HDATA_READONLY | HDATA_EXECUTE, 0, 0);
|
|
if (hData) {
|
|
DoCallback(pcii, (WORD)XTYP_MONITOR, 0, 0, 0, 0, hData, 0L,
|
|
pep->EventType);
|
|
InternalFreeDataHandle((HDDEDATA)hData, TRUE);
|
|
DeleteAtom(LATOM_FROM_HSZ(mc.hszSvc));
|
|
DeleteAtom(LATOM_FROM_HSZ(mc.hszTopic));
|
|
}
|
|
}
|
|
break;
|
|
|
|
case MF_HSZ_INFO:
|
|
if (!(pcii->flags & IIF_UNICODE)) {
|
|
LPSTR pszAnsi;
|
|
/*
|
|
* Translate HSZ string back into ANSI
|
|
*/
|
|
if (WCSToMB(((PMONHSZSTRUCT)&pep->Data)->str,
|
|
((int)pep->cbEventData - (int)((PMONHSZSTRUCT)&pep->Data)->cb) / sizeof(WCHAR),
|
|
&pszAnsi,
|
|
(int)pep->cbEventData - (int)((PMONHSZSTRUCT)&pep->Data)->cb,
|
|
TRUE)) {
|
|
strcpy(((PMONHSZSTRUCTA)&pep->Data)->str, pszAnsi);
|
|
UserLocalFree(pszAnsi);
|
|
}
|
|
((PMONHSZSTRUCT)&pep->Data)->cb = sizeof(MONHSZSTRUCTA);
|
|
}
|
|
// fall through
|
|
case MF_SENDMSGS:
|
|
case MF_POSTMSGS:
|
|
if (pep->EventType == MF_POSTMSGS) {
|
|
PMONMSGSTRUCT pmms = (PMONMSGSTRUCT)&pep->Data;
|
|
BYTE buf[32];
|
|
|
|
/*
|
|
* We may need to translate the Execute string to/from
|
|
* UNICODE depending on what type of monitor this is
|
|
* going to.
|
|
*/
|
|
if (pmms->wMsg == WM_DDE_EXECUTE) {
|
|
BOOL fUnicodeText;
|
|
int flags;
|
|
|
|
flags = (IS_TEXT_UNICODE_UNICODE_MASK |
|
|
IS_TEXT_UNICODE_REVERSE_MASK |
|
|
(IS_TEXT_UNICODE_NOT_UNICODE_MASK &
|
|
(~IS_TEXT_UNICODE_ILLEGAL_CHARS)) |
|
|
IS_TEXT_UNICODE_NOT_ASCII_MASK);
|
|
#ifdef ISTEXTUNICODE_WORKS
|
|
fUnicodeText = RtlIsTextUnicode(pmms->dmhd.Data,
|
|
min(32, pmms->dmhd.cbData), &flags);
|
|
#else
|
|
fUnicodeText = (*(LPSTR)pmms->dmhd.Data == '\0');
|
|
#endif
|
|
|
|
if (pcii->flags & IIF_UNICODE && !fUnicodeText) {
|
|
/* Ascii->UNICODE */
|
|
RtlMultiByteToUnicodeN((LPWSTR)buf, 32, NULL,
|
|
(LPSTR)&pmms->dmhd.Data,
|
|
min(32, pmms->dmhd.cbData));
|
|
RtlCopyMemory(&pmms->dmhd.Data, buf, 32);
|
|
} else if (!(pcii->flags & IIF_UNICODE) && fUnicodeText) {
|
|
/* UNICODE->Ascii */
|
|
RtlUnicodeToMultiByteN((LPSTR)buf, 32, NULL,
|
|
(LPWSTR)&pmms->dmhd.Data,
|
|
min(32, pmms->dmhd.cbData));
|
|
RtlCopyMemory(&pmms->dmhd.Data, buf, 32);
|
|
}
|
|
}
|
|
}
|
|
case MF_ERRORS:
|
|
hData = InternalCreateDataHandle(pcii, (LPSTR)&pep->Data,
|
|
pep->cbEventData, 0,
|
|
HDATA_NOAPPFREE | HDATA_READONLY | HDATA_EXECUTE, 0, 0);
|
|
if (hData) {
|
|
DoCallback(pcii, (WORD)XTYP_MONITOR, 0, 0, 0, 0, hData, 0L,
|
|
pep->EventType);
|
|
InternalFreeDataHandle((HDDEDATA)hData, TRUE);
|
|
}
|
|
break;
|
|
}
|
|
|
|
LeaveDDECrit;
|
|
return (0);
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************\
|
|
* EnableEnumProc
|
|
*
|
|
* Description:
|
|
* Helper function for applying pees->wCmd to each client and server
|
|
* DDEML window.
|
|
*
|
|
* History:
|
|
* 11-12-91 sanfords Created.
|
|
\***************************************************************************/
|
|
BOOL EnableEnumProc(
|
|
HWND hwnd,
|
|
PENABLE_ENUM_STRUCT pees)
|
|
{
|
|
PCONV_INFO pcoi;
|
|
|
|
for (pcoi = (PCONV_INFO)GetWindowLongPtr(hwnd, GWLP_PCI);
|
|
pcoi != NULL; pcoi = pcoi->next) {
|
|
pcoi->cLocks++;
|
|
*pees->pfRet |= SetEnableState(pcoi, pees->wCmd);
|
|
if (pees->wCmd2) {
|
|
/*
|
|
* Only let ES_CHECKQUEUEONCE be done on one window but
|
|
* don't stop the wCmd from getting to all the other
|
|
* windows.
|
|
*/
|
|
if (SetEnableState(pcoi, pees->wCmd2) &&
|
|
pees->wCmd2 == EC_CHECKQUEUEONCE) {
|
|
pees->wCmd2 = 0;
|
|
}
|
|
}
|
|
pcoi->cLocks--;
|
|
if (pcoi->cLocks == 0 && pcoi->state & ST_FREE_CONV_RES_NOW) {
|
|
FreeConversationResources(pcoi);
|
|
break;
|
|
}
|
|
}
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* DdeEnableCallback (DDEML API)
|
|
*
|
|
* Description:
|
|
* Turns on and off asynchronous callbacks (BLOCKABLE).
|
|
*
|
|
* History:
|
|
* 11-12-91 sanfords Created.
|
|
\***************************************************************************/
|
|
|
|
FUNCLOG3(LOG_GENERAL, BOOL, DUMMYCALLINGTYPE, DdeEnableCallback, DWORD, idInst, HCONV, hConv, UINT, wCmd)
|
|
BOOL DdeEnableCallback(
|
|
DWORD idInst,
|
|
HCONV hConv,
|
|
UINT wCmd)
|
|
{
|
|
BOOL fRet = FALSE;
|
|
PCL_INSTANCE_INFO pcii;
|
|
PCONV_INFO pcoi;
|
|
ENABLE_ENUM_STRUCT ees;
|
|
|
|
EnterDDECrit;
|
|
|
|
pcii = (PCL_INSTANCE_INFO)ValidateInstance((HANDLE)LongToHandle( idInst ));
|
|
if (pcii == NULL) {
|
|
BestSetLastDDEMLError(DMLERR_INVALIDPARAMETER);
|
|
goto Exit;
|
|
}
|
|
|
|
switch (wCmd) {
|
|
case EC_QUERYWAITING:
|
|
case EC_DISABLE:
|
|
case EC_ENABLEONE:
|
|
case EC_ENABLEALL:
|
|
break;
|
|
|
|
default:
|
|
SetLastDDEMLError(pcii, DMLERR_INVALIDPARAMETER);
|
|
goto Exit;
|
|
}
|
|
|
|
if (hConv) {
|
|
pcoi = (PCONV_INFO)ValidateCHandle((HANDLE)hConv,
|
|
HTYPE_CLIENT_CONVERSATION, InstFromHandle(idInst));
|
|
if (pcoi == NULL) {
|
|
pcoi = (PCONV_INFO)ValidateCHandle((HANDLE)hConv,
|
|
HTYPE_SERVER_CONVERSATION, InstFromHandle(idInst));
|
|
}
|
|
if (pcoi == NULL) {
|
|
SetLastDDEMLError(pcii, DMLERR_INVALIDPARAMETER);
|
|
goto Exit;
|
|
}
|
|
pcoi->cLocks++;
|
|
fRet = SetEnableState(pcoi, wCmd);
|
|
switch (wCmd) {
|
|
case EC_ENABLEALL:
|
|
case EC_ENABLEONE:
|
|
CheckForQueuedMessages(pcoi);
|
|
}
|
|
pcoi->cLocks--;
|
|
if (pcoi->cLocks == 0 && pcoi->state & ST_FREE_CONV_RES_NOW) {
|
|
FreeConversationResources(pcoi);
|
|
}
|
|
} else {
|
|
if (wCmd == EC_ENABLEONE) {
|
|
wCmd = EC_ENABLEONEOFALL;
|
|
}
|
|
switch (wCmd) {
|
|
case EC_ENABLEONEOFALL:
|
|
pcii->ConvStartupState = ST_BLOCKNEXT | ST_BLOCKALLNEXT;
|
|
break;
|
|
|
|
case EC_DISABLE:
|
|
pcii->ConvStartupState = ST_BLOCKED;
|
|
break;
|
|
|
|
case EC_ENABLEALL:
|
|
pcii->ConvStartupState = 0;
|
|
break;
|
|
}
|
|
ees.pfRet = &fRet;
|
|
ees.wCmd = (WORD)wCmd;
|
|
switch (wCmd) {
|
|
case EC_ENABLEALL:
|
|
ees.wCmd2 = EC_CHECKQUEUE;
|
|
break;
|
|
|
|
case EC_ENABLEONEOFALL:
|
|
ees.wCmd2 = EC_CHECKQUEUEONCE;
|
|
break;
|
|
|
|
default:
|
|
ees.wCmd2 = 0;
|
|
}
|
|
EnumChildWindows(pcii->hwndMother, (WNDENUMPROC)EnableEnumProc,
|
|
(LPARAM)&ees);
|
|
}
|
|
|
|
Exit:
|
|
LeaveDDECrit;
|
|
return (fRet);
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************\
|
|
* SetEnableState
|
|
*
|
|
* Description:
|
|
* Sets the given conversation's enable state based on the EC_ flag
|
|
* given.
|
|
*
|
|
* Returns: fSuccess/fProcessed.
|
|
*
|
|
* History:
|
|
* 11-19-91 sanfords Created.
|
|
\***************************************************************************/
|
|
BOOL SetEnableState(
|
|
PCONV_INFO pcoi,
|
|
UINT wCmd)
|
|
{
|
|
BOOL fRet = TRUE;
|
|
|
|
switch (wCmd) {
|
|
case EC_CHECKQUEUEONCE:
|
|
case EC_CHECKQUEUE:
|
|
fRet = CheckForQueuedMessages(pcoi);
|
|
break;
|
|
|
|
case EC_QUERYWAITING:
|
|
fRet = !(pcoi->dmqOut == NULL ||
|
|
(pcoi->dmqOut->next == NULL &&
|
|
GetClientInfo()->CI_flags & CI_PROCESSING_QUEUE));
|
|
break;
|
|
|
|
case EC_DISABLE:
|
|
pcoi->state |= ST_BLOCKED;
|
|
pcoi->state &= ~(ST_BLOCKNEXT | ST_BLOCKALLNEXT);
|
|
break;
|
|
|
|
case EC_ENABLEONE:
|
|
pcoi->state &= ~ST_BLOCKED;
|
|
pcoi->state |= ST_BLOCKNEXT;
|
|
break;
|
|
|
|
case EC_ENABLEONEOFALL:
|
|
pcoi->state &= ~ST_BLOCKED;
|
|
pcoi->state |= (ST_BLOCKNEXT | ST_BLOCKALLNEXT);
|
|
break;
|
|
|
|
case EC_ENABLEALL:
|
|
pcoi->state &= ~(ST_BLOCKED | ST_BLOCKNEXT | ST_BLOCKALLNEXT);
|
|
break;
|
|
|
|
default:
|
|
return(FALSE);
|
|
}
|
|
return (fRet);
|
|
}
|
|
|
|
|
|
|
|
|
|
/***************************************************************************\
|
|
* _ClientGetDDEHookData
|
|
*
|
|
* Description:
|
|
* Callback from server to extract data from lParam and place it into
|
|
* the pdmhd for use by DDESPY apps. This does a very similar thing
|
|
* to the CopyDDEDataIn/Out apis but this only grabs a limited amount
|
|
* of the data suitable for posting to the DDESPY app(s). This should
|
|
* be merged with the Copy APIs eventually.
|
|
*
|
|
* History:
|
|
* 12-16-91 sanfords Created.
|
|
\***************************************************************************/
|
|
DWORD _ClientGetDDEHookData(
|
|
UINT message,
|
|
LPARAM lParam,
|
|
PDDEML_MSG_HOOK_DATA pdmhd)
|
|
{
|
|
PBYTE pb;
|
|
HANDLE hDDE;
|
|
|
|
UnpackDDElParam(message, lParam, &pdmhd->uiLo, &pdmhd->uiHi);
|
|
switch (message) {
|
|
case WM_DDE_DATA:
|
|
case WM_DDE_POKE:
|
|
case WM_DDE_ADVISE:
|
|
hDDE = (HANDLE)pdmhd->uiLo;
|
|
break;
|
|
|
|
case WM_DDE_EXECUTE:
|
|
hDDE = (HANDLE)pdmhd->uiHi;
|
|
break;
|
|
|
|
case WM_DDE_ACK:
|
|
case WM_DDE_REQUEST:
|
|
case WM_DDE_UNADVISE:
|
|
case WM_DDE_TERMINATE:
|
|
pdmhd->cbData = 0;
|
|
return (1);
|
|
}
|
|
|
|
pdmhd->cbData = (DWORD)UserGlobalSize(hDDE);
|
|
if (pdmhd->cbData) {
|
|
USERGLOBALLOCK(hDDE, pb);
|
|
if (pb == NULL) {
|
|
pdmhd->cbData = 0;
|
|
} else {
|
|
RtlCopyMemory(&pdmhd->Data, pb, min(pdmhd->cbData,
|
|
sizeof(DDEML_MSG_HOOK_DATA) -
|
|
FIELD_OFFSET(DDEML_MSG_HOOK_DATA, Data)));
|
|
USERGLOBALUNLOCK(hDDE);
|
|
}
|
|
}
|
|
return (1);
|
|
}
|