/****************************** Module Header ******************************\ * Module Name: Item.c Object(item) main module * * Purpose: Includes All the object releated routiens. * * Created: Oct 1990. * * Copyright (c) 1990 - 1992 Microsoft Corporation * * History: * Raor (../10/1990) Designed, coded * curts created portable version for WIN16/32 * \***************************************************************************/ #include "windows.h" #include "cmacs.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; #ifdef WIN16 extern BOOL bWin30; #endif HWND hwndItem; HANDLE hddeRename; HWND hwndRename; UINT enummsg; UINT enuminfo; LPOLEOBJECT enumlpoleobject; OLECLIENTVTBL clVtbl; BOOL bClientUnlink; BOOL fAdviseSaveDoc; BOOL fAdviseSaveItem; char * stdStrTable[STDHOSTNAMES+1] = { NULL, "StdTargetDevice", "StdDocDimensions", "StdColorScheme", "StdHostNames" }; #ifdef WIN16 extern HANDLE (FAR PASCAL *lpfnSetMetaFileBitsBetter) (HANDLE); #endif 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 lpdoc, LPSTR lpitemname ){ ATOM aItem; Puts ("SearchItem"); // If the item passed is an atom, get its name. if (!HIWORD(lpitemname)) aItem = (ATOM) (LOWORD(lpitemname)); else if (!lpitemname[0]) aItem = (ATOM)0; 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, (WNDENUMPROC)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 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(lpitemname), (LPSTR)buf, MAX_STR); else buf[0] = '\0'; lpitemname = (LPSTR)buf; } if (hwnd) { // we found the item window lpclient = (LPCLIENT)GetWindowLongPtr (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 lhdoc, LPSTR lpitemname, LPCLIENT FAR * lplpclient, BOOL bSrvr ){ LPDOC lpdoc; HANDLE hclient = NULL; LPCLIENT lpclient = NULL; OLESTATUS 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(lpitemname); } else if (!lpitemname[0]) lpclient->aItem = (ATOM)0; else lpclient->aItem = GlobalAddAtom (lpitemname); lpclient->oleClient.lpvtbl = &clVtbl; lpclient->oleClient.lpvtbl->CallBack = ItemCallBack; 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 = CreateWindowEx (WS_EX_NOPARENTNOTIFY,"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. SetWindowLongPtr (lpclient->hwnd, 0, (LONG_PTR)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 ){ return RevokeObject (lpoleclient, TRUE); } // OleRevokeObject: Revokes an object (unregisres an object // from the document tree. OLESTATUS INTERNAL RevokeObject ( 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 = (ATOM)0; } if (lpclient->hwnd) { SetWindowLongPtr (lpclient->hwnd, 0, (LONG_PTR)NULL); // another static for enumerating the properties. // we need to change these . bClientUnlink = bUnlink; EnumProps(lpclient->hwnd, (PROPENUMPROC)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 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)GetWindowLongPtr (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 hwnd, LONG data ){ LPCLIENT lpclient; int cmd; HANDLE hclinfo; PCLINFO pclinfo = NULL; lpclient = (LPCLIENT)GetWindowLongPtr (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 ( HWND hwndDoc, HWND hwndClient ){ EnumChildWindows (hwndDoc, (WNDENUMPROC)lpFindItemWnd, MAKELONG (hwndClient, ITEM_DELETECLIENT)); } // DeleteAllItems: Deletes all the objects of a given // document window. void INTERNAL DeleteAllItems ( HWND hwndDoc ){ EnumChildWindows (hwndDoc, (WNDENUMPROC)lpFindItemWnd, MAKELONG (NULL, ITEM_DELETE)); } // Object widnow proc: LRESULT FAR PASCAL ItemWndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam ){ LPCLIENT lpclient; lpclient = (LPCLIENT)GetWindowLongPtr (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 lpdoc, HWND hwndClient, LPARAM lparam ){ OLESTATUS retval = OLE_ERROR_MEMORY; LPCLIENT lpclient; DDEPOKE FAR * lpPoke = NULL; HANDLE hPoke = NULL; HANDLE hnew = NULL; OLECLIPFORMAT format; BOOL fRelease = FALSE; ATOM aItem = GET_WM_DDE_POKE_ITEM((WPARAM)NULL,lparam); UNREFERENCED_PARAMETER(hwndClient); // Get the object handle first. Look in the registration // tree and if one is not created otherwise create one. retval = FindItem (lpdoc, (LPSTR) MAKEINTATOM(aItem), (LPCLIENT FAR *)&lpclient); if (retval != OLE_OK) goto errRtn; hPoke = GET_WM_DDE_POKE_HDATA((WPARAM)NULL,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 lpdoc, HWND hwndClient, LPARAM lparam ){ char buf[MAX_STR]; int options; LPCLIENT lpclient; OLESTATUS retval = OLE_ERROR_MEMORY; HANDLE hclinfo = NULL; PCLINFO pclinfo = NULL; UNREFERENCED_PARAMETER(hwndClient); if (!(HIWORD (lparam))) buf[0] = '\0'; 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] == '\0') { // 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 lpdoc, HWND hwndClient, LPARAM lparam, BOOL FAR * lpfack ){ HANDLE hopt = GET_WM_DDE_ADVISE_HOPTIONS((WPARAM)NULL,lparam); ATOM aItem = GET_WM_DDE_ADVISE_ITEM((WPARAM)NULL,lparam); DDEADVISE FAR *lpopt; OLESTATUS retval = OLE_ERROR_MEMORY; if(!(lpopt = (DDEADVISE FAR *) GlobalLock (hopt))) goto errrtn; #ifdef FIREWALLS ASSERT ((aItem == 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 lpdoc, HWND hwndClient, LPARAM lparam, BOOL FAR * lpfack ){ HANDLE hopt = GET_WM_DDE_ADVISE_HOPTIONS((WPARAM)NULL,lparam); ATOM aitem = GET_WM_DDE_ADVISE_ITEM((WPARAM)NULL,lparam); DDEADVISE FAR *lpopt = NULL; OLECLIPFORMAT format = 0; char buf[MAX_STR]; int options; LPCLIENT lpclient; OLESTATUS retval = OLE_ERROR_MEMORY; HANDLE hclinfo = NULL; PCLINFO pclinfo = NULL; if(!(lpopt = (DDEADVISE FAR *) GlobalLock (hopt))) goto errRtn; if (!aitem) buf[0] = '\0'; else GlobalGetAtomName (aitem, (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 == (SHORT)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 = (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 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 ( LPSTR lpbuf, int far *lpoptions ){ ATOM aModifier; *lpoptions = OLE_CHANGED; 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; } // 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 lpdoc, HWND hwndClient, LPARAM lparam, LPHANDLE lphdde ){ OLESTATUS retval = OLE_OK; HANDLE hdata; LPCLIENT lpclient; char buf[20]; // 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, (OLECLIPFORMAT)(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, (OLECLIPFORMAT)(LOWORD(lparam)), (LPHANDLE)&hdata); if (retval != OLE_OK) goto errRtn; if (LOWORD(lparam) == CF_METAFILEPICT) ChangeOwner (hdata); // Duplicate the DDE data if (MakeDDEData(hdata, (OLECLIPFORMAT)(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; } #ifdef WIN32 HANDLE INTERNAL BmDuplicate ( HBITMAP hold ){ 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 | GMEM_DDESHARE, dwSize+sizeof(BITMAP)+sizeof(DWORD)))) return NULL; if (!(lpMem = (LPBYTE)GlobalLock (hMem))){ GlobalFree (hMem); return NULL; } *((DWORD FAR *) lpMem) = dwSize; *(BITMAP FAR *) (lpMem+sizeof(DWORD)) = bm; lpMem += (sizeof(DWORD) + sizeof (BITMAP)); dwSize = GetBitmapBits (hold, 0, NULL); retVal = GetBitmapBits (hold, dwSize, lpMem); GlobalUnlock (hMem); return hMem; } #endif //MakeDDEData: Create a Global DDE data handle from the server // app data handle. BOOL INTERNAL MakeDDEData ( HANDLE hdata, OLECLIPFORMAT format, LPHANDLE lph, 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 || format == CF_ENHMETAFILE)) size = (DWORD)GlobalSize (hdata) + sizeof (DDEDATA); else #ifdef WIN32HACK { if (format == CF_BITMAP) hdata = BmDuplicate(hdata); size = sizeof (HANDLE_PTR) + sizeof (DDEDATA); } #else size = sizeof (HANDLE_PTR) + sizeof (DDEDATA); #endif 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 = (WORD)format; lpdata->fResponse = (WORD)fResponse; if (!bnative) { // If not native, stick in the handle what the server gave us. // Com1x bug 23211: data misalignment: truncate handle to 32 bits on Win64 // because a) handle is only 32 bit significant; b) this was causing data misalignment // error; c) we're only allocating 32 bits for it above. #ifdef _WIN64 if (format == CF_METAFILEPICT) *(void* __unaligned*)lpdata->Value = hdata; else #endif *(LONG*)lpdata->Value = HandleToLong(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 lpoleclient, OLE_NOTIFICATION 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)GetWindowLongPtr (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, cfLink, (LPHANDLE)&hdata); } else { if(IsFormatAvailable (lpclient, cfOwnerLink)) { // Get the link data. retval = (*lpoleobject->lpvtbl->GetData) (lpoleobject, 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 = (ATOM)0; } // 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, cfBinary, (LPHANDLE)&hddeRename, FALSE)) { retval = OLE_ERROR_MEMORY; goto errrtn; } EnumProps(hStdWnd, (PROPENUMPROC)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, (PROPENUMPROC)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, (PROPENUMPROC)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 hwnd, LPSTR lpstr, HANDLE hdata ){ LPDOC lpdoc; UNREFERENCED_PARAMETER(lpstr); lpdoc = (LPDOC)GetWindowLongPtr (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, (WPARAM)hwnd, (LPARAM)0)) lpdoc->termNo++; //DeleteClient (hwnd, (HWND)hdata); //lpdoc->cClients--; return TRUE; } BOOL FAR PASCAL SendRenameMsg ( HWND hwnd, LPSTR lpstr, HANDLE hclinfo ){ ATOM aData = (ATOM)0; HANDLE hdde = NULL; PCLINFO pclinfo = NULL; HWND hwndClient; LPARAM lParamNew; UNREFERENCED_PARAMETER(lpstr); 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 lParamNew = MAKE_DDE_LPARAM(WM_DDE_DATA,hdde,aData); if (!PostMessageToClientWithBlock (hwndClient,WM_DDE_DATA, (WPARAM)GetParent(hwnd),lParamNew)) { DDEFREE(WM_DDE_DATA,lParamNew); 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 hwnd, LPSTR lpstr, HANDLE hclinfo ){ PCLINFO pclinfo = NULL; HANDLE hdde = NULL; ATOM aData = (ATOM)0; int retval; HANDLE hdata; LPCLIENT lpclient; LPARAM lParamNew; if (!(pclinfo = (PCLINFO) LocalLock (hclinfo))) goto errRtn; lpclient = (LPCLIENT)GetWindowLongPtr (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, 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, cfNative, (LPHANDLE)&hdde, FALSE)) goto errRtn; } // Make the item atom with the options. aData = MakeDataAtom (lpclient->aItem, enummsg); lParamNew = MAKE_DDE_LPARAM(WM_DDE_DATA,hdde,aData); // Post the message if (!PostMessageToClientWithBlock (pclinfo->hwnd, WM_DDE_DATA, (WPARAM)GetParent(hwnd), lParamNew)) { DDEFREE(WM_DDE_DATA,lParamNew); goto errRtn; } hdde = NULL; aData = (ATOM)0; } // 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); Puts("sending metafile..."); 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); lParamNew = MAKE_DDE_LPARAM(WM_DDE_DATA,hdde,aData); // now post the message to the client; if (!PostMessageToClientWithBlock (pclinfo->hwnd, WM_DDE_DATA, (WPARAM)GetParent(hwnd), lParamNew)) { DDEFREE(WM_DDE_DATA,lParamNew); goto errRtn; } hdde = NULL; aData = (ATOM)0; } } 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 ( 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 ( ATOM aItem ){ char str[MAX_STR]; if (!aItem) return 0; if (!GlobalGetAtomName (aItem, (LPSTR) str, MAX_STR)) return 0; 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 0; } // // The wire representation of STDDOCDIMENSIONS is a 16-bit // format. This means instead of 4 longs, there are // 4 shorts. This structure is used below to pick the data // from the wire representation. // backward compatible is the name of the game. // typedef struct tagRECT16 { SHORT left; SHORT top; SHORT right; SHORT bottom; } RECT16, *LPRECT16; // 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 lpdoc, HWND hwndClient, HANDLE hdata, int index ){ DDEDATA FAR * lpdata = NULL; HANDLE hnew = NULL; LPOLESERVERDOC lpoledoc; LPHOSTNAMES lphostnames; OLESTATUS retval = OLE_ERROR_MEMORY; OLECLIPFORMAT format; BOOL fRelease; RECT rcDoc; if(!(hdata && (lpdata = (DDEDATA FAR *)GlobalLock (hdata)))) goto errRtn; format = lpdata->cfFormat; fRelease = lpdata->fRelease; #ifdef FIREWALSS ASSERT (format == 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 = ((LPRECT16)(lpdata->Value))->top; rcDoc.bottom = 0; rcDoc.right = ((LPRECT16)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) ULongToPtr(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 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 lpclient, LPSTR lppropname ){ HANDLE hclinfo = NULL; PCLINFO pclinfo = NULL; HANDLE hdata; OLESTATUS retval; HWND hwnd; LPDOC lpdoc; lpdoc = (LPDOC)GetWindowLongPtr (GetParent (lpclient->hwnd), 0); // find if any StdTargetDeviceInfo item is present at all hwnd = SearchItem (lpdoc, (LPSTR)ULongToPtr(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 ( HANDLE hmfp ){ LPMETAFILEPICT lpmfp; #ifdef WIN16 if (lpmfp = (LPMETAFILEPICT) GlobalLock (hmfp)) { if (bWin30) GiveToGDI (lpmfp->hMF); else { if (lpfnSetMetaFileBitsBetter) (*lpfnSetMetaFileBitsBetter) (lpmfp->hMF); } GlobalUnlock (hmfp); } #endif #ifdef WIN32 UNREFERENCED_PARAMETER(hmfp); UNREFERENCED_PARAMETER(lpmfp); #endif } HANDLE INTERNAL MakeItemData ( DDEPOKE FAR * lpPoke, HANDLE hPoke, OLECLIPFORMAT cfFormat ){ HANDLE hnew; LPSTR lpnew; DWORD dwSize; if (cfFormat == CF_ENHMETAFILE) return CopyEnhMetaFile (LongToHandle(*(LONG*)lpPoke->Value), NULL); if (cfFormat == CF_METAFILEPICT) { #ifdef _WIN64 return DuplicateMetaFile(*(void* _unaligned*)lpPoke->Value); #else return DuplicateMetaFile (*(LPHANDLE)lpPoke->Value); #endif } if (cfFormat == CF_BITMAP) return DuplicateBitmap (LongToHandle(*(LONG*)lpPoke->Value)); if (cfFormat == CF_DIB) return DuplicateData (LongToHandle(*(LONG*)lpPoke->Value)); // Now we are dealing with normal case if (!(dwSize = (DWORD)GlobalSize (hPoke))) return NULL; dwSize = dwSize - sizeof (DDEPOKE) + sizeof(BYTE); if (hnew = GlobalAlloc (GMEM_MOVEABLE | GMEM_DDESHARE, dwSize)) { if (lpnew = GlobalLock (hnew)) { UtilMemCpy (lpnew, (LPSTR) lpPoke->Value, dwSize); GlobalUnlock (hnew); } else { GlobalFree (hnew); hnew = NULL; } } return hnew; } HANDLE INTERNAL DuplicateMetaFile ( 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 ( 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; }