CLASSFAC.CPP

//-------------------------------------------------------------------- 
// Microsoft OLE DB Sample Provider
// (C) Copyright 1994 - 1996 Microsoft Corporation. All Rights Reserved.
//
// @doc
//
// @module CLASSFAC.CPP | The module contains the DLL Entry and Exit
// points, plus the OLE ClassFactory class.
//
//

// Includes ------------------------------------------------------------------

#include "headers.h"
#include "classfac.h"


// Static vars ---------------------------------------------------------------

static const char * s_strDllName = "SampProv"; // used with GetModuleHandle
static const struct
{
char * strRegKey;
char * strValue;
} s_rgRegInfo[] =
{
{ "SampProv", "Microsoft OLE DB Sample Provider" },
{ "SampProv\\Clsid", "{E8CCCB79-7C36-101B-AC3A-00AA0044773D}" },
{ "CLSID\\{E8CCCB79-7C36-101B-AC3A-00AA0044773D}", "SampProv" },
{ "CLSID\\{E8CCCB79-7C36-101B-AC3A-00AA0044773D}\\ProgID", "SampProv" },
{ "CLSID\\{E8CCCB79-7C36-101B-AC3A-00AA0044773D}\\VersionIndependentProgID", "SampProv" },
{ "CLSID\\{E8CCCB79-7C36-101B-AC3A-00AA0044773D}\\InprocServer32", "%s" },
{ "CLSID\\{E8CCCB79-7C36-101B-AC3A-00AA0044773D}\\OLE DB Provider", "Microsoft OLE DB Sample Provider"},
};

// Code ----------------------------------------------------------------------

// DllMain -------------------------------------------------------------------
//
// @func DLL Entry point where Instance and Thread attach/detach notifications
// takes place. OLE is initialized and the IMalloc Interface pointer is obtained.
//
// @rdesc Boolean Flag
// @flag TRUE | Successful initialization
// @flag FALSE | Failure to intialize
//
BOOL WINAPI DllMain
(
HINSTANCE hInstDLL, //@parm IN | Application Instance Handle
DWORD fdwReason, //@parm IN | Indicated Process or Thread activity
LPVOID lpvReserved //@parm IN | Reserved...
)
{
BOOL fRetVal = FALSE;
HRESULT hr;
SYSTEM_INFO SystemInformation;

switch (fdwReason)
{
case DLL_PROCESS_ATTACH:

// Assume successfully initialized
fRetVal = TRUE;

// Do one-time initialization when first process attaches
if (!g_cAttachedProcesses)
{
g_hInstance = hInstDLL;

// Get the OLE task memory allocator; we'll use it to allocate
// all memory that we return to the client
hr = CoGetMalloc( MEMCTX_TASK, &g_pIMalloc );
if (!g_pIMalloc || !SUCCEEDED( hr ))
{
fRetVal = FALSE;
break;
}

// Get the system page size
if (!g_dwPageSize)
{
GetSystemInfo( &SystemInformation ); // can't fail
g_dwPageSize = SystemInformation.dwPageSize;
}
}

// Do per-process initialization here...

// Remember that another process successfully attached
g_cAttachedProcesses++;
break;

case DLL_PROCESS_DETACH:
// Clean up when the last process is going away
if (g_cAttachedProcesses == 1)
{
// Release the memory allocator object.
if (g_pIMalloc)
g_pIMalloc->Release();
g_pIMalloc = NULL;

}

// Do per-process clean up here...

// Remember that a process has detached
g_cAttachedProcesses--;
break;

case DLL_THREAD_ATTACH:
break;

case DLL_THREAD_DETACH:
break;
}

return fRetVal;
}


// DllGetClassObject ---------------------------------------------------------
//
// @func This function is exposed to OLE so that the classfactory can
// be obtained.
//
// @rdesc HRESULT indicating status of routine
// @flag S_OK | The object was retrieved successfully.
// @flag CLASS_E_CLASSNOTAVAILABLE | DLL does not support class.
// @flag E_OUTOFMEMORY | Out of memory.
// @flag E_INVALIDARG | One or more arguments are invalid.
// @flag E_UNEXPECTED | An unexpected error occurred.
// @flag OTHER | Other HRESULTs returned by called functions
//
HRESULT CALLBACK DllGetClassObject
(
REFCLSID rclsid, //@parm IN | CLSID of the object class to be loaded
REFIID riid, //@parm IN | Interface on object to be instantiated
LPVOID * ppvObj //@parm OUT | Pointer to interface that was instantiated
)
{
CClassFactory * pClassFactory;
HRESULT hr;

// Check for valid ppvObj pointer
if (!ppvObj)
return ResultFromScode( E_INVALIDARG );

// In case we fail, we need to zero output arguments
*ppvObj = NULL;

// We only service CLSID_SampProv
if (rclsid != CLSID_SampProv)
return ResultFromScode( CLASS_E_CLASSNOTAVAILABLE );

// We only support the IUnknown and IClassFactory interfaces
if (riid != IID_IUnknown &&
riid != IID_IClassFactory)
return ResultFromScode( E_NOINTERFACE );

// Create our ClassFactory object
pClassFactory = new CClassFactory();
if (pClassFactory == NULL)
return ResultFromScode( E_OUTOFMEMORY );

// Get the desired interface on this object
hr = pClassFactory->QueryInterface( riid, ppvObj );
if (!SUCCEEDED( hr ))
delete pClassFactory;

return hr;
}


// DllCanUnloadNow -----------------------------------------------------------
//
// @func Indicates whether the DLL is no longer in use and
// can be unloaded.
//
// @rdesc HRESULT indicating status of routine
// @flag S_OK | DLL can be unloaded now.
// @flag S_FALSE | DLL cannot be unloaded now.
//
STDAPI DllCanUnloadNow( void )
{
if (!g_cObj && !g_cLock)
return ResultFromScode( S_OK );
else
return ResultFromScode( S_FALSE );
}


//-----------------------------------------------------------------------------
// CClassFactory
//-----------------------------------------------------------------------------

// CClassFactory -------------------------------------------------------------
//
// @mfunc Constructor for this class
//
// @rdesc NONE
//
CClassFactory::CClassFactory
(
void
)
{
m_cRef = 0;

// Increment global object count
OBJECT_CONSTRUCTED();
}

// ~CClassFactory ------------------------------------------------------------
//
// @mfunc Destructor for this class
//
// @rdesc NONE
//
CClassFactory:: ~CClassFactory( void )
{
// Decrement global object count
OBJECT_DESTRUCTED();
}

// QueryInterface ------------------------------------------------------------
//
// @mfunc Returns a pointer to a specified interface. Callers use
// QueryInterface to determine which interfaces the called object
// supports.
//
// @rdesc HRESULT indicating the status of the method
// @flag S_OK | Interface is supported and ppvObject is set.
// @flag E_NOINTERFACE | Interface is not supported by the object
// @flag E_INVALIDARG | One or more arguments are invalid.
//
STDMETHODIMP CClassFactory::QueryInterface
(
REFIID riid, //@parm IN | Interface ID of the interface being queried for.
LPVOID * ppvObj //@parm OUT | Pointer to interface that was instantiated
)
{
// Check for valid ppvObj pointer
if (!ppvObj)
return ResultFromScode( E_INVALIDARG );

// In case we fail, we need to zero output arguments
*ppvObj = NULL;

// Do we support this interface?
if (riid == IID_IUnknown ||
riid == IID_IClassFactory)
*ppvObj = (LPVOID) this;

// If we're going to return an interface, AddRef it first
if (*ppvObj)
{
((LPUNKNOWN) *ppvObj)->AddRef();
return ResultFromScode( S_OK );
}
else
return ResultFromScode( E_NOINTERFACE );
}

// AddRef --------------------------------------------------------------------
//
// @mfunc Increments a persistence count for the object
//
// @rdesc Current reference count
//
STDMETHODIMP_( ULONG ) CClassFactory::AddRef( void )
{
return ++m_cRef;
}

// Release -------------------------------------------------------------------
//
// @mfunc Decrements a persistence count for the object and if
// persistence count is 0, the object destroys itself.
//
// @rdesc Current reference count
//
STDMETHODIMP_( ULONG ) CClassFactory::Release( void )
{
if (!--m_cRef)
{
delete this;
return 0;
}

return m_cRef;
}

// CreateInstance ------------------------------------------------------------
//
// @mfunc Creates an uninitialized instance of an object class.
// Initialization is subsequently performed using another
// interface-specific method
//
// @rdesc HRESULT indicating the status of the method
// @flag S_OK | Interface is supported and ppvObject is set.
// @flag E_NOINTERFACE | Interface is not supported by the object
// @flag E_INVALIDARG | One or more arguments are invalid.
// @flag E_OUTOFMEMORY | Memory could not be allocated
// @flag OTHER | Other HRESULTs returned by called functions
//
STDMETHODIMP CClassFactory::CreateInstance
(
LPUNKNOWN pUnkOuter, //@parm IN | Points to the controlling IUnknown interface
REFIID riid, //@parm IN | Interface ID of the interface being queried for.
LPVOID * ppvObj //@parm OUT | Pointer to interface that was instantiated
)
{
PCDATASOURCE pObj;
HRESULT hr;

// Check for valid ppvObj pointer
if (ppvObj == NULL)
return ResultFromScode( E_INVALIDARG );

// In case we fail, we need to zero output arguments
*ppvObj = NULL;

// If we're given a controlling IUnknown, it must ask for IUnknown.
// Otherwise, the caller will end up getting a pointer to their pUnkOuter
// instead of to the new object we create and will have no way of getting
// back to this new object, so they won't be able to free it. Bad!
if (pUnkOuter && riid != IID_IUnknown)
return ResultFromScode( E_NOINTERFACE );

// Prepare for the possibility that there might be an error
hr = ResultFromScode( E_OUTOFMEMORY );

// Create a CDataSource object
if (!(pObj = new CDataSource( pUnkOuter )))
return hr;

// Initialize it
if (pObj->FInit())
hr = pObj->QueryInterface( riid, ppvObj );

if (FAILED( hr ))
delete pObj;

return hr;
}

// LockServer ----------------------------------------------------------------
//
// @mfunc Controls whether an object application is kept in memory.
// Keeping the application alive in memory allows instances of this
// class to be created more quickly.
//
// @rdesc HRESULT indicating the status of the method
// @flag S_OK | Interface is supported and ppvObject is set.
//
STDMETHODIMP CClassFactory::LockServer
(
BOOL fLock //@parm IN | TRUE or FALSE to lock or unlock
)
{
if (fLock)
InterlockedIncrement( &g_cLock );
else
InterlockedDecrement( &g_cLock );

return NOERROR;
}


//---------------------------------------------------------------------------
// @func Removes keys to the registry.
//
// @rdesc Returns NOERROR
//
// @comm
// Special Notes: This allows us to avoid using a .reg file.
//
// Note that a more robust method would be to trace existing
// CLSID and ProgID, and trace each from the other. This would handle the
// case where either changed. Then should probably enumerate all keys under
// the ProgID and CLSID, then delete them.
//
// Also note the problem with our exposing a different CLSID for the debug
// and ndebug versions, yet the ProgID remains the same. Should we have
// different ProgID's also?
//
//---------------------------------------------------------------------------
STDAPI DllUnregisterServer
(
void
)
{
int i;
int iNumErrors = 0;
LONGstat;

// Delete all table entries. Loop in reverse order, since they
// are entered in a basic-to-complex order.
// We cannot delete a key that has subkeys.
// Ignore errors.
for (i=NUMELEM( s_rgRegInfo ) - 1; i >= 0; i--)
{
stat = RegDeleteKey( HKEY_CLASSES_ROOT, s_rgRegInfo[i].strRegKey );
if ((stat != ERROR_SUCCESS) &&
(stat != ERROR_FILE_NOT_FOUND) )
iNumErrors++;
}

return ResultFromScode( iNumErrors ? E_FAIL : S_OK );
}


//---------------------------------------------------------------------------
// @func Adds necessary keys to the registry.
//
// @rdesc Returns one of the following
// @flag NOERROR | Registration succeeded
// @flag E_FAIL | Something didn't work
//
// @comm
// Special Notes: This allows us to avoid using a .reg file.
// Here is what was in the SampProv.REG file of yore.
// Note that now we have two CLSID's, one for DEBUG. Then each one
// can point to a different .DLL.
//
//---------------------------------------------------------------------------
STDAPI DllRegisterServer
(
void
)
{
int i;
HKEY hk;
HMODULE hModule;
DWORD dwDisposition;
LONG stat;
char strOutBuff[300 ], strFileName[MAX_PATH + 1 ];

// Get the full path name for this DLL.
if (NULL == (hModule = GetModuleHandle( s_strDllName )))
return ResultFromScode( E_FAIL );
if (0 == GetModuleFileName( hModule, strFileName, sizeof( strFileName ) / sizeof( char )))
return ResultFromScode( E_FAIL );

// Make a clean start
DllUnregisterServer();

// Loop through s_rgRegInfo, and put everything in it.
// Every entry is based on HKEY_CLASSES_ROOT.
for (i=0; i < NUMELEM( s_rgRegInfo ); i++)
{
// Fill in any "%s" arguments with the name of this DLL.
wsprintf( strOutBuff, s_rgRegInfo[i].strValue, strFileName );

// Create the Key. If it exists, we open it.
// Thus we can still change the value below.
stat = RegCreateKeyEx(
HKEY_CLASSES_ROOT,
s_rgRegInfo[i].strRegKey,
0, // dwReserved
NULL, // lpszClass
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS, // security access mask
NULL, // lpSecurityAttributes
&hk, // phkResult
&dwDisposition );
if (stat != ERROR_SUCCESS)
return ResultFromScode( E_FAIL );

stat = RegSetValueEx(
hk, // created above
NULL, // lpszValueName
0, // dwReserved
REG_SZ, // fdwType
(BYTE *) strOutBuff, // value
strlen( strOutBuff ) + sizeof( char )); // cbData, including null terminator
RegCloseKey( hk );
if (stat != ERROR_SUCCESS)
return ResultFromScode( E_FAIL );
}

return ResultFromScode( S_OK );
}