windows-nt/Source/XPSP1/NT/base/mvdm/wow16/ole/server/item.c

1778 lines
46 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/****************************** 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;
}