📄 dde.c
字号:
/* ================================================================
*
* Global <=> Data handle management
*
* ================================================================ */
/* Note: we use a DDEDATA, but layout of DDEDATA, DDEADVISE and DDEPOKE structures is similar:
* offset size
* (bytes) (bits) comment
* 0 16 bit fields for options (release, ackreq, response...)
* 2 16 clipboard format
* 4 ? data to be used
*/
HDDEDATA WDML_Global2DataHandle(HGLOBAL hMem, WINE_DDEHEAD* p)
{
DDEDATA* pDd;
HDDEDATA ret = 0;
DWORD size;
if (hMem)
{
pDd = GlobalLock(hMem);
size = GlobalSize(hMem) - sizeof(WINE_DDEHEAD);
if (pDd)
{
if (p) memcpy(p, pDd, sizeof(WINE_DDEHEAD));
switch (pDd->cfFormat)
{
default:
FIXME("Unsupported format (%04x) for data %p, passing raw information\n",
pDd->cfFormat, hMem);
/* fall thru */
case 0:
case CF_TEXT:
ret = DdeCreateDataHandle(0, pDd->Value, size, 0, 0, pDd->cfFormat, 0);
break;
case CF_BITMAP:
if (size >= sizeof(BITMAP))
{
BITMAP* bmp = (BITMAP*)pDd->Value;
int count = bmp->bmWidthBytes * bmp->bmHeight * bmp->bmPlanes;
if (size >= sizeof(BITMAP) + count)
{
HBITMAP hbmp;
if ((hbmp = CreateBitmap(bmp->bmWidth, bmp->bmHeight,
bmp->bmPlanes, bmp->bmBitsPixel,
pDd->Value + sizeof(BITMAP))))
{
ret = DdeCreateDataHandle(0, (LPBYTE)&hbmp, sizeof(hbmp),
0, 0, CF_BITMAP, 0);
}
else ERR("Can't create bmp\n");
}
else
{
ERR("Wrong count: %lu / %d\n", size, sizeof(BITMAP) + count);
}
} else ERR("No bitmap header\n");
break;
}
GlobalUnlock(hMem);
}
}
return ret;
}
/******************************************************************
* WDML_DataHandle2Global
*
*
*/
HGLOBAL WDML_DataHandle2Global(HDDEDATA hDdeData, BOOL fResponse, BOOL fRelease,
BOOL fDeferUpd, BOOL fAckReq)
{
DDE_DATAHANDLE_HEAD* pDdh;
DWORD dwSize;
HGLOBAL hMem = 0;
dwSize = GlobalSize((HGLOBAL)hDdeData) - sizeof(DDE_DATAHANDLE_HEAD);
pDdh = (DDE_DATAHANDLE_HEAD*)GlobalLock((HGLOBAL)hDdeData);
if (dwSize && pDdh)
{
WINE_DDEHEAD* wdh = NULL;
switch (pDdh->cfFormat)
{
default:
FIXME("Unsupported format (%04x) for data %p, passing raw information\n",
pDdh->cfFormat, hDdeData);
/* fall thru */
case 0:
case CF_TEXT:
hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, sizeof(WINE_DDEHEAD) + dwSize);
if (hMem && (wdh = GlobalLock(hMem)))
{
memcpy(wdh + 1, pDdh + 1, dwSize);
}
break;
case CF_BITMAP:
if (dwSize >= sizeof(HBITMAP))
{
BITMAP bmp;
DWORD count;
HBITMAP hbmp = *(HBITMAP*)(pDdh + 1);
if (GetObjectW(hbmp, sizeof(bmp), &bmp))
{
count = bmp.bmWidthBytes * bmp.bmHeight;
hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE,
sizeof(WINE_DDEHEAD) + sizeof(bmp) + count);
if (hMem && (wdh = GlobalLock(hMem)))
{
memcpy(wdh + 1, &bmp, sizeof(bmp));
GetBitmapBits(hbmp, count, ((char*)(wdh + 1)) + sizeof(bmp));
}
}
}
break;
}
if (wdh)
{
wdh->unused = 0;
wdh->fResponse = fResponse;
wdh->fRelease = fRelease;
wdh->fDeferUpd = fDeferUpd;
wdh->fAckReq = fAckReq;
wdh->cfFormat = pDdh->cfFormat;
GlobalUnlock(hMem);
}
GlobalUnlock((HGLOBAL)hDdeData);
}
return hMem;
}
/* ================================================================
*
* Server management
*
* ================================================================ */
/******************************************************************
* WDML_AddServer
*
*
*/
WDML_SERVER* WDML_AddServer(WDML_INSTANCE* pInstance, HSZ hszService, HSZ hszTopic)
{
static const WCHAR fmtW[] = {'%','s','(','0','x','%','0','8','l','x',')',0};
WDML_SERVER* pServer;
WCHAR buf1[256];
WCHAR buf2[256];
pServer = (WDML_SERVER*)HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_SERVER));
if (pServer == NULL) return NULL;
pServer->hszService = hszService;
WDML_IncHSZ(pInstance, hszService);
DdeQueryStringW(pInstance->instanceID, hszService, buf1, 256, CP_WINUNICODE);
snprintfW(buf2, 256, fmtW, buf1, GetCurrentProcessId());
pServer->hszServiceSpec = DdeCreateStringHandleW(pInstance->instanceID, buf2, CP_WINUNICODE);
pServer->atomService = WDML_MakeAtomFromHsz(pServer->hszService);
pServer->atomServiceSpec = WDML_MakeAtomFromHsz(pServer->hszServiceSpec);
pServer->filterOn = TRUE;
pServer->next = pInstance->servers;
pInstance->servers = pServer;
return pServer;
}
/******************************************************************
* WDML_RemoveServer
*
*
*/
void WDML_RemoveServer(WDML_INSTANCE* pInstance, HSZ hszService, HSZ hszTopic)
{
WDML_SERVER* pPrev = NULL;
WDML_SERVER* pServer = NULL;
WDML_CONV* pConv;
WDML_CONV* pConvNext;
pServer = pInstance->servers;
while (pServer != NULL)
{
if (DdeCmpStringHandles(pServer->hszService, hszService) == 0)
{
WDML_BroadcastDDEWindows(WDML_szEventClass, WM_WDML_UNREGISTER,
pServer->atomService, pServer->atomServiceSpec);
/* terminate all conversations for given topic */
for (pConv = pInstance->convs[WDML_SERVER_SIDE]; pConv != NULL; pConv = pConvNext)
{
pConvNext = pConv->next;
if (DdeCmpStringHandles(pConv->hszService, hszService) == 0)
{
WDML_RemoveConv(pConv, WDML_SERVER_SIDE);
/* don't care about return code (whether client window is present or not) */
PostMessageW(pConv->hwndClient, WM_DDE_TERMINATE, (WPARAM)pConv->hwndServer, 0);
}
}
if (pServer == pInstance->servers)
{
pInstance->servers = pServer->next;
}
else
{
pPrev->next = pServer->next;
}
DestroyWindow(pServer->hwndServer);
WDML_DecHSZ(pInstance, pServer->hszServiceSpec);
WDML_DecHSZ(pInstance, pServer->hszService);
GlobalDeleteAtom(pServer->atomService);
GlobalDeleteAtom(pServer->atomServiceSpec);
HeapFree(GetProcessHeap(), 0, pServer);
break;
}
pPrev = pServer;
pServer = pServer->next;
}
}
/*****************************************************************************
* WDML_FindServer
*
* generic routine to return a pointer to the relevant ServiceNode
* for a given service name, or NULL if the entry does not exist
*
*/
WDML_SERVER* WDML_FindServer(WDML_INSTANCE* pInstance, HSZ hszService, HSZ hszTopic)
{
WDML_SERVER* pServer;
for (pServer = pInstance->servers; pServer != NULL; pServer = pServer->next)
{
if (hszService == pServer->hszService)
{
return pServer;
}
}
TRACE("Service name missing\n");
return NULL;
}
/* ================================================================
*
* Conversation management
*
* ================================================================ */
/******************************************************************
* WDML_AddConv
*
*
*/
WDML_CONV* WDML_AddConv(WDML_INSTANCE* pInstance, WDML_SIDE side,
HSZ hszService, HSZ hszTopic, HWND hwndClient, HWND hwndServer)
{
WDML_CONV* pConv;
/* no converstation yet, add it */
pConv = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_CONV));
if (!pConv) return NULL;
pConv->instance = pInstance;
WDML_IncHSZ(pInstance, pConv->hszService = hszService);
WDML_IncHSZ(pInstance, pConv->hszTopic = hszTopic);
pConv->magic = WDML_CONV_MAGIC;
pConv->hwndServer = hwndServer;
pConv->hwndClient = hwndClient;
pConv->transactions = NULL;
pConv->hUser = 0;
pConv->wStatus = (side == WDML_CLIENT_SIDE) ? ST_CLIENT : 0L;
/* check if both side of the conversation are of the same instance */
if (GetWindowThreadProcessId(hwndClient, NULL) == GetWindowThreadProcessId(hwndServer, NULL) &&
WDML_GetInstanceFromWnd(hwndClient) == WDML_GetInstanceFromWnd(hwndServer))
{
pConv->wStatus |= ST_ISSELF;
}
pConv->wConvst = XST_NULL;
pConv->next = pInstance->convs[side];
pInstance->convs[side] = pConv;
return pConv;
}
/******************************************************************
* WDML_FindConv
*
*
*/
WDML_CONV* WDML_FindConv(WDML_INSTANCE* pInstance, WDML_SIDE side,
HSZ hszService, HSZ hszTopic)
{
WDML_CONV* pCurrent = NULL;
for (pCurrent = pInstance->convs[side]; pCurrent != NULL; pCurrent = pCurrent->next)
{
if (DdeCmpStringHandles(pCurrent->hszService, hszService) == 0 &&
DdeCmpStringHandles(pCurrent->hszTopic, hszTopic) == 0)
{
return pCurrent;
}
}
return NULL;
}
/******************************************************************
* WDML_RemoveConv
*
*
*/
void WDML_RemoveConv(WDML_CONV* pRef, WDML_SIDE side)
{
WDML_CONV* pPrev = NULL;
WDML_CONV* pCurrent;
WDML_XACT* pXAct;
WDML_XACT* pXActNext;
HWND hWnd;
if (!pRef)
return;
/* remove any pending transaction */
for (pXAct = pRef->transactions; pXAct != NULL; pXAct = pXActNext)
{
pXActNext = pXAct->next;
WDML_FreeTransaction(pRef->instance, pXAct, TRUE);
}
WDML_RemoveAllLinks(pRef->instance, pRef, side);
/* FIXME: should we keep the window around ? it seems so (at least on client side
* to let QueryConvInfo work after conv termination, but also to implement
* DdeReconnect...
*/
/* destroy conversation window, but first remove pConv from hWnd.
* this would help the wndProc do appropriate handling upon a WM_DESTROY message
*/
hWnd = (side == WDML_CLIENT_SIDE) ? pRef->hwndClient : pRef->hwndServer;
SetWindowLongPtrW(hWnd, GWL_WDML_CONVERSATION, 0);
DestroyWindow((side == WDML_CLIENT_SIDE) ? pRef->hwndClient : pRef->hwndServer);
WDML_DecHSZ(pRef->instance, pRef->hszService);
WDML_DecHSZ(pRef->instance, pRef->hszTopic);
for (pCurrent = pRef->instance->convs[side]; pCurrent != NULL; pCurrent = (pPrev = pCurrent)->next)
{
if (pCurrent == pRef)
{
if (pCurrent == pRef->instance->convs[side])
{
pRef->instance->convs[side] = pCurrent->next;
}
else
{
pPrev->next = pCurrent->next;
}
pCurrent->magic = 0;
HeapFree(GetProcessHeap(), 0, pCurrent);
break;
}
}
}
/******************************************************************
* WDML_EnableCallback
*/
static BOOL WDML_EnableCallback(WDML_CONV *pConv, UINT wCmd)
{
if (wCmd == EC_DISABLE)
{
FIXME("EC_DISABLE is not implemented\n");
return TRUE;
}
if (wCmd == EC_QUERYWAITING)
return pConv->transactions ? TRUE : FALSE;
if (wCmd != EC_ENABLEALL && wCmd != EC_ENABLEONE)
{
FIXME("Unknown command code %04x\n", wCmd);
return FALSE;
}
while (pConv->transactions)
{
WDML_XACT *pXAct = pConv->transactions;
WDML_UnQueueTransaction(pConv, pXAct);
if (pConv->wStatus & ST_CLIENT)
{
/*WDML_ClientHandle(pConv, pXAct);*/
FIXME("Client delayed transaction queue handling is not supported\n");
}
else
WDML_ServerHandle(pConv, pXAct);
WDML_FreeTransaction(pConv->instance, pXAct, TRUE);
if (wCmd == EC_ENABLEONE) break;
}
return TRUE;
}
/*****************************************************************
* DdeEnableCallback (USER32.@)
*/
BOOL WINAPI DdeEnableCallback(DWORD idInst, HCONV hConv, UINT wCmd)
{
BOOL ret = FALSE;
WDML_CONV *pConv;
TRACE("(%ld, %p, %04x)\n", idInst, hConv, wCmd);
EnterCriticalSection(&WDML_CritSect);
pConv = WDML_GetConv(hConv, TRUE);
if (pConv && pConv->instance->instanceID == idInst)
ret = WDML_EnableCallback(pConv, wCmd);
LeaveCriticalSection(&WDML_CritSect);
return ret;
}
/******************************************************************
* WDML_GetConv
*
*
*/
WDML_CONV* WDML_GetConv(HCONV hConv, BOOL checkConnected)
{
WDML_CONV* pConv = (WDML_CONV*)hConv;
/* FIXME: should do better checking */
if (pConv == NULL || pConv->magic != WDML_CONV_MAGIC) return NULL;
if (checkConnected && !(pConv->wStatus & ST_CONNECTED))
{
FIXME("found conv but ain't connected\n");
return NULL;
}
if (!pConv->instance || GetCurrentThreadId() != pConv->instance->threadID)
{
FIXME("wrong thread ID\n");
return NULL;
}
return pConv;
}
/******************************************************************
* WDML_GetConvFromWnd
*
*
*/
WDML_CONV* WDML_GetConvFromWnd(HWND hWnd)
{
return (WDML_CONV*)GetWindowLongPtrW(hWnd, GWL_WDML_CONVERSATION);
}
/******************************************************************
* WDML_PostAck
*
*
*/
BOOL WDML_PostAck(WDML_CONV* pConv, WDML_SIDE side, WORD appRetCode,
BOOL fBusy, BOOL fAck, UINT pmt, LPARAM lParam, UINT oldMsg)
{
DDEACK ddeAck;
HWND from, to;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -