MAIN.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. 
** 
**  main.cpp 
** 
**  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> 
  #include <commdlg.h>   
#endif   
#include <initguid.h> 
#include "lines.h"         
 
// Globals   
CApplication FAR* g_pApplication; 
  
SCODE g_scodes[SCODE_COUNT] =                // Array of SCODEs for easy lookup 
{    LINES_E_UNEXPECTED, 
     LINES_E_OUTOFMEMORY, 
     LINES_E_INVALIDINDEX, 
     LINES_E_COLLECTIONFULL, 
     LINES_E_LINEFROMOTHERINSTANCE, 
     LINES_E_CANTADDENDPOINTS, 
     LINES_E_POINTFROMOTHERINSTANCE, 
     LINES_E_NOVISIBLEXCOORDINATE, 
     LINES_E_NOVISIBLEYCOORDINATE, 
     LINES_E_NOSTARTPOINT, 
     LINES_E_NOENDPOINT 
}; 
 
/* 
 * WinMain 
 * 
 * Purpose: 
 *  Main entry point of application.  
 * 
 */ 
int APIENTRY WinMain (HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR pCmdLine, int nCmdShow) 
{ 
    MSG msg; 
    DWORD dwRegisterCF;      
    DWORD dwRegisterActiveObject; 
 
#ifdef WIN16 
   //  It is recommended that all 16 bit OLE applications set 
   //  their message queue size to 96. This improves the capacity 
   //  and performance of OLE's LRPC mechanism. 
   int cMsg = 96;                  // Recommend msg queue size for OLE 
   while (cMsg && !SetMessageQueue(cMsg))  // take largest size we can get. 
       cMsg -= 8; 
   if (!cMsg) 
       return -1;  // ERROR: we got no message queue        
#endif 
    
    if (!hinstPrev) 
       if (!InitApplication(hinst)) // Register window class         
          return FALSE; 
     
    if (!InitInstance(hinst))   // Initialize OLE and create Lines application object 
        return (FALSE);     
     
    // Determine if /Automation was specified in command line and register class 
    // factory and active object. Show window if application was started stand alone. 
    if (!ProcessCmdLine(pCmdLine, &dwRegisterCF, &dwRegisterActiveObject, nCmdShow))    
    { 
       Uninitialize(dwRegisterCF, dwRegisterActiveObject);    
       return (FALSE);     
    } 
 
    while (GetMessage(&msg, NULL, 0, 0)) 
    { 
       TranslateMessage(&msg); 
       DispatchMessage(&msg); 
    } 
    
    Uninitialize(dwRegisterCF, dwRegisterActiveObject);    
    return (msg.wParam);  
} 
 
/* 
 * InitApplication 
 * 
 * Purpose: 
 *  Registers window class 
 * 
 * Parameters: 
 *  hinst       hInstance of application 
 * 
 * Return Value: 
 *  TRUE if initialization succeeded, FALSE otherwise. 
 */ 
BOOL InitApplication (HINSTANCE hinst) 
{ 
   WNDCLASS wc; 
    
   wc.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW; 
   wc.lpfnWndProc = MainWndProc; 
   wc.cbClsExtra = 0; 
   wc.cbWndExtra = 0; 
   wc.hInstance = hinst; 
   wc.hIcon = LoadIcon(hinst, MAKEINTRESOURCE(IDI_ICON)); 
   wc.hCursor = LoadCursor(NULL, IDC_ARROW); 
   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); 
   wc.lpszMenuName = TEXT("AppMenu"); 
   wc.lpszClassName = TEXT("MainWndClass");  
      
   return RegisterClass(&wc);         
} 
 
/* 
 * InitInstance 
 * 
 * Purpose: 
 *  Intializes OLE and creates Lines Application object 
 * 
 * Parameters: 
 *  hinst           hInstance of application 
 * 
 * Return Value: 
 *  TRUE if initialization succeeded, FALSE otherwise. 
 */ 
BOOL InitInstance (HINSTANCE hinst) 
{   
    HRESULT hr; 
     
    // Intialize OLE 
    hr = OleInitialize(NULL); 
    if (FAILED(hr)) 
       return FALSE;  
     
    // Create an instance of the Lines Application object. Object is 
    // created with refcount 0. 
    hr = CApplication::Create(hinst, &g_pApplication);        
    if (FAILED(hr)) 
        return FALSE; 
    return TRUE;           
} 
 
/* 
 * ProcessCmdLine 
 * 
 * Purpose: 
 *  Check if command line contains /Automation. If so, the class factory of the  
 *  application object is registered.  If not, the application was started stand-alone and 
 *  so the window is shown. The application object is registered using RegisterActiveObject. 
 * 
 * Parameters: 
 *  pCmdLine       Command line passed to application 
 *  pdwRegisterCF   Returns id returned after class factory registration. Can be used to  
 *                  revoke class factory registration.  
 *  pdwRegisterActiveObject   Returns id returned after active object registration. Can be used to  
 *                  revoke active object registration.    
 *  nCmdShow        Specifies how window is to be shown if application was started stand alone. 
 * 
 * Return Value: 
 *    TRUE if OLE initialization succeeded, FALSE otherwise. 
 * 
 */ 
BOOL ProcessCmdLine(LPSTR pCmdLine, LPDWORD pdwRegisterCF, LPDWORD pdwRegisterActiveObject, int nCmdShow) 
{ 
   LPCLASSFACTORY pcf = NULL; 
   HRESULT hr; 
    
   *pdwRegisterCF = 0;      
   *pdwRegisterActiveObject = 0; 
    
   // Expose class factory for application object if command line contains the  
   // Automation switch        
   if (_fstrstr(pCmdLine, "-Automation") != NULL 
       || _fstrstr(pCmdLine, "/Automation") != NULL) 
   { 
       pcf = new CApplicationCF; 
       if (!pcf) 
           goto error;   
       pcf->AddRef();            
       hr = CoRegisterClassObject(CLSID_Lines, pcf, 
                                     CLSCTX_LOCAL_SERVER, REGCLS_SINGLEUSE, 
                                     pdwRegisterCF); 
       if (hr != NOERROR) 
           goto error;            
       pcf->Release(); 
   }   
   else g_pApplication->ShowWindow(nCmdShow);    // Show window if started stand-alone        
    
   // Register Lines application object in the Running Object Table (ROT). This  
   // allows controllers to connect to a running application object instead of creating 
   // a new instance. Use weak registration so that the ROT releases it's reference when  
   // all external references are released. If strong registration is used, the ROT will not 
   // release it's reference until RevokeActiveObject is called and so will keep 
   // the object alive even after all external references have been released. 
   RegisterActiveObject(g_pApplication, CLSID_Lines, ACTIVEOBJECT_WEAK, pdwRegisterActiveObject); 
   return TRUE;            
            
error: 
    if (pcf) 
        pcf->Release(); 
    return FALSE; 
} 
 
/* 
 * Uninitialize 
 * 
 *  Purpose: 
 *   Revoke class factory and active object registration and uninitialize OLE. 
 * 
 * Parameters: 
 *  dwRegisterCF ID returned after class factory registration.  
 *  dwRegisterActiveObject ID returned after active object registration. 
 * 
 */ 
void Uninitialize(DWORD dwRegisterCF, DWORD dwRegisterActiveObject) 
{ 
    if (dwRegisterCF != 0) 
        CoRevokeClassObject(dwRegisterCF); 
    if (dwRegisterActiveObject != 0) 
        RevokeActiveObject(dwRegisterActiveObject, NULL);    
    OleUninitialize(); 
} 
 
/* 
 * MainWndProc 
 * 
 * Purpose: 
 *  Window procedure for main window 
 * 
 */ 
LRESULT CALLBACK MainWndProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) 
{      
   switch (msg) 
   {            
      case WM_PAINT:           
          g_pApplication->Draw();             
          break; 
       
      case WM_SIZE: 
          g_pApplication->OnSize(LOWORD(lParam), HIWORD(lParam));         
          break;  
           
      case WM_COMMAND: 
      {    
          switch(GET_WM_COMMAND_ID(wParam,lParam)) 
          { 
              case IDM_DRAWLINE: 
                  g_pApplication->CreateAndDrawLine(); 
                  break; 
                   
              case IDM_CLEAR: 
                  g_pApplication->ClearPane();               
                  break; 
                   
              case IDM_EXIT:  
                  PostMessage(hwnd, WM_CLOSE, 0, 0L); 
                  break;  
          }               
      } 
      break; 
           
      case WM_CLOSE:  
         // Hide the window to release the refcount added by CoLockObjectExternal 
         // (See CLines::ShowWindow) 
         g_pApplication->m_bUserClosing = TRUE; 
         g_pApplication->ShowWindow(SW_HIDE);  
         DestroyWindow(hwnd); 
         return 0L; 
       
      case WM_DESTROY:             
         PostQuitMessage(0); 
         break; 
       
      default:                          
         return DefWindowProc(hwnd, msg, wParam, lParam); 
   } 
    
   return NULL; 
}  
 
/* 
 * DrawLineDialogFunc 
 * 
 * Purpose: 
 *  Dialog function of DrawLine dialog. Prompts user and returns information to draw a line. 
 * 
 */  
BOOL CALLBACK DrawLineDialogFunc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) 
{ 
   static LPLINEINFO plineinfo;       
   BOOL b;   
   CHOOSECOLOR cc;      
   COLORREF clrCustom[16]; 
   int n; 
    
   switch (msg) 
   { 
      case WM_INITDIALOG: 
         plineinfo = (LPLINEINFO)lParam;    
         // Initialize edit controls. Units are twips. 
         SetDlgItemText(hwndDlg, IDC_STARTPOINT_X, TEXT("4500")); 
         SetDlgItemText(hwndDlg, IDC_STARTPOINT_Y, TEXT("4500")); 
         SetDlgItemText(hwndDlg, IDC_ENDPOINT_X, TEXT("8000")); 
         SetDlgItemText(hwndDlg, IDC_ENDPOINT_Y, TEXT("8000")); 
         SetDlgItemText(hwndDlg, IDC_THICKNESS, TEXT("100")); 
         plineinfo->colorref = 0;  
         return TRUE; 
 
      case WM_COMMAND: 
         switch(GET_WM_COMMAND_ID(wParam,lParam)) 
         { 
             case IDOK: 
                 plineinfo->ptStart.x = GetDlgItemInt(hwndDlg, IDC_STARTPOINT_X, &b, TRUE);  
                 plineinfo->ptStart.y = GetDlgItemInt(hwndDlg, IDC_STARTPOINT_Y, &b, TRUE); 
                 plineinfo->ptEnd.x = GetDlgItemInt(hwndDlg, IDC_ENDPOINT_X, &b, TRUE); 
                 plineinfo->ptEnd.y = GetDlgItemInt(hwndDlg, IDC_ENDPOINT_Y, &b, TRUE); 
                 plineinfo->nThickness = GetDlgItemInt(hwndDlg, IDC_THICKNESS, &b, TRUE);         
                 EndDialog(hwndDlg, IDOK); 
                 return TRUE; 
                  
             case IDCANCEL: 
                 EndDialog(hwndDlg, IDCANCEL); 
                 return TRUE; 
                  
             case IDC_CHOOSECOLOR:     
                 memset(&cc, 0, sizeof(CHOOSECOLOR));  
                 cc.lStructSize = sizeof(CHOOSECOLOR);  
                 cc.hwndOwner = hwndDlg; 
                 cc.rgbResult = RGB(0, 0, 0); 
                 cc.Flags = CC_RGBINIT;   
                 for (n=0; n<16; n++) 
                    clrCustom[n] = RGB(255, 255, 255); 
                 cc.lpCustColors = clrCustom; 
                 if (ChooseColor(&cc)) 
                     plineinfo->colorref = cc.rgbResult;                                                                
         } 
         break; 
   } 
   return FALSE; 
} 
 
/* 
 * LoadTypeInfo 
 * 
 *  Purpose: 
 *   Gets type information of an object's interface from type library. 
 * 
 * Parameters: 
 *  ppunkStdDispatch    Returns type information. 
 *  clsid               Interface id of object in type library.  
 * 
 * Return Value: 
 *  HRESULT 
 * 
 */ 
HRESULT LoadTypeInfo(ITypeInfo FAR* FAR* pptinfo, REFCLSID clsid) 
{                           
    HRESULT hr; 
    LPTYPELIB ptlib = NULL; 
    LPTYPEINFO ptinfo = NULL; 
 
    *pptinfo = NULL;      
     
    // Load Type Library.  
    hr = LoadRegTypeLib(LIBID_Lines, 1, 0, 0x09, &ptlib); 
    if (FAILED(hr))  
    {    
        // if it wasn't registered, try to load it from the path 
        // if this succeeds, it will have registered the type library for us 
        // for the next time.   
        hr = LoadTypeLib(OLESTR("lines.tlb"), &ptlib);  
        if(FAILED(hr))         
            return hr;    
    } 
     
    // Get type information for interface of the object.       
    hr = ptlib->GetTypeInfoOfGuid(clsid, &ptinfo); 
    if (FAILED(hr))   
    {  
        ptlib->Release(); 
        return hr; 
    }    
 
    ptlib->Release(); 
    *pptinfo = ptinfo; 
    return NOERROR; 
} 
 
/* 
 * RaiseException 
 * 
 * Parameters: 
 *  nID                 Error number 
 *  rguid               GUID of interface that is raising the exception. 
 * 
 * Return Value: 
 *  HRESULT correspnding to the nID error number. 
 * 
 * Purpose: 
 *  Fills the EXCEPINFO structure.  
 *  Sets ErrorInfo object for vtable-binding controllers. 
 *  For id-binding and late binding controllers DispInvoke 
 *  will return DISP_E_EXCEPTION and fill the EXCEPINFO parameter 
 *  with the error information set by SetErrorInfo. 
 * 
 */   
HRESULT RaiseException(int nID, REFGUID rguid) 
{    
    extern SCODE g_scodes[]; 
    TCHAR szError[STR_LEN];    
    ICreateErrorInfo *pcerrinfo;   
    IErrorInfo *perrinfo; 
    HRESULT hr; 
    BSTR bstrDescription = NULL; 
     
    if (LoadString(g_pApplication->m_hinst, nID, szError, sizeof(szError))) 
        bstrDescription = SysAllocString(TO_OLE_STRING(szError));     
     
    // Set ErrorInfo object so that vtable binding controller can get 
    // rich error information. If the controller is using IDispatch 
    // to access properties or methods, DispInvoke will fill the 
    // EXCEPINFO structure using the values specified in the ErrorInfo 
    // object and DispInvoke will return DISP_E_EXCEPTION. The property 
    // or method must return a failure SCODE for DispInvoke to do this. 
    hr = CreateErrorInfo(&pcerrinfo);  
    if (SUCCEEDED(hr)) 
    { 
       pcerrinfo->SetGUID(rguid); 
       pcerrinfo->SetSource(g_pApplication->m_bstrProgID); 
       if (bstrDescription) 
           pcerrinfo->SetDescription(bstrDescription);   
       hr = pcerrinfo->QueryInterface(IID_IErrorInfo, (LPVOID FAR*) &perrinfo); 
       if (SUCCEEDED(hr)) 
       { 
          SetErrorInfo(0, perrinfo); 
          perrinfo->Release(); 
       }   
       pcerrinfo->Release(); 
    }   
     
    if (bstrDescription) 
        SysFreeString(bstrDescription); 
    return g_scodes[nID-1001]; 
}  
 
 
 
/* 
 * Quick & Dirty ANSI/Unicode conversion routines. These routines use a static 
 * buffer of fixed size to hold the converted string. Consequently these 
 * routines are limited to strings of size STRCONVERT_MAXLEN. Also the same 
 * buffer is reused when the routine is called a second time. So make sure 
 * that the converted string is used before the conversion routine is called 
 * again 
 */ 
#ifdef WIN32 
 
#ifndef UNICODE 
char* ConvertToAnsi(OLECHAR FAR* szW) 
{ 
  static char achA[STRCONVERT_MAXLEN];  
   
  WideCharToMultiByte(CP_ACP, 0, szW, -1, achA, STRCONVERT_MAXLEN, NULL, NULL);   
  return achA;  
}  
 
OLECHAR* ConvertToUnicode(char FAR* szA) 
{ 
  static OLECHAR achW[STRCONVERT_MAXLEN];  
 
  MultiByteToWideChar(CP_ACP, 0, szA, -1, achW, STRCONVERT_MAXLEN);   
  return achW;  
} 
#endif 
 
#endif