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