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.
1322 lines
34 KiB
1322 lines
34 KiB
|
|
/****************************** Module Header ******************************\
|
|
* Module Name: DDE.C (Extensible Compound Documents -DDE)
|
|
*
|
|
* Copyright (c) 1985 - 1991 Microsoft Corporation
|
|
*
|
|
* PURPOSE: Handles all API routines for the dde sub-dll of the ole dll.
|
|
*
|
|
* History:
|
|
* Raor,Srinik (../../90,91) Designed and coded
|
|
* curts created portable version for WIN16/32
|
|
*
|
|
\***************************************************************************/
|
|
|
|
#include <windows.h>
|
|
#include "dde.h"
|
|
#include "dll.h"
|
|
#include "strsafe.h"
|
|
|
|
/* #define GRAPHBUG */
|
|
|
|
|
|
// ### may not need seperate wndproc for system topic!
|
|
HANDLE GetDDEDataHandle (DDEDATA FAR *, UINT, HANDLE);
|
|
|
|
extern ATOM aSystem;
|
|
extern ATOM aOle;
|
|
extern HANDLE hInstDLL;
|
|
|
|
|
|
// DocWndProc: Window procedure used to document DDE conversations
|
|
|
|
LRESULT FAR PASCAL DocWndProc(
|
|
HWND hwnd,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
){
|
|
PEDIT_DDE pedit = NULL;
|
|
LPOBJECT_LE lpobj = NULL;
|
|
|
|
|
|
|
|
Puts("DocWndProc");
|
|
|
|
if (lpobj = (LPOBJECT_LE) GetWindowLongPtr (hwnd, 0))
|
|
{
|
|
pedit = lpobj->pDocEdit;
|
|
}
|
|
else
|
|
{
|
|
// Can't cope so just pass this message on
|
|
DEBUG_OUT ("SYS: doc conv block missing",0);
|
|
return DefWindowProc (hwnd, message, wParam, lParam);
|
|
}
|
|
|
|
switch (message){
|
|
|
|
case WM_DDE_ACK:
|
|
DEBUG_OUT ("WM_DDE_ACK ", 0);
|
|
if (pedit->bTerminating){
|
|
// ### this error recovery may not be correct.
|
|
DEBUG_OUT ("No action due to termination process",0)
|
|
break;
|
|
}
|
|
|
|
switch(pedit->awaitAck){
|
|
|
|
case AA_INITIATE:
|
|
HandleAckInitMsg (pedit, (HWND)wParam);
|
|
if (LOWORD(lParam))
|
|
GlobalDeleteAtom (LOWORD(lParam));
|
|
if (HIWORD(lParam))
|
|
GlobalDeleteAtom (HIWORD(lParam));
|
|
break;
|
|
|
|
case AA_REQUEST:
|
|
Puts("Request");
|
|
HandleAck (lpobj, pedit, wParam, lParam);
|
|
break;
|
|
|
|
case AA_UNADVISE:
|
|
Puts("Unadvise");
|
|
HandleAck (lpobj, pedit, wParam, lParam);
|
|
break;
|
|
|
|
case AA_EXECUTE:
|
|
Puts("Execute");
|
|
HandleAck (lpobj, pedit, wParam, lParam);
|
|
break;
|
|
|
|
case AA_ADVISE:
|
|
Puts("Advise");
|
|
HandleAck (lpobj, pedit, wParam, lParam);
|
|
break;
|
|
|
|
case AA_POKE:
|
|
|
|
// freeing pokedata is done in handleack
|
|
Puts("Poke");
|
|
HandleAck (lpobj, pedit, wParam, lParam);
|
|
break;
|
|
|
|
default:
|
|
DEBUG_OUT ("received ACK We don't know how to handle ",0)
|
|
break;
|
|
|
|
} // end of switch
|
|
break;
|
|
|
|
case WM_TIMER:
|
|
HandleTimerMsg (lpobj, pedit);
|
|
break;
|
|
|
|
case WM_DDE_DATA:
|
|
DEBUG_OUT ("WM_DDE_DATA",0);
|
|
HandleDataMsg (lpobj, GET_WM_DDE_DATA_HDATA(wParam,lParam),
|
|
GET_WM_DDE_DATA_ITEM(wParam,lParam));
|
|
DDEFREE(message, lParam);
|
|
break;
|
|
|
|
case WM_DDE_TERMINATE:
|
|
|
|
DEBUG_OUT ("WM_DDE_TERMINATE",0);
|
|
HandleTermMsg (lpobj, pedit, (HWND)wParam, TRUE);
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
|
|
DEBUG_OUT ("Client window being destroyed", 0)
|
|
pedit->hClient = NULL;
|
|
break;
|
|
|
|
default:
|
|
return DefWindowProc (hwnd, message, wParam, lParam);
|
|
|
|
}
|
|
return 0L;
|
|
}
|
|
|
|
|
|
|
|
// SrvrWndProc: Window Procedure for System Topic DDE conversations
|
|
// wndproc for system topic
|
|
|
|
LRESULT FAR PASCAL SrvrWndProc(
|
|
HWND hwnd,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
){
|
|
PEDIT_DDE pedit = NULL;
|
|
LPOBJECT_LE lpobj = NULL;
|
|
|
|
Puts("SysWndProc");
|
|
|
|
if (lpobj = (LPOBJECT_LE) GetWindowLongPtr (hwnd, 0))
|
|
{
|
|
pedit = lpobj->pSysEdit;
|
|
}
|
|
else
|
|
{
|
|
// Can't cope so just pass this message on
|
|
DEBUG_OUT ("SYS: conv edit block missing",0);
|
|
return DefWindowProc (hwnd, message, wParam, lParam);
|
|
}
|
|
|
|
|
|
switch (message){
|
|
|
|
case WM_DDE_ACK:
|
|
|
|
|
|
DEBUG_OUT ("SYS: WM_DDE_ACK",0);
|
|
|
|
if(pedit->bTerminating){
|
|
//### Error recovery may not be OK.
|
|
DEBUG_OUT ("No action due to termination process",0)
|
|
break;
|
|
}
|
|
|
|
switch (pedit->awaitAck) {
|
|
|
|
|
|
case AA_INITIATE:
|
|
|
|
#ifdef HISTORY
|
|
if (GETWINDOWUINT((HWND)wParam, GWW_HINSTANCE) == pedit->hInst ||
|
|
IsSrvrDLLwnd ((HWND)wParam, pedit->hInst)) {
|
|
// For exact instance match or for
|
|
// DLL instance match, keep the new one
|
|
|
|
pedit->hServer = (HWND)wParam;
|
|
} else {
|
|
|
|
++pedit->extraTerm;
|
|
// This post directly is alright since we are
|
|
// terminating extra initiates.
|
|
|
|
PostMessage ((HWND)wParam,
|
|
WM_DDE_TERMINATE, hwnd, 0);
|
|
}
|
|
#else
|
|
|
|
HandleAckInitMsg (pedit, (HWND)wParam);
|
|
#endif
|
|
if (LOWORD(lParam))
|
|
GlobalDeleteAtom (LOWORD(lParam));
|
|
if (HIWORD(lParam))
|
|
GlobalDeleteAtom (HIWORD(lParam));
|
|
|
|
break;
|
|
|
|
case AA_EXECUTE:
|
|
HandleAck(lpobj, pedit, wParam, lParam);
|
|
break;
|
|
|
|
|
|
default:
|
|
DEBUG_OUT ("received ACK We don't know how to handle ",0)
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case WM_TIMER:
|
|
|
|
HandleTimerMsg (lpobj, pedit);
|
|
break;
|
|
|
|
case WM_DDE_TERMINATE:
|
|
|
|
HandleTermMsg (lpobj, pedit, (HWND)wParam, FALSE);
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
DEBUG_OUT ("destroy window for the sys connection", 0);
|
|
pedit->hClient = NULL;
|
|
break;
|
|
|
|
|
|
default:
|
|
return DefWindowProc (hwnd, message, wParam, lParam);
|
|
|
|
}
|
|
return 0L;
|
|
}
|
|
|
|
void INTERNAL HandleTimerMsg (
|
|
LPOBJECT_LE lpobj,
|
|
PEDIT_DDE pedit
|
|
){
|
|
|
|
|
|
// Since there is only one timer for each client, just
|
|
// repost the message and delete the timer.
|
|
|
|
KillTimer (pedit->hClient, 1);
|
|
pedit->wTimer = 0;
|
|
|
|
if (PostMessageToServer(pedit, pedit->msg, pedit->lParam))
|
|
return ; // return something.
|
|
|
|
// Postmessage failed. We need to getback to the main stream of
|
|
// commands for the object.
|
|
HandleAck (lpobj, pedit, (WPARAM)NULL, pedit->lParam);
|
|
return ;
|
|
}
|
|
|
|
|
|
void INTERNAL HandleTermMsg (lpobj, pedit, hwndPost, bDoc)
|
|
LPOBJECT_LE lpobj;
|
|
PEDIT_DDE pedit;
|
|
HWND hwndPost;
|
|
BOOL bDoc;
|
|
{
|
|
UINT asyncCmd;
|
|
BOOL bBusy;
|
|
|
|
if (pedit->hServer != hwndPost){
|
|
DEBUG_OUT ("Got terminate for extra conversation",0)
|
|
if (--pedit->extraTerm == 0 && pedit->bTerminating)
|
|
ScheduleAsyncCmd (lpobj);
|
|
return;
|
|
|
|
}
|
|
|
|
if (!pedit->bTerminating){
|
|
|
|
// If we are waiting for any ack, then goto next step with error
|
|
|
|
// delete any data if we were in busy mode.
|
|
bBusy = DeleteBusyData (lpobj, pedit);
|
|
|
|
asyncCmd = lpobj->asyncCmd;
|
|
PostMessageToServer(pedit, WM_DDE_TERMINATE, 0);
|
|
pedit->hServer = NULL;
|
|
if (pedit->awaitAck || bBusy) {
|
|
// Set error and goto next step.
|
|
lpobj->subErr = OLE_ERROR_COMM;
|
|
pedit->awaitAck = 0;
|
|
ScheduleAsyncCmd (lpobj);
|
|
}
|
|
|
|
// If the command is delete, do not delete
|
|
// the edit blocks. It will be deleted
|
|
// in the OleLnkDelete routine and for delete it is
|
|
// possible that by the time we come here, the object
|
|
// may not exist at all.
|
|
|
|
if (asyncCmd != OLE_DELETE){
|
|
// QueryOpen() is done because excel is sending WM_DDE_TERMINATE
|
|
// for system without sending for doc in case of failure.
|
|
|
|
if (bDoc || QueryOpen (lpobj)) {
|
|
// if the termination is for document and no async command
|
|
// terminate the server conversation also.
|
|
if ((asyncCmd == OLE_NONE) || (asyncCmd == OLE_REQUESTDATA)
|
|
|| (asyncCmd == OLE_OTHER) || (asyncCmd == OLE_SETDATA)
|
|
|| (asyncCmd == OLE_RUN) || (asyncCmd == OLE_SHOW)
|
|
|| (asyncCmd == OLE_SETUPDATEOPTIONS)) {
|
|
if (lpobj->pDocEdit && lpobj->pDocEdit->awaitAck)
|
|
// we are waiting for an ack on Doc channel. So start
|
|
// the unlaunch process after we get the ack.
|
|
lpobj->bUnlaunchLater = TRUE;
|
|
else
|
|
CallEmbLnkDelete (lpobj);
|
|
} else {
|
|
if (bDoc)
|
|
DeleteDocEdit (lpobj);
|
|
|
|
}
|
|
}else
|
|
DeleteSrvrEdit (lpobj);
|
|
|
|
}
|
|
} else {
|
|
pedit->hServer = NULL;
|
|
if (pedit->extraTerm == 0)
|
|
ScheduleAsyncCmd (lpobj);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// HandleAckInitMsg: Handles WM_DDE_ACKs received while in initiate state. If
|
|
// this is the first reply, save its window handle. If multiple replies
|
|
// are received, take the one with the prefered instance, if there is
|
|
// one. Keep a count of WM_DDE_TERMINATEs we send so that we don't shut
|
|
// the window until we get all of the responses for WM_DDE_TERMINATEs.
|
|
|
|
void INTERNAL HandleAckInitMsg (
|
|
PEDIT_DDE pedit,
|
|
HWND hserver
|
|
){
|
|
|
|
Puts("HandleAckInitMsg");
|
|
|
|
if (pedit->hServer){
|
|
// just take the very first one. Direct post is OK
|
|
PostMessage (hserver, WM_DDE_TERMINATE, (WPARAM)pedit->hClient, 0);
|
|
++pedit->extraTerm;
|
|
} else
|
|
pedit->hServer = hserver;
|
|
|
|
}
|
|
|
|
|
|
// HandleAck: returns 0 if <ack> is not positive, else non-0. Should probably be
|
|
// a macro.
|
|
|
|
BOOL INTERNAL HandleAck (
|
|
LPOBJECT_LE lpobj,
|
|
PEDIT_DDE pedit,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
){
|
|
WORD wStatus = GET_WM_DDE_ACK_STATUS(wParam,lParam);
|
|
HANDLE hData = NULL;
|
|
BOOL retval = TRUE;
|
|
|
|
UNREFERENCED_PARAMETER(wParam);
|
|
|
|
// check for busy bit
|
|
if ((wStatus & 0x4000) && ContextCallBack ((LPOLEOBJECT)lpobj, OLE_QUERY_RETRY)){
|
|
// we got busy from the server. create a timer and wait for time out.
|
|
|
|
// We do not need makeprocinstance since, DLLs are single insance, all
|
|
// we need to do is export for this function.
|
|
|
|
if ((pedit->wTimer = SetTimer (pedit->hClient, 1, 3000, NULL)))
|
|
return TRUE;
|
|
}
|
|
|
|
// even if the client got terminate we have to go thru this path.
|
|
|
|
if (pedit->wTimer) {
|
|
KillTimer (pedit->hClient, 1);
|
|
pedit->wTimer = 0;
|
|
}
|
|
|
|
if (pedit->awaitAck == AA_POKE)
|
|
// We have to free the data first. Handleack can trigger
|
|
// another Poke (like pokehostnames)
|
|
FreePokeData (lpobj, pedit);
|
|
|
|
if (pedit->awaitAck == AA_EXECUTE) {
|
|
hData = GET_WM_DDE_EXECACK_HDATA(wParam,lParam);
|
|
if (hData) GlobalFree (hData);
|
|
} else {
|
|
ATOM aItem = GET_WM_DDE_ACK_ITEM(wParam,lParam);
|
|
|
|
ASSERT (CheckAtomValid(aItem),"Invalid atom in ACK")
|
|
|
|
if (aItem)
|
|
GlobalDeleteAtom (aItem);
|
|
}
|
|
|
|
if (!(wStatus & 0x8000)) {
|
|
// error case. set the error
|
|
DEBUG_OUT ("DDE ACK with failure", 0)
|
|
|
|
if (lpobj->errHint){
|
|
lpobj->subErr = lpobj->errHint;
|
|
lpobj->errHint = OLE_OK;
|
|
} else
|
|
lpobj->subErr = OLE_ERROR_COMM;
|
|
|
|
retval = FALSE;
|
|
|
|
if (pedit->awaitAck == AA_ADVISE) {
|
|
|
|
#ifdef ASSERT
|
|
ASSERT (pedit->hopt, "failed advise, options block missing");
|
|
#endif
|
|
GlobalFree (pedit->hopt);
|
|
}
|
|
}
|
|
|
|
pedit->hopt = NULL;
|
|
pedit->awaitAck = 0;
|
|
ScheduleAsyncCmd (lpobj);
|
|
return retval;
|
|
}
|
|
|
|
// HandleDataMsg: Called for WM_DDE_DATA message. If data is from an
|
|
// ADVISE-ON-CLOSE and this is there are no more outstanding
|
|
// ADVISE-ON-CLOSE requests, close the document and end the
|
|
// conversation.
|
|
|
|
void INTERNAL HandleDataMsg (
|
|
LPOBJECT_LE lpobj,
|
|
HANDLE hdata,
|
|
ATOM aItem
|
|
){
|
|
DDEDATA far *lpdata = NULL;
|
|
BOOL fAck;
|
|
BOOL fRelease;
|
|
int options;
|
|
PEDIT_DDE pedit;
|
|
|
|
Puts("HandleDataMsg");
|
|
|
|
if (ScanItemOptions (aItem, (int far *)&options) != OLE_OK) {
|
|
DEBUG_OUT (FALSE, "Improper item options");
|
|
return;
|
|
}
|
|
|
|
pedit = lpobj->pDocEdit;
|
|
|
|
if (hdata) {
|
|
if (!(lpdata = (DDEDATA FAR *) GlobalLock(hdata)))
|
|
return;
|
|
|
|
fAck = lpdata->fAckReq;
|
|
fRelease = lpdata->fRelease;
|
|
|
|
if (pedit->bTerminating) {
|
|
DEBUG_OUT ("Got DDE_DATA in terminate sequence",0)
|
|
fRelease = TRUE;
|
|
}
|
|
else {
|
|
if ((OLECLIPFORMAT)lpdata->cfFormat == cfBinary && aItem == aStdDocName) {
|
|
ChangeDocName (lpobj, (LPSTR)lpdata->Value);
|
|
}
|
|
else
|
|
SetData (lpobj, hdata, options);
|
|
|
|
|
|
// important that we post the acknowledge first. Otherwist the
|
|
// messages are not in sync.
|
|
|
|
if (fAck)
|
|
{
|
|
LPARAM lparamNew = MAKE_DDE_LPARAM(WM_DDE_ACK,POSITIVE_ACK,aItem);
|
|
PostMessageToServer (pedit, WM_DDE_ACK, lparamNew);
|
|
}
|
|
else if (aItem)
|
|
GlobalDeleteAtom (aItem);
|
|
|
|
if ((lpdata->fResponse) && (pedit->awaitAck == AA_REQUEST)) {
|
|
// we sent the request. So, schedule next step.
|
|
pedit->awaitAck = 0;
|
|
ScheduleAsyncCmd (lpobj);
|
|
}
|
|
}
|
|
|
|
GlobalUnlock (hdata);
|
|
if (fRelease)
|
|
GlobalFree (hdata);
|
|
}
|
|
else {
|
|
if (CanCallback (lpobj, options)) {
|
|
if (options != OLE_CLOSED)
|
|
ContextCallBack ((LPOLEOBJECT)lpobj, options);
|
|
else
|
|
lpobj->bSvrClosing = FALSE;
|
|
|
|
}
|
|
}
|
|
|
|
// PREfix COM+ 27104
|
|
// The call to ScheduleAsyncCmd above can cause EmbLnkDelete to be called.
|
|
// In that case lpobj->pDocEdit has already been deleted, and we don't need
|
|
// to start another EmbLinkDelete.
|
|
if ((options == OLE_CLOSED) && (lpobj->pDocEdit)) {
|
|
if ((lpobj->pDocEdit->nAdviseClose <= 2) && (lpobj->asyncCmd == OLE_NONE)) {
|
|
InitAsyncCmd (lpobj, OLE_SERVERUNLAUNCH, EMBLNKDELETE);
|
|
EmbLnkDelete (lpobj);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
HANDLE GetDDEDataHandle (
|
|
DDEDATA far *lpdata,
|
|
UINT cfFormat,
|
|
HANDLE hdata
|
|
){
|
|
|
|
if (cfFormat == CF_METAFILEPICT) {
|
|
#ifdef _WIN64
|
|
return (*(void* _unaligned*)lpdata->Value);
|
|
#else
|
|
return LongToHandle(*(LONG*)lpdata->Value);
|
|
#endif
|
|
}
|
|
|
|
if (cfFormat == CF_BITMAP || cfFormat == CF_ENHMETAFILE)
|
|
return LongToHandle(*(LONG*)lpdata->Value);
|
|
|
|
if (cfFormat == CF_DIB)
|
|
return GlobalReAlloc (LongToHandle(*(LONG*)lpdata->Value), 0L,
|
|
GMEM_MODIFY|GMEM_SHARE);
|
|
|
|
return CopyData (((LPSTR)lpdata)+4, (DWORD)(GlobalSize (hdata) - 4));
|
|
}
|
|
|
|
// SetData: Given the DDEDATA structure from a WM_DDE_DATA message, set up the
|
|
// appropriate data in lpobj. If the native is in native format, add
|
|
// that field, otherwise, if it is in picture format, ask the picture
|
|
// to add it itself.
|
|
|
|
void INTERNAL SetData (
|
|
LPOBJECT_LE lpobj,
|
|
HANDLE hdata,
|
|
int options
|
|
){
|
|
DDEDATA far *lpdata = NULL;
|
|
OLESTATUS retVal = OLE_ERROR_MEMORY;
|
|
HANDLE hdataDDE;
|
|
|
|
Puts("SetData");
|
|
|
|
if (!(lpdata = (DDEDATA far *) (GlobalLock (hdata))))
|
|
goto errrtn;
|
|
|
|
|
|
if (!(hdataDDE = GetDDEDataHandle (lpdata, lpdata->cfFormat, hdata)))
|
|
goto errrtn;
|
|
|
|
if ((OLECLIPFORMAT)lpdata->cfFormat == cfNative) {
|
|
retVal = (*lpobj->head.lpvtbl->ChangeData) ( (LPOLEOBJECT)lpobj,
|
|
hdataDDE,
|
|
lpobj->head.lpclient,
|
|
TRUE); // use this data, don't copy
|
|
|
|
}
|
|
else if ((BOOL)lpdata->cfFormat && (lpdata->cfFormat == (int)GetPictType (lpobj))) {
|
|
|
|
retVal = (*lpobj->lpobjPict->lpvtbl->ChangeData) (lpobj->lpobjPict,
|
|
hdataDDE,
|
|
lpobj->head.lpclient,
|
|
lpdata->fRelease);
|
|
|
|
} else {
|
|
// case of extra data in the object.
|
|
DeleteExtraData (lpobj);
|
|
lpobj->cfExtra = lpdata->cfFormat;
|
|
lpobj->hextraData = hdataDDE;
|
|
goto end;
|
|
}
|
|
|
|
if (retVal == OLE_OK) {
|
|
SetExtents (lpobj);
|
|
if (CanCallback (lpobj, options)) {
|
|
if (options == OLE_CLOSED) {
|
|
ContextCallBack ((LPOLEOBJECT)lpobj, OLE_CHANGED);
|
|
ContextCallBack ((LPOLEOBJECT)lpobj, OLE_CLOSED);
|
|
lpobj->bSvrClosing = FALSE;
|
|
}
|
|
else
|
|
ContextCallBack ((LPOLEOBJECT)lpobj, options);
|
|
}
|
|
}
|
|
|
|
end:
|
|
errrtn:
|
|
if (lpdata)
|
|
GlobalUnlock (hdata);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
// SysStartConvDDE: Starts a system conversation. Returns a handle to that
|
|
// conversation, or NULL.
|
|
|
|
BOOL INTERNAL InitSrvrConv (
|
|
LPOBJECT_LE lpobj,
|
|
HANDLE hInst
|
|
){
|
|
HANDLE hedit = NULL;
|
|
PEDIT_DDE pedit = NULL;
|
|
|
|
Puts("InitSrvrConv");
|
|
|
|
if (!lpobj->hSysEdit) {
|
|
hedit = LocalAlloc (LMEM_MOVEABLE | LMEM_ZEROINIT, sizeof (EDIT_DDE));
|
|
|
|
if (hedit == NULL || ((pedit = (PEDIT_DDE) LocalLock (hedit)) == NULL))
|
|
goto errRtn;
|
|
|
|
} else {
|
|
hedit = lpobj->hSysEdit;
|
|
pedit = lpobj->pSysEdit;
|
|
UtilMemClr ((PSTR) pedit, sizeof (EDIT_DDE));
|
|
}
|
|
|
|
if((pedit->hClient = CreateWindow ("OleSrvrWndClass", "",
|
|
WS_OVERLAPPED,0,0,0,0,NULL,NULL, hInstDLL, NULL)) == NULL)
|
|
goto errRtn;
|
|
|
|
|
|
lpobj->hSysEdit = hedit;
|
|
lpobj->pSysEdit = pedit;
|
|
pedit->hInst = hInst;
|
|
pedit->awaitAck = AA_INITIATE;
|
|
|
|
SetWindowLongPtr (pedit->hClient, 0, (LONG_PTR)lpobj);
|
|
SendMessage ((HWND)-1, WM_DDE_INITIATE, (WPARAM)pedit->hClient,
|
|
MAKELPARAM (lpobj->app, aOle));
|
|
|
|
ASSERT (CheckAtomValid(aOle),"systopic invalid atom")
|
|
|
|
pedit->awaitAck = 0;
|
|
if (pedit->hServer == NULL) {
|
|
pedit->awaitAck = AA_INITIATE;
|
|
// Now try the System topic
|
|
SendMessage ((HWND)-1, WM_DDE_INITIATE, (WPARAM)pedit->hClient,
|
|
MAKELPARAM (lpobj->app, aSystem));
|
|
|
|
ASSERT (CheckAtomValid(aSystem),"systopic invalid atom")
|
|
|
|
pedit->awaitAck = 0;
|
|
if (pedit->hServer == NULL) {
|
|
DEBUG_OUT ("Srver connection failed", 0);
|
|
goto errRtn;
|
|
}
|
|
}
|
|
|
|
// Put the long ptr handle in the object.
|
|
return TRUE;
|
|
|
|
errRtn:
|
|
|
|
if (pedit) {
|
|
if (pedit->hClient)
|
|
DestroyWindow (pedit->hClient);
|
|
LocalUnlock (hedit);
|
|
}
|
|
|
|
if (hedit)
|
|
LocalFree (hedit);
|
|
|
|
lpobj->hSysEdit = NULL;
|
|
lpobj->pSysEdit = NULL;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
// TermSrvrConv: Ends conversation indicated by hedit.
|
|
void INTERNAL TermSrvrConv (LPOBJECT_LE lpobj)
|
|
{
|
|
PEDIT_DDE pedit;
|
|
|
|
Puts("TermSrvrConv");
|
|
|
|
|
|
if (!(pedit = lpobj->pSysEdit))
|
|
return;
|
|
|
|
if (PostMessageToServer (pedit, WM_DDE_TERMINATE, 0)){
|
|
lpobj->bAsync = TRUE;
|
|
pedit->bTerminating = TRUE;
|
|
} else {
|
|
pedit->bTerminating = FALSE;
|
|
lpobj->subErr = OLE_ERROR_TERMINATE;
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
void INTERNAL DeleteAbortData (
|
|
LPOBJECT_LE lpobj,
|
|
PEDIT_DDE pedit
|
|
){
|
|
UNREFERENCED_PARAMETER(lpobj);
|
|
|
|
// kill if any timer active.
|
|
if (pedit->wTimer) {
|
|
KillTimer (pedit->hClient, 1);
|
|
pedit->wTimer = 0;
|
|
}
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
BOOL INTERNAL DeleteBusyData (
|
|
LPOBJECT_LE lpobj,
|
|
PEDIT_DDE pedit
|
|
){
|
|
UNREFERENCED_PARAMETER(lpobj);
|
|
|
|
// kill if any timer active.
|
|
if (pedit->wTimer) {
|
|
KillTimer (pedit->hClient, 1);
|
|
pedit->wTimer = 0;
|
|
|
|
if (pedit->hData) {
|
|
GlobalFree (pedit->hData);
|
|
pedit->hData = NULL;
|
|
}
|
|
|
|
if (pedit->hopt) {
|
|
GlobalFree (pedit->hopt);
|
|
pedit->hopt = NULL;
|
|
}
|
|
|
|
if (pedit->awaitAck && (HIWORD(pedit->lParam))) {
|
|
if (pedit->awaitAck == AA_EXECUTE) {
|
|
HANDLE hData = GET_WM_DDE_EXECACK_HDATA(pedit->wParam, pedit->lParam);
|
|
if (hData) GlobalFree (hData);
|
|
} else {
|
|
ASSERT (CheckAtomValid(HIWORD(pedit->lParam)),
|
|
"Invalid atom in ACK")
|
|
if (HIWORD(pedit->lParam))
|
|
GlobalDeleteAtom (HIWORD(pedit->lParam));
|
|
}
|
|
|
|
// we want to wipe out the HIWORD of lParam
|
|
pedit->lParam &= 0x0000FFFF;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void INTERNAL DeleteSrvrEdit (
|
|
LPOBJECT_LE lpobj
|
|
){
|
|
|
|
PEDIT_DDE pedit;
|
|
|
|
Puts("deleteSrvrEdit");
|
|
|
|
if (!(pedit = lpobj->pSysEdit))
|
|
return;
|
|
|
|
|
|
// delete any data if we were in busy mode.
|
|
DeleteBusyData (lpobj, pedit);
|
|
|
|
if (pedit->hClient)
|
|
DestroyWindow (pedit->hClient);
|
|
|
|
if (lpobj->pSysEdit)
|
|
LocalUnlock (lpobj->hSysEdit);
|
|
|
|
if (lpobj->hSysEdit)
|
|
LocalFree (lpobj->hSysEdit);
|
|
|
|
lpobj->hSysEdit = NULL;
|
|
lpobj->pSysEdit = NULL;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
void INTERNAL SendStdExit (
|
|
LPOBJECT_LE lpobj
|
|
){
|
|
|
|
|
|
Puts("SendSrvrExit");
|
|
|
|
if (!lpobj->pSysEdit)
|
|
return;
|
|
|
|
SrvrExecute (lpobj, MapStrToH ("[StdExit]"));
|
|
|
|
}
|
|
|
|
void INTERNAL SendStdClose (
|
|
LPOBJECT_LE lpobj
|
|
){
|
|
|
|
|
|
Puts("SendDocClose");
|
|
|
|
if (!lpobj->pDocEdit)
|
|
return;
|
|
|
|
DocExecute (lpobj, MapStrToH ("[StdCloseDocument]"));
|
|
|
|
}
|
|
|
|
|
|
// SrvrExecute: Sends execute command to system conversation.
|
|
BOOL INTERNAL SrvrExecute (
|
|
LPOBJECT_LE lpobj,
|
|
HANDLE hdata
|
|
){
|
|
PEDIT_DDE pedit = NULL;
|
|
int retval = FALSE;
|
|
|
|
Puts("SrvrExecute");
|
|
|
|
pedit = lpobj->pSysEdit;
|
|
|
|
if (hdata == NULL || pedit == NULL) {
|
|
lpobj->subErr = OLE_ERROR_MEMORY;
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
if (lpobj->bOldLink) {
|
|
GlobalFree (hdata);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
if (PostMessageToServer (pedit, WM_DDE_EXECUTE, (LPARAM)hdata)) {
|
|
// data is being freed in the acknowledge
|
|
lpobj->bAsync = TRUE;
|
|
pedit->awaitAck = AA_EXECUTE;
|
|
return TRUE;
|
|
} else {
|
|
lpobj->subErr = OLE_ERROR_COMMAND;
|
|
GlobalFree (hdata);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// StartConvDDE: Starts the document conversation for an object based on
|
|
// .app and .topic atoms.
|
|
BOOL FARINTERNAL InitDocConv (
|
|
LPOBJECT_LE lpobj,
|
|
BOOL fNetDlg
|
|
){
|
|
|
|
// ### This routine looks very similar to IitSrvrConv
|
|
// combine with the it
|
|
|
|
HANDLE hedit = NULL;
|
|
PEDIT_DDE pedit = NULL;
|
|
char buf[MAX_NET_NAME];
|
|
int nDrive = 2; // drive C
|
|
char cOldDrive;
|
|
|
|
Puts("InitDocConv");
|
|
|
|
if (QueryOpen (lpobj)){
|
|
DEBUG_OUT ("Attempt to start already existing conversation",0);
|
|
return FALSE;
|
|
}
|
|
|
|
cOldDrive = lpobj->cDrive;
|
|
if (CheckNetDrive (lpobj, fNetDlg) != OLE_OK)
|
|
return FALSE;
|
|
|
|
if (!lpobj->pDocEdit) {
|
|
hedit = LocalAlloc (LMEM_MOVEABLE | LMEM_ZEROINIT, sizeof (EDIT_DDE));
|
|
|
|
if (hedit == NULL || ((pedit = (PEDIT_DDE) LocalLock (hedit)) == NULL)){
|
|
lpobj->subErr = OLE_ERROR_MEMORY;
|
|
goto errRtn;
|
|
}
|
|
} else {
|
|
hedit = lpobj->hDocEdit;
|
|
pedit = lpobj->pDocEdit;
|
|
UtilMemClr ((PSTR) pedit, sizeof (EDIT_DDE));
|
|
}
|
|
|
|
if ((pedit->hClient = CreateWindow ("OleDocWndClass", "Window Name",
|
|
WS_OVERLAPPED,0,0,0,0,NULL,NULL, hInstDLL, NULL)) == NULL) {
|
|
lpobj->subErr = OLE_ERROR_MEMORY;
|
|
goto errRtn;
|
|
}
|
|
lpobj->hDocEdit = hedit;
|
|
lpobj->pDocEdit = pedit;
|
|
SetWindowLongPtr (pedit->hClient, 0, (LONG_PTR)lpobj);
|
|
|
|
// buf will filled by netname in the first call to SetNextNetDrive()
|
|
buf[0] = '\0';
|
|
do {
|
|
pedit->awaitAck = AA_INITIATE;
|
|
|
|
// !!! Where are the atom counts bumped?
|
|
|
|
SendMessage ((HWND)-1, WM_DDE_INITIATE, (WPARAM)pedit->hClient,
|
|
MAKELPARAM (lpobj->app, lpobj->topic));
|
|
|
|
pedit->awaitAck = 0;
|
|
|
|
if (pedit->hServer) {
|
|
if ((cOldDrive != lpobj->cDrive)
|
|
&& (lpobj->asyncCmd != OLE_CREATEFROMFILE))
|
|
ContextCallBack ((LPOLEOBJECT)lpobj, OLE_RENAMED);
|
|
return TRUE;
|
|
}
|
|
|
|
} while ((lpobj->head.ctype == CT_LINK) && (lpobj->aNetName)
|
|
&& SetNextNetDrive (lpobj, &nDrive, buf)) ;
|
|
|
|
errRtn:
|
|
if (cOldDrive != lpobj->cDrive) {
|
|
// put back the old drive
|
|
lpobj->cDrive = cOldDrive;
|
|
ChangeTopic (lpobj);
|
|
}
|
|
|
|
if (pedit && pedit->hClient)
|
|
DestroyWindow (pedit->hClient);
|
|
|
|
if (pedit)
|
|
LocalUnlock (hedit);
|
|
|
|
if (hedit)
|
|
LocalFree (hedit);
|
|
|
|
lpobj->hDocEdit = NULL;
|
|
lpobj->pDocEdit = NULL;
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
// Execute: Sends an execute string WM_DDE_EXECUTE to the document conversation.
|
|
BOOL INTERNAL DocExecute(
|
|
LPOBJECT_LE lpobj,
|
|
HANDLE hdata
|
|
){
|
|
PEDIT_DDE pedit;
|
|
|
|
Puts("DocExecute");
|
|
pedit = lpobj->pDocEdit;
|
|
|
|
if (hdata == NULL || pedit == NULL)
|
|
return FALSE;
|
|
|
|
|
|
|
|
if (lpobj->bOldLink) {
|
|
GlobalFree (hdata);
|
|
return TRUE;
|
|
}
|
|
|
|
if (PostMessageToServer (pedit, WM_DDE_EXECUTE, (LPARAM)hdata)) {
|
|
// data is being freed in the execute command
|
|
pedit->awaitAck = AA_EXECUTE;
|
|
lpobj->bAsync = TRUE;
|
|
return TRUE;
|
|
} else {
|
|
lpobj->subErr = OLE_ERROR_COMMAND;
|
|
GlobalFree (hdata);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
// EndConvDDE: terminates the doc level conversation.
|
|
void INTERNAL TermDocConv (
|
|
LPOBJECT_LE lpobj
|
|
){
|
|
PEDIT_DDE pedit;
|
|
|
|
Puts ("TermDocConv");
|
|
|
|
DEBUG_OUT ("About to terminate convs from destroy",0)
|
|
|
|
if (!(pedit = lpobj->pDocEdit))
|
|
return;
|
|
|
|
if (PostMessageToServer (pedit, WM_DDE_TERMINATE, 0)) {
|
|
pedit->bTerminating = TRUE;
|
|
lpobj->bAsync = TRUE;
|
|
} else
|
|
lpobj->subErr = OLE_ERROR_TERMINATE;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Deletes the document conversdation memory.
|
|
void INTERNAL DeleteDocEdit (lpobj)
|
|
LPOBJECT_LE lpobj;
|
|
{
|
|
|
|
PEDIT_DDE pedit;
|
|
|
|
Puts ("DeleteDocEdit");
|
|
|
|
if (!(pedit = lpobj->pDocEdit))
|
|
return;
|
|
|
|
// delete any data if we were in busy mode.
|
|
DeleteBusyData (lpobj, pedit);
|
|
|
|
// Delete if any data blocks.
|
|
if (pedit->hClient)
|
|
DestroyWindow (pedit->hClient);
|
|
|
|
if (lpobj->pDocEdit)
|
|
LocalUnlock (lpobj->hDocEdit);
|
|
|
|
if (lpobj->hDocEdit)
|
|
LocalFree (lpobj->hDocEdit);
|
|
|
|
lpobj->hDocEdit = NULL;
|
|
lpobj->pDocEdit = NULL;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
// LeLauchApp: Launches app based on the ClassName in lpobj.
|
|
// History:
|
|
// curts changed LoadModule calls to WinExec
|
|
//
|
|
|
|
HANDLE INTERNAL LeLaunchApp (LPOBJECT_LE lpobj)
|
|
{
|
|
// struct CMDSHOW
|
|
// {
|
|
// WORD first;
|
|
// WORD second;
|
|
// } cmdShow = {2, SW_SHOWNORMAL};
|
|
//
|
|
// struct
|
|
// {
|
|
// WORD wEnvSeg;
|
|
// LPSTR lpcmdline;
|
|
// struct CMDSHOW FAR *lpCmdShow;
|
|
// DWORD dwReserved;
|
|
// } paramBlock;
|
|
|
|
WORD cmdShow = SW_SHOWNORMAL;
|
|
char cmdline[MAX_STR];
|
|
char exeName[MAX_STR];
|
|
char lpstr[2*MAX_STR];
|
|
HANDLE hInst;
|
|
|
|
#define EMB_STR " -Embedding "
|
|
|
|
Puts("LeLaunchApp");
|
|
|
|
if (!GlobalGetAtomName (lpobj->aServer, exeName, MAX_STR))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
cmdline[0] = ' ';
|
|
if (lpobj->bOldLink) {
|
|
cmdShow = SW_SHOWMINIMIZED;
|
|
if (!GlobalGetAtomName (lpobj->topic, cmdline + 1, MAX_STR - 1)) {
|
|
cmdline[1] = '\0';
|
|
}
|
|
} else {
|
|
StringCchCopy ((LPSTR)cmdline+1, sizeof(cmdline)-1, (LPSTR) EMB_STR);
|
|
|
|
// For all link servers we want to give the filename on the command
|
|
// line. But Excel is not registering the document before returning
|
|
// from WinMain, if it has auto load macros. So, we want send StdOpen
|
|
// for the old servers, instead of giving the file name on the command
|
|
// line.
|
|
|
|
if (lpobj->bOleServer && (lpobj->fCmd & LN_MASK) == LN_LNKACT) {
|
|
if (!GlobalGetAtomName (lpobj->topic, cmdline+sizeof(EMB_STR), MAX_STR-sizeof(EMB_STR)-1))
|
|
cmdline[sizeof(EMB_STR)] = '\0';
|
|
}
|
|
if (lpobj->fCmd & ACT_MINIMIZE)
|
|
cmdShow = SW_SHOWMINIMIZED;
|
|
else if (!(lpobj->fCmd & (ACT_SHOW | ACT_DOVERB))
|
|
// we want to launch with show in create invisible case
|
|
// even though ACT_SHOW flag will be false
|
|
&& ((lpobj->fCmd & LN_MASK) != LN_NEW))
|
|
cmdShow = SW_HIDE;
|
|
}
|
|
|
|
// paramBlock.wEnvSeg = NULL;
|
|
// paramBlock.lpcmdline = (LPSTR)cmdline;
|
|
// paramBlock.lpCmdShow = &cmdShow;
|
|
// paramBlock.dwReserved = NULL;
|
|
|
|
if (FAILED(StringCchCopy(lpstr, sizeof(lpstr), exeName)))
|
|
return NULL;
|
|
if (FAILED(StringCchCat(lpstr, sizeof(lpstr), cmdline)))
|
|
return NULL;
|
|
|
|
if ((hInst = (HANDLE)ULongToPtr(WinExec(lpstr, cmdShow))) < (HANDLE)32)
|
|
hInst = NULL;
|
|
|
|
if (!hInst) {
|
|
LPSTR lptmp;
|
|
char ch;
|
|
|
|
// strip off the path and try again
|
|
lptmp = (LPSTR)exeName;
|
|
lptmp += lstrlen ((LPSTR) exeName);
|
|
ch = *lptmp;
|
|
while (ch != '\\' && ch != ':') {
|
|
if (lptmp == (LPSTR) exeName) {
|
|
// exe did not have path in it's name. we already tried
|
|
// loading and it failed, no point trying again.
|
|
return NULL;
|
|
}
|
|
else
|
|
ch = *--lptmp;
|
|
}
|
|
|
|
if (FAILED(StringCchCopy(lpstr, sizeof(lpstr), ++lptmp)))
|
|
return NULL;
|
|
if (FAILED(StringCchCat(lpstr, sizeof(lpstr), cmdline)))
|
|
return NULL;
|
|
|
|
if ((hInst = (HANDLE)ULongToPtr(WinExec(lpstr,cmdShow))) < (HANDLE)32)
|
|
hInst = NULL;
|
|
}
|
|
|
|
return hInst;
|
|
}
|
|
|
|
|
|
|
|
//ScanItemOptions: Scan for the item options like Close/Save etc.
|
|
|
|
int INTERNAL ScanItemOptions (
|
|
ATOM aItem,
|
|
int far *lpoptions
|
|
){
|
|
|
|
ATOM aModifier;
|
|
|
|
LPSTR lpbuf;
|
|
char buf[MAX_STR] = {0};
|
|
|
|
*lpoptions = OLE_CHANGED;
|
|
|
|
if (!aItem) {
|
|
// NULL item with no modifier means OLE_CHANGED for NULL item
|
|
return OLE_OK;
|
|
}
|
|
|
|
if(GlobalGetAtomName (aItem, (LPSTR)buf, MAX_STR) == 0)
|
|
{
|
|
return OLE_ERROR_SYNTAX;
|
|
}
|
|
lpbuf = (LPSTR)buf;
|
|
|
|
while ( *lpbuf && *lpbuf != '/')
|
|
lpbuf++;
|
|
|
|
// no modifier same as /change
|
|
|
|
if (*lpbuf == '\0')
|
|
return OLE_OK;
|
|
|
|
*lpbuf++ = '\0'; // seperate out the item string
|
|
// We are using this in the caller.
|
|
|
|
if (!(aModifier = GlobalFindAtom (lpbuf)))
|
|
return OLE_ERROR_SYNTAX;
|
|
|
|
if (aModifier == aChange)
|
|
return OLE_OK;
|
|
|
|
// Is it a save?
|
|
if (aModifier == aSave){
|
|
*lpoptions = OLE_SAVED;
|
|
return OLE_OK;
|
|
}
|
|
// Is it a Close?
|
|
if (aModifier == aClose){
|
|
*lpoptions = OLE_CLOSED;
|
|
return OLE_OK;
|
|
}
|
|
|
|
// unknown modifier
|
|
return OLE_ERROR_SYNTAX;
|
|
|
|
}
|
|
|
|
void INTERNAL ChangeDocName (
|
|
LPOBJECT_LE lpobj,
|
|
LPSTR lpdata
|
|
|
|
){
|
|
ATOM aOldTopic;
|
|
OLESTATUS retVal;
|
|
|
|
aOldTopic = lpobj->topic;
|
|
lpobj->topic = GlobalAddAtom (lpdata);
|
|
if ((retVal = SetNetName (lpobj)) != OLE_OK) {
|
|
if (lpobj->topic)
|
|
GlobalDeleteAtom (lpobj->topic);
|
|
lpobj->topic = aOldTopic;
|
|
return;
|
|
// !!! what should we do in case of error? Currently, we will not
|
|
// change the topic if SetNetName fails.
|
|
}
|
|
|
|
if (aOldTopic)
|
|
GlobalDeleteAtom (aOldTopic);
|
|
|
|
// Delete the link data block
|
|
if (lpobj->hLink) {
|
|
GlobalFree (lpobj->hLink);
|
|
lpobj->hLink = NULL;
|
|
}
|
|
|
|
ContextCallBack ((LPOLEOBJECT)lpobj, OLE_RENAMED);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
BOOL INTERNAL CanCallback (
|
|
LPOBJECT_LE lpobj,
|
|
int options
|
|
){
|
|
LPINT lpCount;
|
|
|
|
if (options == OLE_CLOSED) {
|
|
lpobj->bSvrClosing = TRUE;
|
|
lpCount = &(lpobj->pDocEdit->nAdviseClose);
|
|
}
|
|
else if (options == OLE_SAVED) {
|
|
if (lpobj->head.ctype == CT_LINK)
|
|
return TRUE;
|
|
lpCount = &(lpobj->pDocEdit->nAdviseSave);
|
|
}
|
|
else {
|
|
// it must be due to request
|
|
if ((lpobj->pDocEdit->awaitAck == AA_REQUEST)
|
|
&& lpobj->pDocEdit->bCallLater)
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
switch (*lpCount) {
|
|
case 1:
|
|
break;
|
|
|
|
case 2:
|
|
++(*lpCount);
|
|
return FALSE;
|
|
|
|
case 3:
|
|
--(*lpCount);
|
|
break;
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void FARINTERNAL CallEmbLnkDelete (
|
|
LPOBJECT_LE lpobj
|
|
){
|
|
InitAsyncCmd (lpobj, OLE_SERVERUNLAUNCH,EMBLNKDELETE);
|
|
EmbLnkDelete (lpobj);
|
|
|
|
if (lpobj->head.ctype == CT_EMBEDDED) {
|
|
lpobj->bSvrClosing = TRUE;
|
|
ContextCallBack ((LPOLEOBJECT)lpobj, OLE_CLOSED);
|
|
if (FarCheckObject ((LPOLEOBJECT)lpobj))
|
|
lpobj->bSvrClosing = FALSE;
|
|
}
|
|
}
|