/*
*********************************************************************
*
*	The Essence of OLE
*
*	Source code sample
*
*	Copyright (c) 1996, Rolling Thunder Computing, Ipswich MA
*
*	e-mail: essence@rollthunder.com
*	CompuServe: 73540,3430
*	Web: http://www.rollthunder.com
*
*	File Name: HELLODRV.CPP
*
*	Purpose: Proxy object for OLE Automation controller. Provides simple
*		type-safe entry functions for the controller's API programmer,
*		and does all the serious negotiation with the IDispatch interface
*		provided by the automation server.
*
*********************************************************************
*/

#include <windows.h>
#include <wchar.h>

#include "hellodrv.h"
#include "resource.h"

extern HWND hMainDlg ;
extern HINSTANCE hInstance ;
BOOL CALLBACK ErrorDlgProc (HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) ;



CHelloDriver::CHelloDriver( )
{
	return ;
}

/*
Destructor.  If we have a connection to the server's object, release it.
*/

CHelloDriver::~CHelloDriver( )
{   
	try
	{
		if (m_pDisp)
		{
			m_pDisp->Release( );
		}
	}
	catch (...)
	{ } 

	return ;
}   


/*
Creation method.  Call CoCreateInstance to tell OLE to look in
registry to find specified server, wake it up, make it create an
object and give us back a pointer to the object's IDispatch interface.
*/


BOOL CHelloDriver::Create(LPSTR lpStr)
{
	HRESULT hr ;  


/*
Open registry key and read CLSID associated with human-readable name of
automation server. Convert ANSI string to wide char.  Convert wide string 
to class ID.
*/


	CLSID clsid ;
	CLSIDFromProgID (L"EssenceOfCOM.HelloAutomation", &clsid) ;
	
/*
Try to create an instance of the Hello object and get a pointer to its
IDispatch interface. Store pointer to object in member variable. If 
unsuccessful, signal with message box and abort.
*/

	hr = CoCreateInstance (
		clsid, 					// class ID of object
		NULL,                   // controlling IUnkown
		CLSCTX_SERVER,			// context
		IID_IDispatch,			// interface wanted
		(LPVOID *) &m_pDisp) ;	// output variable

	
	if (hr != NOERROR)
	{
		MessageBox (NULL, TEXT("Create Instance Not Successful"), TEXT("HelloDrv"), MB_OK) ;
		return FALSE ;
	}
	
/*
Get the ID numbers associated with the names of the methods and properties
in the IDispatch interface; store in member variables.  The L preceding the 
text strings in quotes means that the strings are always wide characters. If 
either ID cannot be found, signal the error and abort. 
*/
	 
	WCHAR *Name = L"SayHello" ;
	
	hr = m_pDisp->GetIDsOfNames (
		IID_NULL,					// reserved
		&Name,						// Array of names to get IDs for
		1,        					// # of names in the array
		LOCALE_SYSTEM_DEFAULT,		// System locale
		&m_dSayHelloId) ;				// Array of IDs to fill on output
			
	if (hr)
	{
		MessageBox (NULL, TEXT("Get ID 0 Unsuccessful"), TEXT(""), MB_OK) ;  
		return FALSE ;
	} 
	                
	
	Name = L"HelloMessage" ;
	         
	hr = m_pDisp->GetIDsOfNames (
		IID_NULL,					// reserved
		&Name,						// Array of names to get IDs for
		1,        					// # of names in the array
		LOCALE_SYSTEM_DEFAULT,		// System locale
		&m_dHelloPropID) ;				// Array of IDs to fill on output
			
	if (hr)
	{
		MessageBox (NULL, TEXT("Get ID 1 Unsuccessful"), TEXT(""), MB_OK) ;
		return FALSE ;
	} 

/*
Object successfully created and IDs successfully gotten.
*/
	return TRUE ;
}


/*
SetString( ) method.  Use the IDispatch interface to set the
"HelloMessage" property exposed by the server.
*/

HRESULT CHelloDriver::SetString(LPOLESTR cp)
{         
	HRESULT hr ;
	DISPPARAMS dp ;  
	VARIANTARG vString ;
 	EXCEPINFO ei ;
	UINT uiErr ; 
	DISPID dPutID ;   
	
/*
Create the BSTR that holds the actual data.
*/
	BSTR bstr = SysAllocString (cp) ;
  
/*
Initialize the variant that will hold the BSTR.  Set the variant's
type flag to indicate the fact that this variant holds a BSTR. Place the
BSTR into the variant structure.
*/
	VariantInit (&vString) ; 
	vString.vt = VT_BSTR ;
	vString.bstrVal = bstr ;
    
/*
Set the DISPPARAMS structure that holds the variant. This gets a little
tricky in the property set case. See the text for a complete explanation.
*/

   	dp.rgvarg = &vString ;
	dp.cArgs = 1 ;

	dPutID = DISPID_PROPERTYPUT ;	
	dp.rgdispidNamedArgs = &dPutID ;
	dp.cNamedArgs = 1 ;
	                 

/*
Call IDispatch::Invoke to set the property's value.
*/

    hr = m_pDisp->Invoke (
		m_dHelloPropID,
		IID_NULL,
		LOCALE_SYSTEM_DEFAULT,
		DISPATCH_PROPERTYPUT,
		&dp,
		NULL,
		&ei,
		&uiErr ) ;  	

/*
If function was successful, recipient should have freed string; otherwise we have to.
*/
	if (!SUCCEEDED(hr))
	{
		SysFreeString (bstr) ;
	}	
	
	return hr ;
	
}


/*
BadSetString( ) method.  Use the IDispatch interface to set the
"HelloMessage" property exposed by the server, but intentionally make
a fatal mistake so as to demonstrate and test runtime error checking.
*/

HRESULT CHelloDriver::BadSetString(void)
{         
	HRESULT hr ;
	DISPPARAMS dp ;  
	VARIANTARG vString[5] ;
 	EXCEPINFO ei ;
	UINT uiErr ; 
	DISPID dPutID ;   
	
/*
Deliberately mess up by packaging parameter as an IDispatch instead of as a string
*/
	VariantInit (&vString[0]) ; 
	vString[0].vt = VT_DISPATCH ;
	vString[0].lVal = NULL ;
    
   	dp.rgvarg = vString ;
	dp.cArgs = 1 ;

	dPutID = DISPID_PROPERTYPUT ;	
	dp.rgdispidNamedArgs = &dPutID ;
	dp.cNamedArgs = 1 ;
	                 

    hr = m_pDisp->Invoke (
		m_dHelloPropID,
		IID_NULL,
		LOCALE_SYSTEM_DEFAULT,
		DISPATCH_PROPERTYPUT,
		&dp,
		NULL,
		&ei,
		&uiErr ) ;  	


/*
The call should not succeed, so this message box should never appear.
*/

	if (SUCCEEDED(hr))
	{
		MessageBox (hMainDlg, TEXT("BadSetString( ) succeeded, and it should not have"),
			TEXT("Error"), MB_ICONSTOP) ;
	}

/*
We messed up deliberately, so we should get here.  Look in the EXCEPINFO
structure for extended error information, and take action based on it.
*/

	else
	{
/*
Server may not provide an EXCEPINFO structure.  MFC apps generally don't for 
a parameter mismatch.  Signal this case to the user.
*/
		if (hr != DISP_E_EXCEPTION)
		{
			MessageBox (hMainDlg, TEXT("Unsuccessful, and server doesn't provide EXCEPINFO."), TEXT("Error"), MB_ICONSTOP) ;
		}
/*
Server provided and EXCEPINFO structure.  Pop up a dialog box to show the
server's explanation string to the user.
*/
		else
		{
			WCHAR wout [256] ;
			swprintf (wout, L"The application %s reported the following error:%c%c%s",
				ei.bstrSource, 0x0d, 0x0a, ei.bstrDescription) ;

			char out [256] ;
 			WideCharToMultiByte (CP_ACP, 0, wout,  wcslen(wout)+1, out, sizeof(out), NULL, NULL) ;

			int ret = DialogBoxParam (hInstance, MAKEINTRESOURCE(IDD_DIALOG2), 
				hMainDlg, (DLGPROC) ErrorDlgProc, (long) out) ;


/*
If user clicke "Help" button in dialog box, launch WinHelp with the file
and context specified by the server in the EXCEPINFO structure.
*/
			WideCharToMultiByte (CP_ACP, 0, ei.bstrHelpFile,  wcslen(ei.bstrHelpFile)+1, out, sizeof(out), NULL, NULL) ;

			if (ret == IDHELP)
			{
				WinHelp (hMainDlg, out, HELP_CONTEXT, ei.dwHelpContext) ;
			}

/*
We are responsible for freeing all the BSTRs.
*/
			if (ei.bstrHelpFile) SysFreeString(ei.bstrHelpFile) ;
			if (ei.bstrSource)	SysFreeString(ei.bstrSource) ;
			if (ei.bstrDescription) SysFreeString (ei.bstrDescription) ;
		}
				
	}
	
	return hr ;
	
}

/*
GetString( ) method.  Use the IDispatch interface to get the
value of the "HelloMessage" property exposed by the server.
*/

HRESULT CHelloDriver::GetString(LPOLESTR pOut)
{
	HRESULT hr ; 
	VARIANT var ; 
	EXCEPINFO ei ;
	UINT uiErr ;   

/*
Initialize variables as required.
*/
	DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0};
 	var.vt = VT_EMPTY ;
	
/*
Call IDispatch::Invoke( ) to get the string from the server.
*/
 
	hr = m_pDisp->Invoke (
		m_dHelloPropID,
		IID_NULL,
		LOCALE_SYSTEM_DEFAULT,
		DISPATCH_PROPERTYGET,
		&dispparamsNoArgs,
		&var,
		&ei,
		&uiErr ) ; 

/*
If call succeeded, we are responsible for freeing the BSTR in which
the value was returned.
*/
	if (SUCCEEDED(hr))
	{
		wcscpy (pOut, var.bstrVal) ;
		SysFreeString (var.bstrVal) ;
	} 		
		
	return hr ;
}


/*
SayHowdy( ) method.  Use the IDispatch interface to call the
server object's "SayHello" method.
*/

HRESULT CHelloDriver::SayHowdy(void)
{                   
	HRESULT hr ; 
	EXCEPINFO ei ;
	UINT uiErr ;   
 	
/*
Initialize parameter array.  Here it is empty.
*/
	DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0};


/*
Call IDispatch::Invoke( )
*/
	hr = m_pDisp->Invoke (
		m_dSayHelloId,
		IID_NULL,
		LOCALE_SYSTEM_DEFAULT,
		DISPATCH_METHOD,
		&dispparamsNoArgs,
		NULL,
		&ei,
		&uiErr ) ;  

	return hr ;
}


BOOL CALLBACK ErrorDlgProc (HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) 
{
	switch (msg)
	{
		case WM_INITDIALOG:
		{
			SetDlgItemText (hDlg, IDC_EDIT1, (LPTSTR) lParam) ;
			return TRUE ;
		}

		case WM_COMMAND:
		{
			if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDHELP)
			{
				EndDialog (hDlg, wParam) ;
			}
			return TRUE ;
		}
	}
	return FALSE ;
}
