DKOALA3.CPP

/* 
* DKOALA3.CPP
* Koala Object DLL Licensed Server Chapter 5
*
* Example object structured in a DLL server.
*
* Copyright (c)1993-1995 Microsoft Corporation, All Rights Reserved
*
* Kraig Brockschmidt, Microsoft
* Internet : kraigb@microsoft.com
* Compuserve: >INTERNET:kraigb@microsoft.com
*/


#define INITGUIDS
#include "dkoala3.h"


//Count number of objects and number of locks.
ULONG g_cObj=0;
ULONG g_cLock=0;

HINSTANCE g_hInst=NULL;


//License key string, stored in ANSI to match contents of LIC file
char g_szLic[]="Koala Object #3 Copyright (c)1993-1995 Microsoft Corp.";
BOOL g_fMachineLicensed=FALSE;


/*
* LibMain(32)
*
* Purpose:
* Entry point conditionally compiled for Win32 and Win16.
* Provides the proper structure for each environment.
*/

#ifdef WIN32
BOOL WINAPI LibMain32(HINSTANCE hInstance, ULONG ulReason
, LPVOID pvReserved)
{
if (DLL_PROCESS_DETACH==ulReason)
{
return TRUE;
}
else
{
if (DLL_PROCESS_ATTACH!=ulReason)
return TRUE;
}

g_fMachineLicensed=CheckForLicenseFile(hInstance
, TEXT("DKOALA3.LIC"), (BYTE *)g_szLic, lstrlenA(g_szLic));

g_hInst=hInstance;
return TRUE;
}
#else
int PASCAL LibMain(HINSTANCE hInstance, WORD wDataSeg
, WORD cbHeapSize, LPSTR lpCmdLine)
{
if (0!=cbHeapSize)
UnlockData(0);

g_fMachineLicensed=CheckForLicenseFile(hInstance
, TEXT("DKOALA3.LIC"), (BYTE *)g_szLic, lstrlen(g_szLic));

g_hInst=hInstance;
return (int)hInstance;
}
#endif




/*
* CheckForLicenseFile
*
* Purpose:
* Attempts to load DKOALA3.LIC from the same directory as
* this DLL, attempting to match the first part of that file
* with our license string (typical licensing scheme). If
* the match is successful, then this DLL can create any
* instances of Koala objects without additional trouble.
* Otherwise the client has to call IClassFactory2::CreateInstance-
* Lic to instantiate anything.
*
* Parameters:
* hInst HINSTANCE of the module describing the
* directory in which we expect to find the
* license file.
* pszFile LPTSTR to the name of the file to look for.
* pbLic LPBYTE to the expected contents of the file.
* cb UINT number of butes to compare.
*
* Return Value:
* BOOL TRUE if the file is available, FALSE otherwise.
*/

BOOL CheckForLicenseFile(HINSTANCE hInst, LPTSTR pszFile
, LPBYTE pbLic, UINT cb)
{
BOOL fFound=FALSE;
TCHAR szPath[_MAX_PATH];
LPTSTR pszTemp;
LPBYTE pbCompare;
#ifdef WIN32
HANDLE hFile;
#else
OFSTRUCT of;
HFILE hFile;
#endif
UINT cbRead;
ULONG cbWasRead;

//Get the module path, then replace DLL name with LIC filename
GetModuleFileName(hInst, szPath, _MAX_PATH);
pszTemp=_tcsrchr(szPath, '\\')+1;
lstrcpy(pszTemp, pszFile);

/*
* Now open the file and read contents into an allocated
* pbCompare. The check if the contents of that file and
* pbLic match. If so, then return success, otherwise
* failure.
*/
#ifdef WIN32
hFile=CreateFile(szPath, GENERIC_READ, FILE_SHARE_READ
, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
#else
hFile=OpenFile(szPath, &of, OF_READ);
#endif

/*
* NOTE: INVALID_HANDLE_VALUE, ReadFile, and CloseHandle
* are macros in INC\book1632.h when compiling for Win16.
*/
if (INVALID_HANDLE_VALUE==hFile)
return FALSE;

cbRead=cb*sizeof(BYTE);
pbCompare=(LPBYTE)malloc(cbRead+4);

if (NULL!=pbCompare)
{
ReadFile(hFile, pbCompare, cbRead, &cbWasRead, NULL);
fFound=(0==memcmp(pbLic, pbCompare, cb));
free(pbCompare);
}

CloseHandle(hFile);
return fFound;
}



/*
* DllGetClassObject
*
* Purpose:
* Provides an IClassFactory for a given CLSID that this DLL is
* registered to support. This DLL is placed under the CLSID
* in the registration database as the InProcServer.
*
* Parameters:
* clsID REFCLSID that identifies the class factory
* desired. Since this parameter is passed this
* DLL can handle any number of objects simply
* by returning different class factories here
* for different CLSIDs.
*
* riid REFIID specifying the interface the caller wants
* on the class object, usually IID_ClassFactory.
*
* ppv PPVOID in which to return the interface
* pointer.
*
* Return Value:
* HRESULT NOERROR on success, otherwise an error code.
*/

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, PPVOID ppv)
{
HRESULT hr;
CKoalaClassFactory *pObj;

if (CLSID_Koala!=rclsid)
return ResultFromScode(E_FAIL);

pObj=new CKoalaClassFactory();

if (NULL==pObj)
return ResultFromScode(E_OUTOFMEMORY);

hr=pObj->QueryInterface(riid, ppv);

if (FAILED(hr))
delete pObj;
else
g_cObj++;

return hr;
}





/*
* DllCanUnloadNow
*
* Purpose:
* Answers if the DLL can be freed, that is, if there are no
* references to anything this DLL provides.
*
* Parameters:
* None
*
* Return Value:
* BOOL TRUE if nothing is using us, FALSE otherwise.
*/

STDAPI DllCanUnloadNow(void)
{
SCODE sc;

//Our answer is whether there are any object or locks
sc=(0L==g_cObj && 0L==g_cLock) ? S_OK : S_FALSE;
return ResultFromScode(sc);
}





/*
* ObjectDestroyed
*
* Purpose:
* Function for the Koala object to call when it gets destroyed.
* Since we're in a DLL we only track the number of objects here,
* letting DllCanUnloadNow take care of the rest.
*/

void ObjectDestroyed(void)
{
g_cObj--;
return;
}





/*
* CKoalaClassFactory::CKoalaClassFactory
* CKoalaClassFactory::~CKoalaClassFactory
*
* Constructor Parameters:
* None
*/

CKoalaClassFactory::CKoalaClassFactory(void)
{
m_cRef=0L;
return;
}

CKoalaClassFactory::~CKoalaClassFactory(void)
{
return;
}




/*
* CKoalaClassFactory::QueryInterface
* CKoalaClassFactory::AddRef
* CKoalaClassFactory::Release
*/

STDMETHODIMP CKoalaClassFactory::QueryInterface(REFIID riid
, PPVOID ppv)
{
*ppv=NULL;

if (IID_IUnknown==riid || IID_IClassFactory==riid
|| IID_IClassFactory2==riid)
*ppv=this;

if (NULL!=*ppv)
{
((LPUNKNOWN)*ppv)->AddRef();
return NOERROR;
}

return ResultFromScode(E_NOINTERFACE);
}


STDMETHODIMP_(ULONG) CKoalaClassFactory::AddRef(void)
{
return ++m_cRef;
}


STDMETHODIMP_(ULONG) CKoalaClassFactory::Release(void)
{
if (0L!=--m_cRef)
return m_cRef;

delete this;
ObjectDestroyed();
return 0L;
}



/*
* CKoalaClassFactory::CreateAnObject
* (Private)
*
* Purpose:
* Central function to create instances of objects that is called
* from CreateInstance and CreateInstanceLic. This takes the same
* parameters as CreateInstance below.
*/

HRESULT CKoalaClassFactory::CreateAnObject(LPUNKNOWN pUnkOuter
, REFIID riid, PPVOID ppvObj)
{
PCKoala pObj;
HRESULT hr;

//Verify that a controlling unknown asks for IUnknown
if (NULL!=pUnkOuter && IID_IUnknown!=riid)
return ResultFromScode(CLASS_E_NOAGGREGATION);

hr=ResultFromScode(E_OUTOFMEMORY);

//Create the object passing function to notify on destruction.
pObj=new CKoala(pUnkOuter, ObjectDestroyed);

if (NULL==pObj)
return hr;

if (pObj->Init())
hr=pObj->QueryInterface(riid, ppvObj);

//Kill the object if initial creation or Init failed.
if (FAILED(hr))
delete pObj;
else
g_cObj++;

return hr;
}




/*
* CKoalaClassFactory::CreateInstance
*
* Purpose:
* Instantiates a Koala object returning an interface pointer.
*
* Parameters:
* pUnkOuter LPUNKNOWN to the controlling IUnknown if we are
* being used in an aggregation.
* riid REFIID identifying the interface the caller
* desires to have for the new object.
* ppvObj PPVOID in which to store the desired
* interface pointer for the new object.
*
* Return Value:
* HRESULT NOERROR if successful, otherwise E_NOINTERFACE
* if we cannot support the requested interface.
*/

STDMETHODIMP CKoalaClassFactory::CreateInstance(LPUNKNOWN pUnkOuter
, REFIID riid, PPVOID ppvObj)
{
*ppvObj=NULL;

/*
* The license file *must* be around for this simple
* CreateInstance to work. One we've checked, call the central
* creation function.
*/
if (!g_fMachineLicensed)
return ResultFromScode(CLASS_E_NOTLICENSED);

return CreateAnObject(pUnkOuter, riid, ppvObj);
}




/*
* CKoalaClassFactory::LockServer
*
* Purpose:
* Increments or decrements the lock count of the DLL. If the
* lock count goes to zero and there are no objects, the DLL
* is allowed to unload. See DllCanUnloadNow.
*
* Parameters:
* fLock BOOL specifying whether to increment or
* decrement the lock count.
*
* Return Value:
* HRESULT NOERROR always.
*/

STDMETHODIMP CKoalaClassFactory::LockServer(BOOL fLock)
{
if (fLock)
g_cLock++;
else
g_cLock--;

return NOERROR;
}




/*
* CKoalaClassFactory::GetLicInfo
*
* Purpose:
* Fills a LICINFO structure with license information for
* this class factory.
*
* Parameters:
* pLicInfo LPLICINFO to the structure to fill
*/

STDMETHODIMP CKoalaClassFactory::GetLicInfo(LPLICINFO pLicInfo)
{
if (NULL==pLicInfo)
return ResultFromScode(E_POINTER);

pLicInfo->cbLicInfo=sizeof(LICINFO);

//This says whether RequestLicKey will work
pLicInfo->fRuntimeKeyAvail=g_fMachineLicensed;

//This says whether the standard CreateInstance will work
pLicInfo->fLicVerified=g_fMachineLicensed;

return NOERROR;
}




/*
* CKoalaClassFactory::RequestLicKey
*
* Purpose:
* Retrieves a license key from this class factory for use with
* CreateInstanceLic.
*
* Parameters:
* dwReserved DWORD reserved for future use with multiple
* licenses.
* pbstrKey BSTR * in which to return the key.
*/

STDMETHODIMP CKoalaClassFactory::RequestLicKey(DWORD dwReserved
, BSTR *pbstrKey)
{
OLECHAR szTemp[256];

//Can't give away a key on an unlicensed machine
if (!g_fMachineLicensed)
return ResultFromScode(CLASS_E_NOTLICENSED);

#ifndef WIN32
lstrcpy(g_szLic, szTemp);
#else
mbstowcs(szTemp, g_szLic, sizeof(g_szLic));
#endif
*pbstrKey=SysAllocString(szTemp);
return (NULL!=*pbstrKey) ? NOERROR : ResultFromScode(E_OUTOFMEMORY);
}





/*
* CKoalaClassFactory::CreateInstanceLic
*
* Purpose:
* Creates and instance of the object given a license key.
* Same as CreateInstance, and implementations of this function
* will typically just validate the key and call CreateInstance.
*
* Parameters:
* pUnkOuter LPUNKNOWN to the controlling IUnknown if we are
* being used in an aggregation.
* pUnkReserved LPUNKNOWN reserved.
* riid REFIID identifying the interface the caller
* desires to have for the new object.
* bstrKey BSTR key used to validate creation.
* ppvObj PPVOID in which to store the desired
* interface pointer for the new object.
*/

STDMETHODIMP CKoalaClassFactory::CreateInstanceLic(LPUNKNOWN pUnkOuter
, LPUNKNOWN pUnkReserved, REFIID riid, BSTR bstrKey
, PPVOID ppvObj)
{
BOOL fMatch;
BSTR bstrTemp;
UINT cch;
OLECHAR szLic[256];
#ifdef WIN32ANSI
char szTemp[256];
#endif

*ppvObj=NULL;

/*
* Get our own license key that should match bstrKey exactly.
* This code is coped from RequestLicKey.
*/
#ifndef WIN32
lstrcpy(g_szLic, szLic);
#else
mbstowcs(szLic, g_szLic, sizeof(g_szLic));
#endif
bstrTemp=SysAllocString(szLic);

if (NULL==bstrTemp)
return ResultFromScode(E_OUTOFMEMORY);

#ifdef WIN32ANSI
WideCharToMultiByte(CP_ACP, 0, bstrTemp, -1, szTemp
, 256, NULL, NULL);
cch=lstrlen(szTemp);
#else
cch=lstrlen(bstrTemp);
#endif
fMatch=(0==memcmp(bstrTemp, bstrKey, cch*sizeof(OLECHAR)));
SysFreeString(bstrTemp);

if (!fMatch)
return ResultFromScode(CLASS_E_NOTLICENSED);

return CreateAnObject(pUnkOuter, riid, ppvObj);
}