|
|
/*
* clidemo.c - OLE client application sample code * * Created by Microsoft Corporation. * (c) Copyright Microsoft Corp. 1990 - 1992 All Rights Reserved * */
/***************************************************************************
* IMPORTANT - README: * OLE client applications are windows programs which use the OLE client * APIs. Therefore it is imperative that you understand how these APIs * operate. Most importantly it is essential that you keep in mind which * procedure calls result in asynchronous states: a state where the operation * is not truely complete after a return from the call. * * Many functions produce asynchronous states, for example, OleActivate, * OleClose, OleCopyFromLink, OleCreate ... Reference your SDK manual for * a complete list. * * So whenever you call any of these library functions keep in mind that * the operation is not necessarily complete once a return is made. * These operations require communications with a server application. With * OLE the inter-application communication is done through DDE. In order * for a DDE conversation to complete several DDE messages need to be * sent and recieved by both the server and client OLE DLLs. So, the * asynchronous operations will not complete until the client application * enters a message dipatch loop. Therefore, it is necessary to enter * a dispatch loop and wait for completion. It is not necessary to block * all other operation; however, it is very important to coordinate the * user activity to prevent disastrous re-entry cases. * * In this application I have written a macro to prevent re-entry * problems. Namely: ANY_OBJECT_BUSY which prevents a user from initiating * an action which will result in an asynchronous call if there is an object * already in an asynchronous state. * * The following is brief summary of the three macros: * * ANY_OBJECT_BUSY: checks to see if any object in the document is busy. * This prevents a new document from being saved to file if there are * objects in asynchronous states. * * So, the problem is that we have to enter a message dispatch loop in order * to let DDE messages get through so that asynchronous operations can finish. * And while we are in the message dispatch loops (WaitForObject or WaitForAllObjects) * we have to prevent the user from doing things that can't be done when an * object(s) is busy. Yes, it is confusing , but, the end result is a super * cool application that can have linked and embbeded objects! ***************************************************************************/
//*** INCLUDES ***
#include <windows.h> //* WINDOWS
#include <ole.h> //* OLE structs and defines
#include <shellapi.h> //* Shell, drag and drop headers
#include "demorc.h" //* header for resource file
#include "global.h" //* global app variables
#include "clidemo.h" //* app includes:
#include "register.h"
#include "stream.h"
#include "object.h"
#include "dialog.h"
#include "utility.h"
//*** VARIABLES ***
//** Global
HANDLE hInst; BOOL fRetry = FALSE; HWND hwndFrame; //* main window
HANDLE hAccTable; //* accelerator table
CHAR szFrameClass[] = "CliDemo";//* main window class name
CHAR szItemClass[] = "ItemClass";//* item window class name
CHAR szAppName[CBMESSAGEMAX];//* Application name
INT iObjects = 0; //* object count
INT iObjectNumber = 0; //* object number for object name
CHAR szFileName[CBPATHMAX];
extern INT giXppli ; extern INT giYppli ; //* ClipBoard formats:
OLECLIPFORMAT vcfLink; //* "ObjectLink"
OLECLIPFORMAT vcfNative; //* "Native"
OLECLIPFORMAT vcfOwnerLink; //* "OwnerLink"
/***************************************************************************
* WinMain() - Main Windows routine ***************************************************************************/ int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInst, LPSTR lpCmdLine, INT nCmdLine ){ hInst = hInstance;
if (!InitApplication(hInst)) //* register window classes
return FALSE;
if (!InitInstance(hInst)) //* create window instance
return FALSE;
OfnInit(hInst); //* setup to use <commdlg.dll>
//* register clipboard formats
//* used for OLE
vcfLink = (OLECLIPFORMAT)RegisterClipboardFormat("ObjectLink"); vcfNative = (OLECLIPFORMAT)RegisterClipboardFormat("Native"); vcfOwnerLink = (OLECLIPFORMAT)RegisterClipboardFormat("OwnerLink");
ShowWindow(hwndFrame, SW_SHOWNORMAL); UpdateWindow(hwndFrame); ProcessCmdLine(lpCmdLine);
while (ProcessMessage(hwndFrame, hAccTable)) ;
return FALSE; }
/***************************************************************************
* InitApplication() * * registers the window classes used by the application. * * Returns BOOL: - TRUE if successful. ***************************************************************************/
static BOOL InitApplication( //* ENTRY:
HANDLE hInst //* instance handle
){ //* LOCAL:
WNDCLASS wc; //* temp wind-class structure
wc.style = 0; wc.lpfnWndProc = (WNDPROC)FrameWndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInst; wc.hIcon = LoadIcon(hInst, MAKEINTRESOURCE(ID_APPLICATION)); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE + 1); wc.lpszMenuName = MAKEINTRESOURCE(ID_APPLICATION); wc.lpszClassName = szFrameClass;
if (!RegisterClass(&wc)) return FALSE; //* application item class
wc.style = CS_DBLCLKS | CS_VREDRAW | CS_HREDRAW; wc.lpfnWndProc = (WNDPROC)ItemWndProc; wc.hIcon = NULL; wc.cbWndExtra = sizeof(APPITEMPTR); wc.lpszMenuName = NULL; wc.lpszClassName = szItemClass;
if (!RegisterClass(&wc)) return FALSE;
return TRUE;
}
/***************************************************************************
* InitInstance() * * create the main application window. * * Returns BOOL: - TRUE if successful else FALSE. ***************************************************************************/
static BOOL InitInstance( //* ENTRY:
HANDLE hInst //* instance handel
){ HDC hDC ;
hAccTable = LoadAccelerators(hInst, MAKEINTRESOURCE(ID_APPLICATION));
if (!(hwndFrame = CreateWindow( szFrameClass, "", WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInst, NULL ))) return FALSE; //* ERROR return
LoadString(hInst, IDS_APPNAME, szAppName, CBMESSAGEMAX); DragAcceptFiles(hwndFrame, TRUE); //* allow dragged and dropped files
hDC = GetDC (NULL); // Get the hDC of the desktop window
giXppli = GetDeviceCaps (hDC, LOGPIXELSX); giYppli = GetDeviceCaps (hDC, LOGPIXELSY); ReleaseDC (NULL, hDC);
return TRUE; //* SUCCESS return
}
/***************************************************************************
* ProcessCmdLine() * * process command line getting any command arguments. ***************************************************************************/
VOID ProcessCmdLine(LPSTR lpCmdLine) { //* LOCAL:
OFSTRUCT ofs;
if (*lpCmdLine) { //* look for file extension
LPSTR lpstrExt = lpCmdLine; //* pointer to file extension
while (*lpstrExt && *lpstrExt != '.') lpstrExt = AnsiNext(lpstrExt);
lstrcpy(szFileName, lpCmdLine); if (!(*lpstrExt)) //* append default extension
{ lstrcat(szFileName,"."); lstrcat(szFileName,szDefExtension); } //* get the files fully
OpenFile(szFileName, &ofs, OF_PARSE);//* qualified name
lstrcpy(szFileName, ofs.szPathName); } else *szFileName = 0; //* pass filename to main winproc
SendMessage(hwndFrame,WM_INIT,(WPARAM)0,(LPARAM)0);
}
/***************************************************************************
* FrameWndProc() * * Message handler for the application frame window. * * Returns long - Variable, depends on message. ***************************************************************************/
LONG APIENTRY FrameWndProc( //* ENTRY:
HWND hwnd, //* standard wind-proc parameters
UINT msg, DWORD wParam, LONG lParam ){ //* LOCAL:
//* ^ Document file name
static LHCLIENTDOC lhcDoc; //* Document Handle
static LPOLECLIENT lpClient; //* pointer to client
static LPAPPSTREAM lpStream; //* pointer to stream vtbl
APPITEMPTR pItem; //* application item pointer
switch (msg) { case WM_INIT: //* user defined message
if (!InitAsOleClient(hInst, hwnd, szFileName, &lhcDoc, &lpClient, &lpStream)) DestroyWindow(hwnd); break; //* the following three messages are
//* used to avoid problems with OLE
//* see the comment in object.h
case WM_DELETE: //* user defined message
pItem = (APPITEMPTR) lParam; //* delete object
WaitForObject(pItem); ObjDelete(pItem,OLE_OBJ_DELETE); if (wParam) cOleWait--; break;
case WM_ERROR: //* user defined message
ErrorMessage(wParam); //* display error message
break;
case WM_RETRY: //* user defined message
RetryMessage((APPITEMPTR)lParam, RD_RETRY | RD_CANCEL); break;
case WM_INITMENU: UpdateMenu((HMENU)wParam); break;
case WM_COMMAND: { WORD wID = LOWORD(wParam);
pItem = GetTopItem();
switch (wID) { case IDM_NEW: ANY_OBJECT_BUSY; NewFile(szFileName,&lhcDoc,lpStream); break;
case IDM_OPEN: ANY_OBJECT_BUSY; MyOpenFile(szFileName,&lhcDoc,lpClient,lpStream); break;
case IDM_SAVE: ANY_OBJECT_BUSY; SaveFile(szFileName,lhcDoc,lpStream); break;
case IDM_SAVEAS: ANY_OBJECT_BUSY; SaveasFile(szFileName,lhcDoc,lpStream); break;
case IDM_ABOUT: AboutBox(); break;
case IDM_INSERT: ANY_OBJECT_BUSY; ObjInsert(lhcDoc, lpClient); break;
case IDM_INSERTFILE: ANY_OBJECT_BUSY; ObjCreateFromTemplate(lhcDoc,lpClient); break;
case IDM_PASTE: case IDM_PASTELINK: ANY_OBJECT_BUSY; ObjPaste(wID == IDM_PASTE,lhcDoc,lpClient); break;
case IDM_LINKS: ANY_OBJECT_BUSY; pItem = GetTopItem(); LinkProperties(); break;
case IDM_EXIT: ANY_OBJECT_BUSY; SendMessage(hwnd, WM_SYSCOMMAND, SC_CLOSE, 0L); break;
case IDM_COPY: case IDM_CUT: ANY_OBJECT_BUSY;
if (!ObjCopy(pItem)) { ErrorMessage((wParam == IDM_CUT) ? E_CLIPBOARD_CUT_FAILED : E_CLIPBOARD_COPY_FAILED); break; }
if (wParam == IDM_COPY) break;
case IDM_CLEAR: //* CUT falls through to clear
ANY_OBJECT_BUSY; ClearItem(pItem); break;
case IDM_CLEARALL: ANY_OBJECT_BUSY; ClearAll(lhcDoc,OLE_OBJ_DELETE); Dirty(DOC_DIRTY); break;
default: if( (wParam >= IDM_VERBMIN) && (wParam <= IDM_VERBMAX) ) { ANY_OBJECT_BUSY; ExecuteVerb(wParam - IDM_VERBMIN,pItem); break; } return DefWindowProc(hwnd, msg, wParam, lParam); } break; }
case WM_DROPFILES: ANY_OBJECT_BUSY; ObjCreateWrap((HANDLE)wParam, lhcDoc, lpClient); break;
case WM_CLOSE: ANY_OBJECT_BUSY; if (!SaveAsNeeded(szFileName, lhcDoc, lpStream)) break; DeregDoc(lhcDoc); DestroyWindow(hwnd); break;
case WM_DESTROY: EndStream(lpStream); EndClient(lpClient); PostQuitMessage(0); break;
case WM_QUERYENDSESSION: //* don't let windows terminate
return (QueryEndSession(szFileName,lhcDoc, lpStream));
default: return DefWindowProc(hwnd, msg, wParam, lParam); } return 0L;
}
/***************************************************************************
* InitAsOleClient() * * Initiates the creation of stream and client vtbls. These vtbls are very * important for the proper operation of this application. The stream vtbl * lets the OLE librarys know where the location of the stream I/O routines * reside. The stream routines are used by OleLoadFromStream and the like. * The client vtbl is used to hold the pointer to the CallBack function. * IMPORTANT: both the client and the stream structures have pointers to * vtbls which have the pointers to the functions. Therefore, it is * necessary to allocate space for the vtbl and the client structure * which has the pointer to the vtbl. **************************************************************************/
static BOOL InitAsOleClient( //* ENTRY:
HANDLE hInstance, //* applicaion instance handle
HWND hwnd, //* main window handle
PSTR pFileName, //* document file name
LHCLIENTDOC *lhcDoc, //* pointer to document Handle
LPOLECLIENT *lpClient, //* pointer to client pointer
LPAPPSTREAM *lpStream //* pointer to APPSTREAM pointer
){ //* initiate client vtbl creation
if (!(*lpClient = InitClient(hInstance))) { SendMessage(hwnd, WM_SYSCOMMAND, SC_CLOSE, 0L); return FALSE; //* ERROR return
} //* initiate stream vtbl creation
if (!(*lpStream = InitStream(hInstance))) { SendMessage(hwnd, WM_SYSCOMMAND, SC_CLOSE, 0L); return FALSE; //* ERROR return
}
if (*pFileName && RegDoc(pFileName,lhcDoc) && LoadFile(pFileName,*lhcDoc,*lpClient,*lpStream)) { SetTitle(pFileName); return TRUE; //* SUCCESS return
}
NewFile(pFileName, lhcDoc, *lpStream); return TRUE; //* SUCCESS return
} //* SUCCESS return
/****************************************************************************
* InitClient() * * Initialize the OLE client structure, create and fill the OLECLIENTVTBL * structure. * * Returns LPOLECLIENT - if successful a pointer to a client structure * , otherwise NULL. ***************************************************************************/
static LPOLECLIENT InitClient( //* ENTRY:
HANDLE hInstance //* application instance handle
){ //* LOCAL:
LPOLECLIENT lpClient=NULL; //* pointer to client struct
//* Allocate vtbls
if (!(lpClient = (LPOLECLIENT)GlobalLock( GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(OLECLIENT)) ))) goto Error; //* ERROR jump
if (!(lpClient->lpvtbl = (LPOLECLIENTVTBL)GlobalLock( GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(OLECLIENTVTBL)) ))) goto Error; //* ERROR jump
//* set the CALLBACK function
//* pointer
lpClient->lpvtbl->CallBack = CallBack;
return lpClient; //* SUCCESS return
Error: //* ERROR Tag
ErrorMessage(E_FAILED_TO_ALLOC); EndClient(lpClient); //* free any allocated space
return NULL; //* ERROR return
}
/****************************************************************************
* InitStream() * * Create and fill the STREAMVTBL. Create a stream structure and initialize * pointer to stream vtbl. * * Returns LPAPPSTREAM - if successful a pointer to a stream structure * , otherwise NULL . ***************************************************************************/
static LPAPPSTREAM InitStream( //* ENTRY:
HANDLE hInstance //* handle to application instance
){ //* LOCAL:
LPAPPSTREAM lpStream = NULL; //* pointer to stream structure
if (!(lpStream = (LPAPPSTREAM)GlobalLock( GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(APPSTREAM)) ))) goto Error; //* ERROR jump
if (!(lpStream->olestream.lpstbl = (LPOLESTREAMVTBL)GlobalLock( GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(OLESTREAMVTBL)) ))) goto Error; //* ERROR jump
//* set stream func. pointers
lpStream->olestream.lpstbl->Get = (DWORD ( CALLBACK *)(LPOLESTREAM, VOID FAR *, DWORD)) ReadStream; lpStream->olestream.lpstbl->Put = (DWORD ( CALLBACK *)(LPOLESTREAM, OLE_CONST VOID FAR *, DWORD)) WriteStream;
return lpStream; //* SUCCESS return
Error: //* ERROR Tag
ErrorMessage(E_FAILED_TO_ALLOC); EndStream(lpStream);
return NULL; //* ERROR return
}
/***************************************************************************
* UpdateMenu() * * Enabling or disable menuitems based upon program state. ***************************************************************************/
static VOID UpdateMenu( //* ENTRY:
HMENU hMenu //* menu handle to updated
){ //* LOCAL:
INT mf; //* generic menu flag
APPITEMPTR paItem; //* app item pointer
HMENU hSub; //* there must be at least on object
//* for the following to be enabled
paItem = GetTopItem() ;
mf = (paItem ? MF_ENABLED : MF_GRAYED); EnableMenuItem(hMenu, IDM_CUT, mf); //* i.e. Cut,Copy,Clear,Clearall...
EnableMenuItem(hMenu, IDM_COPY, mf); EnableMenuItem(hMenu, IDM_CLEAR, mf); EnableMenuItem(hMenu, IDM_CLEARALL, mf); //* enable links option only if there
//* is at least one linked object
EnableMenuItem(hMenu, IDM_LINKS, MF_GRAYED); for (; paItem; paItem = GetNextItem(paItem)) { if (paItem->otObject == OT_LINK) { EnableMenuItem(hMenu, IDM_LINKS, MF_ENABLED); break; } }
if (hSub = GetSubMenu(hMenu,POS_EDITMENU)) UpdateObjectMenuItem(hSub);
if (OleQueryCreateFromClip(STDFILEEDITING, olerender_draw, 0) == OLE_OK) EnableMenuItem(hMenu, IDM_PASTE, MF_ENABLED); else if (OleQueryCreateFromClip(STATICP, olerender_draw, 0) == OLE_OK) EnableMenuItem(hMenu, IDM_PASTE, MF_ENABLED); else EnableMenuItem(hMenu, IDM_PASTE, MF_GRAYED);
if (OleQueryLinkFromClip(STDFILEEDITING, olerender_draw, 0) == OLE_OK) EnableMenuItem(hMenu, IDM_PASTELINK, MF_ENABLED); else EnableMenuItem(hMenu, IDM_PASTELINK, MF_GRAYED);
}
/***************************************************************************
* NewFile() * * Save the present document and open a new blank one. ***************************************************************************/
static VOID NewFile( //* ENTRY:
PSTR pFileName, //* open file name
LHCLIENTDOC *lhcptrDoc, //* pointer to client doc. handle
LPAPPSTREAM lpStream //* pointer to stream structure
){ //* LOCAL:
static CHAR szUntitled[CBMESSAGEMAX] = "";//* "(Untitled)" string
LHCLIENTDOC lhcDocNew; //* handle for new doc.
if (!(*szUntitled)) LoadString(hInst, IDS_UNTITLED, (LPSTR)szUntitled, CBMESSAGEMAX);
if (SaveAsNeeded(pFileName, *lhcptrDoc, lpStream)) { //* try to register new document
if (!RegDoc(szUntitled, &lhcDocNew)) return; //* before deregistring the old one
DeregDoc(*lhcptrDoc); *lhcptrDoc = lhcDocNew; Dirty(DOC_CLEAN); //* new document is clean
lstrcpy(pFileName,szUntitled); SetTitle(pFileName); iObjectNumber = 0; }
}
/***************************************************************************
* MyOpenFile() * * Open a file and load it. Notice that the new file is loaded before * the old is removed. This is done to assure a succesful file load * before removing an existing document. ***************************************************************************/
static VOID MyOpenFile( //* ENTRY:
PSTR pFileName, //* open file name
LHCLIENTDOC *lhcptrDoc, //* pointer to document handle
LPOLECLIENT lpClient, //* pointer to client structure
LPAPPSTREAM lpStream //* pointer to stream structure
){ //* LOCAL:
CHAR szNewFile[CBPATHMAX];//* new file name buffer
LHCLIENTDOC lhcDocNew; //* handle of new document
APPITEMPTR pItem; //* hold top item
if (SaveAsNeeded(pFileName, *lhcptrDoc, lpStream)) { *szNewFile = 0;
if (!OfnGetName(hwndFrame, szNewFile, IDM_OPEN)) return; //* ERROR return
if (!RegDoc(szNewFile,&lhcDocNew)) return; //* ERROR return
pItem = GetTopItem(); ShowDoc(*lhcptrDoc,0); //* make old doc objects hidden.
//* try to load the new file before
if (!LoadFile(szNewFile, lhcDocNew, lpClient, lpStream)) { //* before removing the old.
DeregDoc(lhcDocNew); //* restore old document if new
SetTopItem(pItem); //* file did not load
ShowDoc(*lhcptrDoc,1); return; //* ERROR return
}
DeregDoc(*lhcptrDoc); //* deregister old document
*lhcptrDoc = lhcDocNew; lstrcpy(pFileName,szNewFile); SetTitle(pFileName); //* set new title
Dirty(DOC_CLEAN); }
} //* SUCCESS return
/***************************************************************************
* SaveasFile() * * Prompt the user for a new file name. Write the document to the new * filename. ***************************************************************************/
static VOID SaveasFile( //* ENTRY:
PSTR pFileName, //* old filename
LHCLIENTDOC lhcDoc, //* document handle
LPAPPSTREAM lpStream //* pointer to stream structure
){ CHAR szNewFile[CBPATHMAX];//* new file name
*szNewFile = 0; //* prompt user for new file name
if (!OfnGetName(hwndFrame, szNewFile, IDM_SAVEAS)) return; //* ERROR return
//* rename document
if (!SaveFile(szNewFile, lhcDoc, lpStream)) return;
if (Error(OleRenameClientDoc(lhcDoc, szNewFile))) { ErrorMessage(W_FAILED_TO_NOTIFY); return; //* ERROR return
}
lstrcpy(pFileName,szNewFile); SetTitle(pFileName);
} //* SUCCESS return
/***************************************************************************
* SaveFile() * * Save a compound document file. If the file is untitled, ask the user * for a name and save the document to that file. ***************************************************************************/
static BOOL SaveFile( //* ENTRY:
PSTR pFileName, //* file to save document to
LHCLIENTDOC lhcDoc, //* OLE document handle
LPAPPSTREAM lpStream //* pointer to app. stream struct
){ //* LOCAL:
CHAR szNewFile[CBPATHMAX];//* New file name strings
CHAR szOemFileName[2*CBPATHMAX]; static CHAR szUntitled[CBMESSAGEMAX] = ""; int fh; //* file handle
*szNewFile = 0; if (!(*szUntitled)) LoadString(hInst, IDS_UNTITLED, (LPSTR)szUntitled, CBMESSAGEMAX);
if (!lstrcmp(szUntitled, pFileName))//* get filename for the untitled case
{ if (!OfnGetName(hwndFrame, szNewFile, IDM_SAVEAS)) return FALSE; //* CANCEL return
lstrcpy(pFileName,szNewFile); SetTitle(pFileName); }
AnsiToOem(pFileName, szOemFileName); if ((fh = _lcreat((LPSTR)szOemFileName, 0)) <= 0) { ErrorMessage(E_INVALID_FILENAME); return FALSE; //* ERROR return
}
lpStream->fh = fh; //* save file on disk
if (!WriteToFile(lpStream)) { _lclose(fh); ErrorMessage(E_FAILED_TO_SAVE_FILE); return FALSE; //* ERROR return
} _lclose(fh);
if (Error(OleSavedClientDoc(lhcDoc))) { ErrorMessage(W_FAILED_TO_NOTIFY); return FALSE; //* ERROR return
}
Dirty(DOC_CLEAN); return TRUE; //* SUCCESS return
}
/***************************************************************************
* LoadFile() * * Load a document file from disk. ***************************************************************************/
static BOOL LoadFile( //* ENTRY:
PSTR pFileName, //* file name
LHCLIENTDOC lhcDoc, //* document handle
LPOLECLIENT lpClient, //* pointer to client structure
LPAPPSTREAM lpStream //* pointer to stream structure
){ //* LOCAL:
//* OEM file name
CHAR szOemFileName[2*CBPATHMAX]; int fh; //* file handle
INT iObjectNumberHold; //* hold object number
AnsiToOem(pFileName, szOemFileName); if ((fh = _lopen(szOemFileName, OF_READ | OF_SHARE_DENY_WRITE)) == -1) { ErrorMessage(E_FAILED_TO_READ_FILE); return FALSE; //* ERROR return
}
lpStream->fh = fh;
iObjectNumberHold = iObjectNumber; //* save object number so it can
iObjectNumber = 0; //* be restored if read from file
//* fails
if (!ReadFromFile(lpStream, lhcDoc, lpClient)) { _lclose(fh); ErrorMessage(E_FAILED_TO_READ_FILE); iObjectNumber = iObjectNumberHold; return FALSE; //* ERROR return
} _lclose(fh); return TRUE; //* SUCCESS return
}
/***************************************************************************
* RegDoc() * * Register the client document with the OLE library. **************************************************************************/
static BOOL RegDoc( //* ENTRY:
PSTR pFileName, //* file name
LHCLIENTDOC *lhcptrDoc //* pointer to client document handle
){
if (Error(OleRegisterClientDoc(szAppName, (LPSTR)pFileName, 0L, lhcptrDoc))) { ErrorMessage(W_FAILED_TO_NOTIFY); return FALSE; //* ERROR return
} return TRUE; //* SUCCESS return
}
/****************************************************************************
* DeregDoc() * * This function initiates the removal of all OLE objects from the * current document and deregisters the document with the OLE library. ***************************************************************************/
static VOID DeregDoc( //* ENTRY:
LHCLIENTDOC lhcDoc //* client document handle
){
if (lhcDoc) { //* release all OLE objects
ClearAll(lhcDoc,OLE_OBJ_RELEASE); //* and remove them from the screen
WaitForAllObjects(); if (Error(OleRevokeClientDoc(lhcDoc))) ErrorMessage(W_FAILED_TO_NOTIFY); }
} //* SUCCESS return
/***************************************************************************
* ClearAll() * * This function will destroy all of the item windows in the current * document and delete all OLE objects. The loop is basically an enum * of all child windows. **************************************************************************/
static VOID ClearAll( //* ENTRY:
LHCLIENTDOC lhcDoc, //* application document handle
BOOL fDelete //* Delete / Release
){ //* LOCAL:
APPITEMPTR pItemNext; //* working handles
APPITEMPTR pItem; //* pointer to application item
pItem = GetTopItem();
while (pItem) { pItemNext = GetNextItem(pItem); if (pItem->lhcDoc == lhcDoc) ObjDelete(pItem, fDelete); pItem = pItemNext; }
} //* SUCCESS return
/***************************************************************************
* ClearItem() * * This function will destroy an item window, and make the * next window active. **************************************************************************/
VOID FAR ClearItem( //* ENTRY:
APPITEMPTR pItem //* application item pointer
){
pItem->fVisible = FALSE; SetTopItem(GetNextActiveItem()); ObjDelete(pItem, OLE_OBJ_DELETE); Dirty(DOC_DIRTY);
}
/****************************************************************************
* SaveAsNeeded() * * This function will have the file saved if and only * if the document has been modified. If the fDirty flag has * been set to TRUE, then the document needs to be saved. * * Returns: BOOL - TRUE if document doesn't need saving or if the * document has been saved successfully. ***************************************************************************/
static BOOL SaveAsNeeded( //* ENTRY:
PSTR pFileName, //* file to save
LHCLIENTDOC lhcDoc, //* OLE doc handle
LPAPPSTREAM lpStream //* pointer to OLE stream vtbl ...
){ //* LOCAL:
CHAR sz[CBMESSAGEMAX]; //* work strings
CHAR sz2[CBMESSAGEMAX + CBPATHMAX];
if (Dirty(DOC_QUERY)) //* if doc is clean don't bother
{
LoadString(hInst, IDS_MAYBESAVE, sz, CBMESSAGEMAX); wsprintf(sz2, sz, (LPSTR)pFileName );
switch (MessageBox(hwndFrame, sz2, szAppName, MB_YESNOCANCEL | MB_ICONQUESTION)) {
case IDCANCEL: return FALSE; //* CANCEL return
case IDYES: return (SaveFile(pFileName,lhcDoc,lpStream));
default: break; } } return TRUE; //* SUCCESS return
}
/****************************************************************************
* SetTitle() * * Set the window caption to the current file name. If szFileName is * NULL, the caption will be set to "(Untitled)". ***************************************************************************/
static VOID SetTitle( //* ENTRY:
PSTR pFileName //* file name
){ //* LOCAL
//* window title string
CHAR szTitle[CBMESSAGEMAX + CBPATHMAX];
wsprintf(szTitle, "%s - %s", (LPSTR)szAppName, (LPSTR)pFileName); SetWindowText(hwndFrame, szTitle);
}
/***************************************************************************
* EndClient() * * Perform cleanup prior to app termination. The OLECLIENT * memory blocks and procedure instance thunks freed. **************************************************************************/
static VOID EndStream( //* ENTRY:
LPAPPSTREAM lpStream //* pointer to stream structure
){ //* LOCAL:
HANDLE hGeneric; //* temp handle
if (lpStream) //* is there a STREAM struct?
{ if (lpStream->olestream.lpstbl) { FreeProcInstance((FARPROC)lpStream->olestream.lpstbl->Get); FreeProcInstance((FARPROC)lpStream->olestream.lpstbl->Put); hGeneric = GlobalHandle((LPSTR)lpStream->olestream.lpstbl); GlobalUnlock(hGeneric); GlobalFree(hGeneric); } hGeneric = GlobalHandle((LPSTR)lpStream); GlobalUnlock(hGeneric); GlobalFree(hGeneric); }
} //* SUCCESS return
/***************************************************************************
* EndClient() * * Perform cleanup prior to app termination. The OLECLIENT * memory blocks and procedure instance thunks are freed. **************************************************************************/
static VOID EndClient( //* ENTRY:
LPOLECLIENT lpClient //* pointer to client structure
){ //* LOCAL:
HANDLE hGeneric; //* temp handle
if (lpClient) //* is there a client structure
{ if (lpClient->lpvtbl) { FreeProcInstance(lpClient->lpvtbl->CallBack); hGeneric = GlobalHandle((LPSTR)lpClient->lpvtbl); GlobalUnlock(hGeneric); GlobalFree(hGeneric); } hGeneric = GlobalHandle((LPSTR)lpClient); GlobalUnlock(hGeneric); GlobalFree(hGeneric); }
} //* SUCCESS return
/****************************************************************************
* QueryEndSession() ***************************************************************************/
static LONG QueryEndSession( //* ENTRY:
PSTR pFileName, //* document name
LHCLIENTDOC lhcDoc, //* client document handle
LPAPPSTREAM lpStream //* application stream pointer
){ //* LOCAL:
APPITEMPTR pItem; //* application item pointer
for (pItem = GetTopItem(); pItem; pItem = GetNextItem(pItem)) if (OleQueryOpen(pItem->lpObject) == OLE_OK) { MessageBox(hwndFrame,"Exit CliDemo before closing Windows", szAppName, MB_OK | MB_ICONSTOP); return 0L; }
if (!SaveAsNeeded(pFileName, lhcDoc, lpStream)) return 0L; DeregDoc(lhcDoc); return 1L;
}
|