690 lines
13 KiB
C++
690 lines
13 KiB
C++
/***
|
|
*dspcalc2.cpp
|
|
*
|
|
* Copyright (C) 1993-1994, Microsoft Corporation. All Rights Reserved.
|
|
* Information Contained Herein Is Proprietary and Confidential.
|
|
*
|
|
*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 ResultFromScode(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::Clear()
|
|
{
|
|
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 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 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_LOPND){
|
|
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){
|
|
|
|
Clear();
|
|
|
|
} 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 ResultFromScode(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 ResultFromScode(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 = ResultFromScode(E_OUTOFMEMORY);
|
|
goto LError0;
|
|
}
|
|
|
|
if((pcf = CCalcCF::Create()) == NULL)
|
|
goto LError1;
|
|
|
|
hresult = CoRegisterClassObject(
|
|
CLSID_CCalc2,
|
|
pcf,
|
|
CLSCTX_LOCAL_SERVER,
|
|
REGCLS_MULTIPLEUSE,
|
|
&g_dwCCalcCF);
|
|
if(hresult != NOERROR)
|
|
goto LError2;
|
|
|
|
hresult = RegisterActiveObject(
|
|
g_pcalc, CLSID_CCalc2, NULL, &g_dwRegisterCCalc);
|
|
if(hresult != NOERROR)
|
|
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;
|
|
}
|