/****************************** Module Header ******************************\ * Module Name: Item.c Object(item) main module * * Purpose: Includes All the object releated routiens. * * Created: Oct 1990. * * Copyright (c) 1990, 1991 Microsoft Corporation * * History: * Raor (../10/1990) Designed, coded * * \***************************************************************************/ #include "cmacs.h" #include "windows.h" #include "ole.h" #include "dde.h" #include "srvr.h" extern HANDLE hdllInst; extern FARPROC lpFindItemWnd; extern FARPROC lpItemCallBack; extern FARPROC lpSendDataMsg; extern FARPROC lpSendRenameMsg; extern FARPROC lpDeleteClientInfo; extern FARPROC lpEnumForTerminate; extern ATOM cfNative; extern ATOM cfBinary; extern ATOM aClose; extern ATOM aChange; extern ATOM aSave; extern ATOM aEditItems; extern ATOM aStdDocName; extern WORD cfLink; extern WORD cfOwnerLink; extern BOOL bWin30; HWND hwndItem; HANDLE hddeRename; HWND hwndRename; WORD enummsg; WORD enuminfo; LPOLEOBJECT enumlpoleobject; OLECLIENTVTBL clVtbl; BOOL bClientUnlink; BOOL fAdviseSaveDoc; BOOL fAdviseSaveItem; char * stdStrTable[STDHOSTNAMES+1] = {NULL, "StdTargetDevice", "StdDocDimensions", "StdColorScheme", "StdHostNames"}; extern HANDLE (FAR PASCAL *lpfnSetMetaFileBitsBetter) (HANDLE); void ChangeOwner (HANDLE hmfp); // !!!change child enumeration. // !!!No consistency in errors (Sometimes Bools and sometimes OLESTATUS). //SearchItem: Searches for a given item in a document tree. //If found, returns the corresponding child windows handle. HWND INTERNAL SearchItem (lpdoc, lpitemname) LPDOC lpdoc; LPSTR lpitemname; { ATOM aItem; Puts ("SearchItem"); // If the item passed is an atom, get its name. if (!HIWORD(lpitemname)) aItem = (ATOM) (LOWORD((DWORD)lpitemname)); else if (!lpitemname[0]) aItem = NULL; else aItem = GlobalFindAtom (lpitemname); hwndItem = NULL; // !!! We should avoid hwndItem static. It should not cause // any problems since while enumerating we will not be calling // any window procs or no PostMessages are entertained. EnumChildWindows (lpdoc->hwnd, lpFindItemWnd, MAKELONG (aItem, ITEM_FIND)); return hwndItem; } // FindItem: Given the itemname and the document handle, // searches for the the item (object) in the document tree. // Items are child windows for the document window. // !!! change the child windows to somekind of // linked lists at the item level. This will free up // the space taken by the item windows. int INTERNAL FindItem (lpdoc, lpitemname, lplpclient) LPDOC lpdoc; LPSTR lpitemname; LPCLIENT FAR * lplpclient; { LPCLIENT lpclient; HWND hwnd; char buf[MAX_STR]; Puts ("FindItem"); hwnd = SearchItem (lpdoc, lpitemname); if (!HIWORD(lpitemname)){ if (LOWORD(lpitemname)) GlobalGetAtomName ((ATOM)LOWORD((DWORD)lpitemname), (LPSTR)buf, MAX_STR); else buf[0] = NULL; lpitemname = (LPSTR)buf; } if (hwnd) { // we found the item window lpclient = (LPCLIENT)GetWindowLong (hwnd, 0); #ifdef FIREWALLS ASSERT ((CheckPointer(lpclient, WRITE_ACCESS)), "In Item the client handle missing") ASSERT ((CheckPointer(lpclient->lpoleobject, WRITE_ACCESS)), "In Item object handle missing") #endif *lplpclient = lpclient; return OLE_OK; } // Item (object)window is not create yet. Let us create one. return RegisterItem ((LHDOC)lpdoc, lpitemname, lplpclient, TRUE); } //RegisterItem: Given the document handle and the item string //creates item with the given document. int INTERNAL RegisterItem (lhdoc, lpitemname, lplpclient, bSrvr) LHDOC lhdoc; LPSTR lpitemname; LPCLIENT FAR * lplpclient; BOOL bSrvr; { LPDOC lpdoc; HANDLE hclient = NULL; LPCLIENT lpclient = NULL; int retval = OLE_ERROR_MEMORY; LPOLESERVERDOC lpoledoc; LPOLEOBJECT lpoleobject = NULL; Puts ("CreateItem"); lpdoc = (LPDOC)lhdoc; #ifdef FIREWALLS ASSERT ((CheckPointer (lplpclient, WRITE_ACCESS)), "invalid lplpclient"); #endif // First create the callback client structure. hclient = GlobalAlloc (GMEM_MOVEABLE | GMEM_ZEROINIT | GMEM_DDESHARE, sizeof (CLIENT)); if(!(hclient && (lpclient = (LPCLIENT)GlobalLock (hclient)))) goto errRtn; lpclient->hclient = hclient; hclient = NULL; if (!HIWORD(lpitemname)) { ASSERT (!bSrvr, "invalid lpitemname in RegisterItem\n"); lpclient->aItem = LOWORD((DWORD)lpitemname); } else if (!lpitemname[0]) lpclient->aItem = NULL; else lpclient->aItem = GlobalAddAtom (lpitemname); lpclient->oleClient.lpvtbl = &clVtbl; lpclient->oleClient.lpvtbl->CallBack = (int (CALLBACK *)(LPOLECLIENT, OLE_NOTIFICATION, LPOLEOBJECT))lpItemCallBack; lpoledoc = lpdoc->lpoledoc; // Call the server app to create its own object structure and link // it to the given document. // Call the server if the item is not one of the standard items. if (bSrvr) { retval = (*lpoledoc->lpvtbl->GetObject)(lpoledoc, lpitemname, (LPOLEOBJECT FAR *)&lpoleobject, (LPOLECLIENT)lpclient); if (retval != OLE_OK) goto errRtn; } lpclient->lpoleobject = lpoleobject; lpclient->hwnd = CreateWindow ("ItemWndClass", "ITEM", WS_CHILD,0,0,0,0,lpdoc->hwnd,NULL, hdllInst, NULL); if (lpclient->hwnd == NULL) goto errRtn; // save the ptr to the item in the window. SetWindowLong (lpclient->hwnd, 0, (LONG)lpclient); *lplpclient = lpclient; return OLE_OK; errRtn: if (lpclient) RevokeObject ((LPOLECLIENT)lpclient, FALSE); else { if(hclient) GlobalFree (hclient); } return retval; } OLESTATUS FAR PASCAL OleRevokeObject (lpoleclient) LPOLECLIENT lpoleclient; { return RevokeObject (lpoleclient, TRUE); } // OleRevokeObject: Revokes an object (unregisres an object // from the document tree. OLESTATUS INTERNAL RevokeObject (lpoleclient, bUnlink) LPOLECLIENT lpoleclient; BOOL bUnlink; { HANDLE hclient; LPCLIENT lpclient; lpclient = (LPCLIENT)lpoleclient; PROBE_WRITE(lpoleclient); if (lpclient->lpoleobject) { // first call the object for deletetion. #ifdef FIREWALLS if (!CheckPointer (lpclient->lpoleobject, WRITE_ACCESS)) ASSERT (0, "Invalid LPOLEOBECT") if (!CheckPointer (lpclient->lpoleobject->lpvtbl, WRITE_ACCESS)) ASSERT (0, "Invalid LPOLEOBJECTVTBL") else ASSERT(lpclient->lpoleobject->lpvtbl->Release, "Invalid pointer to Release method") #endif (*lpclient->lpoleobject->lpvtbl->Release)(lpclient->lpoleobject); } if (ISATOM(lpclient->aItem)) { GlobalDeleteAtom (lpclient->aItem); lpclient->aItem = NULL; } if (lpclient->hwnd) { SetWindowLong (lpclient->hwnd, 0, (LONG)NULL); // another static for enumerating the properties. // we need to change these . bClientUnlink = bUnlink; EnumProps(lpclient->hwnd, lpDeleteClientInfo); // post all the messages with yield which have been collected in enum // UnblockPostMsgs (lpclient->hwnd, FALSE); DestroyWindow (lpclient->hwnd); } GlobalUnlock (hclient = lpclient->hclient); GlobalFree (hclient); return OLE_OK; } BOOL FAR PASCAL DeleteClientInfo (hwnd, lpstr, hclinfo) HWND hwnd; LPSTR lpstr; HANDLE hclinfo; { PCLINFO pclinfo = NULL; HWND hwndDoc; LPDOC lpdoc; #ifdef FIREWALLS ASSERT (hclinfo, "Client info null in item property list"); #endif // delete the printer dev info block if(pclinfo = (PCLINFO)LocalLock (hclinfo)){ if(pclinfo->hdevInfo) GlobalFree (pclinfo->hdevInfo); if (bClientUnlink) { // terminate the conversation for the client. TerminateDocClients ((hwndDoc = GetParent(hwnd)), NULL, pclinfo->hwnd); lpdoc = (LPDOC)GetWindowLong (hwndDoc, 0); // for some reason this delete is gving circular lists for properties //DeleteClient (hwndDoc, pclinfo->hwnd); //lpdoc->cClients--; } LocalUnlock (hclinfo); } LocalFree (hclinfo); RemoveProp (hwnd, lpstr); return TRUE; } // Call back for the Object windows numeration. data field // has the command and the extra information BOOL FAR PASCAL FindItemWnd (hwnd, data) HWND hwnd; LONG data; { LPCLIENT lpclient; int cmd; HANDLE hclinfo; PCLINFO pclinfo; lpclient = (LPCLIENT)GetWindowLong (hwnd, 0); #ifdef FIREWALLS // ASSERT (lpclient, "In Item the client handle missing") #endif cmd = HIWORD(data); switch (cmd) { case ITEM_FIND: if (lpclient->aItem == (ATOM)(LOWORD (data))) { // we found the window we required. Remember the // object window. hwndItem = hwnd; return FALSE; // terminate enumeration. } break; case ITEM_SAVED: if (lpclient->lpoleobject) { if (ItemCallBack ((LPOLECLIENT) lpclient, OLE_SAVED, lpclient->lpoleobject) == OLE_ERROR_CANT_UPDATE_CLIENT) fAdviseSaveDoc = FALSE; } break; case ITEM_DELETECLIENT: // delete the client from our list if we have one hclinfo = FindClient (hwnd, (HWND) (LOWORD(data))); if (hclinfo){ // delete the printer dev info block if(pclinfo = (PCLINFO)LocalLock (hclinfo)){ if(pclinfo->hdevInfo) GlobalFree (pclinfo->hdevInfo); LocalUnlock (hclinfo); } LocalFree (hclinfo); DeleteClient ( hwnd, (HWND) (LOWORD(data))); } break; case ITEM_DELETE: // delete the client it self. RevokeObject ((LPOLECLIENT)lpclient, FALSE); break; } return TRUE; // continue enumeration. } //DeleteFromItemsList: Deletes a client from the object lists of //all the objects of a given document. Thie client possibly //is terminating the conversation with our doc window. void INTERNAL DeleteFromItemsList (hwndDoc, hwndClient) HWND hwndDoc; HWND hwndClient; { EnumChildWindows (hwndDoc, lpFindItemWnd, MAKELONG (hwndClient, ITEM_DELETECLIENT)); } // DeleteAllItems: Deletes all the objects of a given // document window. void INTERNAL DeleteAllItems (hwndDoc) HWND hwndDoc; { EnumChildWindows (hwndDoc, lpFindItemWnd, MAKELONG (NULL, ITEM_DELETE)); } // Object widnow proc: long FAR PASCAL ItemWndProc(hwnd, msg, wParam, lParam) HWND hwnd; WORD msg; WORD wParam; LONG lParam; { LPCLIENT lpclient; lpclient = (LPCLIENT)GetWindowLong (hwnd, 0); switch (msg) { case WM_DESTROY: DEBUG_OUT("Item: Destroy window",0) #ifdef FIREWALLS ASSERT (!lpclient, "while destroy Item client is not null") #endif break; default: DEBUG_OUT("item: Default message",0) return DefWindowProc (hwnd, msg, wParam, lParam); } return 0L; } // PokeData: Prepares and gives the data to the server app thru // the SetData object method. OLESTATUS INTERNAL PokeData (lpdoc, hwndClient, lparam) LPDOC lpdoc; HWND hwndClient; LONG lparam; { int retval = OLE_ERROR_MEMORY; LPCLIENT lpclient; DDEPOKE FAR * lpPoke = NULL; HANDLE hPoke = NULL; HANDLE hnew = NULL; int format; BOOL fRelease = FALSE; // Get the object handle first. Look in the registration // tree and if one is not created otherwise create one. retval = FindItem (lpdoc, (LPSTR) MAKEINTATOM(HIWORD(lparam)), (LPCLIENT FAR *)&lpclient); if (retval != OLE_OK) goto errRtn; hPoke = (HANDLE)(LOWORD (lparam)); if(!(hPoke && (lpPoke = (DDEPOKE FAR *) GlobalLock (hPoke)))) goto errRtn; GlobalUnlock (hPoke); format = lpPoke->cfFormat; fRelease = lpPoke->fRelease; // We found the item. Now prepare the data to be given to the object if (!(hnew = MakeItemData (lpPoke, hPoke, format))) goto errRtn; // Now send the data to the object #ifdef FIREWALLS if (!CheckPointer (lpclient->lpoleobject->lpvtbl, WRITE_ACCESS)) ASSERT (0, "Invalid LPOLEOBJECTVTBL") else ASSERT (lpclient->lpoleobject->lpvtbl->SetData, "Invalid pointer to SetData method") #endif retval = (*lpclient->lpoleobject->lpvtbl->SetData) (lpclient->lpoleobject, format, hnew); // We free the data if server returns OLE_ERROR_SETDATA_FORMAT. // Otherwise server must've deleted it. if (retval == OLE_ERROR_SETDATA_FORMAT) { if (!FreeGDIdata (hnew, format)) GlobalFree (hnew); } errRtn: if (retval == OLE_OK && fRelease) { if (hPoke) GlobalFree (hPoke); } return retval; } OLESTATUS INTERNAL UnAdviseData (lpdoc, hwndClient, lparam) LPDOC lpdoc; HWND hwndClient; LONG lparam; { char buf[MAX_STR]; int options; LPCLIENT lpclient; int retval = OLE_ERROR_MEMORY; HANDLE hclinfo = NULL; PCLINFO pclinfo = NULL; if (!(HIWORD (lparam))) buf[0] = NULL; else GlobalGetAtomName ((ATOM)(HIWORD (lparam)), (LPSTR)buf, MAX_STR); // Scan for the advise options like "Close", "Save" etc // at the end of the item. if((retval = ScanItemOptions ((LPSTR)buf, (int far *)&options)) != OLE_OK) goto errRtn; if (buf[0] == NULL) { // Unadvise for null should terminate all the advises DeleteFromItemsList (lpdoc->hwnd, hwndClient); return OLE_OK; } // Now get the corresponding object. retval = FindItem (lpdoc, (LPSTR)buf, (LPCLIENT FAR *)&lpclient); if (retval != OLE_OK) goto errRtn; // Find the client structure to be attcahed to the object. if ((hclinfo = FindClient (lpclient->hwnd, hwndClient)) == NULL || (pclinfo = (PCLINFO) LocalLock (hclinfo)) == NULL ){ retval = OLE_ERROR_MEMORY; goto errRtn; } pclinfo->options &= (~(0x0001 << options)); errRtn: if (pclinfo) LocalUnlock (hclinfo); return retval; } // AdviseStdItems: This routine takes care of the DDEADVISE for a //particular object in given document. Creates a client strutcure //and attaches to the property list of the object window. OLESTATUS INTERNAL AdviseStdItems (lpdoc, hwndClient, lparam, lpfack) LPDOC lpdoc; HWND hwndClient; LONG lparam; BOOL FAR * lpfack; { HANDLE hopt = NULL; DDEADVISE FAR *lpopt; OLESTATUS retval = OLE_ERROR_MEMORY; hopt = (HANDLE) (LOWORD (lparam)); if(!(lpopt = (DDEADVISE FAR *) GlobalLock (hopt))) goto errrtn; #ifdef FIREWALLS ASSERT ((ATOM) (HIWORD (lparam) == aStdDocName), "AdviseStdItem is not Documentname"); #endif *lpfack = lpopt->fAckReq; retval = SetStdInfo (lpdoc, hwndClient, (LPSTR)"StdDocumentName", NULL); if (lpopt) GlobalUnlock (hopt); errrtn: if (retval == OLE_OK) // !!! make sure that we have to free the data for error case GlobalFree (hopt); return retval; } //AdviseData: This routine takes care of the DDEADVISE for a //particular object in given document. Creates a client strutcure //and attaches to the property list of the object window. OLESTATUS INTERNAL AdviseData (lpdoc, hwndClient, lparam, lpfack) LPDOC lpdoc; HWND hwndClient; LONG lparam; BOOL FAR * lpfack; { HANDLE hopt = NULL; DDEADVISE FAR *lpopt = NULL; int format = NULL; char buf[MAX_STR]; int options; LPCLIENT lpclient; int retval = OLE_ERROR_MEMORY; HANDLE hclinfo = NULL; PCLINFO pclinfo = NULL; hopt = (HANDLE) (LOWORD (lparam)); if(!(lpopt = (DDEADVISE FAR *) GlobalLock (hopt))) goto errRtn; if (!(HIWORD (lparam))) buf[0] = NULL; else GlobalGetAtomName ((ATOM)(HIWORD (lparam)), (LPSTR)buf, MAX_STR); // Scan for the advise options like "Close", "Save" etc // at the end of the item. if((retval = ScanItemOptions ((LPSTR)buf, (int far *)&options)) != OLE_OK) goto errRtn; // Now get the corresponding object. retval = FindItem (lpdoc, (LPSTR)buf, (LPCLIENT FAR *)&lpclient); if (retval != OLE_OK) goto errRtn; if (!IsFormatAvailable (lpclient, lpopt->cfFormat)){ retval = OLE_ERROR_DATATYPE; // this format is not supported; goto errRtn; } *lpfack = lpopt->fAckReq; // Create the client structure to be attcahed to the object. if (!(hclinfo = FindClient (lpclient->hwnd, hwndClient))) hclinfo = LocalAlloc (LMEM_MOVEABLE | LMEM_ZEROINIT, sizeof (CLINFO)); if (hclinfo == NULL || (pclinfo = (PCLINFO) LocalLock (hclinfo)) == NULL){ retval = OLE_ERROR_MEMORY; goto errRtn; } // Remember the client window (Needed for sending DATA later on // when the data change message comes from the server) pclinfo->hwnd = hwndClient; if (lpopt->cfFormat == (int)cfNative) pclinfo->bnative = TRUE; else pclinfo->format = lpopt->cfFormat; // Remeber the data transfer options. pclinfo->options |= (0x0001 << options); pclinfo->bdata = !lpopt->fDeferUpd; LocalUnlock (hclinfo); pclinfo = NULL; // if the entry exists already, delete it. DeleteClient (lpclient->hwnd, hwndClient); // Now add this client to item client list // !!! This error recovery is not correct. if(!AddClient (lpclient->hwnd, hwndClient, hclinfo)) goto errRtn; errRtn: if (lpopt) GlobalUnlock (hopt); if (pclinfo) LocalUnlock (hclinfo); if (retval == OLE_OK) { // !!! make sure that we have to free the data GlobalFree (hopt); }else { if (hclinfo) LocalFree (hclinfo); } return retval; } BOOL INTERNAL IsFormatAvailable (lpclient, cfFormat) LPCLIENT lpclient; OLECLIPFORMAT cfFormat; { OLECLIPFORMAT cfNext = 0; do{ #ifdef FIREWALLS if (!CheckPointer (lpclient->lpoleobject, WRITE_ACCESS)) ASSERT (0, "Invalid LPOLEOBECT") else if (!CheckPointer (lpclient->lpoleobject->lpvtbl, WRITE_ACCESS)) ASSERT (0, "Invalid LPOLEOBJECTVTBL") else ASSERT (lpclient->lpoleobject->lpvtbl->EnumFormats, "Invalid pointer to EnumFormats method") #endif cfNext = (*lpclient->lpoleobject->lpvtbl->EnumFormats) (lpclient->lpoleobject, cfNext); if (cfNext == cfFormat) return TRUE; }while (cfNext != 0); return FALSE; } //ScanItemOptions: Scan for the item options like Close/Save etc. OLESTATUS INTERNAL ScanItemOptions (lpbuf, lpoptions) LPSTR lpbuf; int far *lpoptions; { ATOM aModifier; *lpoptions = OLE_CHANGED; while ( *lpbuf && *lpbuf != '/') { #if defined(FE_SB) //[J1] lpbuf = AnsiNext( lpbuf ); //[J1] #else //[J1] lpbuf++; #endif } //[J1] // no modifier same as /change if (*lpbuf == NULL) return OLE_OK; *lpbuf++ = NULL; // 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; } // unknow modifier return OLE_ERROR_SYNTAX; } //RequestData: Sends data in response to a DDE Request message. // for agiven doc and an object. OLESTATUS INTERNAL RequestData (lpdoc, hwndClient, lparam, lphdde) LPDOC lpdoc; HWND hwndClient; LONG lparam; LPHANDLE lphdde; { OLESTATUS retval = OLE_OK; HANDLE hdata; LPCLIENT lpclient; char buf[6]; // If edit environment Send data if we can if ((HIWORD (lparam)) == aEditItems) return RequestDataStd (lparam, lphdde); // Get the object. retval = FindItem (lpdoc, (LPSTR) MAKEINTATOM(HIWORD(lparam)), (LPCLIENT FAR *)&lpclient); if (retval != OLE_OK) goto errRtn; retval = OLE_ERROR_DATATYPE; if (!IsFormatAvailable (lpclient, (int)(LOWORD (lparam)))) goto errRtn; // Now ask the item for the given format data #ifdef FIREWALLS ASSERT (lpclient->lpoleobject->lpvtbl->GetData, "Invalid pointer to GetData method") #endif MapToHexStr ((LPSTR)buf, hwndClient); SendDevInfo (lpclient, (LPSTR)buf); retval = (*lpclient->lpoleobject->lpvtbl->GetData) (lpclient->lpoleobject, (int)(LOWORD (lparam)), (LPHANDLE)& hdata); if (retval != OLE_OK) goto errRtn; if (LOWORD(lparam) == CF_METAFILEPICT) ChangeOwner (hdata); // Duplicate the DDE data if(MakeDDEData(hdata, (int)(LOWORD (lparam)), lphdde, TRUE)){ // !!! Why do we have to duplicate the atom DuplicateAtom ((ATOM)(HIWORD (lparam))); return OLE_OK; } else return OLE_ERROR_MEMORY; errRtn: return retval; } //MakeDDEData: Create a Global DDE data handle from the server // app data handle. BOOL INTERNAL MakeDDEData (hdata, format, lph, fResponse) HANDLE hdata; LPHANDLE lph; int format; BOOL fResponse; { DWORD size; HANDLE hdde = NULL; DDEDATA FAR *lpdata= NULL; BOOL bnative; LPSTR lpdst; LPSTR lpsrc; if (!hdata) { *lph = NULL; return TRUE; } if (bnative = !(format == CF_METAFILEPICT || format == CF_DIB || format == CF_BITMAP)) size = GlobalSize (hdata) + sizeof (DDEDATA); else size = sizeof (LONG) + sizeof (DDEDATA); hdde = (HANDLE) GlobalAlloc (GMEM_DDESHARE | GMEM_ZEROINIT, size); if (hdde == NULL || (lpdata = (DDEDATA FAR *) GlobalLock (hdde)) == NULL) goto errRtn; // set the data otions. Ask the client to delete // it always. lpdata->fRelease = TRUE; // release the data lpdata->cfFormat = format; lpdata->fResponse = fResponse; if (!bnative) // If not native, stick in the handle what the server gave us. *(LPHANDLE)lpdata->Value = hdata; else { // copy the native data junk here. lpdst = (LPSTR)lpdata->Value; if(!(lpsrc = (LPSTR)GlobalLock (hdata))) goto errRtn; size -= sizeof (DDEDATA); UtilMemCpy (lpdst, lpsrc, size); GlobalUnlock (hdata); GlobalFree (hdata); } GlobalUnlock (hdde); *lph = hdde; return TRUE; errRtn: if (lpdata) GlobalUnlock (hdde); if (hdde) GlobalFree (hdde); if (bnative) GlobalFree (hdata); return FALSE; } // ItemCallback: Calback routine for the server to inform the // data changes. When the change message is received, DDE data // message is sent to each of the clients depending on the // options. int FAR PASCAL ItemCallback (lpoleclient, msg, lpoleobject) LPOLECLIENT lpoleclient; WORD msg; // notification message LPOLEOBJECT lpoleobject; { LPCLIENT lpclient; int retval = OLE_OK; HANDLE hdata = NULL; LPSTR lpdata = NULL; LPDOC lpdoc; HWND hStdWnd; lpclient = (LPCLIENT)lpoleclient; lpdoc = (LPDOC)GetWindowLong (GetParent (lpclient->hwnd), 0); if (msg == OLE_RENAMED) { #ifdef FIREWALLS if (!CheckPointer (lpoleobject, WRITE_ACCESS)) ASSERT (0, "Invalid lpoleobject") else if (!CheckPointer (lpoleobject->lpvtbl, WRITE_ACCESS)) ASSERT (0, "Invalid LPOLEOBJECTVTBL") else ASSERT (lpoleobject->lpvtbl->GetData, "Invalid pointer to GetData method") #endif if (IsFormatAvailable (lpclient, cfLink)) { // Get the link data. retval = (*lpoleobject->lpvtbl->GetData) (lpoleobject, (int)cfLink, (LPHANDLE)&hdata); } else { if(IsFormatAvailable (lpclient, cfOwnerLink)) { // Get the link data. retval = (*lpoleobject->lpvtbl->GetData) (lpoleobject, (int)cfOwnerLink, (LPHANDLE)& hdata); #ifdef FIREWALLS ASSERT (retval != OLE_BUSY, "Getdata returns with OLE_BUSY") #endif } else retval = OLE_ERROR_DATATYPE; } if (retval != OLE_OK) goto errrtn; if (!(lpdata = (LPSTR)GlobalLock (hdata))) goto errrtn; if (lpdoc->aDoc) { GlobalDeleteAtom (lpdoc->aDoc); lpdoc->aDoc = NULL; } // Move the string to the beginning and still terminated by null; lstrcpy (lpdata, lpdata + lstrlen (lpdata) + 1); lpdoc->aDoc = GlobalAddAtom (lpdata); // Now make the DDE data block GlobalUnlock (hdata); lpdata = NULL; // find if any StdDocName item is present at all if (!(hStdWnd = SearchItem (lpdoc, (LPSTR) MAKEINTATOM(aStdDocName)))) GlobalFree (hdata); else { // hdata is freed by Makeddedata if (!MakeDDEData (hdata, (int)cfBinary, (LPHANDLE)&hddeRename, FALSE)) { retval = OLE_ERROR_MEMORY; goto errrtn; } EnumProps(hStdWnd, lpSendRenameMsg); // post all the messages with yield which have been collected in enum // UnblockPostMsgs (hStdWnd, FALSE); GlobalFree (hddeRename); } // static. Avoid this. This may not cause any problems for now. // if there is any better way, change it. hwndRename = hStdWnd; // Post termination for each of the doc clients. EnumProps (lpdoc->hwnd, lpEnumForTerminate); lpdoc->fEmbed = FALSE; // post all the messages with yield which have been collected in enum // UnblockPostMsgs (lpdoc->hwnd, FALSE); return OLE_OK; errrtn: if (lpdata) GlobalUnlock (hdata); if (hdata) GlobalFree (hdata); return retval; } else { // !!! any better way to do instead of putting in static // (There may not be any problems since we are not allowing // any messages to get thru while we are posting messages). if ((enummsg = msg) == OLE_SAVED) fAdviseSaveItem = FALSE; enumlpoleobject = lpoleobject; #ifdef FIREWALLS ASSERT (lpclient->hwnd && IsWindowValid (lpclient->hwnd), " Not valid object") #endif // Enumerate all the clients and send DDE_DATA if necessary. EnumProps(lpclient->hwnd, lpSendDataMsg); // post all the messages with yield which have been collected in enum // UnblockPostMsgs (lpclient->hwnd, FALSE); if ((msg == OLE_SAVED) && lpdoc->fEmbed && !fAdviseSaveItem) return OLE_ERROR_CANT_UPDATE_CLIENT; return OLE_OK; } } BOOL FAR PASCAL EnumForTerminate (hwnd, lpstr, hdata) HWND hwnd; LPSTR lpstr; HANDLE hdata; { LPDOC lpdoc; lpdoc = (LPDOC)GetWindowLong (hwnd , 0); // This client is in the rename list. So, no terminate if(hwndRename && FindClient (hwndRename, (HWND)hdata)) return TRUE; if (PostMessageToClientWithBlock ((HWND)hdata, WM_DDE_TERMINATE, hwnd, NULL)) lpdoc->termNo++; //DeleteClient (hwnd, (HWND)hdata); //lpdoc->cClients--; return TRUE; } BOOL FAR PASCAL SendRenameMsg (hwnd, lpstr, hclinfo) HWND hwnd; LPSTR lpstr; HANDLE hclinfo; { ATOM aData = NULL; HANDLE hdde = NULL; PCLINFO pclinfo = NULL; HWND hwndClient; if (!(pclinfo = (PCLINFO) LocalLock (hclinfo))) goto errrtn; // Make the item atom with the options. aData = DuplicateAtom (aStdDocName); hdde = DuplicateData (hddeRename); hwndClient = pclinfo->hwnd; LocalUnlock (hclinfo); // Post the message if (!PostMessageToClientWithBlock (hwndClient, WM_DDE_DATA, (HWND)GetParent (hwnd), MAKELONG (hdde, aData))) goto errrtn; return TRUE; errrtn: if (hdde) GlobalFree (hdde); if (aData) GlobalDeleteAtom (aData); return TRUE; } //SendDataMsg: Send data to the clients, if the data change options //match the data advise options. BOOL FAR PASCAL SendDataMsg (hwnd, lpstr, hclinfo) HWND hwnd; LPSTR lpstr; HANDLE hclinfo; { PCLINFO pclinfo = NULL; HANDLE hdde = NULL; ATOM aData = NULL; int retval; HANDLE hdata; LPCLIENT lpclient; if (!(pclinfo = (PCLINFO) LocalLock (hclinfo))) goto errRtn; lpclient = (LPCLIENT)GetWindowLong (hwnd, 0); #ifdef FIREWALLS ASSERT ((CheckPointer(lpclient, WRITE_ACCESS)), "In Item the client handle missing") #endif // if the client dead, then no message if (!IsWindowValid(pclinfo->hwnd)) goto errRtn; if (pclinfo->options & (0x0001 << enummsg)) { fAdviseSaveItem = TRUE; SendDevInfo (lpclient, lpstr); // send message if the client needs data for every change or // only for the selective ones he wants. // now look for the data option. if (pclinfo->bnative){ // prepare native data if (pclinfo->bdata){ // Wants the data with DDE_DATA message // Get native data from the server. #ifdef FIREWALLS if (!CheckPointer (enumlpoleobject, WRITE_ACCESS)) ASSERT (0, "Invalid LPOLEOBECT") else if (!CheckPointer (enumlpoleobject->lpvtbl,WRITE_ACCESS)) ASSERT (0, "Invalid LPOLEOBJECTVTBL") else ASSERT (enumlpoleobject->lpvtbl->GetData, "Invalid pointer to GetData method") #endif retval = (*enumlpoleobject->lpvtbl->GetData) (enumlpoleobject, (int)cfNative, (LPHANDLE)& hdata); #ifdef FIREWALLS ASSERT (retval != OLE_BUSY, "Getdata returns with OLE_BUSY"); #endif if (retval != OLE_OK) goto errRtn; // Prepare the DDE data block. if(!MakeDDEData (hdata, (int)cfNative, (LPHANDLE)&hdde, FALSE)) goto errRtn; } // Make the item atom with the options. aData = MakeDataAtom (lpclient->aItem, enummsg); // Post the message if (!PostMessageToClientWithBlock (pclinfo->hwnd, WM_DDE_DATA, (HWND)GetParent (hwnd), MAKELONG (hdde, aData))) goto errRtn; hdde = NULL; aData = NULL; } // Now post the data for the disply format if (pclinfo->format){ if (pclinfo->bdata){ #ifdef FIREWALLS if (!CheckPointer (enumlpoleobject, WRITE_ACCESS)) ASSERT (0, "Invalid LPOLEOBECT") else if (!CheckPointer (enumlpoleobject->lpvtbl,WRITE_ACCESS)) ASSERT (0, "Invalid LPOLEOBJECTVTBL") else ASSERT (enumlpoleobject->lpvtbl->GetData, "Invalid pointer to GetData method") #endif retval = (*enumlpoleobject->lpvtbl->GetData) (enumlpoleobject, pclinfo->format, (LPHANDLE)& hdata); #ifdef FIREWALLS ASSERT (retval != OLE_BUSY, "Getdata returns with OLE_BUSY"); #endif if (retval != OLE_OK) goto errRtn; if (pclinfo->format == CF_METAFILEPICT) ChangeOwner (hdata); if(!MakeDDEData (hdata, pclinfo->format, (LPHANDLE)&hdde, FALSE)) goto errRtn; } // atom is deleted. So, we need to duplicate for every post aData = MakeDataAtom (lpclient->aItem, enummsg); // now post the message to the client; if (!PostMessageToClientWithBlock (pclinfo->hwnd, WM_DDE_DATA, (HWND)GetParent (hwnd), MAKELONG (hdde, aData))) goto errRtn; hdde = NULL; aData = NULL; } } errRtn: if (pclinfo) LocalUnlock (hclinfo); if (hdde) GlobalFree (hdde); if (aData) GlobalDeleteAtom (aData); return TRUE; } // IsAdviseStdItems: returns true if the item is one of the standard items // StdDocName; BOOL INTERNAL IsAdviseStdItems (aItem) ATOM aItem; { if ( aItem == aStdDocName) return TRUE; else return FALSE; } // GetStdItemIndex: returns index to Stditems in the "stdStrTable" if the item // is one of the standard items StdHostNames, StdTargetDevice, // StdDocDimensions, StdColorScheme int INTERNAL GetStdItemIndex (aItem) ATOM aItem; { char str[MAX_STR]; if (!aItem) return NULL; if (!GlobalGetAtomName (aItem, (LPSTR) str, MAX_STR)) return NULL; if (!lstrcmpi (str, stdStrTable[STDTARGETDEVICE])) return STDTARGETDEVICE; else if (!lstrcmpi (str, stdStrTable[STDHOSTNAMES])) return STDHOSTNAMES; else if (!lstrcmpi (str, stdStrTable[STDDOCDIMENSIONS])) return STDDOCDIMENSIONS; else if (!lstrcmpi (str, stdStrTable[STDCOLORSCHEME])) return STDCOLORSCHEME; return NULL; } // PokeStdItems: Pokes the data for the standard items. // For StdHostnames, StdDocDimensions and SetColorScheme the data is // sent immediately and for the the StdTargetDeviceinfo the // data is set in each client block and the data is sent just // before the GetData call for rendering the right data. OLESTATUS INTERNAL PokeStdItems (lpdoc, hwndClient, lparam) LPDOC lpdoc; HWND hwndClient; LONG lparam; { int index; DDEDATA FAR * lpdata = NULL; HANDLE hdata = NULL; HANDLE hnew = NULL; LPOLESERVERDOC lpoledoc; LPHOSTNAMES lphostnames; OLESTATUS retval = OLE_ERROR_MEMORY; int format; BOOL fRelease; RECT rcDoc; index = HIWORD(lparam); hdata = (HANDLE)(LOWORD (lparam)); if(!(hdata && (lpdata = (DDEDATA FAR *)GlobalLock (hdata)))) goto errRtn; format = lpdata->cfFormat; fRelease = lpdata->fRelease; #ifdef FIREWALSS ASSERT (format == (int)cfBinary, "Format is not binary"); #endif // we have extracted the data successfully. lpoledoc = lpdoc->lpoledoc; #ifdef FIREWALLS if (!CheckPointer (lpoledoc, WRITE_ACCESS)) ASSERT (0, "Invalid LPOLESERVERDOC") else if (!CheckPointer (lpoledoc->lpvtbl, WRITE_ACCESS)) ASSERT (0, "Invalid LPOLESERVERDOCVTBL") #endif if (index == STDHOSTNAMES){ lphostnames = (LPHOSTNAMES)lpdata->Value; #ifdef FIREWALLS ASSERT (lpoledoc->lpvtbl->SetHostNames, "Invalid pointer to SetHostNames method") #endif retval = (*lpoledoc->lpvtbl->SetHostNames)(lpdoc->lpoledoc, (LPSTR)lphostnames->data, ((LPSTR)lphostnames->data) + lphostnames->documentNameOffset); goto end; } if (index == STDDOCDIMENSIONS){ #ifdef FIREWALLS ASSERT (lpoledoc->lpvtbl->SetDocDimensions, "Invalid pointer to SetDocDimensions method") #endif rcDoc.left = 0; rcDoc.top = ((LPRECT)(lpdata->Value))->top; rcDoc.bottom = 0; rcDoc.right = ((LPRECT)lpdata->Value)->left; retval = (*lpoledoc->lpvtbl->SetDocDimensions)(lpdoc->lpoledoc, (LPRECT)&rcDoc); goto end; } if (index == STDCOLORSCHEME) { #ifdef FIREWALLS ASSERT (lpoledoc->lpvtbl->SetColorScheme, "Invalid pointer to SetColorScheme method") #endif retval = (*lpoledoc->lpvtbl->SetColorScheme)(lpdoc->lpoledoc, (LPLOGPALETTE) lpdata->Value); goto end; } #ifdef FIREWALLS ASSERT (index == STDTARGETDEVICE, "Unknown standard item"); #endif // case of the printer decvice info if (!(hnew = MakeItemData ((DDEPOKE FAR *)lpdata, hdata, format))) goto errRtn; // Go thru the all the items lists for this doc and replace the // printer device info information. // Free the block we duplicated. retval = SetStdInfo (lpdoc, hwndClient, (LPSTR) (MAKELONG(STDTARGETDEVICE,0)),hnew); end: errRtn: if (hnew) // can only be global memory block GlobalFree (hnew); if (lpdata) { GlobalUnlock (hdata); if (retval == OLE_OK && fRelease) GlobalFree (hdata); } return retval; } // SetStdInfo: Sets the targetdevice info. Creates a client // for "StdTargetDevice". This item is created only within the // lib and it is never visible in server app. When the change // message comes from the server app, before we ask for // the data, we send the targetdevice info if there is // info for the client whom we are trying to send the data // on advise. int INTERNAL SetStdInfo (lpdoc, hwndClient, lpitemname, hdata) LPDOC lpdoc; HWND hwndClient; LPSTR lpitemname; HANDLE hdata; { HWND hwnd; HANDLE hclinfo = NULL; PCLINFO pclinfo = NULL; LPCLIENT lpclient; OLESTATUS retval = OLE_OK; // first create/find the StdTargetDeviceItem. if ((hwnd = SearchItem (lpdoc, lpitemname)) == NULL){ retval = RegisterItem ((LHDOC)lpdoc, lpitemname, (LPCLIENT FAR *)&lpclient, FALSE); if (retval != OLE_OK) goto errRtn; hwnd = lpclient->hwnd; } #ifdef FIREWALLS ASSERT (retval == OLE_OK, "No StdTragetDevice or StdDocname item"); #endif if(hclinfo = FindClient (hwnd, hwndClient)){ if (pclinfo = (PCLINFO) LocalLock (hclinfo)){ if (pclinfo->hdevInfo) GlobalFree (pclinfo->hdevInfo); pclinfo->bnewDevInfo = TRUE; if (hdata) pclinfo->hdevInfo = DuplicateData (hdata); else pclinfo->hdevInfo = NULL; pclinfo->hwnd = hwndClient; LocalUnlock (hclinfo); // We do not have to reset the client because we did not // change the handle it self. } } else { // Create the client structure to be attcahed to the object. hclinfo = LocalAlloc (LMEM_MOVEABLE | LMEM_ZEROINIT, sizeof (CLINFO)); if (hclinfo == NULL || (pclinfo = (PCLINFO) LocalLock (hclinfo)) == NULL) goto errRtn; pclinfo->bnewDevInfo = TRUE; if (hdata) pclinfo->hdevInfo = DuplicateData (hdata); else pclinfo->hdevInfo = NULL; pclinfo->hwnd = hwndClient; LocalUnlock (hclinfo); // Now add this client to item client list // !!! This error recovery is not correct. if (!AddClient (hwnd, hwndClient, hclinfo)) goto errRtn; } return OLE_OK; errRtn: if (pclinfo) LocalUnlock (hclinfo); if (hclinfo) LocalFree (hclinfo); return OLE_ERROR_MEMORY; } // SendDevInfo: Sends targetdevice info to the the object. // Caches the last targetdevice info sent to the object. // If the targetdevice block is same as the one in the // cache, then no targetdevice info is sent. // (!!! There might be some problem here getting back // the same global handle). void INTERNAL SendDevInfo (lpclient, lppropname) LPCLIENT lpclient; LPSTR lppropname; { HANDLE hclinfo = NULL; PCLINFO pclinfo = NULL; HANDLE hdata; OLESTATUS retval; HWND hwnd; LPDOC lpdoc; lpdoc = (LPDOC)GetWindowLong (GetParent (lpclient->hwnd), 0); // find if any StdTargetDeviceInfo item is present at all hwnd = SearchItem (lpdoc, (LPSTR) (MAKELONG(STDTARGETDEVICE, 0))); if (hwnd == NULL) return; hclinfo = GetProp (hwnd, lppropname); // This client has not set any target device info. no need to send // any stdtargetdevice info if (hclinfo != NULL) { if (!(pclinfo = (PCLINFO)LocalLock (hclinfo))) goto end; // if we cached it, do not send it again. if ((!pclinfo->bnewDevInfo) && pclinfo->hdevInfo == lpclient->hdevInfo) goto end; pclinfo->bnewDevInfo = FALSE; if(!(hdata = DuplicateData (pclinfo->hdevInfo))) goto end; } else { // already screen if (!lpclient->hdevInfo) goto end; //for screen send NULL. hdata = NULL; } // Now send the targetdevice info #ifdef FIREWALLS if (!CheckPointer (lpclient->lpoleobject, WRITE_ACCESS)) ASSERT (0, "Invalid LPOLEOBECT") else if (!CheckPointer (lpclient->lpoleobject->lpvtbl, WRITE_ACCESS)) ASSERT (0, "Invalid LPOLEOBJECTVTBL") else ASSERT (lpclient->lpoleobject->lpvtbl->SetTargetDevice, "Invalid pointer to SetTargetDevice method") #endif retval = (*lpclient->lpoleobject->lpvtbl->SetTargetDevice) (lpclient->lpoleobject, hdata); if (retval == OLE_OK) { if (pclinfo) lpclient->hdevInfo = pclinfo->hdevInfo; else lpclient->hdevInfo = NULL; } // !!! error case who frees the data?' end: if (pclinfo) LocalUnlock (hclinfo); return; } void ChangeOwner (hmfp) HANDLE hmfp; { LPMETAFILEPICT lpmfp; if (lpmfp = (LPMETAFILEPICT) GlobalLock (hmfp)) { if (bWin30) GiveToGDI (lpmfp->hMF); else { if (lpfnSetMetaFileBitsBetter) (*lpfnSetMetaFileBitsBetter) (lpmfp->hMF); } GlobalUnlock (hmfp); } } HANDLE INTERNAL MakeItemData (lpPoke, hPoke, cfFormat) DDEPOKE FAR * lpPoke; HANDLE hPoke; OLECLIPFORMAT cfFormat; { HANDLE hnew; LPSTR lpnew; DWORD dwSize; if (cfFormat == CF_METAFILEPICT) return DuplicateMetaFile (*(LPHANDLE)lpPoke->Value); if (cfFormat == CF_BITMAP) return DuplicateBitmap (*(LPHANDLE)lpPoke->Value); if (cfFormat == CF_DIB) return DuplicateData (*(LPHANDLE)lpPoke->Value); // Now we are dealing with normal case if (!(dwSize = GlobalSize (hPoke))) return NULL; dwSize = dwSize - sizeof (DDEPOKE) + sizeof(BYTE); if (hnew = GlobalAlloc (GMEM_MOVEABLE, dwSize)) { if (lpnew = GlobalLock (hnew)) { UtilMemCpy (lpnew, (LPSTR) lpPoke->Value, dwSize); GlobalUnlock (hnew); } else { GlobalFree (hnew); hnew = NULL; } } return hnew; } HANDLE INTERNAL DuplicateMetaFile (hSrcData) HANDLE hSrcData; { LPMETAFILEPICT lpSrcMfp; LPMETAFILEPICT lpDstMfp = NULL; HANDLE hMF = NULL; HANDLE hDstMfp = NULL; if (!(lpSrcMfp = (LPMETAFILEPICT) GlobalLock(hSrcData))) return NULL; GlobalUnlock (hSrcData); if (!(hMF = CopyMetaFile (lpSrcMfp->hMF, NULL))) return NULL; if (!(hDstMfp = GlobalAlloc (GMEM_MOVEABLE, sizeof(METAFILEPICT)))) goto errMfp; if (!(lpDstMfp = (LPMETAFILEPICT) GlobalLock (hDstMfp))) goto errMfp; GlobalUnlock (hDstMfp); *lpDstMfp = *lpSrcMfp; lpDstMfp->hMF = hMF; return hDstMfp; errMfp: if (hMF) DeleteMetaFile (hMF); if (hDstMfp) GlobalFree (hDstMfp); return NULL; } HBITMAP INTERNAL DuplicateBitmap (hold) HBITMAP hold; { HBITMAP hnew; HANDLE hMem; LPSTR lpMem; LONG retVal = TRUE; DWORD dwSize; BITMAP bm; // !!! another way to duplicate the bitmap GetObject (hold, sizeof(BITMAP), (LPSTR) &bm); dwSize = ((DWORD) bm.bmHeight) * ((DWORD) bm.bmWidthBytes) * ((DWORD) bm.bmPlanes) * ((DWORD) bm.bmBitsPixel); if (!(hMem = GlobalAlloc (GMEM_MOVEABLE | GMEM_ZEROINIT, dwSize))) return NULL; if (!(lpMem = GlobalLock (hMem))){ GlobalFree (hMem); return NULL; } GetBitmapBits (hold, dwSize, lpMem); if (hnew = CreateBitmap (bm.bmWidth, bm.bmHeight, bm.bmPlanes, bm.bmBitsPixel, NULL)) retVal = SetBitmapBits (hnew, dwSize, lpMem); GlobalUnlock (hMem); GlobalFree (hMem); if (hnew && (!retVal)) { DeleteObject (hnew); hnew = NULL; } return hnew; }