|
|
1.1 ! root 1: /* ! 2: * clidemo.c - OLE client application sample code ! 3: * ! 4: * Created by Microsoft Corporation. ! 5: * (c) Copyright Microsoft Corp. 1990 - 1992 All Rights Reserved ! 6: * ! 7: */ ! 8: ! 9: /*************************************************************************** ! 10: * IMPORTANT - README: ! 11: * OLE client applications are windows programs which use the OLE client ! 12: * APIs. Therefore it is imperative that you understand how these APIs ! 13: * operate. Most importantly it is essential that you keep in mind which ! 14: * procedure calls result in asynchronous states: a state where the operation ! 15: * is not truely complete after a return from the call. ! 16: * ! 17: * Many functions produce asynchronous states, for example, OleActivate, ! 18: * OleClose, OleCopyFromLink, OleCreate ... Reference your SDK manual for ! 19: * a complete list. ! 20: * ! 21: * So whenever you call any of these library functions keep in mind that ! 22: * the operation is not necessarily complete once a return is made. ! 23: * These operations require communications with a server application. With ! 24: * OLE the inter-application communication is done through DDE. In order ! 25: * for a DDE conversation to complete several DDE messages need to be ! 26: * sent and recieved by both the server and client OLE DLLs. So, the ! 27: * asynchronous operations will not complete until the client application ! 28: * enters a message dipatch loop. Therefore, it is necessary to enter ! 29: * a dispatch loop and wait for completion. It is not necessary to block ! 30: * all other operation; however, it is very important to coordinate the ! 31: * user activity to prevent disastrous re-entry cases. ! 32: * ! 33: * In this application I have written a macro to prevent re-entry ! 34: * problems. Namely: ANY_OBJECT_BUSY which prevents a user from initiating ! 35: * an action which will result in an asynchronous call if there is an object ! 36: * already in an asynchronous state. ! 37: * ! 38: * The following is brief summary of the three macros: ! 39: * ! 40: * ANY_OBJECT_BUSY: checks to see if any object in the document is busy. ! 41: * This prevents a new document from being saved to file if there are ! 42: * objects in asynchronous states. ! 43: * ! 44: * So, the problem is that we have to enter a message dispatch loop in order ! 45: * to let DDE messages get through so that asynchronous operations can finish. ! 46: * And while we are in the message dispatch loops (WaitForObject or WaitForAllObjects) ! 47: * we have to prevent the user from doing things that can't be done when an ! 48: * object(s) is busy. Yes, it is confusing , but, the end result is a super ! 49: * cool application that can have linked and embbeded objects! ! 50: ***************************************************************************/ ! 51: ! 52: //*** INCLUDES *** ! 53: ! 54: #include <windows.h> //* WINDOWS ! 55: #include <ole.h> //* OLE structs and defines ! 56: #include <shellapi.h> //* Shell, drag and drop headers ! 57: ! 58: #include "demorc.h" //* header for resource file ! 59: #include "global.h" //* global app variables ! 60: #include "clidemo.h" //* app includes: ! 61: #include "register.h" ! 62: #include "stream.h" ! 63: #include "object.h" ! 64: #include "dialog.h" ! 65: #include "utility.h" ! 66: ! 67: //*** VARIABLES *** ! 68: ! 69: //** Global ! 70: HANDLE hInst; ! 71: BOOL fRetry = FALSE; ! 72: HWND hwndFrame; //* main window ! 73: HANDLE hAccTable; //* accelerator table ! 74: CHAR szFrameClass[] = "CliDemo";//* main window class name ! 75: CHAR szItemClass[] = "ItemClass";//* item window class name ! 76: CHAR szAppName[CBMESSAGEMAX];//* Application name ! 77: INT iObjects = 0; //* object count ! 78: INT iObjectNumber = 0; //* object number for object name ! 79: CHAR szFileName[CBPATHMAX]; ! 80: //* ClipBoard formats: ! 81: OLECLIPFORMAT vcfLink; //* "ObjectLink" ! 82: OLECLIPFORMAT vcfNative; //* "Native" ! 83: OLECLIPFORMAT vcfOwnerLink; //* "OwnerLink" ! 84: ! 85: ! 86: /*************************************************************************** ! 87: * WinMain() - Main Windows routine ! 88: ***************************************************************************/ ! 89: ! 90: int WinMain( ! 91: HANDLE hInstance, ! 92: HANDLE hPrevInst, ! 93: LPSTR lpCmdLine, ! 94: INT nCmdLine ! 95: ){ ! 96: hInst = hInstance; ! 97: ! 98: if (!InitApplication(hInst)) //* register window classes ! 99: return FALSE; ! 100: ! 101: if (!InitInstance(hInst)) //* create window instance ! 102: return FALSE; ! 103: ! 104: OfnInit(hInst); //* setup to use <commdlg.dll> ! 105: ! 106: //* register clipboard formats ! 107: //* used for OLE ! 108: vcfLink = RegisterClipboardFormat("ObjectLink"); ! 109: vcfNative = RegisterClipboardFormat("Native"); ! 110: vcfOwnerLink = RegisterClipboardFormat("OwnerLink"); ! 111: ! 112: ! 113: ShowWindow(hwndFrame, SW_SHOWNORMAL); ! 114: UpdateWindow(hwndFrame); ! 115: ProcessCmdLine(lpCmdLine); ! 116: ! 117: while (ProcessMessage(hwndFrame, hAccTable)) ; ! 118: ! 119: return FALSE; ! 120: } ! 121: ! 122: /*************************************************************************** ! 123: * InitApplication() ! 124: * ! 125: * registers the window classes used by the application. ! 126: * ! 127: * Returns BOOL: - TRUE if successful. ! 128: ***************************************************************************/ ! 129: ! 130: static BOOL InitApplication( //* ENTRY: ! 131: HANDLE hInst //* instance handle ! 132: ){ //* LOCAL: ! 133: WNDCLASS wc; //* temp wind-class structure ! 134: ! 135: wc.style = NULL; ! 136: wc.lpfnWndProc = (WNDPROC)FrameWndProc; ! 137: wc.cbClsExtra = 0; ! 138: wc.cbWndExtra = 0; ! 139: wc.hInstance = hInst; ! 140: wc.hIcon = LoadIcon(hInst, MAKEINTRESOURCE(ID_APPLICATION)); ! 141: wc.hCursor = LoadCursor(NULL, IDC_ARROW); ! 142: wc.hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE + 1); ! 143: wc.lpszMenuName = MAKEINTRESOURCE(ID_APPLICATION); ! 144: wc.lpszClassName = szFrameClass; ! 145: ! 146: if (!RegisterClass(&wc)) ! 147: return FALSE; ! 148: //* application item class ! 149: wc.style = CS_DBLCLKS | CS_VREDRAW | CS_HREDRAW; ! 150: wc.lpfnWndProc = (WNDPROC)ItemWndProc; ! 151: wc.hIcon = NULL; ! 152: wc.cbWndExtra = sizeof(APPITEMPTR); ! 153: wc.lpszMenuName = NULL; ! 154: wc.lpszClassName = szItemClass; ! 155: ! 156: if (!RegisterClass(&wc)) ! 157: return FALSE; ! 158: ! 159: return TRUE; ! 160: ! 161: } ! 162: ! 163: /*************************************************************************** ! 164: * InitInstance() ! 165: * ! 166: * create the main application window. ! 167: * ! 168: * Returns BOOL: - TRUE if successful else FALSE. ! 169: ***************************************************************************/ ! 170: ! 171: static BOOL InitInstance( //* ENTRY: ! 172: HANDLE hInst //* instance handel ! 173: ){ ! 174: ! 175: hAccTable = LoadAccelerators(hInst, MAKEINTRESOURCE(ID_APPLICATION)); ! 176: ! 177: if (!(hwndFrame = ! 178: CreateWindow( ! 179: szFrameClass, "", ! 180: WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, ! 181: CW_USEDEFAULT, CW_USEDEFAULT, ! 182: CW_USEDEFAULT, CW_USEDEFAULT, ! 183: NULL, ! 184: NULL, ! 185: hInst, ! 186: NULL ! 187: ))) ! 188: return FALSE; //* ERROR return ! 189: ! 190: LoadString(hInst, IDS_APPNAME, szAppName, CBMESSAGEMAX); ! 191: DragAcceptFiles(hwndFrame, TRUE); //* allow dragged and dropped files ! 192: ! 193: ! 194: return TRUE; //* SUCCESS return ! 195: ! 196: } ! 197: ! 198: /*************************************************************************** ! 199: * ProcessCmdLine() ! 200: * ! 201: * process command line getting any command arguments. ! 202: ***************************************************************************/ ! 203: ! 204: VOID ProcessCmdLine(LPSTR lpCmdLine) ! 205: { //* LOCAL: ! 206: OFSTRUCT ofs; ! 207: ! 208: ! 209: if (*lpCmdLine) ! 210: { //* look for file extension ! 211: LPSTR lpstrExt = lpCmdLine; //* pointer to file extension ! 212: ! 213: while (*lpstrExt && *lpstrExt != '.') ! 214: lpstrExt = AnsiNext(lpstrExt); ! 215: ! 216: lstrcpy(szFileName, lpCmdLine); ! 217: if (!(*lpstrExt)) //* append default extension ! 218: { ! 219: lstrcat(szFileName,"."); ! 220: lstrcat(szFileName,szDefExtension); ! 221: } ! 222: //* get the files fully ! 223: OpenFile(szFileName, &ofs, OF_PARSE);//* qualified name ! 224: lstrcpy(szFileName, ofs.szPathName); ! 225: } ! 226: else ! 227: *szFileName = NULL; ! 228: //* pass filename to main winproc ! 229: SendMessage(hwndFrame,WM_INIT,NULL,(LONG)NULL); ! 230: ! 231: } ! 232: ! 233: ! 234: /*************************************************************************** ! 235: * FrameWndProc() ! 236: * ! 237: * Message handler for the application frame window. ! 238: * ! 239: * Returns long - Variable, depends on message. ! 240: ***************************************************************************/ ! 241: ! 242: LONG APIENTRY FrameWndProc( //* ENTRY: ! 243: HWND hwnd, //* standard wind-proc parameters ! 244: UINT msg, ! 245: DWORD wParam, ! 246: LONG lParam ! 247: ){ //* LOCAL: ! 248: //* ^ Document file name ! 249: static LHCLIENTDOC lhcDoc; //* Document Handle ! 250: static LPOLECLIENT lpClient; //* pointer to client ! 251: static LPAPPSTREAM lpStream; //* pointer to stream vtbl ! 252: APPITEMPTR pItem; //* application item pointer ! 253: ! 254: switch (msg) ! 255: { ! 256: case WM_INIT: //* user defined message ! 257: if (!InitAsOleClient(hInst, hwnd, szFileName, &lhcDoc, &lpClient, &lpStream)) ! 258: DestroyWindow(hwnd); ! 259: break; ! 260: //* the following three messages are ! 261: //* used to avoid problems with OLE ! 262: //* see the comment in object.h ! 263: case WM_DELETE: //* user defined message ! 264: pItem = (APPITEMPTR) wParam; //* delete object ! 265: WaitForObject(pItem); ! 266: ObjDelete(pItem,OLE_OBJ_DELETE); ! 267: if (lParam) ! 268: cOleWait--; ! 269: break; ! 270: ! 271: case WM_ERROR: //* user defined message ! 272: ErrorMessage(wParam); //* display error message ! 273: break; ! 274: ! 275: case WM_RETRY: //* user defined message ! 276: RetryMessage((APPITEMPTR)wParam, RD_RETRY | RD_CANCEL); ! 277: break; ! 278: ! 279: case WM_INITMENU: ! 280: UpdateMenu((HMENU)wParam); ! 281: break; ! 282: ! 283: case WM_COMMAND: ! 284: { ! 285: WORD wID = LOWORD(wParam); ! 286: ! 287: pItem = GetTopItem(); ! 288: ! 289: switch (wID) ! 290: { ! 291: case IDM_NEW: ! 292: ANY_OBJECT_BUSY; ! 293: NewFile(szFileName,&lhcDoc,lpStream); ! 294: break; ! 295: ! 296: case IDM_OPEN: ! 297: ANY_OBJECT_BUSY; ! 298: MyOpenFile(szFileName,&lhcDoc,lpClient,lpStream); ! 299: break; ! 300: ! 301: case IDM_SAVE: ! 302: ANY_OBJECT_BUSY; ! 303: SaveFile(szFileName,lhcDoc,lpStream); ! 304: break; ! 305: ! 306: case IDM_SAVEAS: ! 307: ANY_OBJECT_BUSY; ! 308: SaveasFile(szFileName,lhcDoc,lpStream); ! 309: break; ! 310: ! 311: case IDM_ABOUT: ! 312: AboutBox(); ! 313: break; ! 314: ! 315: case IDM_INSERT: ! 316: ANY_OBJECT_BUSY; ! 317: ObjInsert(lhcDoc, lpClient); ! 318: break; ! 319: ! 320: case IDM_INSERTFILE: ! 321: ANY_OBJECT_BUSY; ! 322: ObjCreateFromTemplate(lhcDoc,lpClient); ! 323: break; ! 324: ! 325: case IDM_PASTE: ! 326: case IDM_PASTELINK: ! 327: ANY_OBJECT_BUSY; ! 328: ObjPaste(wID == IDM_PASTE,lhcDoc,lpClient); ! 329: break; ! 330: ! 331: case IDM_LINKS: ! 332: ANY_OBJECT_BUSY; ! 333: pItem = GetTopItem(); ! 334: LinkProperties(); ! 335: break; ! 336: ! 337: case IDM_EXIT: ! 338: ANY_OBJECT_BUSY; ! 339: SendMessage(hwnd, WM_SYSCOMMAND, SC_CLOSE, 0L); ! 340: break; ! 341: ! 342: case IDM_COPY: ! 343: case IDM_CUT: ! 344: ANY_OBJECT_BUSY; ! 345: ! 346: if (!ObjCopy(pItem)) ! 347: { ! 348: ErrorMessage((wParam == IDM_CUT) ? ! 349: E_CLIPBOARD_CUT_FAILED : E_CLIPBOARD_COPY_FAILED); ! 350: break; ! 351: } ! 352: ! 353: if (wParam == IDM_COPY) ! 354: break; ! 355: ! 356: case IDM_CLEAR: //* CUT falls through to clear ! 357: ANY_OBJECT_BUSY; ! 358: ClearItem(pItem); ! 359: break; ! 360: ! 361: case IDM_CLEARALL: ! 362: ANY_OBJECT_BUSY; ! 363: ClearAll(lhcDoc,OLE_OBJ_DELETE); ! 364: Dirty(DOC_DIRTY); ! 365: break; ! 366: ! 367: default: ! 368: if( (wParam >= IDM_VERBMIN) && (wParam <= IDM_VERBMAX) ) ! 369: { ! 370: ANY_OBJECT_BUSY; ! 371: ExecuteVerb(wParam - IDM_VERBMIN,pItem); ! 372: break; ! 373: } ! 374: return DefWindowProc(hwnd, msg, wParam, lParam); ! 375: } ! 376: break; ! 377: } ! 378: ! 379: case WM_DROPFILES: ! 380: ANY_OBJECT_BUSY; ! 381: ObjCreateWrap((HANDLE)wParam, lhcDoc, lpClient); ! 382: break; ! 383: ! 384: case WM_CLOSE: ! 385: ANY_OBJECT_BUSY; ! 386: if (!SaveAsNeeded(szFileName, lhcDoc, lpStream)) ! 387: break; ! 388: DeregDoc(lhcDoc); ! 389: DestroyWindow(hwnd); ! 390: break; ! 391: ! 392: case WM_DESTROY: ! 393: EndStream(lpStream); ! 394: EndClient(lpClient); ! 395: PostQuitMessage(0); ! 396: break; ! 397: ! 398: case WM_QUERYENDSESSION: //* don't let windows terminate ! 399: return (QueryEndSession(szFileName,lhcDoc, lpStream)); ! 400: ! 401: default: ! 402: return DefWindowProc(hwnd, msg, wParam, lParam); ! 403: } ! 404: return 0L; ! 405: ! 406: } ! 407: ! 408: /*************************************************************************** ! 409: * InitAsOleClient() ! 410: * ! 411: * Initiates the creation of stream and client vtbls. These vtbls are very ! 412: * important for the proper operation of this application. The stream vtbl ! 413: * lets the OLE librarys know where the location of the stream I/O routines ! 414: * reside. The stream routines are used by OleLoadFromStream and the like. ! 415: * The client vtbl is used to hold the pointer to the CallBack function. ! 416: * IMPORTANT: both the client and the stream structures have pointers to ! 417: * vtbls which have the pointers to the functions. Therefore, it is ! 418: * necessary to allocate space for the vtbl and the client structure ! 419: * which has the pointer to the vtbl. ! 420: **************************************************************************/ ! 421: ! 422: static BOOL InitAsOleClient( //* ENTRY: ! 423: HANDLE hInstance, //* applicaion instance handle ! 424: HWND hwnd, //* main window handle ! 425: PSTR pFileName, //* document file name ! 426: LHCLIENTDOC *lhcDoc, //* pointer to document Handle ! 427: LPOLECLIENT *lpClient, //* pointer to client pointer ! 428: LPAPPSTREAM *lpStream //* pointer to APPSTREAM pointer ! 429: ){ ! 430: //* initiate client vtbl creation ! 431: if (!(*lpClient = InitClient(hInstance))) ! 432: { ! 433: SendMessage(hwnd, WM_SYSCOMMAND, SC_CLOSE, 0L); ! 434: return FALSE; //* ERROR return ! 435: } ! 436: //* initiate stream vtbl creation ! 437: if (!(*lpStream = InitStream(hInstance))) ! 438: { ! 439: SendMessage(hwnd, WM_SYSCOMMAND, SC_CLOSE, 0L); ! 440: return FALSE; //* ERROR return ! 441: } ! 442: ! 443: if (*pFileName && RegDoc(pFileName,lhcDoc) ! 444: && LoadFile(pFileName,*lhcDoc,*lpClient,*lpStream)) ! 445: { ! 446: SetTitle(pFileName); ! 447: return TRUE; //* SUCCESS return ! 448: } ! 449: ! 450: NewFile(pFileName, lhcDoc, *lpStream); ! 451: return TRUE; //* SUCCESS return ! 452: ! 453: } //* SUCCESS return ! 454: ! 455: /**************************************************************************** ! 456: * InitClient() ! 457: * ! 458: * Initialize the OLE client structure, create and fill the OLECLIENTVTBL ! 459: * structure. ! 460: * ! 461: * Returns LPOLECLIENT - if successful a pointer to a client structure ! 462: * , otherwise NULL. ! 463: ***************************************************************************/ ! 464: ! 465: static LPOLECLIENT InitClient( //* ENTRY: ! 466: HANDLE hInstance //* application instance handle ! 467: ){ //* LOCAL: ! 468: LPOLECLIENT lpClient=NULL; //* pointer to client struct ! 469: //* Allocate vtbls ! 470: if (!(lpClient = (LPOLECLIENT)GlobalLock( ! 471: GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(OLECLIENT)) ! 472: ))) ! 473: goto Error; //* ERROR jump ! 474: ! 475: if (!(lpClient->lpvtbl = (LPOLECLIENTVTBL)GlobalLock( ! 476: GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(OLECLIENTVTBL)) ! 477: ))) ! 478: goto Error; //* ERROR jump ! 479: //* set the CALLBACK function ! 480: //* pointer ! 481: lpClient->lpvtbl->CallBack = CallBack; ! 482: ! 483: return lpClient; //* SUCCESS return ! 484: ! 485: Error: //* ERROR Tag ! 486: ! 487: ErrorMessage(E_FAILED_TO_ALLOC); ! 488: EndClient(lpClient); //* free any allocated space ! 489: ! 490: return NULL; //* ERROR return ! 491: ! 492: } ! 493: ! 494: /**************************************************************************** ! 495: * InitStream() ! 496: * ! 497: * Create and fill the STREAMVTBL. Create a stream structure and initialize ! 498: * pointer to stream vtbl. ! 499: * ! 500: * Returns LPAPPSTREAM - if successful a pointer to a stream structure ! 501: * , otherwise NULL . ! 502: ***************************************************************************/ ! 503: ! 504: static LPAPPSTREAM InitStream( //* ENTRY: ! 505: HANDLE hInstance //* handle to application instance ! 506: ){ //* LOCAL: ! 507: LPAPPSTREAM lpStream = NULL; //* pointer to stream structure ! 508: ! 509: if (!(lpStream = (LPAPPSTREAM)GlobalLock( ! 510: GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(APPSTREAM)) ! 511: ))) ! 512: goto Error; //* ERROR jump ! 513: ! 514: if (!(lpStream->olestream.lpstbl = (LPOLESTREAMVTBL)GlobalLock( ! 515: GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(OLESTREAMVTBL)) ! 516: ))) ! 517: goto Error; //* ERROR jump ! 518: ! 519: //* set stream func. pointers ! 520: lpStream->olestream.lpstbl->Get = (DWORD ( CALLBACK *)(LPOLESTREAM, VOID FAR *, DWORD)) ReadStream; ! 521: lpStream->olestream.lpstbl->Put = (DWORD ( CALLBACK *)(LPOLESTREAM, OLE_CONST VOID FAR *, DWORD)) WriteStream; ! 522: ! 523: return lpStream; //* SUCCESS return ! 524: ! 525: Error: //* ERROR Tag ! 526: ! 527: ErrorMessage(E_FAILED_TO_ALLOC); ! 528: EndStream(lpStream); ! 529: ! 530: return NULL; //* ERROR return ! 531: ! 532: } ! 533: ! 534: /*************************************************************************** ! 535: * UpdateMenu() ! 536: * ! 537: * Enabling or disable menuitems based upon program state. ! 538: ***************************************************************************/ ! 539: ! 540: static VOID UpdateMenu( //* ENTRY: ! 541: HMENU hMenu //* menu handle to updated ! 542: ){ //* LOCAL: ! 543: INT mf; //* generic menu flag ! 544: APPITEMPTR paItem; //* app item pointer ! 545: HMENU hSub; ! 546: //* there must be at least on object ! 547: //* for the following to be enabled ! 548: ! 549: paItem = GetTopItem() ; ! 550: ! 551: mf = (paItem ? MF_ENABLED : MF_GRAYED); ! 552: EnableMenuItem(hMenu, IDM_CUT, mf); //* i.e. Cut,Copy,Clear,Clearall... ! 553: EnableMenuItem(hMenu, IDM_COPY, mf); ! 554: EnableMenuItem(hMenu, IDM_CLEAR, mf); ! 555: EnableMenuItem(hMenu, IDM_CLEARALL, mf); ! 556: //* enable links option only if there ! 557: //* is at least one linked object ! 558: EnableMenuItem(hMenu, IDM_LINKS, MF_GRAYED); ! 559: for (; paItem; paItem = GetNextItem(paItem)) ! 560: { ! 561: if (paItem->otObject == OT_LINK) ! 562: { ! 563: EnableMenuItem(hMenu, IDM_LINKS, MF_ENABLED); ! 564: break; ! 565: } ! 566: } ! 567: ! 568: if (hSub = GetSubMenu(hMenu,POS_EDITMENU)) ! 569: UpdateObjectMenuItem(hSub); ! 570: ! 571: if (OleQueryCreateFromClip(STDFILEEDITING, olerender_draw, 0) == OLE_OK) ! 572: EnableMenuItem(hMenu, IDM_PASTE, MF_ENABLED); ! 573: else if (OleQueryCreateFromClip(STATICP, olerender_draw, 0) == OLE_OK) ! 574: EnableMenuItem(hMenu, IDM_PASTE, MF_ENABLED); ! 575: else ! 576: EnableMenuItem(hMenu, IDM_PASTE, MF_GRAYED); ! 577: ! 578: if (OleQueryLinkFromClip(STDFILEEDITING, olerender_draw, 0) == OLE_OK) ! 579: EnableMenuItem(hMenu, IDM_PASTELINK, MF_ENABLED); ! 580: else ! 581: EnableMenuItem(hMenu, IDM_PASTELINK, MF_GRAYED); ! 582: ! 583: } ! 584: ! 585: /*************************************************************************** ! 586: * NewFile() ! 587: * ! 588: * Save the present document and open a new blank one. ! 589: ***************************************************************************/ ! 590: ! 591: static VOID NewFile( //* ENTRY: ! 592: PSTR pFileName, //* open file name ! 593: LHCLIENTDOC *lhcptrDoc, //* pointer to client doc. handle ! 594: LPAPPSTREAM lpStream //* pointer to stream structure ! 595: ){ //* LOCAL: ! 596: static CHAR szUntitled[CBMESSAGEMAX] = "";//* "(Untitled)" string ! 597: LHCLIENTDOC lhcDocNew; //* handle for new doc. ! 598: ! 599: if (!(*szUntitled)) ! 600: LoadString(hInst, IDS_UNTITLED, (LPSTR)szUntitled, CBMESSAGEMAX); ! 601: ! 602: if (SaveAsNeeded(pFileName, *lhcptrDoc, lpStream)) ! 603: { //* try to register new document ! 604: if (!RegDoc(szUntitled, &lhcDocNew)) ! 605: return; //* before deregistring the old one ! 606: DeregDoc(*lhcptrDoc); ! 607: *lhcptrDoc = lhcDocNew; ! 608: Dirty(DOC_CLEAN); //* new document is clean ! 609: lstrcpy(pFileName,szUntitled); ! 610: SetTitle(pFileName); ! 611: iObjectNumber = 0; ! 612: } ! 613: ! 614: } ! 615: ! 616: /*************************************************************************** ! 617: * MyOpenFile() ! 618: * ! 619: * Open a file and load it. Notice that the new file is loaded before ! 620: * the old is removed. This is done to assure a succesful file load ! 621: * before removing an existing document. ! 622: ***************************************************************************/ ! 623: ! 624: static VOID MyOpenFile( //* ENTRY: ! 625: PSTR pFileName, //* open file name ! 626: LHCLIENTDOC *lhcptrDoc, //* pointer to document handle ! 627: LPOLECLIENT lpClient, //* pointer to client structure ! 628: LPAPPSTREAM lpStream //* pointer to stream structure ! 629: ){ //* LOCAL: ! 630: CHAR szNewFile[CBPATHMAX];//* new file name buffer ! 631: LHCLIENTDOC lhcDocNew; //* handle of new document ! 632: APPITEMPTR pItem; //* hold top item ! 633: ! 634: if (SaveAsNeeded(pFileName, *lhcptrDoc, lpStream)) ! 635: { ! 636: *szNewFile = NULL; ! 637: ! 638: if (!OfnGetName(hwndFrame, szNewFile, IDM_OPEN)) ! 639: return; //* ERROR return ! 640: ! 641: if (!RegDoc(szNewFile,&lhcDocNew)) ! 642: return; //* ERROR return ! 643: ! 644: pItem = GetTopItem(); ! 645: ShowDoc(*lhcptrDoc,0); //* make old doc objects hidden. ! 646: //* try to load the new file before ! 647: if (!LoadFile(szNewFile, lhcDocNew, lpClient, lpStream)) ! 648: { //* before removing the old. ! 649: DeregDoc(lhcDocNew); //* restore old document if new ! 650: SetTopItem(pItem); //* file did not load ! 651: ShowDoc(*lhcptrDoc,1); ! 652: return; //* ERROR return ! 653: } ! 654: ! 655: DeregDoc(*lhcptrDoc); //* deregister old document ! 656: *lhcptrDoc = lhcDocNew; ! 657: lstrcpy(pFileName,szNewFile); ! 658: SetTitle(pFileName); //* set new title ! 659: Dirty(DOC_CLEAN); ! 660: } ! 661: ! 662: } //* SUCCESS return ! 663: ! 664: /*************************************************************************** ! 665: * SaveasFile() ! 666: * ! 667: * Prompt the user for a new file name. Write the document to the new ! 668: * filename. ! 669: ***************************************************************************/ ! 670: ! 671: static VOID SaveasFile( //* ENTRY: ! 672: PSTR pFileName, //* old filename ! 673: LHCLIENTDOC lhcDoc, //* document handle ! 674: LPAPPSTREAM lpStream //* pointer to stream structure ! 675: ){ ! 676: CHAR szNewFile[CBPATHMAX];//* new file name ! 677: ! 678: *szNewFile = NULL; //* prompt user for new file name ! 679: if (!OfnGetName(hwndFrame, szNewFile, IDM_SAVEAS)) ! 680: return; //* ERROR return ! 681: //* rename document ! 682: if (!SaveFile(szNewFile, lhcDoc, lpStream)) ! 683: return; ! 684: ! 685: if (Error(OleRenameClientDoc(lhcDoc, szNewFile))) ! 686: { ! 687: ErrorMessage(W_FAILED_TO_NOTIFY); ! 688: return; //* ERROR return ! 689: } ! 690: ! 691: lstrcpy(pFileName,szNewFile); ! 692: SetTitle(pFileName); ! 693: ! 694: } //* SUCCESS return ! 695: ! 696: /*************************************************************************** ! 697: * SaveFile() ! 698: * ! 699: * Save a compound document file. If the file is untitled, ask the user ! 700: * for a name and save the document to that file. ! 701: ***************************************************************************/ ! 702: ! 703: static BOOL SaveFile( //* ENTRY: ! 704: PSTR pFileName, //* file to save document to ! 705: LHCLIENTDOC lhcDoc, //* OLE document handle ! 706: LPAPPSTREAM lpStream //* pointer to app. stream struct ! 707: ){ //* LOCAL: ! 708: CHAR szNewFile[CBPATHMAX];//* New file name strings ! 709: CHAR szOemFileName[2*CBPATHMAX]; ! 710: static CHAR szUntitled[CBMESSAGEMAX] = ""; ! 711: int fh; //* file handle ! 712: ! 713: *szNewFile = NULL; ! 714: if (!(*szUntitled)) ! 715: LoadString(hInst, IDS_UNTITLED, (LPSTR)szUntitled, CBMESSAGEMAX); ! 716: ! 717: if (!lstrcmp(szUntitled, pFileName))//* get filename for the untitled case ! 718: { ! 719: if (!OfnGetName(hwndFrame, szNewFile, IDM_SAVEAS)) ! 720: return FALSE; //* CANCEL return ! 721: lstrcpy(pFileName,szNewFile); ! 722: SetTitle(pFileName); ! 723: } ! 724: ! 725: AnsiToOem(pFileName, szOemFileName); ! 726: if ((fh = _lcreat((LPSTR)szOemFileName, 0)) <= 0) ! 727: { ! 728: ErrorMessage(E_INVALID_FILENAME); ! 729: return FALSE; //* ERROR return ! 730: } ! 731: ! 732: lpStream->fh = fh; ! 733: //* save file on disk ! 734: if (!WriteToFile(lpStream)) ! 735: { ! 736: _lclose(fh); ! 737: ErrorMessage(E_FAILED_TO_SAVE_FILE); ! 738: return FALSE; //* ERROR return ! 739: } ! 740: _lclose(fh); ! 741: ! 742: if (Error(OleSavedClientDoc(lhcDoc))) ! 743: { ! 744: ErrorMessage(W_FAILED_TO_NOTIFY); ! 745: return FALSE; //* ERROR return ! 746: } ! 747: ! 748: Dirty(DOC_CLEAN); ! 749: return TRUE; //* SUCCESS return ! 750: ! 751: } ! 752: ! 753: /*************************************************************************** ! 754: * LoadFile() ! 755: * ! 756: * Load a document file from disk. ! 757: ***************************************************************************/ ! 758: ! 759: static BOOL LoadFile( //* ENTRY: ! 760: PSTR pFileName, //* file name ! 761: LHCLIENTDOC lhcDoc, //* document handle ! 762: LPOLECLIENT lpClient, //* pointer to client structure ! 763: LPAPPSTREAM lpStream //* pointer to stream structure ! 764: ){ //* LOCAL: ! 765: //* OEM file name ! 766: CHAR szOemFileName[2*CBPATHMAX]; ! 767: int fh; //* file handle ! 768: INT iObjectNumberHold; //* hold object number ! 769: ! 770: AnsiToOem(pFileName, szOemFileName); ! 771: if ((fh = _lopen(szOemFileName, OF_READ | OF_SHARE_DENY_WRITE)) == -1) ! 772: { ! 773: ErrorMessage(E_FAILED_TO_READ_FILE); ! 774: return FALSE; //* ERROR return ! 775: } ! 776: ! 777: lpStream->fh = fh; ! 778: ! 779: iObjectNumberHold = iObjectNumber; //* save object number so it can ! 780: iObjectNumber = 0; //* be restored if read from file ! 781: //* fails ! 782: if (!ReadFromFile(lpStream, lhcDoc, lpClient)) ! 783: { ! 784: _lclose(fh); ! 785: ErrorMessage(E_FAILED_TO_READ_FILE); ! 786: iObjectNumber = iObjectNumberHold; ! 787: return FALSE; //* ERROR return ! 788: } ! 789: return TRUE; //* SUCCESS return ! 790: ! 791: } ! 792: ! 793: /*************************************************************************** ! 794: * RegDoc() ! 795: * ! 796: * Register the client document with the OLE library. ! 797: **************************************************************************/ ! 798: ! 799: static BOOL RegDoc( //* ENTRY: ! 800: PSTR pFileName, //* file name ! 801: LHCLIENTDOC *lhcptrDoc //* pointer to client document handle ! 802: ){ ! 803: ! 804: if (Error(OleRegisterClientDoc(szAppName, (LPSTR)pFileName, 0L, lhcptrDoc))) ! 805: { ! 806: ErrorMessage(W_FAILED_TO_NOTIFY); ! 807: return FALSE; //* ERROR return ! 808: } ! 809: return TRUE; //* SUCCESS return ! 810: ! 811: } ! 812: ! 813: /**************************************************************************** ! 814: * DeregDoc() ! 815: * ! 816: * This function initiates the removal of all OLE objects from the ! 817: * current document and deregisters the document with the OLE library. ! 818: ***************************************************************************/ ! 819: ! 820: static VOID DeregDoc( //* ENTRY: ! 821: LHCLIENTDOC lhcDoc //* client document handle ! 822: ){ ! 823: ! 824: if (lhcDoc) ! 825: { //* release all OLE objects ! 826: ClearAll(lhcDoc,OLE_OBJ_RELEASE); //* and remove them from the screen ! 827: WaitForAllObjects(); ! 828: if (Error(OleRevokeClientDoc(lhcDoc))) ! 829: ErrorMessage(W_FAILED_TO_NOTIFY); ! 830: } ! 831: ! 832: } //* SUCCESS return ! 833: ! 834: /*************************************************************************** ! 835: * ClearAll() ! 836: * ! 837: * This function will destroy all of the item windows in the current ! 838: * document and delete all OLE objects. The loop is basically an enum ! 839: * of all child windows. ! 840: **************************************************************************/ ! 841: ! 842: static VOID ClearAll( //* ENTRY: ! 843: LHCLIENTDOC lhcDoc, //* application document handle ! 844: BOOL fDelete //* Delete / Release ! 845: ){ //* LOCAL: ! 846: APPITEMPTR pItemNext; //* working handles ! 847: APPITEMPTR pItem; //* pointer to application item ! 848: ! 849: pItem = GetTopItem(); ! 850: ! 851: while (pItem) ! 852: { ! 853: pItemNext = GetNextItem(pItem); ! 854: if (pItem->lhcDoc == lhcDoc) ! 855: ObjDelete(pItem, fDelete); ! 856: pItem = pItemNext; ! 857: } ! 858: ! 859: } ! 860: //* SUCCESS return ! 861: /*************************************************************************** ! 862: * ClearItem() ! 863: * ! 864: * This function will destroy an item window, and make the ! 865: * next window active. ! 866: **************************************************************************/ ! 867: ! 868: VOID FAR ClearItem( //* ENTRY: ! 869: APPITEMPTR pItem //* application item pointer ! 870: ){ ! 871: ! 872: pItem->fVisible = FALSE; ! 873: SetTopItem(GetNextActiveItem()); ! 874: ObjDelete(pItem, OLE_OBJ_DELETE); ! 875: Dirty(DOC_DIRTY); ! 876: ! 877: } ! 878: ! 879: /**************************************************************************** ! 880: * SaveAsNeeded() ! 881: * ! 882: * This function will have the file saved if and only ! 883: * if the document has been modified. If the fDirty flag has ! 884: * been set to TRUE, then the document needs to be saved. ! 885: * ! 886: * Returns: BOOL - TRUE if document doesn't need saving or if the ! 887: * document has been saved successfully. ! 888: ***************************************************************************/ ! 889: ! 890: static BOOL SaveAsNeeded( //* ENTRY: ! 891: PSTR pFileName, //* file to save ! 892: LHCLIENTDOC lhcDoc, //* OLE doc handle ! 893: LPAPPSTREAM lpStream //* pointer to OLE stream vtbl ... ! 894: ){ //* LOCAL: ! 895: CHAR sz[CBMESSAGEMAX]; //* work strings ! 896: CHAR sz2[CBMESSAGEMAX + CBPATHMAX]; ! 897: ! 898: if (Dirty(DOC_QUERY)) //* if doc is clean don't bother ! 899: { ! 900: ! 901: LoadString(hInst, IDS_MAYBESAVE, sz, CBMESSAGEMAX); ! 902: wsprintf(sz2, sz, (LPSTR)pFileName ); ! 903: ! 904: switch (MessageBox(hwndFrame, sz2, szAppName, MB_YESNOCANCEL | MB_ICONQUESTION)) ! 905: { ! 906: ! 907: case IDCANCEL: ! 908: return FALSE; //* CANCEL return ! 909: ! 910: case IDYES: ! 911: return (SaveFile(pFileName,lhcDoc,lpStream)); ! 912: ! 913: default: ! 914: break; ! 915: } ! 916: } ! 917: return TRUE; //* SUCCESS return ! 918: ! 919: } ! 920: ! 921: /**************************************************************************** ! 922: * SetTitle() ! 923: * ! 924: * Set the window caption to the current file name. If szFileName is ! 925: * NULL, the caption will be set to "(Untitled)". ! 926: ***************************************************************************/ ! 927: ! 928: static VOID SetTitle( //* ENTRY: ! 929: PSTR pFileName //* file name ! 930: ){ //* LOCAL ! 931: //* window title string ! 932: CHAR szTitle[CBMESSAGEMAX + CBPATHMAX]; ! 933: ! 934: wsprintf(szTitle, "%s - %s", (LPSTR)szAppName, (LPSTR)pFileName); ! 935: SetWindowText(hwndFrame, szTitle); ! 936: ! 937: } ! 938: ! 939: /*************************************************************************** ! 940: * EndClient() ! 941: * ! 942: * Perform cleanup prior to app termination. The OLECLIENT ! 943: * memory blocks and procedure instance thunks freed. ! 944: **************************************************************************/ ! 945: ! 946: static VOID EndStream( //* ENTRY: ! 947: LPAPPSTREAM lpStream //* pointer to stream structure ! 948: ){ //* LOCAL: ! 949: HANDLE hGeneric; //* temp handle ! 950: ! 951: if (lpStream) //* is there a STREAM struct? ! 952: { ! 953: if (lpStream->olestream.lpstbl) ! 954: { ! 955: FreeProcInstance((FARPROC)lpStream->olestream.lpstbl->Get); ! 956: FreeProcInstance((FARPROC)lpStream->olestream.lpstbl->Put); ! 957: hGeneric = GlobalHandle((LPSTR)lpStream->olestream.lpstbl); ! 958: GlobalUnlock(hGeneric); ! 959: GlobalFree(hGeneric); ! 960: } ! 961: hGeneric = GlobalHandle((LPSTR)lpStream); ! 962: GlobalUnlock(hGeneric); ! 963: GlobalFree(hGeneric); ! 964: } ! 965: ! 966: } //* SUCCESS return ! 967: ! 968: /*************************************************************************** ! 969: * EndClient() ! 970: * ! 971: * Perform cleanup prior to app termination. The OLECLIENT ! 972: * memory blocks and procedure instance thunks are freed. ! 973: **************************************************************************/ ! 974: ! 975: static VOID EndClient( //* ENTRY: ! 976: LPOLECLIENT lpClient //* pointer to client structure ! 977: ){ //* LOCAL: ! 978: HANDLE hGeneric; //* temp handle ! 979: ! 980: if (lpClient) //* is there a client structure ! 981: { ! 982: if (lpClient->lpvtbl) ! 983: { ! 984: FreeProcInstance(lpClient->lpvtbl->CallBack); ! 985: hGeneric = GlobalHandle((LPSTR)lpClient->lpvtbl); ! 986: GlobalUnlock(hGeneric); ! 987: GlobalFree(hGeneric); ! 988: } ! 989: hGeneric = GlobalHandle((LPSTR)lpClient); ! 990: GlobalUnlock(hGeneric); ! 991: GlobalFree(hGeneric); ! 992: } ! 993: ! 994: } //* SUCCESS return ! 995: ! 996: /**************************************************************************** ! 997: * QueryEndSession() ! 998: ***************************************************************************/ ! 999: ! 1000: static LONG QueryEndSession( //* ENTRY: ! 1001: PSTR pFileName, //* document name ! 1002: LHCLIENTDOC lhcDoc, //* client document handle ! 1003: LPAPPSTREAM lpStream //* application stream pointer ! 1004: ){ //* LOCAL: ! 1005: APPITEMPTR pItem; //* application item pointer ! 1006: ! 1007: ! 1008: for (pItem = GetTopItem(); pItem; pItem = GetNextItem(pItem)) ! 1009: if (OleQueryOpen(pItem->lpObject) == OLE_OK) ! 1010: { ! 1011: MessageBox(hwndFrame,"Exit CliDemo before closing Windows", ! 1012: szAppName, MB_OK | MB_ICONSTOP); ! 1013: return 0L; ! 1014: } ! 1015: ! 1016: if (!SaveAsNeeded(pFileName, lhcDoc, lpStream)) ! 1017: return 0L; ! 1018: DeregDoc(lhcDoc); ! 1019: return 1L; ! 1020: ! 1021: } ! 1022:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.