APP.CPP
/************************************************************************* 
** 
**  This is a part of the Microsoft Source Code Samples. 
** 
**  Copyright 1992 - 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. 
** 
**  OLE Automation Lines Object. 
** 
**  app.cpp 
** 
**  CApplication implementation 
** 
**  Written by Microsoft Product Support Services, Windows Developer Support 
** 
*************************************************************************/ 
 
#include <windows.h> 
#include <windowsx.h> 
#ifdef WIN16    
  #include <ole2.h> 
  #include <compobj.h>     
  #include <dispatch.h>  
  #include <variant.h> 
  #include <olenls.h>   
#endif  
#include "lines.h"   
 
/* 
 * CApplication::Create 
 * 
 * Purpose: 
 *  Creates an instance of the Application automation object and initializes it. 
 * 
 * Parameters:        
 *  hinst            HINSTANCE of application. 
 *  ppApplication    Returns Application automation object. 
 * 
 * Return Value: 
 *  HRESULT 
 * 
 */ 
HRESULT 
CApplication::Create(HINSTANCE hinst, CApplication FAR* FAR* ppApplication )  
{    
    TCHAR ach[STR_LEN];    
    TCHAR achFullName[260]; 
    HRESULT hr; 
    CApplication FAR* pApplication = NULL;   
    HWND hwnd; 
      
    *ppApplication = NULL; 
     
    // Create application object. 
    pApplication = new CApplication(); 
    if (pApplication == NULL) 
    { 
        hr = E_OUTOFMEMORY;  
        goto error; 
    } 
     
    LoadString(hinst, IDS_Name, ach, sizeof(ach)); 
    hwnd = CreateWindow(TEXT("MainWndClass"), ach, 
                       WS_OVERLAPPEDWINDOW, 
                       CW_USEDEFAULT, CW_USEDEFAULT, 
                       650, 650, 
                       NULL, NULL, hinst, NULL); 
    if (!hwnd) 
    { 
       hr = E_OUTOFMEMORY;   
       goto error; 
    }   
    pApplication->m_hwnd = hwnd; 
    pApplication->m_hinst = hinst; 
     
    // Set name and fullname of application.   
    pApplication->m_bstrName = SysAllocString(TO_OLE_STRING(ach)); 
    if (NULL == pApplication->m_bstrName) 
    { 
        hr = E_OUTOFMEMORY;     
        goto error; 
    }  
    GetModuleFileName(hinst, achFullName, sizeof(achFullName)); 
    pApplication->m_bstrFullName = SysAllocString(TO_OLE_STRING(achFullName)); 
    if (NULL == pApplication->m_bstrFullName) 
    { 
        hr = E_OUTOFMEMORY;     
        goto error; 
    } 
    // ProgID  
    LoadString(hinst, IDS_ProgID, ach, sizeof(ach));   
    pApplication->m_bstrProgID = SysAllocString(TO_OLE_STRING(ach)); 
    if (NULL == pApplication->m_bstrProgID) 
    { 
        hr = E_OUTOFMEMORY;     
        goto error; 
    }       
     
    // Load type information for the application object from type library. 
    // If required, notify user on failure.  
    hr = LoadTypeInfo(&pApplication->m_ptinfo, IID_IApplication); 
    if (FAILED(hr))  
    {        
        LoadString(hinst, IDS_ErrorLoadingTypeLib, ach, sizeof(ach)); 
        MessageBox(NULL, ach, FROM_OLE_STRING(pApplication->m_bstrName), MB_OK); 
        goto error; 
    } 
         
    // Create Pane     
    hr = CPane::Create(hwnd, &pApplication->m_pPane); 
    if (FAILED(hr)) 
       goto error;     
    pApplication->m_pPane->AddRef();     
 
    *ppApplication = pApplication; 
    return NOERROR; 
     
error:                         
    if (pApplication == NULL) 
        return E_OUTOFMEMORY;  
     
    if (pApplication->m_bstrFullName) 
        SysFreeString(pApplication->m_bstrFullName);   
    if (pApplication->m_bstrName) 
        SysFreeString(pApplication->m_bstrName); 
    if (pApplication->m_bstrProgID)  
        SysFreeString(pApplication->m_bstrProgID);                        
    if (pApplication->m_ptinfo) 
        pApplication->m_ptinfo->Release();         
    if (pApplication->m_pPane) 
        pApplication->m_pPane->Release(); 
     
    // Set to NULL to prevent destructor from attempting to free again 
    pApplication->m_bstrFullName = NULL; 
    pApplication->m_bstrName = NULL; 
        pApplication->m_bstrProgID = NULL; 
    pApplication->m_ptinfo = NULL; 
    pApplication->m_pPane = NULL; 
     
    delete pApplication; 
    return hr; 
} 
 
/* 
 * CApplication::CApplication 
 * 
 * Purpose: 
 *  Constructor for CApplication object. Initializes members to NULL. 
 * 
 */ 
#pragma warning (disable : 4355) 
CApplication::CApplication() : m_SupportErrorInfo(this, IID_IApplication) 
#pragma warning (default : 4355) 
{    
    extern ULONG g_cObj; 
      
    m_hwnd = NULL; 
    m_bstrFullName = NULL; 
    m_bstrName = NULL; 
    m_bstrProgID = NULL; 
    m_ptinfo = NULL; 
    m_pPane = NULL; 
    m_cRef = 0;    
    m_bVisible = 0; 
    m_bUserClosing = FALSE; 
} 
 
/* 
 * CApplication::~CApplication 
 * 
 * Purpose: 
 *  Destructor for CApplication object. Frees Application message BSTR and default 
 *  IDispatch implementation. Closes the aplication. 
 * 
 */ 
CApplication::~CApplication() 
{  
     extern ULONG g_cObj; 
      
     if (m_bstrFullName) SysFreeString(m_bstrFullName); 
     if (m_bstrName) SysFreeString(m_bstrName); 
     if (m_bstrProgID) SysFreeString(m_bstrProgID);           
     if (m_ptinfo) m_ptinfo->Release();   
     if (m_pPane) m_pPane->Release(); 
     if (!m_bUserClosing && IsWindow(m_hwnd)) DestroyWindow(m_hwnd);      
} 
 
/* 
 * CApplication::QueryInterface, AddRef, Release 
 * 
 * Purpose: 
 *  Implements IUnknown::QueryInterface, AddRef, Release 
 * 
 */ 
STDMETHODIMP 
CApplication::QueryInterface(REFIID iid, void FAR* FAR* ppv)  
{    
    *ppv = NULL; 
     
    if (iid == IID_IUnknown || iid == IID_IDispatch || iid == IID_IApplication) 
        *ppv = this;  
    else if (iid == IID_ISupportErrorInfo) 
        *ppv = &m_SupportErrorInfo; 
    else return E_NOINTERFACE;  
 
    AddRef(); 
    return NOERROR;     
} 
 
 
STDMETHODIMP_(ULONG) 
CApplication::AddRef(void) 
{     
#ifdef _DEBUG   
    TCHAR ach[50]; 
    wsprintf(ach, TEXT("Ref = %ld, App\r\n"), m_cRef+1);  
    OutputDebugString(ach);  
#endif   
 
    return ++m_cRef; 
} 
 
STDMETHODIMP_(ULONG) 
CApplication::Release(void) 
{ 
#ifdef _DEBUG   
    TCHAR ach[50]; 
    wsprintf(ach, TEXT("Ref = %ld, App\r\n"), m_cRef-1);  
    OutputDebugString(ach);    
#endif 
     
    if(--m_cRef == 0) 
    { 
        delete this; 
        return 0; 
    } 
    return m_cRef; 
} 
 
/* 
 * CApplication::GetTypeInfoCount 
 * 
 * Purpose: 
 *  Implements IDispatch::GetTypeInfoCount. 
 * 
 */ 
STDMETHODIMP 
CApplication::GetTypeInfoCount(UINT FAR* pctinfo) 
{ 
    *pctinfo = 1; 
    return NOERROR; 
} 
 
/* 
 * CApplication::GetTypeInfo 
 * 
 * Purpose: 
 *  Implements IDispatch::GetTypeInfo.  
 * 
 */ 
STDMETHODIMP 
CApplication::GetTypeInfo( 
      UINT itinfo, 
      LCID lcid, 
      ITypeInfo FAR* FAR* pptinfo) 
{     
    *pptinfo = NULL; 
      
    if(itinfo != 0) 
        return DISP_E_BADINDEX; 
     
    m_ptinfo->AddRef();  
    *pptinfo = m_ptinfo; 
     
    return NOERROR; 
} 
 
/* 
 * CApplication::GetIDsOfNames 
 * 
 * Purpose: 
 *  Implements IDispatch::GetIDsOfNames.  The standard implementation, DispGetIDsOfNames, 
 *  is used. 
 * 
 */ 
STDMETHODIMP  
CApplication::GetIDsOfNames( 
      REFIID riid, 
      OLECHAR FAR* FAR* rgszNames, 
      UINT cNames, 
      LCID lcid, 
      DISPID FAR* rgdispid) 
{ 
    return DispGetIDsOfNames(m_ptinfo, rgszNames, cNames, rgdispid); 
} 
 
/* 
 * CApplication::Invoke 
 * 
 * Purpose: 
 *  Implements IDispatch::Invoke.  The standard implementation, DispInvoke, 
 *  is used. 
 * 
 */ 
STDMETHODIMP 
CApplication::Invoke( 
      DISPID dispidMember, 
      REFIID riid, 
      LCID lcid, 
      WORD wFlags, 
      DISPPARAMS FAR* pdispparams, 
      VARIANT FAR* pvarResult, 
      EXCEPINFO FAR* pexcepinfo, 
      UINT FAR* puArgErr) 
{   
   return DispInvoke( 
        this, m_ptinfo, 
        dispidMember, wFlags, pdispparams, 
        pvarResult, pexcepinfo, puArgErr);    
} 
 
/* 
 * CApplication::get_Application, get_FullName, get_Name, get_Parent, get_Visible, put_Visible, Quit  
 * 
 * Purpose: 
 *  Implements the standard Application, FullName, Name, Parent & Visible properties 
 *  and the Quit method.  
 * 
 */ 
STDMETHODIMP 
CApplication::get_Application(IApplication FAR* FAR* ppApplication) 
{ 
    HRESULT hr; 
     
    hr = QueryInterface(IID_IDispatch, (void FAR* FAR*)ppApplication);  
    if (FAILED(hr)) 
        return RaiseException(IDS_Unexpected, IID_IApplication);  
    return NOERROR; 
} 
 
STDMETHODIMP 
CApplication::get_FullName(BSTR FAR* pbstr) 
{ 
    *pbstr = SysAllocString(m_bstrFullName);    
    return NOERROR; 
} 
 
STDMETHODIMP 
CApplication::get_Name(BSTR FAR* pbstr) 
{ 
    *pbstr = SysAllocString(m_bstrName);  
    return NOERROR;  
} 
 
STDMETHODIMP 
CApplication::get_Parent(IApplication FAR* FAR* ppApplication)       
{ 
    HRESULT hr; 
     
    hr = QueryInterface(IID_IDispatch, (void FAR* FAR*)ppApplication);  
    if (FAILED(hr)) 
        return RaiseException(IDS_Unexpected, IID_IApplication);  
    return NOERROR; 
} 
 
STDMETHODIMP 
CApplication::put_Visible(VARIANT_BOOL bVisible) 
{ 
    ShowWindow(bVisible ? SW_SHOW : SW_HIDE);   
    return NOERROR; 
}  
 
STDMETHODIMP 
CApplication::get_Visible(VARIANT_BOOL FAR* pbVisible) 
{ 
    *pbVisible = m_bVisible;      
    return NOERROR; 
} 
 
STDMETHODIMP 
CApplication::Quit() 
{ 
    // CoDisconnectObject has no effect for an inproc server.  So the controller 
    // will GP fault if it attempts to access the object (including calling IUnknown::Release()) 
    // after Quit has been called. For a local server, CoDisconnectObject will disconnect 
    // the object from external connections. So the controller will get an RPC error if 
    // it access the object after calling Quit. The controller will not GP fault in this case.  
    CoDisconnectObject((LPUNKNOWN)this, 0);   
    PostMessage(m_hwnd, WM_CLOSE, 0, 0L); 
    return NOERROR;                                      
}  
 
/* 
 * CApplication::get_Pane 
 * 
 * Purpose: 
 *  Returns pane object. 
 * 
 */       
STDMETHODIMP 
CApplication::get_Pane(IPane FAR* FAR* ppPane) 
{ 
    HRESULT hr; 
     
    hr = m_pPane->QueryInterface(IID_IDispatch, (void FAR* FAR*)ppPane);  
    if (FAILED(hr)) 
        return RaiseException(IDS_Unexpected, IID_IApplication);  
    return NOERROR; 
} 
 
/* 
 * CApplication::CreateLine 
 * 
 * Purpose: 
 *  Returns a newly created line object with no start or end point. 
 * 
 */   
STDMETHODIMP 
CApplication::CreateLine(ILine FAR* FAR* ppLine) 
{ 
    CLine FAR* pline = NULL; 
    HRESULT hr; 
     
    // Create new item and QI for IDispatch    
    hr = CLine::Create(&pline); 
    if (FAILED(hr))  
        {hr = RaiseException(IDS_OutOfMemory, IID_IApplication); goto error;}  
               
    hr = pline->QueryInterface(IID_IDispatch, (void FAR* FAR*)ppLine);  
    if (FAILED(hr))  
        {hr = RaiseException(IDS_Unexpected, IID_IApplication); goto error;} 
    return NOERROR; 
     
error:  
    if (pline) 
        delete pline;        
    return hr;      
} 
 
/* 
 * CApplication::CreateLine 
 * 
 * Purpose: 
 *  Returns a newly created point object intialized to (0,0). 
 * 
 */  
STDMETHODIMP 
CApplication::CreatePoint(IPoint FAR* FAR* ppPoint) 
{ 
    CPoint FAR* ppoint = NULL; 
    HRESULT hr; 
     
    // Create new item and QI for IDispatch    
    hr = CPoint::Create(&ppoint); 
    if (FAILED(hr)) 
        {hr = RaiseException(IDS_OutOfMemory, IID_IApplication); goto error;}           
         
    hr = ppoint->QueryInterface(IID_IDispatch, (void FAR* FAR*)ppPoint);  
    if (FAILED(hr)) 
        {hr = RaiseException(IDS_Unexpected, IID_IApplication); goto error;} 
    return NOERROR; 
     
error:  
    if (ppoint) 
        delete ppoint;         
    return hr;      
}    
 
/*  
 * 
 * The following methods are not exposed through Automation 
 * 
 */ 
 
STDMETHODIMP_(void) 
CApplication::Draw() 
{ 
   m_pPane->Draw(); 
} 
 
/* 
 * CApplication::OnSize 
 * 
 * Purpose: 
 *  Called when application window receives WM_SIZE. 
 * 
 */  
STDMETHODIMP_(void) 
CApplication::OnSize(unsigned int nWidth, unsigned int nHeight)  
{ 
   m_pPane->OnSize(nWidth, nHeight); 
}  
 
STDMETHODIMP_(void) 
CApplication::ShowWindow(int nCmdShow) 
{    
    // Return if curently hidden and asked to hide or currently visible 
    // and asked to show. 
    if ((!m_bVisible && nCmdShow == SW_HIDE) || (m_bVisible && nCmdShow != SW_HIDE)) 
        return;  
     
    m_bVisible = (nCmdShow == SW_HIDE) ? FALSE : TRUE; 
     
    // The Automation object shutdown behavior is as follows: 
    // 1. If the object application is visible, it shuts down only in response to an 
    // explicit user command (File/Exit) or it's programmatic equivalent (for example 
    // the Quit method of the Application object). 
    // 2. If the object application is not visible, it goes away when it's last 
    // object is released.    
    //    
    // CoLockObjectExternal can be used to increment the ref count of the application object 
    // when it is visible. This will implement shutdown behavior 1. When the application 
    // goes invisible, CoLockObjectExternal is used to decrement the ref count. This will 
    // implement shutdown behavior 2. 
     
    if (m_bVisible) 
        CoLockObjectExternal(this, TRUE /*fLock*/, TRUE/*ignored when fLock==TRUE*/); 
    else CoLockObjectExternal(this, FALSE/*fLock*/, TRUE/*fLastLockReleases*/);   
    ::ShowWindow (m_hwnd, nCmdShow); 
} 
 
STDMETHODIMP_(void) 
CApplication::CreateAndDrawLine() 
{    
    LINEINFO lineinfo; 
    CLine FAR* pLine = NULL; 
    CPoint FAR* pPointStart = NULL; 
    CPoint FAR* pPointEnd = NULL;  
    ILines FAR* pLines = NULL;  
    int nRet;      
    HRESULT hr; 
                   
    nRet = DialogBoxParam(m_hinst, MAKEINTRESOURCE(IDD_DRAWLINE), m_hwnd,  
                      (DLGPROC)DrawLineDialogFunc, (LPARAM)(LPLINEINFO)&lineinfo);     
    if (nRet != IDOK)  
        return; 
    hr = CLine::Create(&pLine); 
    if (FAILED(hr))   
        goto error; 
    hr = CPoint::Create(&pPointStart); 
    if (FAILED(hr))  
        goto error;  
    hr = CPoint::Create(&pPointEnd); 
    if (FAILED(hr)) 
        goto error; 
 
    pPointStart->put_x(lineinfo.ptStart.x); 
    pPointStart->put_y(lineinfo.ptStart.y);  
    pPointEnd->put_x(lineinfo.ptEnd.x); 
    pPointEnd->put_y(lineinfo.ptEnd.y);    
 
    pLine->putref_StartPoint(pPointStart);   
    pLine->putref_EndPoint(pPointEnd);   
    pLine->put_Thickness(lineinfo.nThickness); 
    pLine->put_Color(lineinfo.colorref); 
     
    hr = m_pPane->get_Lines(&pLines);  
    if (FAILED(hr))  
        goto error;  
    hr = pLines->Add(pLine);  
    if (FAILED(hr))  
        goto error;   
    pLines->Release(); 
    return; 
 
error: 
    if (pLine) delete pLine; 
    if (pPointStart) delete pPointStart; 
    if (pPointEnd) delete pPointEnd;  
    if (pLines) pLines->Release();    
    MessageBox(m_hwnd, TEXT("Cannot create Line"), FROM_OLE_STRING(m_bstrName), MB_OK); 
} 
 
STDMETHODIMP_(void) 
CApplication::ClearPane()   
{ 
    m_pPane->Clear(); 
}