|
|
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.