mirror of https://github.com/tongzx/nt5src
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.
1261 lines
31 KiB
1261 lines
31 KiB
/*
|
|
* DATAUSER.CPP
|
|
* Data Object User Chapter 6
|
|
*
|
|
* Copyright (c)1993-1995 Microsoft Corporation, All Rights Reserved
|
|
*
|
|
* Kraig Brockschmidt, Software Design Engineer
|
|
* Microsoft Systems Developer Relations
|
|
*
|
|
* Internet : [email protected]
|
|
* Compuserve: >INTERNET:[email protected]
|
|
*/
|
|
|
|
|
|
#define INIT_MY_GUIDS
|
|
#include "datausr.h"
|
|
#include "perror.h"
|
|
#include <stdio.h>
|
|
|
|
#ifdef WIN32
|
|
#define APP_TITLE TEXT("32 Bit IDataObject User")
|
|
#else
|
|
#define APP_TITLE TEXT("16 Bit IDataObject User")
|
|
#endif
|
|
|
|
//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!");
|
|
|
|
TCHAR tcMessageBuf[4096]; // Misc use buffer for messages.
|
|
int cKSizes[NUM_POINTS] = { 1, 2, 4, 6, 8,
|
|
10, 12, 16, 20, 24,
|
|
28, 32, 40, 48, 56 };
|
|
|
|
/*
|
|
* WinMain
|
|
*
|
|
* Purpose:
|
|
* Main entry point of application.
|
|
*/
|
|
|
|
int PASCAL WinMain(
|
|
HINSTANCE hInst,
|
|
HINSTANCE hInstPrev,
|
|
LPSTR pszCmdLine,
|
|
int nCmdShow)
|
|
{
|
|
MSG msg;
|
|
PAPPVARS pAV;
|
|
|
|
#ifndef WIN32
|
|
int cMsg=96;
|
|
|
|
while (!SetMessageQueue(cMsg) && (cMsg-=8));
|
|
#endif
|
|
|
|
pAV=new CAppVars(hInst, hInstPrev, nCmdShow);
|
|
|
|
if (NULL==pAV)
|
|
return -1;
|
|
|
|
if (pAV->FInit())
|
|
{
|
|
while (GetMessage(&msg, NULL, 0,0 ))
|
|
{
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
}
|
|
|
|
delete pAV;
|
|
return msg.wParam;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
* DataUserWndProc
|
|
*
|
|
* Purpose:
|
|
* Window class procedure. Standard callback.
|
|
*/
|
|
|
|
LRESULT API_ENTRY DataUserWndProc(HWND hWnd, UINT iMsg
|
|
, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
HRESULT hr;
|
|
PAPPVARS pAV;
|
|
HMENU hMenu;
|
|
FORMATETC fe;
|
|
WORD wID;
|
|
int i;
|
|
|
|
pAV=(PAPPVARS)GetWindowLong(hWnd, DATAUSERWL_STRUCTURE);
|
|
|
|
switch (iMsg)
|
|
{
|
|
case WM_NCCREATE:
|
|
pAV=(PAPPVARS)((LPCREATESTRUCT)lParam)->lpCreateParams;
|
|
SetWindowLong(hWnd, DATAUSERWL_STRUCTURE, (LONG)pAV);
|
|
return (DefWindowProc(hWnd, iMsg, wParam, lParam));
|
|
|
|
case WM_DESTROY:
|
|
PostQuitMessage(0);
|
|
break;
|
|
|
|
case WM_PAINT:
|
|
pAV->Paint();
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
SETDefFormatEtc(fe, 0, TYMED_HGLOBAL | TYMED_GDI| TYMED_MFPICT);
|
|
|
|
hMenu=GetMenu(hWnd);
|
|
wID=LOWORD(wParam);
|
|
|
|
if(wID >= IDM_OBJECTSETDATA && wID <= IDM_OBJECTSETDATA+64)
|
|
{
|
|
// Blast all possible SetData menu items. Some don't exist.
|
|
for(i=IDM_OBJECTSETDATA; i<=IDM_OBJECTSETDATA+64; i++)
|
|
CheckMenuItem(hMenu,i, MF_UNCHECKED);
|
|
CheckMenuItem(hMenu, wID, MF_CHECKED);
|
|
|
|
pAV->m_SetData_SetSize(wID-IDM_OBJECTSETDATA);
|
|
break;
|
|
}
|
|
|
|
switch (wID)
|
|
{
|
|
case IDM_USE16BITSERVER:
|
|
if (pAV->m_f16Bit)
|
|
break;
|
|
pAV->m_f16Bit = TRUE;
|
|
pAV->FReloadDataObjects(TRUE);
|
|
break;
|
|
|
|
case IDM_USE32BITSERVER:
|
|
if (!pAV->m_f16Bit)
|
|
break;
|
|
pAV->m_f16Bit = FALSE;
|
|
pAV->FReloadDataObjects(TRUE);
|
|
break;
|
|
|
|
case IDM_OBJECTQUERYGETDATA:
|
|
if (NULL==pAV->m_pIDataObject)
|
|
break;
|
|
|
|
fe.tymed=TYMED_HGLOBAL | TYMED_GDI
|
|
| TYMED_MFPICT;
|
|
|
|
pAV->TryQueryGetData(&fe, CF_TEXT, TRUE, 0);
|
|
pAV->TryQueryGetData(&fe, CF_BITMAP, TRUE, 1);
|
|
#ifdef NOT_SIMPLE
|
|
pAV->TryQueryGetData(&fe, CF_DIB, FALSE, 2);
|
|
pAV->TryQueryGetData(&fe, CF_METAFILEPICT, TRUE, 3);
|
|
pAV->TryQueryGetData(&fe, CF_WAVE, FALSE, 4);
|
|
#endif /* NOT_SIMPLE */
|
|
break;
|
|
|
|
|
|
case IDM_OBJECTGETDATA_TEXT:
|
|
case IDM_OBJECTGETDATA_BITMAP:
|
|
#ifdef NOT_SIMPLE
|
|
case IDM_OBJECTGETDATA_METAFILEPICT:
|
|
#endif /* NOT_SIMPLE */
|
|
if (pAV->m_GetData(wID) )
|
|
{
|
|
InvalidateRect(hWnd, NULL, TRUE);
|
|
UpdateWindow(hWnd);
|
|
}
|
|
|
|
if(pAV->m_fDisplayTime)
|
|
pAV->m_DisplayTimerResults();
|
|
break;
|
|
|
|
case IDM_OBJECTGETDATAHERE_TEXT:
|
|
case IDM_OBJECTGETDATAHERE_NULLTEXT:
|
|
case IDM_OBJECTGETDATAHERE_BITMAP:
|
|
case IDM_OBJECTGETDATAHERE_NULLBITMAP:
|
|
if (pAV->m_GetDataHere(wID) )
|
|
{
|
|
InvalidateRect(hWnd, NULL, TRUE);
|
|
UpdateWindow(hWnd);
|
|
}
|
|
|
|
if(pAV->m_fDisplayTime)
|
|
pAV->m_DisplayTimerResults();
|
|
break;
|
|
|
|
case IDM_OBJECTSETDATAPUNK_TEXT:
|
|
case IDM_OBJECTSETDATAPUNK_BITMAP:
|
|
pAV->m_SetData_WithPUnk(wID);
|
|
break;
|
|
|
|
case IDM_MEASUREMENT_1:
|
|
case IDM_MEASUREMENT_50:
|
|
case IDM_MEASUREMENT_300:
|
|
case IDM_MEASUREMENT_OFF:
|
|
case IDM_MEASUREMENT_ON:
|
|
case IDM_MEASUREMENT_TEST:
|
|
pAV->m_SetMeasurement(wID);
|
|
break;
|
|
|
|
case IDM_BATCH_GETDATA:
|
|
pAV->m_MeasureAllSizes(IDM_OBJECTGETDATA_TEXT,
|
|
TEXT("GetData w/HGLOBAL"),
|
|
NULL);
|
|
break;
|
|
|
|
case IDM_BATCH_GETDATAHERE:
|
|
pAV->m_MeasureAllSizes(IDM_OBJECTGETDATAHERE_TEXT,
|
|
TEXT("GetDataHere w/HGLOBAL"),
|
|
NULL);
|
|
break;
|
|
|
|
case IDM_BATCHTOFILE:
|
|
pAV->m_BatchToFile();
|
|
break;
|
|
|
|
case IDM_OBJECTEXIT:
|
|
PostMessage(hWnd, WM_CLOSE, 0, 0L);
|
|
break;
|
|
|
|
#ifdef NOT_SIMPLE
|
|
case IDM_ADVISETEXT:
|
|
case IDM_ADVISEBITMAP:
|
|
case IDM_ADVISEMETAFILEPICT:
|
|
if (NULL==pAV->m_pIDataObject)
|
|
break;
|
|
|
|
//Terminate the old connection
|
|
if (0!=pAV->m_dwConn)
|
|
{
|
|
pAV->m_pIDataObject->DUnadvise(pAV
|
|
->m_dwConn);
|
|
}
|
|
|
|
CheckMenuItem(hMenu, pAV->m_cfAdvise
|
|
+IDM_ADVISEMIN, MF_UNCHECKED);
|
|
CheckMenuItem(hMenu, wID, MF_CHECKED);
|
|
|
|
//New format is wID-IDM_ADVISEMIN
|
|
pAV->m_cfAdvise=(UINT)(wID-IDM_ADVISEMIN);
|
|
fe.cfFormat=pAV->m_cfAdvise;
|
|
pAV->m_pIDataObject->DAdvise(&fe, ADVF_NODATA
|
|
, pAV->m_pIAdviseSink, &pAV->m_dwConn);
|
|
|
|
break;
|
|
|
|
case IDM_ADVISEGETDATA:
|
|
pAV->m_fGetData=!pAV->m_fGetData;
|
|
CheckMenuItem(hMenu, wID, pAV->m_fGetData
|
|
? MF_CHECKED : MF_UNCHECKED);
|
|
break;
|
|
|
|
case IDM_ADVISEREPAINT:
|
|
pAV->m_fRepaint=!pAV->m_fRepaint;
|
|
CheckMenuItem(hMenu, wID, pAV->m_fRepaint
|
|
? MF_CHECKED : MF_UNCHECKED);
|
|
break;
|
|
#endif /* NOT_SIMPLE*/
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return (DefWindowProc(hWnd, iMsg, wParam, lParam));
|
|
}
|
|
|
|
return 0L;
|
|
}
|
|
|
|
|
|
/*
|
|
* CAppVars::CAppVars
|
|
* CAppVars::~CAppVars
|
|
*
|
|
* Constructor Parameters: (from WinMain)
|
|
* hInst HINSTANCE of the application.
|
|
* hInstPrev HINSTANCE of a previous instance.
|
|
* nCmdShow UINT specifying how to show the app window.
|
|
*/
|
|
|
|
CAppVars::CAppVars(HINSTANCE hInst, HINSTANCE hInstPrev
|
|
, UINT nCmdShow)
|
|
{
|
|
m_hInst =hInst;
|
|
m_hInstPrev =hInstPrev;
|
|
m_nCmdShow =nCmdShow;
|
|
|
|
m_hWnd =NULL;
|
|
#ifdef NOT_SIMPLE
|
|
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;
|
|
#endif /* NOT_SIMPLE */
|
|
m_pIDataObject=NULL;
|
|
m_f16Bit=FALSE;
|
|
m_cfFormat=0;
|
|
m_stm.tymed=TYMED_NULL;
|
|
m_stm.lpszFileName=NULL; //Initializes union to NULL
|
|
m_stm.pUnkForRelease=NULL;
|
|
|
|
m_HereAllocCount=0; // For debugging
|
|
|
|
m_fInitialized=FALSE;
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
CAppVars::~CAppVars(void)
|
|
{
|
|
//This releases the data object interfaces and advises
|
|
FReloadDataObjects(FALSE);
|
|
|
|
ReleaseStgMedium(&m_stm);
|
|
|
|
#ifdef NOT_SIMPLE
|
|
if (NULL!=m_pIAdviseSink)
|
|
m_pIAdviseSink->Release();
|
|
#endif /* NOT_SIMPLE */
|
|
|
|
if (IsWindow(m_hWnd))
|
|
DestroyWindow(m_hWnd);
|
|
|
|
if (m_fInitialized)
|
|
CoUninitialize();
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* CAppVars::FInit
|
|
*
|
|
* Purpose:
|
|
* Initializes an CAppVars 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 CAppVars::FInit(void)
|
|
{
|
|
WNDCLASS wc;
|
|
DWORD dwVer;
|
|
BOOL fRet;
|
|
|
|
dwVer=CoBuildVersion();
|
|
|
|
if (rmm!=HIWORD(dwVer))
|
|
return FALSE;
|
|
|
|
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"),
|
|
APP_TITLE,
|
|
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_iDataSizeIndex=1;
|
|
CheckMenuItem(GetMenu(m_hWnd), IDM_OBJECTSETDATA+1, MF_CHECKED);
|
|
for(int i=0; i<64; i++)
|
|
m_hgHereBuffers[i] = NULL;
|
|
|
|
m_cIterations = 1;
|
|
CheckMenuItem(GetMenu(m_hWnd), IDM_MEASUREMENT_1, MF_CHECKED);
|
|
|
|
m_fDisplayTime = FALSE;
|
|
CheckMenuItem(GetMenu(m_hWnd), IDM_MEASUREMENT_OFF, MF_CHECKED);
|
|
|
|
#ifdef NOT_SIMPLE
|
|
m_pIAdviseSink=new CImpIAdviseSink(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);
|
|
#endif /* NOT_SIMPLE */
|
|
|
|
//Load the initial objects
|
|
fRet=FReloadDataObjects(TRUE);
|
|
#ifdef NOT_SIMPLE
|
|
m_pIDataObject=m_pIDataSmall;
|
|
#endif /* NOT_SIMPLE */
|
|
|
|
m_swTimer.m_ClassInit();
|
|
|
|
return fRet;
|
|
}
|
|
|
|
|
|
/*
|
|
* CAppVars::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 CAppVars::FReloadDataObjects(BOOL fReload)
|
|
{
|
|
HCURSOR hCur, hCurT;
|
|
|
|
//Clean out any data we're holding
|
|
m_cfFormat=0;
|
|
ReleaseStgMedium(&m_stm);
|
|
|
|
//Turn off whatever data connection we have
|
|
#ifdef NOT_SIMPLE
|
|
if (NULL!=m_pIDataObject && 0!=m_dwConn)
|
|
m_pIDataObject->DUnadvise(m_dwConn);
|
|
|
|
if (NULL!=m_pIDataLarge)
|
|
m_pIDataLarge->Release();
|
|
|
|
if (NULL!=m_pIDataMedium)
|
|
m_pIDataMedium->Release();
|
|
|
|
if (NULL!=m_pIDataSmall)
|
|
m_pIDataSmall->Release();
|
|
#else /* IS SIMPLE */
|
|
if (NULL != m_pIDataObject)
|
|
m_pIDataObject->Release();
|
|
#endif /* NOT_SIMPLE */
|
|
|
|
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);
|
|
|
|
#ifdef NOT_SIMPLE
|
|
HRESULT hr1, hr2, hr3;
|
|
DWORD dwClsCtx;
|
|
|
|
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);
|
|
#else /* IS SIMPLE */
|
|
HRESULT hr;
|
|
|
|
if(m_f16Bit)
|
|
{
|
|
hr = CoCreateInstance(CLSID_DataObjectTest16,
|
|
NULL,
|
|
CLSCTX_LOCAL_SERVER,
|
|
IID_IDataObject,
|
|
(PPVOID)&m_pIDataObject);
|
|
}else
|
|
{
|
|
hr = CoCreateInstance(CLSID_DataObjectTest32,
|
|
NULL,
|
|
CLSCTX_LOCAL_SERVER,
|
|
IID_IDataObject,
|
|
(PPVOID)&m_pIDataObject);
|
|
}
|
|
|
|
#endif /* NOT_SIMPLE */
|
|
|
|
ShowCursor(FALSE);
|
|
SetCursor(hCurT);
|
|
|
|
//If anything fails, recurse to clean up...
|
|
#ifdef NOT_SIMPLE
|
|
if (FAILED(hr1) || FAILED(hr2) || FAILED(hr3))
|
|
#else /* IS SIMPLE */
|
|
if (FAILED(hr))
|
|
#endif /* NOT_SIMPLE */
|
|
{
|
|
perror_OKBox(0, TEXT("CoCreateInstance Failed: "), hr);
|
|
return FReloadDataObjects(FALSE);
|
|
}
|
|
|
|
HMENU hMenu = GetMenu(m_hWnd);
|
|
UINT uTempD, uTempE;
|
|
|
|
if(m_f16Bit)
|
|
{
|
|
CheckMenuItem(hMenu, IDM_USE16BITSERVER, MF_CHECKED);
|
|
CheckMenuItem(hMenu, IDM_USE32BITSERVER, MF_UNCHECKED);
|
|
}
|
|
else
|
|
{
|
|
CheckMenuItem(hMenu, IDM_USE16BITSERVER, MF_UNCHECKED);
|
|
CheckMenuItem(hMenu, IDM_USE32BITSERVER, MF_CHECKED);
|
|
}
|
|
|
|
|
|
hMenu=GetMenu(m_hWnd);
|
|
for(int i=IDM_OBJECTSETDATA; i<=IDM_OBJECTSETDATA+64; i++)
|
|
{
|
|
CheckMenuItem(hMenu, i, MF_UNCHECKED);
|
|
}
|
|
m_iDataSizeIndex = 1;
|
|
CheckMenuItem(hMenu,
|
|
IDM_OBJECTSETDATA + m_iDataSizeIndex,
|
|
MF_CHECKED);
|
|
|
|
//Reset the state of the menus for Small, no advise, no options.
|
|
#ifdef NOT_SIMPLE
|
|
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);
|
|
#endif /* NOT_SIMPLE */
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* CAppVars::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 CAppVars::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;
|
|
}
|
|
|
|
|
|
int
|
|
CAppVars::m_GetData(WORD wID)
|
|
{
|
|
FORMATETC fe;
|
|
HRESULT hr;
|
|
|
|
if (NULL == m_pIDataObject)
|
|
return(0); // Don't redraw.
|
|
|
|
//Clean up whatever we currently have.
|
|
m_cfFormat = 0;
|
|
ReleaseStgMedium(&m_stm);
|
|
|
|
switch (wID)
|
|
{
|
|
case IDM_OBJECTGETDATA_TEXT:
|
|
SETDefFormatEtc(fe, CF_TEXT, TYMED_HGLOBAL);
|
|
break;
|
|
|
|
#ifdef NOT_SIMPLE
|
|
case IDM_OBJECTGETDATA_BITMAP:
|
|
SETDefFormatEtc(fe, CF_BITMAP, TYMED_GDI);
|
|
break;
|
|
|
|
case IDM_OBJECTGETDATA_METAFILEPICT:
|
|
SETDefFormatEtc(fe, CF_METAFILEPICT, TYMED_MFPICT);
|
|
break;
|
|
#endif /* NOT_SIMPLE */
|
|
|
|
default:
|
|
MessageBox(0,
|
|
TEXT("Type is Unsupported in the Client"),
|
|
TEXT("GetData"),
|
|
MB_OK);
|
|
return(0);
|
|
}
|
|
|
|
m_swTimer.m_Start();
|
|
HRESULT didfail = NOERROR;
|
|
|
|
for(int i=0; i<m_cIterations; i++)
|
|
{
|
|
hr = m_pIDataObject->GetData(&fe, &m_stm);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// If we are just whacking off for the benchmark.
|
|
// Then release all but the last one we recieve.
|
|
if(i < m_cIterations-1)
|
|
ReleaseStgMedium(&m_stm);
|
|
}
|
|
else
|
|
didfail = hr;
|
|
}
|
|
m_swTimer.m_Stop();
|
|
|
|
if (SUCCEEDED(didfail))
|
|
m_cfFormat=fe.cfFormat;
|
|
else
|
|
{
|
|
perror_OKBox(0,
|
|
TEXT("GetData Failed"),
|
|
didfail);
|
|
}
|
|
|
|
return(1); // Do redraw even if it failed (draw blank).
|
|
}
|
|
|
|
|
|
int
|
|
CAppVars::m_GetDataHere(WORD wID)
|
|
{
|
|
FORMATETC fe;
|
|
HRESULT hr;
|
|
|
|
if(NULL == m_pIDataObject)
|
|
return(0); // Don't redraw
|
|
|
|
m_cfFormat = 0;
|
|
|
|
// Don't Release the STGMedium. We recycle them!
|
|
|
|
switch(wID)
|
|
{
|
|
case IDM_OBJECTGETDATAHERE_TEXT:
|
|
SETDefFormatEtc(fe, CF_TEXT, TYMED_HGLOBAL);
|
|
break;
|
|
|
|
case IDM_OBJECTGETDATAHERE_NULLTEXT:
|
|
SETDefFormatEtc(fe, CF_TEXT, TYMED_NULL);
|
|
break;
|
|
|
|
/* Other cases go here.... */
|
|
|
|
default:
|
|
MessageBox(0,
|
|
TEXT("Type is Unsupported in the Client"),
|
|
TEXT("GetDataHere"),
|
|
MB_OK);
|
|
return(0);
|
|
}
|
|
|
|
HGLOBAL* phg = &m_hgHereBuffers[m_iDataSizeIndex];
|
|
if(NULL == *phg)
|
|
{
|
|
++m_HereAllocCount;
|
|
*phg = GlobalAlloc(GMEM_SHARE | GMEM_MOVEABLE,
|
|
DATASIZE_FROM_INDEX(m_iDataSizeIndex) );
|
|
if(NULL == *phg)
|
|
{
|
|
MessageBox(0,
|
|
TEXT("GlobalAlloc Return NULL"),
|
|
TEXT("Failure"),
|
|
MB_OK);
|
|
PostQuitMessage(0);
|
|
return(0); // Don't redraw
|
|
}
|
|
}
|
|
|
|
m_stm.hGlobal=*phg;
|
|
m_stm.tymed=TYMED_HGLOBAL;
|
|
m_stm.pUnkForRelease=NULL;
|
|
|
|
// The TYMED_NULL case tests code in olethk where it is written:
|
|
// "If tymed == TYMED_NULL then GetDataHere should behave like GetData."
|
|
// I can't find this in any manual (OLE2 or Ole). I wanted to see what
|
|
// good that code was. (there is also bug #15974) Aug 8th 1995 BChapman.
|
|
|
|
if (IDM_OBJECTGETDATAHERE_NULLTEXT == wID)
|
|
{
|
|
m_stm.hGlobal=NULL;
|
|
m_stm.tymed=TYMED_NULL;
|
|
}
|
|
|
|
// The other side "knows" the size of the data.
|
|
// (It is told via. SetData)
|
|
|
|
HRESULT didfail = NOERROR;
|
|
m_swTimer.m_Start();
|
|
for(int i=0; i<m_cIterations; i++)
|
|
{
|
|
hr = m_pIDataObject->GetDataHere(&fe, &m_stm);
|
|
if (FAILED(hr))
|
|
didfail = hr;
|
|
// We don't ReleaseSTGMedium because this
|
|
// is GetDataHere !
|
|
}
|
|
m_swTimer.m_Stop();
|
|
|
|
if (SUCCEEDED(didfail))
|
|
m_cfFormat=fe.cfFormat;
|
|
else
|
|
{
|
|
perror_OKBox(0,
|
|
TEXT("GetDataHere Failed"),
|
|
didfail);
|
|
}
|
|
return(1); // redraw (if FAILED(hr) then draw blank)
|
|
}
|
|
|
|
|
|
int
|
|
CAppVars::m_SetData_SetSize(long iSizeIndex)
|
|
{
|
|
FORMATETC fe;
|
|
HRESULT hr;
|
|
|
|
if (NULL == m_pIDataObject)
|
|
return 0;
|
|
|
|
SETDefFormatEtc(fe, CF_TEXT, TYMED_HGLOBAL);
|
|
|
|
m_iDataSizeIndex = iSizeIndex;
|
|
|
|
HGLOBAL hMem=GlobalAlloc(GMEM_SHARE | GMEM_MOVEABLE, sizeof(ULONG) );
|
|
if(NULL == hMem)
|
|
{
|
|
MessageBox(0,
|
|
TEXT("GlobalAlloc Return NULL"),
|
|
TEXT("Failure"),
|
|
MB_OK);
|
|
PostQuitMessage(0);
|
|
return 0;
|
|
}
|
|
|
|
long* pl=(long*)GlobalLock(hMem); // Lock
|
|
*((long*)pl) = DATASIZE_FROM_INDEX(m_iDataSizeIndex);
|
|
GlobalUnlock(hMem); // Unlock
|
|
|
|
m_stm.hGlobal=hMem;
|
|
m_stm.tymed=TYMED_HGLOBAL;
|
|
m_stm.pUnkForRelease=NULL;
|
|
|
|
hr = m_pIDataObject->SetData(&fe, &m_stm, FALSE); // Keep Ownership.
|
|
if (FAILED(hr))
|
|
{
|
|
perror_OKBox(0,
|
|
TEXT("SetData Failed"),
|
|
hr);
|
|
return 0;
|
|
}
|
|
return 1;;
|
|
// release the hMem HGLOBAL perhaps ???
|
|
}
|
|
|
|
|
|
int
|
|
CAppVars::m_SetData_WithPUnk(WORD wID)
|
|
{
|
|
FORMATETC fe;
|
|
HRESULT hr;
|
|
|
|
if(NULL == m_pIDataObject)
|
|
return 0;
|
|
|
|
switch(wID)
|
|
{
|
|
case IDM_OBJECTSETDATAPUNK_TEXT:
|
|
SETDefFormatEtc(fe, CF_TEXT, TYMED_HGLOBAL);
|
|
break;
|
|
|
|
/* Other cases go here.... */
|
|
|
|
default:
|
|
MessageBox(0,
|
|
TEXT("Type is Unsupported in the Client"),
|
|
TEXT("SetData"),
|
|
MB_OK);
|
|
return(0);
|
|
}
|
|
|
|
HGLOBAL hMem=GlobalAlloc( GMEM_SHARE | GMEM_MOVEABLE, sizeof(ULONG) );
|
|
if(NULL == hMem)
|
|
{
|
|
MessageBox(0,
|
|
TEXT("GlobalAlloc Return NULL"),
|
|
TEXT("Failure"),
|
|
MB_OK);
|
|
PostQuitMessage(0);
|
|
return 0;
|
|
}
|
|
|
|
long* pl=(long*)GlobalLock(hMem); // Lock
|
|
*((long*)pl) = 0xffffffff; // Use
|
|
GlobalUnlock(hMem); // Unlock
|
|
|
|
|
|
m_stm.hGlobal=hMem;
|
|
m_stm.tymed=TYMED_HGLOBAL;
|
|
hr = GetStgMedpUnkForRelease(&m_stm.pUnkForRelease);
|
|
if(NOERROR != hr)
|
|
{
|
|
perror_OKBox(0, TEXT("Can't get pUnk For Release"), hr);
|
|
}
|
|
|
|
hr = m_pIDataObject->SetData(&fe, &m_stm, TRUE); // Pass Ownership.
|
|
// We passed ownership so SetData took the HGLOBAL from us.
|
|
if (FAILED(hr))
|
|
{
|
|
perror_OKBox(0,
|
|
TEXT("SetData Failed"),
|
|
hr);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
#define NUM_RUNS 5
|
|
|
|
|
|
void
|
|
CAppVars::m_BatchToFile()
|
|
{
|
|
dataset_t dsGetDataText;
|
|
dataset_t dsGetDataHereText;
|
|
|
|
pm_ClearDataset(&dsGetDataText);
|
|
pm_ClearDataset(&dsGetDataHereText);
|
|
|
|
int iRun;
|
|
for(iRun=0; iRun < NUM_RUNS; iRun++)
|
|
{
|
|
m_MeasureAllSizes(IDM_OBJECTGETDATA_TEXT,
|
|
NULL,
|
|
&dsGetDataText);
|
|
m_MeasureAllSizes(IDM_OBJECTGETDATAHERE_TEXT,
|
|
NULL,
|
|
&dsGetDataHereText);
|
|
}
|
|
|
|
FILE *fp;
|
|
int i;
|
|
if(NULL == (fp = fopen(FILENAME, "w")))
|
|
{
|
|
MessageBox(0, TEXT("Cannot Open Output File"),
|
|
TEXT(FILENAME),
|
|
MB_OK | MB_ICONSTOP);
|
|
return;
|
|
}
|
|
|
|
fprintf(fp, " GetData w/ HGLOBAL GetDataHere w/ HGLOBAL\n");
|
|
fprintf(fp, " Size Best Worst Average Best Worst Average\n");
|
|
for (i=0; i<NUM_POINTS; i++)
|
|
{
|
|
fprintf(fp, "%5d\t", cKSizes[i]);
|
|
#define PR_TIME(fp, v) (fprintf(fp, "%3lu.%03lu\t", (v)/1000, (v)%1000))
|
|
|
|
PR_TIME(fp, dsGetDataText.cBest[i]);
|
|
PR_TIME(fp, dsGetDataText.cWorst[i]);
|
|
PR_TIME(fp, dsGetDataText.cTotal[i]/NUM_RUNS);
|
|
PR_TIME(fp, dsGetDataHereText.cBest[i]);
|
|
PR_TIME(fp, dsGetDataHereText.cWorst[i]);
|
|
PR_TIME(fp, dsGetDataHereText.cTotal[i]/NUM_RUNS);
|
|
fprintf(fp, "\n");
|
|
}
|
|
fclose(fp);
|
|
|
|
MessageBox(0, TEXT("Output Written to file.dat!"),
|
|
TEXT("Done"), MB_OK);
|
|
}
|
|
|
|
void
|
|
CAppVars::pm_ClearDataset(dataset_t *ds)
|
|
{
|
|
int i;
|
|
for(i=0; i<NUM_POINTS; i++)
|
|
{
|
|
ds->cTotal[i] = 0;
|
|
ds->cBest[i] = 0xFFFFFFFF;
|
|
ds->cWorst[i] = 0;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
CAppVars::m_MeasureAllSizes(
|
|
WORD wID,
|
|
LPTSTR tstrTitle,
|
|
dataset_t *ds)
|
|
{
|
|
int i;
|
|
ULONG cUSecs[NUM_POINTS];
|
|
|
|
// Save some state.
|
|
ULONG iOldDataSizeIndex = m_iDataSizeIndex;
|
|
|
|
for (i=0; i<NUM_POINTS; i++)
|
|
{
|
|
m_SetData_SetSize(cKSizes[i]);
|
|
|
|
switch(wID)
|
|
{
|
|
case IDM_OBJECTGETDATA_TEXT:
|
|
case IDM_OBJECTGETDATA_BITMAP:
|
|
m_GetData(wID);
|
|
break;
|
|
|
|
case IDM_OBJECTGETDATAHERE_TEXT:
|
|
case IDM_OBJECTGETDATAHERE_BITMAP:
|
|
m_GetDataHere(wID);
|
|
break;
|
|
}
|
|
m_swTimer.m_Read(&cUSecs[i]);
|
|
cUSecs[i] /= m_cIterations;
|
|
}
|
|
|
|
// Restore save state.
|
|
m_iDataSizeIndex = iOldDataSizeIndex;
|
|
m_SetData_SetSize(m_iDataSizeIndex);
|
|
|
|
|
|
// If the caller provided memory then return the data in it.
|
|
if(NULL != ds)
|
|
{
|
|
for (i=0; i<NUM_POINTS; i++)
|
|
{
|
|
ds->cData[i] = cUSecs[i];
|
|
ds->cTotal[i] += cUSecs[i];
|
|
|
|
if(ds->cBest[i] > cUSecs[i])
|
|
ds->cBest[i] = cUSecs[i];
|
|
if( ds->cWorst[i] < cUSecs[i])
|
|
ds->cWorst[i] = cUSecs[i];
|
|
}
|
|
}
|
|
|
|
// If the caller passed a NULL Title then no message box.
|
|
if(NULL == tstrTitle)
|
|
return;
|
|
|
|
// Render Results.
|
|
LPTSTR tstr = &tcMessageBuf[0];
|
|
for (i=0; i<NUM_POINTS; i++)
|
|
{
|
|
wsprintf(tstr, TEXT("%dK: %lu.%03lu%c"),
|
|
cKSizes[i], cUSecs[i]/1000, cUSecs[i]%1000,
|
|
(i%4==3)? TEXT('\n'):TEXT('\t') );
|
|
tstr += lstrlen(tstr);
|
|
}
|
|
MessageBox(0, tcMessageBuf, tstrTitle, MB_OK);
|
|
}
|
|
|
|
|
|
|
|
void
|
|
CAppVars::m_SetMeasurement(WORD wID)
|
|
{
|
|
HMENU hMenu=GetMenu(m_hWnd);
|
|
switch (wID)
|
|
{
|
|
case IDM_MEASUREMENT_ON:
|
|
m_fDisplayTime = TRUE;
|
|
CheckMenuItem(hMenu, IDM_MEASUREMENT_ON, MF_CHECKED);
|
|
CheckMenuItem(hMenu, IDM_MEASUREMENT_OFF, MF_UNCHECKED);
|
|
break;
|
|
|
|
case IDM_MEASUREMENT_OFF:
|
|
m_fDisplayTime = FALSE;
|
|
CheckMenuItem(hMenu, IDM_MEASUREMENT_ON, MF_UNCHECKED);
|
|
CheckMenuItem(hMenu, IDM_MEASUREMENT_OFF, MF_CHECKED);
|
|
break;
|
|
|
|
|
|
case IDM_MEASUREMENT_1:
|
|
m_cIterations = 1;
|
|
goto set_menu;
|
|
case IDM_MEASUREMENT_50:
|
|
m_cIterations = 50;
|
|
goto set_menu;
|
|
case IDM_MEASUREMENT_300:
|
|
m_cIterations = 300;
|
|
goto set_menu;
|
|
set_menu:
|
|
CheckMenuItem(hMenu, IDM_MEASUREMENT_1, MF_UNCHECKED);
|
|
CheckMenuItem(hMenu, IDM_MEASUREMENT_50, MF_UNCHECKED);
|
|
CheckMenuItem(hMenu, IDM_MEASUREMENT_300, MF_UNCHECKED);
|
|
CheckMenuItem(hMenu, wID, MF_CHECKED);
|
|
break;
|
|
|
|
|
|
case IDM_MEASUREMENT_TEST:
|
|
m_swTimer.m_Start();
|
|
m_swTimer.m_Sleep(777);
|
|
m_swTimer.m_Stop();
|
|
m_DisplayTimerResults();
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
CAppVars::m_DisplayTimerResults()
|
|
{
|
|
ULONG usecs;
|
|
m_swTimer.m_Read(&usecs);
|
|
usecs /= m_cIterations;
|
|
wprintf_OKBox(0, TEXT("MilliSeconds"),
|
|
TEXT("%lu.%03lu"),
|
|
usecs/1000, usecs%1000);
|
|
}
|
|
|
|
|
|
/*
|
|
* CAppVars::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 CAppVars::Paint(void)
|
|
{
|
|
PAINTSTRUCT ps;
|
|
HDC hDC;
|
|
#ifdef NOT_SIMPLE
|
|
HDC hMemDC;
|
|
LPMETAFILEPICT pMF;
|
|
#endif /* NOT_SIMPLE */
|
|
LPTSTR psz;
|
|
RECT rc;
|
|
FORMATETC fe;
|
|
|
|
GetClientRect(m_hWnd, &rc);
|
|
|
|
hDC=BeginPaint(m_hWnd, &ps);
|
|
|
|
//May need to retrieve the data with EXE objects
|
|
#ifdef NOT_SIMPLE
|
|
if (m_fEXE)
|
|
{
|
|
if (TYMED_NULL==m_stm.tymed && 0!=m_cfFormat)
|
|
{
|
|
SETDefFormatEtc(fe, m_cfFormat, TYMED_HGLOBAL
|
|
| TYMED_MFPICT | TYMED_GDI);
|
|
|
|
if (NULL!=m_pIDataObject)
|
|
m_pIDataObject->GetData(&fe, &m_stm);
|
|
}
|
|
}
|
|
#endif /* NOT_SIMPLE */
|
|
|
|
switch (m_cfFormat)
|
|
{
|
|
case CF_TEXT:
|
|
psz=(LPTSTR)GlobalLock(m_stm.hGlobal);
|
|
|
|
if (NULL==psz)
|
|
break;
|
|
|
|
SetTextColor(hDC, GetSysColor(COLOR_WINDOWTEXT));
|
|
SetBkColor(hDC, GetSysColor(COLOR_WINDOW));
|
|
|
|
pm_DrawText(hDC, psz, &rc, DT_LEFT | DT_WORDBREAK);
|
|
|
|
GlobalUnlock(m_stm.hGlobal);
|
|
break;
|
|
|
|
#ifdef NOT_SIMPLE
|
|
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;
|
|
|
|
#else /* IS SIMPLE */
|
|
case CF_BITMAP:
|
|
case CF_METAFILEPICT:
|
|
DebugBreak();
|
|
break;
|
|
|
|
#endif /* NOT_SIMPLE */
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
EndPaint(m_hWnd, &ps);
|
|
return;
|
|
}
|
|
|
|
|
|
void
|
|
CAppVars::pm_DrawText(
|
|
HDC hDC,
|
|
LPTSTR psz,
|
|
RECT* prc,
|
|
UINT flags)
|
|
{
|
|
SetTextColor(hDC, GetSysColor(COLOR_WINDOWTEXT));
|
|
SetBkColor(hDC, GetSysColor(COLOR_WINDOW));
|
|
|
|
// If we are WIN32 and the server is 16 bits this must be ASCII.
|
|
#ifdef WIN32
|
|
if(m_f16Bit)
|
|
DrawTextA(hDC, (char*)psz, -1, prc, flags);
|
|
else
|
|
#endif
|
|
DrawText(hDC, psz, -1, prc, flags);
|
|
}
|
|
|