/***
*dspcalc2.cpp
*
* This is a part of the Microsoft Source Code Samples.
*
* Copyright 1993 - 1998 Microsoft Corporation. All rights reserved.
*
* This source code is only intended as a supplement to Microsoft Development
* Tools and/or WinHelp documentation. See these sources for detailed
* information regarding the Microsoft samples programs.
*
*Purpose:
* This module implements the basic user interface and arithmetic
* functionality of the IDispatch calculator.
*
* The implementation of IDispatch is via aggregation with an
* instance of the "standard" IDispatch implementation, which is
* initialized with a TypeInfo loaded from the TypeLib that was
* constructed from the ODL description of the calculator.
*
*Implementation Notes:
*
*****************************************************************************/
#include "dspcalc2.h"
CCalc FAR* g_pcalc = NULL;
unsigned long g_dwCCalcCF = 0;
unsigned long g_dwRegisterCCalc = 0;
#ifdef _MAC
extern Boolean g_fQuit;
#endif //_MAC
/***
*CCalc *CCalc::Create(void)
*Purpose:
* Create an instance of the IDispatch calculator, load the
* TypeInfo that describes the exposed functionality and
* aggregate with an instance of CStdDispatch that has been
* initialized with this TypeInfo.
*
*Entry:
* None
*
*Exit:
* return value = CCalc*, NULL if the creation failed.
*
***********************************************************************/
CCalc FAR*
CCalc::Create()
{
HRESULT hresult;
CCalc FAR* pcalc;
ITypeLib FAR* ptlib;
ITypeInfo FAR* ptinfo;
IUnknown FAR* punkStdDisp;
ptlib = NULL;
ptinfo = NULL;
if((pcalc = new FAR CCalc()) == NULL)
return NULL;
pcalc->AddRef();
// first try to load the type library from the information in the registry
if((hresult = LoadRegTypeLib(LIBID_DspCalc2, 1, 0, 0x0409, &ptlib)) != NOERROR){
#define TLB_NAME OLESTR("dspcalc2.tlb")
// if it wasn't registered, try to load it from the path/current directory
// if this succeeds, it will have registered the type library for us
// for the next time.
if((hresult = LoadTypeLib(TLB_NAME, &ptlib)) != NOERROR){
#ifndef _MAC
MessageBox(NULL, TSTR("error loading TypeLib"),
TSTR("dspcalc2"), MB_OK);
#endif
goto LError0;
}
}
if((hresult = ptlib->GetTypeInfoOfGuid(IID_ICalculator, &ptinfo)) != NOERROR){
#ifndef _MAC
MessageBox(NULL, TSTR("error accessing TypeInfo"),
TSTR("dspcalc2"), MB_OK);
#endif
goto LError0;
}
// Create and aggregate with an instance of the default
// implementation of IDispatch that is initialized with our
// TypeInfo.
//
hresult = CreateStdDispatch(
pcalc, // controlling unknown
&(pcalc->m_arith), // vtable* to dispatch on
ptinfo,
&punkStdDisp);
if(hresult != NOERROR)
goto LError0;
pcalc->m_punkStdDisp = punkStdDisp;
ptinfo->Release();
ptlib->Release();
return pcalc;
LError0:;
pcalc->Release();
if(ptinfo != NULL)
ptinfo->Release();
if(ptlib != NULL)
ptlib->Release();
return NULL;
}
//---------------------------------------------------------------------
// IUnknown methods
//---------------------------------------------------------------------
STDMETHODIMP
CCalc::QueryInterface(REFIID riid, void FAR* FAR* ppv)
{
if(IsEqualIID(riid, IID_IUnknown)){
*ppv = this;
}else
if(IsEqualIID(riid, IID_IDispatch) ||
IsEqualIID(riid, IID_DCalculator)){
return m_punkStdDisp->QueryInterface(IID_IDispatch, ppv);
}else
if(IsEqualIID(riid, IID_ICalculator)){
*ppv = &m_arith;
}else {
*ppv = NULL;
return E_NOINTERFACE;
}
AddRef();
return NOERROR;
}
STDMETHODIMP_(ULONG)
CCalc::AddRef()
{
return ++m_refs;
}
STDMETHODIMP_(ULONG)
CCalc::Release()
{
if(--m_refs == 0){
if(m_punkStdDisp != NULL)
m_punkStdDisp->Release();
#ifndef _MAC
PostQuitMessage(0);
#endif
delete this;
return 0;
}
return m_refs;
}
STDMETHODIMP
CArith::QueryInterface(REFIID riid, void FAR* FAR* ppv)
{
return m_pcalc->QueryInterface(riid, ppv);
}
STDMETHODIMP_(ULONG)
CArith::AddRef()
{
return m_pcalc->AddRef();
}
STDMETHODIMP_(ULONG)
CArith::Release()
{
return m_pcalc->Release();
}
//---------------------------------------------------------------------
// Arithmetic features
//---------------------------------------------------------------------
STDMETHODIMP_(void)
CArith::DCClear()
{
m_opnd = 0;
m_accum = 0;
m_op = OP_NONE;
m_state = STATE_LOPND;
}
STDMETHODIMP_(void)
CArith::put_Accum(long l)
{
m_accum = l;
}
STDMETHODIMP_(long)
CArith::get_Accum()
{
return m_accum;
}
STDMETHODIMP_(void)
CArith::put_Opnd(long l)
{
m_opnd = l;
}
STDMETHODIMP_(long)
CArith::get_Opnd()
{
return m_opnd;
}
STDMETHODIMP_(void)
CArith::put_Op(OPERATORS op)
{
m_op = op;
}
STDMETHODIMP_(OPERATORS)
CArith::get_Op()
{
return m_op;
}
STDMETHODIMP_(VARIANT_BOOL)
CArith::Eval()
{
if(m_op == OP_NONE)
return FALSE;
switch(m_op){
case OP_PLUS:
m_accum += m_opnd;
break;
case OP_MINUS:
m_accum -= m_opnd;
break;
case OP_MULT:
m_accum *= m_opnd;
break;
case OP_DIV:
m_accum = (m_opnd == 0) ? 0 : (m_accum / m_opnd);
break;
default:
// ASSERT(UNREACHED);
return FALSE;
}
m_state = STATE_EVAL;
return VARIANT_TRUE;
}
//---------------------------------------------------------------------
// User Interface features
//---------------------------------------------------------------------
/***
*void CArith::Display()
*Purpose:
* Display the contents of the register currently being edited.
*
*Entry:
* None
*
*Exit:
* None
*
***********************************************************************/
STDMETHODIMP_(void)
CArith::Display()
{
VARIANT var;
VariantInit(&var);
V_VT(&var) = VT_I4;
V_I4(&var) = (m_state == STATE_ROPND) ? m_opnd : m_accum;
VariantChangeType(&var, &var, 0, VT_BSTR);
#ifdef _MAC
{
Rect rcItem;
Handle hItem;
char str[255];
short sItemKind;
strcpy(str, V_BSTR(&var));
GetDItem(m_pcalc->m_pdlg, IDC_DISPLAY, &sItemKind, &hItem, &rcItem);
SetIText(hItem, c2pstr(str));
}
#else
SetDlgItemText(m_pcalc->m_hwnd, IDC_DISPLAY, STRING(V_BSTR(&var)));
#endif
VariantClear(&var);
}
STDMETHODIMP_(VARIANT_BOOL)
CArith::Button(SAFEARRAY FAR * psa)
{
int i, button;
static struct {
OLECHAR ch;
int idc;
} NEAR rgIdcOfCh[] = {
{ OLESTR('+'), IDC_PLUS }
, { OLESTR('-'), IDC_MINUS }
, { OLESTR('*'), IDC_MULT }
, { OLESTR('/'), IDC_DIV }
, { OLESTR('C'), IDC_CLEAR }
, { OLESTR('c'), IDC_CLEAR }
, { OLESTR('='), IDC_EQUALS }
, { OLESTR('0'), IDC_ZERO }
, { OLESTR('1'), IDC_ONE }
, { OLESTR('2'), IDC_TWO }
, { OLESTR('3'), IDC_THREE }
, { OLESTR('4'), IDC_FOUR }
, { OLESTR('5'), IDC_FIVE }
, { OLESTR('6'), IDC_SIX }
, { OLESTR('7'), IDC_SEVEN }
, { OLESTR('8'), IDC_EIGHT }
, { OLESTR('9'), IDC_NINE }
, { (OLECHAR)-1 , -1 }
};
LONG saIndex, saUbound;
VARIANT varButton;
// Since this is a vararg function, we should be given a 1-dimensional
// array with 0 for the lower bound. The array could be uninitialized
// if 0 args were passed to us -- this call will give an error in this case.
if (SafeArrayGetUBound(psa, 1, &saUbound) != NOERROR)
return FALSE; // most likely 0 args were passed to us
for (saIndex = 0; saIndex <= saUbound; saIndex++) {
// get next parameter
if (SafeArrayGetElement(psa, &saIndex, &varButton) != NOERROR)
return FALSE;
// convert it to a string in-place
if (VariantChangeType(&varButton, &varButton, 0, VT_BSTR) != NOERROR)
goto Error;
// if the string is more that 1 character long, then we know its wrong.
if(SysStringLen(varButton.bstrVal) > 1)
goto Error;
// translate button string into control ID
for(i = 0;; ++i){
if(rgIdcOfCh[i].ch == -1)
goto Error;
if(rgIdcOfCh[i].ch == varButton.bstrVal[0]){
button = rgIdcOfCh[i].idc;
break;
}
}
VariantClear(&varButton); // done with the parameter
if (!ButtonPush(button))
return FALSE;
} // for
return VARIANT_TRUE; // success
Error:
VariantClear(&varButton);
return FALSE; // failure
}
// the following method is internal, and not exposed for programmability
BOOL
CArith::ButtonPush(int button)
{
if(button >= IDC_ZERO && button <= IDC_NINE){
long lVal = button - IDC_ZERO;
switch(m_state){
case STATE_EVAL:
m_accum = lVal;
m_state = STATE_LOPND;
break;
case STATE_OP:
m_opnd = lVal;
m_state = STATE_ROPND;
break;
case STATE_LOPND:
m_accum = (m_accum * 10) + lVal;
break;
case STATE_ROPND:
m_opnd = (m_opnd * 10) + lVal;
break;
}
}else if(button >= IDC_PLUS && button <= IDC_DIV){
if(m_state == STATE_ROPND)
Eval();
m_opnd = m_accum;
m_state = STATE_OP;
m_op = (OPERATORS)(button - IDC_PLUS + OP_PLUS);
}else if(button == IDC_EQUALS){
if(m_state > STATE_LOPND)
Eval();
}else if (button == IDC_CLEAR){
DCClear();
} else {
return 0; // unknown button
}
// Flash the button
#ifdef _MAC
{
Rect rcItem;
long lDummy;
Handle hItem;
short sItemKind;
GetDItem(m_pcalc->m_pdlg, button, &sItemKind, &hItem, &rcItem);
HiliteControl((ControlHandle)hItem, 1);
Delay(6, &lDummy);
HiliteControl((ControlHandle)hItem, 0);
}
#else
SendMessage(m_pcalc->m_hwnd, BM_SETSTATE, 1, 0L);
SendMessage(m_pcalc->m_hwnd, BM_SETSTATE, 0, 0L);
#endif
// Update the calculator display
Display();
return TRUE;
}
/***
*void CArith::Quit()
*Purpose:
*
*Entry:
* None
*
*Exit:
* None
*
***********************************************************************/
STDMETHODIMP_(void)
CArith::Quit()
{
#ifndef _MAC
PostQuitMessage(0);
#else
g_fQuit = TRUE;
#endif
}
//---------------------------------------------------------------------
// The CCalc Class Factory
//---------------------------------------------------------------------
IClassFactory FAR*
CCalcCF::Create()
{
return new FAR CCalcCF();
}
STDMETHODIMP
CCalcCF::QueryInterface(REFIID riid, void FAR* FAR* ppv)
{
if(IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IClassFactory)){
AddRef();
*ppv = this;
return NOERROR;
}
*ppv = NULL;
return E_NOINTERFACE;
}
STDMETHODIMP_(ULONG)
CCalcCF::AddRef()
{
return ++m_refs;
}
STDMETHODIMP_(ULONG)
CCalcCF::Release()
{
if(--m_refs == 0){
delete this;
return 0;
}
return m_refs;
}
STDMETHODIMP
CCalcCF::CreateInstance(
IUnknown FAR* punkOuter,
REFIID riid,
void FAR* FAR* ppv)
{
extern CCalc FAR* g_pcalc;
UNUSED(punkOuter);
return g_pcalc->QueryInterface(riid, ppv);
}
STDMETHODIMP
#ifdef _MAC
CCalcCF::LockServer(unsigned long fLock)
#else
CCalcCF::LockServer(BOOL fLock)
#endif
{
UNUSED(fLock);
return NOERROR;
}
#ifdef _MAC
struct regentry{
char *szKey;
char *szValue;
} g_rgregentry[] = {
{ "CLSID\\{00020469-0000-0000-C000-000000000046}",
"OLE Automation DspCalc2 1.0 Application" }
, { "CLSID\\{00020469-0000-0000-C000-000000000046}\\LocalServer",
"DCL2" }
, { "CLSID\\{00020469-0000-0000-C000-000000000046}\\ProgID",
"Dspcalc2.Application" }
, { "CLSID\\{00020469-0000-0000-C000-000000000046}\\InprocHandler",
"OLE2:Def$DefFSet" }
, { "DCL2", "{00020469-0000-0000-C000-000000000046}" }
, { "Dspcalc2.Application\\CLSID",
"{00020469-0000-0000-C000-000000000046}" }
};
HRESULT
EnsureRegistration()
{
HKEY hkey;
if(RegOpenKey(HKEY_CLASSES_ROOT, "DCL2", &hkey) == NOERROR){
RegCloseKey(hkey);
return NOERROR;
}
for(int i = 0; i < DIM(g_rgregentry); ++i){
if(RegSetValue(HKEY_CLASSES_ROOT, g_rgregentry[i].szKey, REG_SZ, g_rgregentry[i].szValue, 0) != ERROR_SUCCESS)
return E_FAIL;
}
return NOERROR;
}
#endif //_MAC
/***
*HRESULT InitOle(void)
*Purpose:
* Initialize Ole, and register our class factories.
*
*Entry:
* None
*
*Exit:
* None
*
***********************************************************************/
HRESULT
InitOle()
{
HRESULT hresult;
IClassFactory FAR* pcf;
if((hresult = OleInitialize(NULL)) != NOERROR)
goto LError0;
#ifdef _MAC
if((hresult = EnsureRegistration()) != NOERROR)
goto LError0;
#endif
// create the single global instance of CCalc
if((g_pcalc = CCalc::Create()) == NULL){
hresult = E_OUTOFMEMORY;
goto LError0;
}
if((pcf = CCalcCF::Create()) == NULL)
goto LError1;
hresult = CoRegisterClassObject(
CLSID_CCalc2,
pcf,
CLSCTX_LOCAL_SERVER,
REGCLS_MULTIPLEUSE,
&g_dwCCalcCF);
if(FAILED(hresult))
goto LError2;
hresult = RegisterActiveObject(
g_pcalc, CLSID_CCalc2, NULL, &g_dwRegisterCCalc);
if(FAILED(hresult))
goto LError2;
pcf->Release();
return NOERROR;
LError2:;
pcf->Release();
LError1:;
UninitOle();
LError0:;
return hresult;
}
HRESULT
UninitOle()
{
if(g_dwRegisterCCalc != 0)
RevokeActiveObject(g_dwRegisterCCalc, NULL);
if(g_dwCCalcCF != 0)
CoRevokeClassObject(g_dwCCalcCF);
// cause the remaining typeinfo to be released
if(g_pcalc != NULL)
g_pcalc->Release();
OleUninitialize();
return NOERROR;
}