/* server.c - This module contains the OLE server worker/public routines. * * Created by Microsoft Corporation. */ #include "packager.h" #define CBLINKMAX 260 /* * This server only supports one document per instance. The items are * just rectangles over the document, possibly overlapping. */ static LHCLIENTDOC lhClipDoc = 0; static OLESERVERDOCVTBL vdocvtbl; // Document virtual table static OLEOBJECTVTBL vitemvtbl; // Item virtual table static OLESERVERVTBL vsrvrvtbl; // Server virtual table static LPSAMPITEM vlpitem[CITEMSMAX]; // Pointers to active OLE items static INT cItems = 0; // Number of active OLE items static CHAR szClip[] = "Clipboard"; static VOID DeleteDoc(LPSAMPDOC lpdoc); static BOOL SendItemChangeMsg(LPSAMPITEM lpitem, UINT options); static INT FindItem(LPSAMPITEM lpitem); /************ Server initialization and termination routines **********/ /* InitServer() - Initializes the OLE server */ BOOL InitServer( VOID ) { // Allocate the server block if (!(ghServer = LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT, sizeof(PBSRVR))) || !(glpsrvr = (LPSAMPSRVR)LocalLock(ghServer))) goto errRtn; // Initialize the server, document, and item virtual tables vsrvrvtbl.Open = SrvrOpen; vsrvrvtbl.Create = SrvrCreate; vsrvrvtbl.CreateFromTemplate = SrvrCreateFromTemplate; vsrvrvtbl.Edit = SrvrEdit; vsrvrvtbl.Exit = SrvrExit; vsrvrvtbl.Release = SrvrRelease; vsrvrvtbl.Execute = SrvrExecute; vdocvtbl.Save = DocSave; vdocvtbl.Close = DocClose; vdocvtbl.SetHostNames = DocSetHostNames; vdocvtbl.SetDocDimensions = DocSetDocDimensions; vdocvtbl.GetObject = DocGetObject; vdocvtbl.Release = DocRelease; vdocvtbl.SetColorScheme = DocSetColorScheme; vdocvtbl.Execute = DocExecute; vitemvtbl.QueryProtocol = ItemQueryProtocol; vitemvtbl.Release = ItemDelete; vitemvtbl.Show = ItemShow; vitemvtbl.DoVerb = ItemDoVerb; vitemvtbl.GetData = ItemGetData; vitemvtbl.SetData = ItemSetData; vitemvtbl.SetTargetDevice = ItemSetTargetDevice; vitemvtbl.SetBounds = ItemSetBounds; vitemvtbl.EnumFormats = ItemEnumFormats; vitemvtbl.SetColorScheme = ItemSetColorScheme; // Try to register the server glpsrvr->olesrvr.lpvtbl = &vsrvrvtbl; if (Error(OleRegisterServer(gszAppClassName, (LPOLESERVER)glpsrvr, (LONG_PTR * )&glpsrvr->lhsrvr, ghInst, OLE_SERVER_MULTI))) goto errRtn; // Initialize the client name lstrcpy(gszClientName, ""); return TRUE; errRtn: ErrorMessage(E_FAILED_TO_REGISTER_SERVER); // If we failed, clean up if (glpsrvr) { LocalUnlock(ghServer); glpsrvr = NULL; } if (ghServer) LocalFree(ghServer); ghServer = NULL; return FALSE; } /* DeleteServer() - Revokes the OLE server. */ VOID DeleteServer( LPSAMPSRVR lpsrvr ) { if (gfServer) { gfServer = FALSE; OleRevokeServer(lpsrvr->lhsrvr); } } /* DestroyServer() - Deallocates the OLE server. */ VOID DestroyServer( VOID ) { if (ghServer) { // Release the server virtual table and info LocalUnlock(ghServer); LocalFree(ghServer); ghServer = NULL; // Destroy the window only when we're all through DestroyWindow(ghwndFrame); gfServer = FALSE; } } /********************* Document support functions ********************/ /* InitDoc() - Initialize and register the document with the OLE library. */ LPSAMPDOC InitDoc( LPSAMPSRVR lpsrvr, LHSERVERDOC lhdoc, LPSTR lptitle ) { HANDLE hdoc = NULL; LPSAMPDOC lpdoc = NULL; if (!(hdoc = LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT, sizeof(PBDOC))) || !(lpdoc = (LPSAMPDOC)LocalLock(hdoc))) goto errRtn; lpdoc->hdoc = hdoc; lpdoc->aName = GlobalAddAtom(lptitle); lpdoc->oledoc.lpvtbl = &vdocvtbl; if (!lhdoc) { if (Error(OleRegisterServerDoc(lpsrvr->lhsrvr, lptitle, (LPOLESERVERDOC)lpdoc, (LHSERVERDOC * ) & lpdoc->lhdoc))) goto errRtn; } else { lpdoc->lhdoc = lhdoc; } gfDocExists = TRUE; gfDocCleared = FALSE; return lpdoc; errRtn: ErrorMessage(E_FAILED_TO_REGISTER_DOCUMENT); // Clean up if (lpdoc) LocalUnlock(hdoc); if (hdoc) LocalFree(hdoc); return NULL; } /* DeleteDoc() - Notify the OLE library that the document is to be deleted. */ static VOID DeleteDoc( LPSAMPDOC lpdoc ) { if (gfOleClosed) SendDocChangeMsg(lpdoc, OLE_CLOSED); OleRevokeServerDoc(lpdoc->lhdoc); } /* ChangeDocName() - Notify the OLE library that the document name is changing. */ VOID ChangeDocName( LPSAMPDOC *lplpdoc, LPSTR lpname ) { // If the document exists, delete and re-register. if (*lplpdoc) { GlobalDeleteAtom((*lplpdoc)->aName); (*lplpdoc)->aName = GlobalAddAtom(lpname); // // If the document contains items, just notify the children. // If we aren't embedded, always delete and re-register. // OleRenameServerDoc((*lplpdoc)->lhdoc, lpname); if (gfEmbedded && cItems) return; DeleteDoc(*lplpdoc); } *lplpdoc = InitDoc(glpsrvr, 0, lpname); } /* SendDocChangeMsg() - Notify the client that the document has changed. */ BOOL SendDocChangeMsg( LPSAMPDOC lpdoc, UINT options ) { BOOL fSuccess = FALSE; INT i; for (i = 0; i < cItems; i++) { if (SendItemChangeMsg(vlpitem[i], options)) fSuccess = TRUE; } return fSuccess; } /* CreateNewDoc() - Called when a document is newly created. * * Returns: hDocument if document successfully created, NULL otherwise. * Note: This function is only called when the document is being * created through OLE actions. */ LPSAMPDOC CreateNewDoc( LPSAMPSRVR lpsrvr, LHSERVERDOC lhdoc, LPSTR lpstr ) { glpdoc = InitDoc(lpsrvr, lhdoc, lpstr); StringCchCopy(szUntitled, ARRAYSIZE(szUntitled), lpstr); SetTitle(TRUE); return glpdoc; } /* CreateDocFromFile() - Called when a document is to be created from a file. * * Returns: hDocument if document successfully created, NULL otherwise. * Note: This function is only called when the document is being * created through OLE actions. The file name is temporarily * set to load the file, then it is reset to "". This is so * that we won't save back to the template if we exit. */ LPSAMPDOC CreateDocFromFile( LPSAMPSRVR lpsrvr, LHSERVERDOC lhdoc, LPSTR lpstr ) { // Initialize document if (!(glpdoc = InitDoc(lpsrvr, lhdoc, lpstr)) || !(*lpstr)) return NULL; lstrcpy(szUntitled, lpstr); // This could overrun, but I don't see how I can check the length of the lpstr coming in SetTitle(TRUE); return glpdoc; } /********************** Item support functions ************************/ /* CopyObjects() - Copies selection to the clipboard. */ BOOL CopyObjects( VOID ) { HANDLE hdata; // If we can't open the clipboard, fail if (!OpenClipboard(ghwndFrame)) return FALSE; Hourglass(TRUE); // Empty the clipboard EmptyClipboard(); // // Copy the clipboard contents. // // Start with Native Data - which will just contain all the objects // which intersect with the selection rectangle. // if (hdata = GetNative(TRUE)) { SetClipboardData(gcfNative, hdata); OleSavedClientDoc(lhClipDoc); } if (lhClipDoc) { OleRevokeClientDoc(lhClipDoc); lhClipDoc = 0; } if (hdata = GetLink()) SetClipboardData(gcfOwnerLink, hdata); // // Metafile data: Re-invert the image before putting // it onto the clipboard. // if (hdata = GetMF()) SetClipboardData(CF_METAFILEPICT, hdata); CloseClipboard(); Hourglass(FALSE); return TRUE; } /* CreateNewItem() - Allocate a new item. * * Note: lpitem->rc will be filled out by the caller. */ LPSAMPITEM CreateNewItem( LPSAMPDOC lpdoc ) { HANDLE hitem = NULL; LPSAMPITEM lpitem = NULL; // Now create the item if (!(hitem = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, sizeof(ITEM))) || !(lpitem = (LPSAMPITEM)GlobalLock(hitem))) goto errRtn; lpitem->hitem = hitem; lpitem->oleobject.lpvtbl = &vitemvtbl; return lpitem; errRtn: if (lpitem) GlobalUnlock(hitem); if (hitem) GlobalFree(hitem); return NULL; } /* SendItemChangeMsg() - Notify the client that the item has changed. */ static BOOL SendItemChangeMsg( LPSAMPITEM lpitem, UINT options ) { if (lpitem->lpoleclient) { (*lpitem->lpoleclient->lpvtbl->CallBack) (lpitem->lpoleclient, options, (LPOLEOBJECT)lpitem); return TRUE; } return FALSE; } /******************** Data reading/writing functions *********************/ /* GetNative(fClip) - Write the item native format to a memory block. * * This function will just write the objects which intersect * with the selection rectangle into a memory block. If we * are running as an embedded instance, return ALL items, even * those which are not in the selection area. Then we will * never lose objects that we move out of the selection area * when editing an embedded object. * * Args: fClip - TRUE means native data is for copying to clipboard * * Returns: A handle containing the native format, or NULL. */ HANDLE GetNative( BOOL fClip ) { BOOL fSuccess = FALSE; DWORD cBytes = 0L; HANDLE hdata = NULL; LPSTR lpdata = NULL; DWORD cEmbWrite; LPOLEOBJECT lpobjapp = NULL; LPOLEOBJECT lpobjcon = NULL; LPPICT lpcPict; LPPICT lpaPict; WORD w; // Compute the size of the appearance lpaPict = glpobj[APPEARANCE]; lpcPict = glpobj[CONTENT]; switch (gpty[APPEARANCE]) { case ICON: cBytes += IconWriteToNative(glpobj[APPEARANCE], NULL); break; case PICTURE: if (fClip) { if (Error(OleRegisterClientDoc( gszAppClassName, szClip, 0L, &lhClipDoc))) goto Error; if (Error(OleClone( lpaPict->lpObject, glpclient, lhClipDoc, szAppearance, &lpobjapp))) goto Error; cBytes += PicWriteToNative(lpaPict, lpobjapp, NULL); } else { cBytes += PicWriteToNative(lpaPict, lpaPict->lpObject, NULL); } break; default: break; } // Compute the content size switch (gpty[CONTENT]) { case CMDLINK: cBytes += CmlWriteToNative(glpobj[CONTENT], NULL); break; case PEMBED: /* EmbWrite returns -1L if the user cancels */ cEmbWrite = EmbWriteToNative(glpobj[CONTENT], NULL); if (cEmbWrite == (DWORD) - 1L) return FALSE; cBytes += cEmbWrite; break; case PICTURE: if (fClip) { if (!lhClipDoc && (Error(OleRegisterClientDoc( gszAppClassName, szClip, 0L, &lhClipDoc)))) goto Error; if (Error(OleClone(lpcPict->lpObject, glpclient, lhClipDoc, szContent, &lpobjcon))) goto Error; cBytes += PicWriteToNative(lpcPict, lpobjcon, NULL); } else { cBytes += PicWriteToNative(lpcPict, lpcPict->lpObject, NULL); } break; default: break; } if (cBytes == 0L) // then no data goto Error; cBytes += (DWORD)(2 * sizeof(WORD)); // Allocate a memory block for the data if (!(hdata = GlobalAlloc(GMEM_ZEROINIT, cBytes)) || !(lpdata = (LPSTR)GlobalLock(hdata))) goto Error; // Write out the appearance w = (WORD)gpty[APPEARANCE]; MemWrite(&lpdata, (LPSTR)&w, sizeof(WORD)); switch (gpty[APPEARANCE]) { case ICON: IconWriteToNative(glpobj[APPEARANCE], &lpdata); break; case PICTURE: if (fClip) PicWriteToNative(lpaPict, lpobjapp, &lpdata); else PicWriteToNative(lpaPict, lpaPict->lpObject, &lpdata); break; default: break; } // Write out the content w = (WORD)gpty[CONTENT]; MemWrite(&lpdata, (LPSTR)&w, sizeof(WORD)); switch (gpty[CONTENT]) { case CMDLINK: CmlWriteToNative(glpobj[CONTENT], &lpdata); break; case PEMBED: EmbWriteToNative(glpobj[CONTENT], &lpdata); break; case PICTURE: if (fClip) PicWriteToNative(lpcPict, lpobjcon, &lpdata); else PicWriteToNative(lpcPict, lpcPict->lpObject, &lpdata); break; default: break; } fSuccess = TRUE; Error: if (lpobjcon) OleRelease (lpobjcon); if (lpobjapp) OleRelease (lpobjapp); if (lpdata) GlobalUnlock(hdata); if (!fSuccess && hdata) { GlobalFree(hdata); hdata = NULL; } return hdata; } /* PutNative() - Read the item native data from a selector. * * Reads as many objects as it can, in upwards order (better error recovery). * Note: It may be worthwhile to scale the object(s) to the window here. * * Returns: TRUE iff successful. */ BOOL PutNative( HANDLE hdata ) { BOOL fSuccess = FALSE; LPSTR lpdata; WORD w; if (!(lpdata = (LPSTR)GlobalLock(hdata))) goto Error; // Delete any previous panes DeletePane(APPEARANCE, TRUE); DeletePane(CONTENT, TRUE); // Read in the appearance MemRead(&lpdata, (LPSTR)&w, sizeof(WORD)); gpty[APPEARANCE] = w; switch (gpty[APPEARANCE]) { case ICON: if (!(glpobj[APPEARANCE] = IconReadFromNative(&lpdata))) gpty[APPEARANCE] = NOTHING; break; case PICTURE: if (glpobj[APPEARANCE] = PicReadFromNative(&lpdata, gszCaption[APPEARANCE])) { SendMessage(ghwndPane[APPEARANCE], WM_FIXSCROLL, 0, 0L); break; } default: gpty[APPEARANCE] = NOTHING; break; } // Read the content MemRead(&lpdata, (LPSTR)&w, sizeof(WORD)); gpty[CONTENT] = w; switch (gpty[CONTENT]) { case CMDLINK: if (!(glpobj[CONTENT] = CmlReadFromNative(&lpdata))) gpty[CONTENT] = NOTHING; break; case PEMBED: if (!(glpobj[CONTENT] = (LPVOID)EmbReadFromNative(&lpdata))) gpty[CONTENT] = NOTHING; break; case PICTURE: if (glpobj[CONTENT] = (LPVOID)PicReadFromNative(&lpdata, gszCaption[CONTENT])) { SendMessage(ghwndPane[CONTENT], WM_FIXSCROLL, 0, 0L); EnableWindow(ghwndPict, TRUE); break; } default: gpty[CONTENT] = NOTHING; break; } fSuccess = TRUE; InvalidateRect(ghwndFrame, NULL, TRUE); Error: if (lpdata) GlobalUnlock(hdata); return fSuccess; } /* GetLink() - Retrieves ObjectLink/OwnerLink information. * * This function returns a string describing the selected area. */ HANDLE GetLink( VOID ) { CHAR pchlink[CBLINKMAX]; INT cblink; HANDLE hlink; LPSTR lplink; // Link data - \0\0\0\0 StringCchCopy((LPSTR)pchlink, ARRAYSIZE(pchlink), gszAppClassName); // ok const cblink = lstrlen((LPSTR)pchlink) + 1; // Copy the file name StringCchCopy((LPSTR)(pchlink + cblink), ARRAYSIZE(pchlink) - cblink, szDummy); // szDummy size = 20 cblink += lstrlen((LPSTR)(pchlink + cblink)) + 1; // Copy the item name StringCchCopy((LPSTR)(pchlink + cblink), ARRAYSIZE(pchlink) - cblink, szDummy); cblink += lstrlen((LPSTR)(pchlink + cblink)) + 1; pchlink[cblink++] = 0; /* throw in another NULL at the end */ // Allocate a memory block for the data if (!(hlink = GlobalAlloc(GMEM_ZEROINIT, cblink)) || !(lplink = (LPSTR)GlobalLock(hlink))) goto Error; // Copy the data, then return the memory block MemWrite(&lplink, (LPSTR)pchlink, cblink); GlobalUnlock(hlink); return hlink; Error: if (hlink) GlobalFree(hlink); return NULL; } /* GetMF() - Retrieve a metafile of the selected area. * * Note: Originally, tried to Blt directly from the Window DC. This * doesn't work very well because when the window is obscured, * the obscured portion shows up when the link is updated. */ HANDLE GetMF( VOID ) { BOOL fError = TRUE; HANDLE hdata = NULL; HDC hdcMF = NULL; HDC hdcWnd = NULL; HFONT hfont; HANDLE hmfpict; LPMETAFILEPICT lpmfpict; LPIC lpic; LPPICT lppict; RECT rcTemp; RECT rcText; INT cxImage; INT cyImage; hmfpict = GlobalAlloc(GMEM_ZEROINIT, sizeof(METAFILEPICT)); if (!hmfpict) goto Error; lpmfpict = (LPMETAFILEPICT)GlobalLock(hmfpict); // If the picture has a metafile, use it! if (gpty[APPEARANCE] == PICTURE) { LPMETAFILEPICT lpmfpictOrg = NULL; if (Error(OleGetData( ((LPPICT)glpobj[APPEARANCE])->lpObject, CF_METAFILEPICT, &hdata)) || !hdata || !(lpmfpictOrg = (LPMETAFILEPICT)GlobalLock(hdata))) goto NoPicture; // Copy the metafile lpmfpict->hMF = CopyMetaFile(lpmfpictOrg->hMF, NULL); GlobalUnlock(hdata); // If we failed, just draw it if (!lpmfpict->hMF) goto NoPicture; // Finish filling in the metafile header lpmfpict->mm = lpmfpictOrg->mm; lpmfpict->xExt = lpmfpictOrg->xExt; lpmfpict->yExt = lpmfpictOrg->yExt; GlobalUnlock(hmfpict); return hmfpict; } NoPicture: // Get the window DC, and make a DC compatible to it. if (!(hdcWnd = GetDC(NULL))) goto Error; switch (gpty[APPEARANCE]) { case ICON: lpic = (LPIC)glpobj[APPEARANCE]; // Set the icon text rectangle, and the icon font SetRect(&rcText, 0, 0, gcxArrange, gcyArrange); hfont = SelectObject(hdcWnd, ghfontTitle); // Figure out how large the text region will be // since this is going in a metafile we will not wrap // the icon text DrawText(hdcWnd, lpic->szIconText, -1, &rcText, DT_CALCRECT | DT_WORDBREAK | DT_NOPREFIX | DT_SINGLELINE); if (hfont) SelectObject(hdcWnd, hfont); // Compute the image size rcText.right++; cxImage = (rcText.right > gcxIcon) ? rcText.right : gcxIcon; cyImage = gcyIcon + rcText.bottom + 1; break; case PICTURE: lppict = (LPPICT)glpobj[APPEARANCE]; cxImage = lppict->rc.right - lppict->rc.left + 1; cyImage = lppict->rc.bottom - lppict->rc.top + 1; break; default: cxImage = GetSystemMetrics(SM_CXICON); cyImage = GetSystemMetrics(SM_CYICON); break; } cxImage += cxImage / 4; // grow the image a bit cyImage += cyImage / 8; // Create the metafile if (!(hdcMF = CreateMetaFile(NULL))) goto Error; // Initialize the metafile SetWindowOrgEx(hdcMF, 0, 0, NULL); SetWindowExtEx(hdcMF, cxImage - 1, cyImage - 1, NULL); // // Fill in the background // // We displace back to (0, 0) because that's where the BITMAP resides. // SetRect(&rcTemp, 0, 0, cxImage, cyImage); switch (gpty[APPEARANCE]) { case ICON: IconDraw(glpobj[APPEARANCE], hdcMF, &rcTemp, FALSE, cxImage, cyImage); break; case PICTURE: PicDraw(glpobj[APPEARANCE], hdcMF, &rcTemp, 0, 0, TRUE, FALSE); break; default: DrawIcon(hdcMF, 0, 0, LoadIcon(ghInst, MAKEINTRESOURCE(ID_APPLICATION))); break; } // Map to device independent coordinates rcTemp.right = MulDiv((rcTemp.right - rcTemp.left), HIMETRIC_PER_INCH, giXppli); rcTemp.bottom = MulDiv((rcTemp.bottom - rcTemp.top), HIMETRIC_PER_INCH, giYppli); // Finish filling in the metafile header lpmfpict->mm = MM_ANISOTROPIC; lpmfpict->xExt = rcTemp.right; lpmfpict->yExt = rcTemp.bottom; lpmfpict->hMF = CloseMetaFile(hdcMF); fError = FALSE; Error: if (hdcWnd) ReleaseDC(NULL, hdcWnd); // If we had an error, return NULL if (fError && hmfpict) { GlobalUnlock(hmfpict); GlobalFree(hmfpict); hmfpict = NULL; } return hmfpict; } /* InitEmbedded() - Perform operations specific to editing embedded objects. * * This routine changes the menu items as appropriate. */ VOID InitEmbedded( BOOL fCreate ) { HMENU hmenu; if (hmenu = GetMenu(ghwndFrame)) EnableMenuItem(hmenu, IDM_UPDATE, fCreate ? MF_GRAYED : MF_ENABLED); gfEmbedded = TRUE; } /***************** Item circular queue/utility functions *****************/ /* AddItem() - Add an item to the global item list. */ LPSAMPITEM AddItem( LPSAMPITEM lpitem ) { INT i; HANDLE hitem; i = FindItem((LPSAMPITEM)lpitem); if (i < cItems) { vlpitem[i]->ref++; // Free the duplicate item GlobalUnlock(hitem = lpitem->hitem); GlobalFree(hitem); } else { if (i < CITEMSMAX) { vlpitem[cItems] = (LPSAMPITEM)lpitem; vlpitem[cItems++]->ref = 1; } else { return NULL; } } return vlpitem[i]; } /* DeleteItem() - Delete an item from the global item list. * * Returns: TRUE iff successful. */ BOOL DeleteItem( LPSAMPITEM lpitem ) { BOOL fFound; HANDLE hitem; INT i; i = FindItem(lpitem); if ((fFound = (i < cItems && vlpitem[i]->ref)) && !(--vlpitem[i]->ref)) { // Free the item GlobalUnlock(hitem = vlpitem[i]->hitem); GlobalFree(hitem); // Shift everything else down cItems--; for ( ; i < cItems; i++) vlpitem[i] = vlpitem[i + 1]; } return fFound; } /* FindItem() - Locate an item in the global item list. */ static INT FindItem( LPSAMPITEM lpitem ) { BOOL fFound = FALSE; INT i; for (i = 0; i < cItems && !fFound;) { if (lpitem->aName == vlpitem[i]->aName) { fFound = TRUE; } else { i++; } } return i; } /* EndEmbedding() - Return to normal editing. * * This routine changes the menu items as appropriate. */ VOID EndEmbedding( VOID ) { HMENU hmenu; // Fix the "Untitled" string LoadString(ghInst, IDS_UNTITLED, szUntitled, CBMESSAGEMAX); if (hmenu = GetMenu(ghwndFrame)) EnableMenuItem(hmenu, IDM_UPDATE, MF_GRAYED); gfEmbedded = FALSE; }