📄 ddeclient.c
字号:
/*
* DDEML library
*
* Copyright 1997 Alexandre Julliard
* Copyright 1997 Len White
* Copyright 1999 Keith Matthews
* Copyright 2000 Corel
* Copyright 2001 Eric Pouech
* Copyright 2004, 2005 Dmitry Timoshkov
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <user32.h>
#include <wine/debug.h>
WINE_DEFAULT_DEBUG_CHANNEL(ddeml);
static LRESULT CALLBACK WDML_ClientProc(HWND, UINT, WPARAM, LPARAM); /* only for one client, not conv list */
const char WDML_szClientConvClassA[] = "DdeClientAnsi";
const WCHAR WDML_szClientConvClassW[] = {'D','d','e','C','l','i','e','n','t','U','n','i','c','o','d','e',0};
/******************************************************************************
* DdeConnectList [USER32.@] Establishes conversation with DDE servers
*
* PARAMS
* idInst [I] Instance identifier
* hszService [I] Handle to service name string
* hszTopic [I] Handle to topic name string
* hConvList [I] Handle to conversation list
* pCC [I] Pointer to structure with context data
*
* RETURNS
* Success: Handle to new conversation list
* Failure: 0
*/
HCONVLIST WINAPI DdeConnectList(DWORD idInst, HSZ hszService, HSZ hszTopic,
HCONVLIST hConvList, PCONVCONTEXT pCC)
{
FIXME("(%ld,%p,%p,%p,%p): stub\n", idInst, hszService, hszTopic, hConvList, pCC);
return (HCONVLIST)1;
}
/*****************************************************************
* DdeQueryNextServer [USER32.@]
*/
HCONV WINAPI DdeQueryNextServer(HCONVLIST hConvList, HCONV hConvPrev)
{
FIXME("(%p,%p): stub\n", hConvList, hConvPrev);
return 0;
}
/******************************************************************************
* DdeDisconnectList [USER32.@] Destroys list and terminates conversations
*
*
* PARAMS
* hConvList [I] Handle to conversation list
*
* RETURNS
* Success: TRUE
* Failure: FALSE
*/
BOOL WINAPI DdeDisconnectList(HCONVLIST hConvList)
{
FIXME("(%p): stub\n", hConvList);
return TRUE;
}
/*****************************************************************
* DdeConnect (USER32.@)
*/
HCONV WINAPI DdeConnect(DWORD idInst, HSZ hszService, HSZ hszTopic,
PCONVCONTEXT pCC)
{
HWND hwndClient;
WDML_INSTANCE* pInstance;
WDML_CONV* pConv = NULL;
ATOM aSrv = 0, aTpc = 0;
TRACE("(0x%lx,%p,%p,%p)\n", idInst, hszService, hszTopic, pCC);
EnterCriticalSection(&WDML_CritSect);
pInstance = WDML_GetInstance(idInst);
if (!pInstance)
{
goto theEnd;
}
/* make sure this conv is never created */
pConv = WDML_FindConv(pInstance, WDML_CLIENT_SIDE, hszService, hszTopic);
if (pConv != NULL)
{
ERR("This Conv already exists: (%p)\n", pConv);
goto theEnd;
}
/* we need to establish a conversation with
server, so create a window for it */
if (pInstance->unicode)
{
WNDCLASSEXW wndclass;
wndclass.cbSize = sizeof(wndclass);
wndclass.style = 0;
wndclass.lpfnWndProc = WDML_ClientProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 2 * sizeof(ULONG_PTR);
wndclass.hInstance = 0;
wndclass.hIcon = 0;
wndclass.hCursor = 0;
wndclass.hbrBackground = 0;
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = WDML_szClientConvClassW;
wndclass.hIconSm = 0;
RegisterClassExW(&wndclass);
hwndClient = CreateWindowW(WDML_szClientConvClassW, NULL, WS_POPUP, 0, 0, 0, 0, 0, 0, 0, 0);
}
else
{
WNDCLASSEXA wndclass;
wndclass.cbSize = sizeof(wndclass);
wndclass.style = 0;
wndclass.lpfnWndProc = WDML_ClientProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 2 * sizeof(ULONG_PTR);
wndclass.hInstance = 0;
wndclass.hIcon = 0;
wndclass.hCursor = 0;
wndclass.hbrBackground = 0;
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = WDML_szClientConvClassA;
wndclass.hIconSm = 0;
RegisterClassExA(&wndclass);
hwndClient = CreateWindowA(WDML_szClientConvClassA, NULL, WS_POPUP, 0, 0, 0, 0, 0, 0, 0, 0);
}
SetWindowLongPtrW(hwndClient, GWL_WDML_INSTANCE, (ULONG_PTR)pInstance);
if (hszService)
{
aSrv = WDML_MakeAtomFromHsz(hszService);
if (!aSrv) goto theEnd;
}
if (hszTopic)
{
aTpc = WDML_MakeAtomFromHsz(hszTopic);
if (!aTpc) goto theEnd;
}
LeaveCriticalSection(&WDML_CritSect);
/* note: sent messages shall not use packing */
SendMessageTimeoutW( HWND_BROADCAST, WM_DDE_INITIATE, (WPARAM)hwndClient, MAKELPARAM(aSrv, aTpc),
SMTO_ABORTIFHUNG, 2000, NULL );
EnterCriticalSection(&WDML_CritSect);
pInstance = WDML_GetInstance(idInst);
if (!pInstance)
{
goto theEnd;
}
/* At this point, Client WM_DDE_ACK should have saved hwndServer
for this instance id and hwndClient if server responds.
So get HCONV and return it. And add it to conv list */
pConv = WDML_GetConvFromWnd(hwndClient);
if (pConv == NULL || pConv->hwndServer == 0)
{
WARN("Done with INITIATE, but no Server window available\n");
pConv = NULL;
goto theEnd;
}
TRACE("Connected to Server window (%p)\n", pConv->hwndServer);
pConv->wConvst = XST_CONNECTED;
/* finish init of pConv */
if (pCC != NULL)
{
pConv->convContext = *pCC;
}
else
{
memset(&pConv->convContext, 0, sizeof(pConv->convContext));
pConv->convContext.cb = sizeof(pConv->convContext);
pConv->convContext.iCodePage = (pInstance->unicode) ? CP_WINUNICODE : CP_WINANSI;
}
theEnd:
LeaveCriticalSection(&WDML_CritSect);
if (aSrv) GlobalDeleteAtom(aSrv);
if (aTpc) GlobalDeleteAtom(aTpc);
return (HCONV)pConv;
}
/*****************************************************************
* DdeReconnect (DDEML.37)
* DdeReconnect (USER32.@)
*/
HCONV WINAPI DdeReconnect(HCONV hConv)
{
WDML_CONV* pConv;
WDML_CONV* pNewConv = NULL;
ATOM aSrv = 0, aTpc = 0;
TRACE("(%p)\n", hConv);
EnterCriticalSection(&WDML_CritSect);
pConv = WDML_GetConv(hConv, FALSE);
if (pConv != NULL && (pConv->wStatus & ST_CLIENT))
{
BOOL ret;
/* to reestablist a connection, we have to make sure that:
* 1/ pConv is the converstation attached to the client window (it wouldn't be
* if a call to DdeReconnect would have already been done...)
* FIXME: is this really an error ???
* 2/ the pConv conversation had really been deconnected
*/
if (pConv == WDML_GetConvFromWnd(pConv->hwndClient) &&
(pConv->wStatus & ST_TERMINATED) && !(pConv->wStatus & ST_CONNECTED))
{
HWND hwndClient = pConv->hwndClient;
HWND hwndServer = pConv->hwndServer;
ATOM aSrv, aTpc;
SetWindowLongPtrW(pConv->hwndClient, GWL_WDML_CONVERSATION, 0);
aSrv = WDML_MakeAtomFromHsz(pConv->hszService);
aTpc = WDML_MakeAtomFromHsz(pConv->hszTopic);
if (!aSrv || !aTpc) goto theEnd;
LeaveCriticalSection(&WDML_CritSect);
/* note: sent messages shall not use packing */
ret = SendMessageW(hwndServer, WM_DDE_INITIATE, (WPARAM)hwndClient,
MAKELPARAM(aSrv, aTpc));
EnterCriticalSection(&WDML_CritSect);
pConv = WDML_GetConv(hConv, FALSE);
if (pConv == NULL)
{
FIXME("Should fail reconnection\n");
goto theEnd;
}
if (ret && (pNewConv = WDML_GetConvFromWnd(pConv->hwndClient)) != NULL)
{
/* re-establish all links... */
WDML_LINK* pLink;
for (pLink = pConv->instance->links[WDML_CLIENT_SIDE]; pLink; pLink = pLink->next)
{
if (pLink->hConv == hConv)
{
/* try to reestablish the links... */
DdeClientTransaction(NULL, 0, (HCONV)pNewConv, pLink->hszItem, pLink->uFmt,
pLink->transactionType, 1000, NULL);
}
}
}
else
{
/* reset the conversation as it was */
SetWindowLongPtrW(pConv->hwndClient, GWL_WDML_CONVERSATION, (ULONG_PTR)pConv);
}
}
}
theEnd:
LeaveCriticalSection(&WDML_CritSect);
if (aSrv) GlobalDeleteAtom(aSrv);
if (aTpc) GlobalDeleteAtom(aTpc);
return (HCONV)pNewConv;
}
/******************************************************************
* WDML_ClientQueueAdvise
*
* Creates and queue an WM_DDE_ADVISE transaction
*/
static WDML_XACT* WDML_ClientQueueAdvise(WDML_CONV* pConv, UINT wType, UINT wFmt, HSZ hszItem)
{
DDEADVISE* pDdeAdvise;
WDML_XACT* pXAct;
ATOM atom;
TRACE("XTYP_ADVSTART (with%s data) transaction\n", (wType & XTYPF_NODATA) ? "out" : "");
atom = WDML_MakeAtomFromHsz(hszItem);
if (!atom) return NULL;
pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_ADVISE, wFmt, hszItem);
if (!pXAct)
{
GlobalDeleteAtom(atom);
return NULL;
}
pXAct->wType = wType & ~0x0F;
pXAct->hMem = GlobalAlloc(GHND | GMEM_DDESHARE, sizeof(DDEADVISE));
/* FIXME: hMem is unfreed for now... should be deleted in server */
/* pack DdeAdvise */
pDdeAdvise = (DDEADVISE*)GlobalLock(pXAct->hMem);
pDdeAdvise->fAckReq = (wType & XTYPF_ACKREQ) ? TRUE : FALSE;
pDdeAdvise->fDeferUpd = (wType & XTYPF_NODATA) ? TRUE : FALSE;
pDdeAdvise->cfFormat = wFmt;
GlobalUnlock(pXAct->hMem);
pXAct->lParam = PackDDElParam(WM_DDE_ADVISE, (UINT_PTR)pXAct->hMem, atom);
return pXAct;
}
/******************************************************************
* WDML_HandleAdviseReply
*
* handles the reply to an advise request
*/
static WDML_QUEUE_STATE WDML_HandleAdviseReply(WDML_CONV* pConv, MSG* msg, WDML_XACT* pXAct, DWORD *ack)
{
DDEACK ddeAck;
UINT_PTR uiLo, uiHi;
HSZ hsz;
if (msg->message != WM_DDE_ACK || (HWND)msg->wParam != pConv->hwndServer)
{
return WDML_QS_PASS;
}
UnpackDDElParam(WM_DDE_ACK, msg->lParam, &uiLo, &uiHi);
hsz = WDML_MakeHszFromAtom(pConv->instance, uiHi);
if (DdeCmpStringHandles(hsz, pXAct->hszItem) != 0)
return WDML_QS_PASS;
GlobalDeleteAtom(uiHi);
FreeDDElParam(WM_DDE_ACK, msg->lParam);
if (ack) *ack = uiLo;
WDML_ExtractAck(uiLo, &ddeAck);
if (ddeAck.fAck)
{
WDML_LINK* pLink;
/* billx: first to see if the link is already created. */
pLink = WDML_FindLink(pConv->instance, (HCONV)pConv, WDML_CLIENT_SIDE,
pXAct->hszItem, TRUE, pXAct->wFmt);
if (pLink != NULL)
{
/* we found a link, and only need to modify it in case it changes */
pLink->transactionType = pXAct->wType;
}
else
{
WDML_AddLink(pConv->instance, (HCONV)pConv, WDML_CLIENT_SIDE,
pXAct->wType, pXAct->hszItem, pXAct->wFmt);
}
pXAct->hDdeData = (HDDEDATA)1;
}
else
{
TRACE("Returning FALSE on XTYP_ADVSTART - fAck was FALSE\n");
GlobalFree(pXAct->hMem);
pXAct->hDdeData = NULL;
}
return WDML_QS_HANDLED;
}
/******************************************************************
* WDML_ClientQueueUnadvise
*
* queues an unadvise transaction
*/
static WDML_XACT* WDML_ClientQueueUnadvise(WDML_CONV* pConv, UINT wFmt, HSZ hszItem)
{
WDML_XACT* pXAct;
ATOM atom;
TRACE("XTYP_ADVSTOP transaction\n");
atom = WDML_MakeAtomFromHsz(hszItem);
if (!atom) return NULL;
pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_UNADVISE, wFmt, hszItem);
if (!pXAct)
{
GlobalDeleteAtom(atom);
return NULL;
}
/* end advise loop: post WM_DDE_UNADVISE to server to terminate link
* on the specified item.
*/
pXAct->lParam = PackDDElParam(WM_DDE_UNADVISE, wFmt, atom);
return pXAct;
}
/******************************************************************
* WDML_HandleUnadviseReply
*
*
*/
static WDML_QUEUE_STATE WDML_HandleUnadviseReply(WDML_CONV* pConv, MSG* msg, WDML_XACT* pXAct, DWORD *ack)
{
DDEACK ddeAck;
UINT_PTR uiLo, uiHi;
HSZ hsz;
if (msg->message != WM_DDE_ACK || (HWND)msg->wParam != pConv->hwndServer)
{
return WDML_QS_PASS;
}
UnpackDDElParam(WM_DDE_ACK, msg->lParam, &uiLo, &uiHi);
hsz = WDML_MakeHszFromAtom(pConv->instance, uiHi);
if (DdeCmpStringHandles(hsz, pXAct->hszItem) != 0)
return WDML_QS_PASS;
FreeDDElParam(WM_DDE_ACK, msg->lParam);
GlobalDeleteAtom(uiHi);
if (ack) *ack = uiLo;
WDML_ExtractAck(uiLo, &ddeAck);
TRACE("WM_DDE_ACK received while waiting for a timeout\n");
if (!ddeAck.fAck)
{
TRACE("Returning FALSE on XTYP_ADVSTOP - fAck was FALSE\n");
pXAct->hDdeData = NULL;
}
else
{
/* billx: remove the link */
WDML_RemoveLink(pConv->instance, (HCONV)pConv, WDML_CLIENT_SIDE,
pXAct->hszItem, pXAct->wFmt);
pXAct->hDdeData = (HDDEDATA)1;
}
return WDML_QS_HANDLED;
}
/******************************************************************
* WDML_ClientQueueRequest
*
*
*/
static WDML_XACT* WDML_ClientQueueRequest(WDML_CONV* pConv, UINT wFmt, HSZ hszItem)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -