/*************************************************************************
**
**  This is a part of the Microsoft Source Code Samples.
**
**  Copyright (C) 1992-1996 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 = ResultFromScode(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 = ResultFromScode(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 = ResultFromScode(E_OUTOFMEMORY);    
        goto error;
    } 
    GetModuleFileName(hinst, achFullName, sizeof(achFullName));
    pApplication->m_bstrFullName = SysAllocString(TO_OLE_STRING(achFullName));
    if (NULL == pApplication->m_bstrFullName)
    {
        hr = ResultFromScode(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 = ResultFromScode(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 ResultFromScode(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 ResultFromScode(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 ResultFromScode(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();
}  

