Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1032 lines
36 KiB

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