DATAUSER.CPP

/* 
* DATAUSER.CPP
* Data Object User Chapter 10
*
* Copyright (c)1993-1995 Microsoft Corporation, All Rights Reserved
*
* Kraig Brockschmidt, Microsoft
* Internet : kraigb@microsoft.com
* Compuserve: >INTERNET:kraigb@microsoft.com
*/


#define INITGUIDS
#include "datauser.h"


//These are for displaying clipboard formats textually.
static TCHAR * rgszCF[13]={TEXT("Unknown"), TEXT("CF_TEXT")
, TEXT("CF_BITMAP"), TEXT("CF_METAFILEPICT")
, TEXT("CF_SYLK"), TEXT("CF_DIF"), TEXT("CF_TIFF")
, TEXT("CF_OEMTEXT"), TEXT("CF_DIB")
, TEXT("CF_PALETTE"), TEXT("CF_PENDATA")
, TEXT("CF_RIFF"), TEXT("CF_WAVE")};


static TCHAR szSuccess[] =TEXT("succeeded");
static TCHAR szFailed[] =TEXT("failed");
static TCHAR szExpected[] =TEXT("expected");
static TCHAR szUnexpected[] =TEXT("unexpected!");


/*
* WinMain
*
* Purpose:
* Main entry point of application.
*/

int PASCAL WinMain(HINSTANCE hInst, HINSTANCE hInstPrev
, LPSTR pszCmdLine, int nCmdShow)
{
MSG msg;
PAPP pApp;

SETMESSAGEQUEUE(96);

pApp=new CApp(hInst, hInstPrev, nCmdShow);

if (NULL==pApp)
return -1;

if (pApp->Init())
{
while (GetMessage(&msg, NULL, 0,0 ))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}

delete pApp;
return msg.wParam;
}





/*
* DataUserWndProc
*
* Purpose:
* Window class procedure. Standard callback.
*/

LRESULT APIENTRY DataUserWndProc(HWND hWnd, UINT iMsg
, WPARAM wParam, LPARAM lParam)
{
HRESULT hr;
PAPP pApp;
HMENU hMenu;
FORMATETC fe;
WORD wID;

pApp=(PAPP)GetWindowLong(hWnd, DATAUSERWL_STRUCTURE);

switch (iMsg)
{
case WM_NCCREATE:
pApp=(PAPP)((LPCREATESTRUCT)lParam)->lpCreateParams;
SetWindowLong(hWnd, DATAUSERWL_STRUCTURE, (LONG)pApp);
return (DefWindowProc(hWnd, iMsg, wParam, lParam));

case WM_DESTROY:
PostQuitMessage(0);
break;

case WM_PAINT:
pApp->Paint();
break;

case WM_COMMAND:
SETDefFormatEtc(fe, 0, TYMED_HGLOBAL | TYMED_GDI
| TYMED_MFPICT);

hMenu=GetMenu(hWnd);
wID=LOWORD(wParam);

switch (wID)
{
case IDM_OBJECTUSEDLL:
if (!pApp->m_fEXE)
break;

pApp->m_fEXE=FALSE;
pApp->FReloadDataObjects(TRUE);
break;


case IDM_OBJECTUSEEXE:
if (pApp->m_fEXE)
break;

pApp->m_fEXE=TRUE;
pApp->FReloadDataObjects(TRUE);
break;

case IDM_OBJECTDATASIZESMALL:
case IDM_OBJECTDATASIZEMEDIUM:
case IDM_OBJECTDATASIZELARGE:
CheckMenuItem(hMenu
, IDM_OBJECTDATASIZESMALL, MF_UNCHECKED);
CheckMenuItem(hMenu
, IDM_OBJECTDATASIZEMEDIUM, MF_UNCHECKED);
CheckMenuItem(hMenu
, IDM_OBJECTDATASIZELARGE, MF_UNCHECKED);
CheckMenuItem(hMenu, wID, MF_CHECKED);

//Kill old advise.
if (NULL!=pApp->m_pIDataObject
|| 0!=pApp->m_dwConn)
{
pApp->m_pIDataObject->DUnadvise(pApp
->m_dwConn);
}

if (IDM_OBJECTDATASIZELARGE==wID)
pApp->m_pIDataObject=pApp->m_pIDataLarge;
else if (IDM_OBJECTDATASIZEMEDIUM==wID)
pApp->m_pIDataObject=pApp->m_pIDataMedium;
else
pApp->m_pIDataObject=pApp->m_pIDataSmall;

//Setup new advise.
fe.cfFormat=pApp->m_cfAdvise;
pApp->m_pIDataObject->DAdvise(&fe, ADVF_NODATA
, pApp->m_pIAdviseSink, &pApp->m_dwConn);

break;

case IDM_OBJECTQUERYGETDATA:
if (NULL==pApp->m_pIDataObject)
break;

fe.tymed=TYMED_HGLOBAL | TYMED_GDI
| TYMED_MFPICT;

pApp->TryQueryGetData(&fe, CF_TEXT, TRUE, 0);
pApp->TryQueryGetData(&fe, CF_BITMAP, TRUE, 1);
pApp->TryQueryGetData(&fe, CF_DIB, FALSE, 2);
pApp->TryQueryGetData(&fe, CF_METAFILEPICT
, TRUE, 3);
pApp->TryQueryGetData(&fe, CF_WAVE, FALSE, 4);
break;


case IDM_OBJECTGETDATATEXT:
case IDM_OBJECTGETDATABITMAP:
case IDM_OBJECTGETDATAMETAFILEPICT:
if (NULL==pApp->m_pIDataObject)
break;

//Clean up whatever we currently have.
pApp->m_cf=0;
ReleaseStgMedium(&pApp->m_stm);

if (IDM_OBJECTGETDATATEXT==wID)
SETDefFormatEtc(fe, CF_TEXT, TYMED_HGLOBAL);

if (IDM_OBJECTGETDATABITMAP==wID)
SETDefFormatEtc(fe, CF_BITMAP, TYMED_GDI);

if (IDM_OBJECTGETDATAMETAFILEPICT==wID)
{
SETDefFormatEtc(fe, CF_METAFILEPICT
, TYMED_MFPICT);
}

hr=pApp->m_pIDataObject->GetData(&fe
, &(pApp->m_stm));

if (SUCCEEDED(hr))
pApp->m_cf=fe.cfFormat;

InvalidateRect(hWnd, NULL, TRUE);
UpdateWindow(hWnd);
break;

case IDM_OBJECTEXIT:
PostMessage(hWnd, WM_CLOSE, 0, 0L);
break;


case IDM_ADVISETEXT:
case IDM_ADVISEBITMAP:
case IDM_ADVISEMETAFILEPICT:
if (NULL==pApp->m_pIDataObject)
break;

//Terminate the old connection
if (0!=pApp->m_dwConn)
{
pApp->m_pIDataObject->DUnadvise(pApp
->m_dwConn);
}

CheckMenuItem(hMenu, pApp->m_cfAdvise
+IDM_ADVISEMIN, MF_UNCHECKED);
CheckMenuItem(hMenu, wID, MF_CHECKED);

//New format is wID-IDM_ADVISEMIN
pApp->m_cfAdvise=(UINT)(wID-IDM_ADVISEMIN);
fe.cfFormat=pApp->m_cfAdvise;
pApp->m_pIDataObject->DAdvise(&fe, ADVF_NODATA
, pApp->m_pIAdviseSink, &pApp->m_dwConn);

break;

case IDM_ADVISEGETDATA:
pApp->m_fGetData=!pApp->m_fGetData;
CheckMenuItem(hMenu, wID, pApp->m_fGetData
? MF_CHECKED : MF_UNCHECKED);
break;

case IDM_ADVISEREPAINT:
pApp->m_fRepaint=!pApp->m_fRepaint;
CheckMenuItem(hMenu, wID, pApp->m_fRepaint
? MF_CHECKED : MF_UNCHECKED);
break;

default:
break;
}
break;

default:
return (DefWindowProc(hWnd, iMsg, wParam, lParam));
}

return 0L;
}





/*
* CApp::CApp
* CApp::~CApp
*
* Constructor Parameters: (from WinMain)
* hInst HINSTANCE of the application.
* hInstPrev HINSTANCE of a previous instance.
* nCmdShow UINT specifying how to show the app window.
*/

CApp::CApp(HINSTANCE hInst, HINSTANCE hInstPrev
, UINT nCmdShow)
{
m_hInst=hInst;
m_hInstPrev=hInstPrev;
m_nCmdShow=nCmdShow;

m_hWnd=NULL;
m_fEXE=FALSE;

m_pIAdviseSink=NULL;
m_dwConn=0;
m_cfAdvise=0;
m_fGetData=FALSE;
m_fRepaint=FALSE;

m_pIDataSmall =NULL;
m_pIDataMedium=NULL;
m_pIDataLarge =NULL;
m_pIDataObject=NULL;

m_cf=0;
m_stm.tymed=TYMED_NULL;
m_stm.lpszFileName=NULL; //Initializes union to NULL
m_stm.pUnkForRelease=NULL;

m_fInitialized=FALSE;
return;
}


CApp::~CApp(void)
{
//This releases the data object interfaces and advises
FReloadDataObjects(FALSE);

ReleaseStgMedium(&m_stm);

//We called AddRef before so we could do this
ReleaseInterface(m_pIAdviseSink);

if (IsWindow(m_hWnd))
DestroyWindow(m_hWnd);

if (m_fInitialized)
CoUninitialize();

return;
}




/*
* CApp::Init
*
* Purpose:
* Initializes an CApp object by registering window classes,
* creating the main window, and doing anything else prone to
* failure such as calling CoInitialize. If this function fails
* the caller should insure that the destructor is called.
*
* Parameters:
* None
*
* Return Value:
* BOOL TRUE if successful, FALSE otherwise.
*/

BOOL CApp::Init(void)
{
WNDCLASS wc;
BOOL fRet;

CHECKVER_COM;

if (FAILED(CoInitialize(NULL)))
return FALSE;

m_fInitialized=TRUE;

//Register our window classes.
if (!m_hInstPrev)
{
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = DataUserWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = CBWNDEXTRA;
wc.hInstance = m_hInst;
wc.hIcon = LoadIcon(m_hInst, TEXT("Icon"));
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszMenuName = MAKEINTRESOURCE(IDR_MENU);
wc.lpszClassName = TEXT("DATAUSER");

if (!RegisterClass(&wc))
return FALSE;
}

//Create the main window.
m_hWnd=CreateWindow(TEXT("DATAUSER"), TEXT("Data Object User")
, WS_OVERLAPPEDWINDOW,35, 35, 350, 250, NULL
, NULL, m_hInst, this);

if (NULL==m_hWnd)
return FALSE;

ShowWindow(m_hWnd, m_nCmdShow);
UpdateWindow(m_hWnd);

m_pIAdviseSink=new CAdviseSink(this);

if (NULL==m_pIAdviseSink)
return FALSE;

m_pIAdviseSink->AddRef();

CheckMenuItem(GetMenu(m_hWnd), IDM_OBJECTUSEDLL, MF_CHECKED);
CheckMenuItem(GetMenu(m_hWnd), IDM_OBJECTDATASIZESMALL
, MF_CHECKED);

//Load the initial objects
fRet=FReloadDataObjects(TRUE);
m_pIDataObject=m_pIDataSmall;

return fRet;
}





/*
* CApp::FReloadDataObjects
*
* Purpose:
* Releases the old data objects we're holding on to and reloads
* the new ones from either EXE or DLL depending on m_fEXE.
*
* Parameters:
* fReload BOOL indicating if we are to recreate everything
* or just release the old ones (so we can use this
* from the destructor).
*
* Return Value:
* BOOL TRUE if there are usable objects in us now.
*/

BOOL CApp::FReloadDataObjects(BOOL fReload)
{
HRESULT hr1, hr2, hr3;
DWORD dwClsCtx;
HCURSOR hCur, hCurT;
HMENU hMenu;
UINT uTempD, uTempE;

//Clean out any data we're holding
m_cf=0;
ReleaseStgMedium(&m_stm);

//Turn off whatever data connection we have
if (NULL!=m_pIDataObject && 0!=m_dwConn)
m_pIDataObject->DUnadvise(m_dwConn);

ReleaseInterface(m_pIDataLarge);
ReleaseInterface(m_pIDataMedium);
ReleaseInterface(m_pIDataSmall);

m_pIDataObject=NULL;
CoFreeUnusedLibraries();

//Exit if we just wanted to free.
if (!fReload)
return FALSE;


hCur=LoadCursor(NULL, MAKEINTRESOURCE(IDC_WAIT));
hCurT=SetCursor(hCur);
ShowCursor(TRUE);

dwClsCtx=(m_fEXE) ? CLSCTX_LOCAL_SERVER : CLSCTX_INPROC_SERVER;

hr1=CoCreateInstance(CLSID_DataObjectSmall, NULL, dwClsCtx
, IID_IDataObject, (PPVOID)&m_pIDataSmall);

hr2=CoCreateInstance(CLSID_DataObjectMedium, NULL, dwClsCtx
, IID_IDataObject, (PPVOID)&m_pIDataMedium);

hr3=CoCreateInstance(CLSID_DataObjectLarge, NULL, dwClsCtx
, IID_IDataObject, (PPVOID)&m_pIDataLarge);

ShowCursor(FALSE);
SetCursor(hCurT);

//If anything fails, recurse to clean up...
if (FAILED(hr1) || FAILED(hr2) || FAILED(hr3))
return FReloadDataObjects(FALSE);

//Reset the state of the menus for Small, no advise, no options.
hMenu=GetMenu(m_hWnd);
CheckMenuItem(hMenu, IDM_OBJECTDATASIZESMALL, MF_CHECKED);
CheckMenuItem(hMenu, IDM_OBJECTDATASIZEMEDIUM, MF_UNCHECKED);
CheckMenuItem(hMenu, IDM_OBJECTDATASIZELARGE, MF_UNCHECKED);

m_pIDataObject=m_pIDataSmall;
CheckMenuItem(hMenu, m_cfAdvise+IDM_ADVISEMIN, MF_UNCHECKED);

uTempE=m_fEXE ? MF_CHECKED : MF_UNCHECKED;
uTempD=!m_fEXE ? MF_CHECKED : MF_UNCHECKED;

CheckMenuItem(hMenu, IDM_OBJECTUSEDLL, uTempD);
CheckMenuItem(hMenu, IDM_OBJECTUSEEXE, uTempE);

CheckMenuItem(hMenu, IDM_ADVISEGETDATA, MF_UNCHECKED);
CheckMenuItem(hMenu, IDM_ADVISEREPAINT, MF_UNCHECKED);

m_fGetData=FALSE;
m_fRepaint=FALSE;

//Cannot request data using async advises, so disable these.
uTempE=m_fEXE ? MF_DISABLED | MF_GRAYED : MF_ENABLED;
EnableMenuItem(hMenu, IDM_ADVISEGETDATA, uTempE);
EnableMenuItem(hMenu, IDM_ADVISEREPAINT, uTempE);

return TRUE;
}



/*
* CApp::TryQueryGetData
*
* Purpose:
* Centralized function call and output code for displaying results
* of various IDataObject::QueryGetData calls.
*
* Parameters:
* pFE LPFORMATETC to test.
* cf UINT specific clipboard format to stuff in pFE
* before calling. If zero, use whatever is
* already in pFE.
* fExpect BOOL indicating expected results
* y UINT line on which to print results.
*
* Return Value:
* None
*/

void CApp::TryQueryGetData(LPFORMATETC pFE, UINT cf
, BOOL fExpect, UINT y)
{
TCHAR szTemp[80];
LPTSTR psz1;
LPTSTR psz2;
UINT cch;
HRESULT hr;
HDC hDC;

if (0!=cf)
pFE->cfFormat=cf;

hr=m_pIDataObject->QueryGetData(pFE);
psz1=(NOERROR==hr) ? szSuccess : szFailed;
psz2=((NOERROR==hr)==fExpect) ? szExpected : szUnexpected;

hDC=GetDC(m_hWnd);
SetTextColor(hDC, GetSysColor(COLOR_WINDOWTEXT));
SetBkColor(hDC, GetSysColor(COLOR_WINDOW));

if (CF_WAVE < cf || 0==cf)
{
cch=wsprintf(szTemp, TEXT("QueryGetData on %d %s (%s)")
, cf, psz1, psz2);
}
else
{
cch=wsprintf(szTemp, TEXT("QueryGetData on %s %s (%s)")
, (LPTSTR)rgszCF[cf], psz1, psz2);
}

//Don't overwrite other painted display.
SetBkMode(hDC, TRANSPARENT);
TextOut(hDC, 0, 16*y, szTemp, cch);

ReleaseDC(m_hWnd, hDC);

return;
}






/*
* CApp::Paint
*
* Purpose:
* Handles WM_PAINT for the main window by drawing whatever
* data we have sitting in the STGMEDIUM at this time.
*
* Parameters:
* None
*
* Return Value:
* None
*/

void CApp::Paint(void)
{
PAINTSTRUCT ps;
HDC hDC;
HDC hMemDC;
LPMETAFILEPICT pMF;
LPTSTR psz;
RECT rc;
FORMATETC fe;

GetClientRect(m_hWnd, &rc);

hDC=BeginPaint(m_hWnd, &ps);

//May need to retrieve the data with EXE objects
if (m_fEXE)
{
if (TYMED_NULL==m_stm.tymed && 0!=m_cf)
{
SETDefFormatEtc(fe, m_cf, TYMED_HGLOBAL
| TYMED_MFPICT | TYMED_GDI);

if (NULL!=m_pIDataObject)
m_pIDataObject->GetData(&fe, &m_stm);
}
}

switch (m_cf)
{
case CF_TEXT:
psz=(LPTSTR)GlobalLock(m_stm.hGlobal);

if (NULL==psz)
break;

SetTextColor(hDC, GetSysColor(COLOR_WINDOWTEXT));
SetBkColor(hDC, GetSysColor(COLOR_WINDOW));

DrawText(hDC, psz, lstrlen(psz), &rc
, DT_LEFT | DT_WORDBREAK);
GlobalUnlock(m_stm.hGlobal);
break;


case CF_BITMAP:
hMemDC=CreateCompatibleDC(hDC);

if (NULL!=SelectObject(hMemDC, (HGDIOBJ)m_stm.hGlobal))
{
BitBlt(hDC, 0, 0, rc.right-rc.left, rc.bottom-rc.top
, hMemDC, 0, 0, SRCCOPY);
}

DeleteDC(hMemDC);
break;


case CF_METAFILEPICT:
pMF=(LPMETAFILEPICT)GlobalLock(m_stm.hGlobal);

if (NULL==pMF)
break;

SetMapMode(hDC, pMF->mm);
SetWindowOrgEx(hDC, 0, 0, NULL);
SetWindowExtEx(hDC, pMF->xExt, pMF->yExt, NULL);

SetViewportExtEx(hDC, rc.right-rc.left
, rc.bottom-rc.top, NULL);

PlayMetaFile(hDC, pMF->hMF);
GlobalUnlock(m_stm.hGlobal);
break;


default:
break;
}

EndPaint(m_hWnd, &ps);
return;
}