Solved

OLE Automation Server

Posted on 1997-03-05
1
574 Views
Last Modified: 2013-11-25
I have an application (EXE) written under IBM's  Visual Age C++ for Windows (under NT).  I need to add OLE Automation Server Objects to this application.  My thought i to create a DLL using Microsoft C++ that contains the OLE  automation objects and let the Visual Age EXe init and run the OLE server in this DLL (which will allow other client EXE's to interface with the OLE objects).

Is this possible?  I'm trying to understand OLE under MFC, but there is so much done behind the scenes that I can't really get a handle on this.  Any help or example would be appreciated!

0
Comment
Question by:rmichels
1 Comment
 

Accepted Solution

by:
mtr earned 75 total points
ID: 1300447
Yes, this is possible.  In fact, most of the time OLE servers are put in DLL's -- its common.  You have to implement the IClassFactory interface from the DLL, plus the three standard
DLL functions: DllMain (save off the instance handle),
DllGetClassObject (called to create the new COM object), and DllCanUnloadNow (called to see if the ref count is 0 so NT can unload the dll). There are many examples in a number of books of implementing these servers.  

Here is an example I had that wasn't copyrighted -- I think its from the Sams book Programming Windows NT 4 Unleashed
by Hamilton and Williams.

This example shell extension that creates a custom context menus when a C++ file is right clicked in the shell.  NT's shel user interface in NT4 uses COM to support user extensions of a number of types -- this one is "Context menu extensions".  Anyway, There is a header file and a C++ file for this in-process server.  Also a reg file for creating the registry entries specifc to the object, and the shell extension.   Anyway, you can get the point with this example, or you can buy this book or one of 20 others that will step you through the process piece by piece.  Using a Visual Age C++ exe to run an OLE server shouldn't make any difference (a co-worker I know has used VAge for the same kinds of things) -- its a generic architecture, with standard DLL entry points.

file CppExt.def:
LIBRARY     CppExt
DESCRIPTION "Shell extension for CPP files"

EXPORTS
    DllCanUnloadNow
    DllGetClassObject



File CppExt.h:

// The class ID for the CPP file extension
/* d3d834c0-9a3b-11cf-82a0-00608cca2a2a */
DEFINE_GUID( CLSID_CppExtension, 0xd3d834c0, 0x9a3b, 0x11cf, 0x82,
             0xa0, 0x00, 0x60, 0x8c, 0xca, 0x2a, 0x2a );

#define     OFFSET_COUNTLINES   0
//
// A typical class factory, nothing out of the ordinary. Includes
// the IClassFactory and IUnknown interfaces
class CClassFactory : public IClassFactory
{
public:
    CClassFactory();
    ~CClassFactory();

    // IUnknown
    STDMETHODIMP QueryInterface( REFIID riid, LPVOID* ppv );
    STDMETHODIMP_(ULONG) AddRef();
    STDMETHODIMP_(ULONG) Release();

    // IClassFactory
    STDMETHODIMP CreateInstance( LPUNKNOWN punk,
                                 REFIID riid,
                                 LPVOID* ppv );
    STDMETHODIMP LockServer( BOOL f );

protected:
    ULONG   m_cRef;
};
//
// The CCppShellExt class
// As a context menu handler, the CCppShellExt class implements the
// IContextMenu and IShellExtInit interfaces.
class CCppShellExt: public IContextMenu, IShellExtInit
{
    ULONG   m_cRef;
    char    m_szFile[MAX_PATH];
public:
    CCppShellExt();
    ~CCppShellExt();
    //
    // IUnknown
    STDMETHODIMP QueryInterface( REFIID riid, LPVOID* ppv );
    STDMETHODIMP_(ULONG) AddRef();
    STDMETHODIMP_(ULONG) Release();
    //
    // IContextMenu
    STDMETHODIMP QueryContextMenu( HMENU hMenu,
                                   UINT  nMenuIndex,
                                   UINT  nFirstID,
                                   UINT  nLastID,
                                   UINT  nFlags );
    STDMETHODIMP InvokeCommand( LPCMINVOKECOMMANDINFO pInfo );
    STDMETHODIMP GetCommandString( UINT  nItemID,
                                   UINT  nFlags,
                                   UINT* pReserved,
                                   LPSTR pszName,
                                   UINT  cchMax );
    //
    // IShellExtInit
    STDMETHODIMP Initialize( LPCITEMIDLIST pidl,
                             LPDATAOBJECT  pObj,
                             HKEY          hKeyProgID );
private:
    //
    // Operations
    void DoCountCppFile( LPCMINVOKECOMMANDINFO pInfo );
};



File CppExt.cpp:


#define STRICT
#define INC_OLE2        // WIN32, get ole2 from windows.h

#include <windows.h>
#include <windowsx.h>
#include <shlobj.h>
#pragma data_seg(".text")
#define INITGUID
#include <initguid.h>
#include <shlguid.h>
#pragma data_seg()

#include "CppExt.h"

LONG    g_cRef = 0;
HANDLE  g_hInst = NULL;

// -------------------------------------------------------------
// General DLL functions
// -------------------------------------------------------------
// Main DLL entry point - stash the module instance handle for use
// later, return TRUE in all cases.
extern "C" int APIENTRY
DllMain( HANDLE hInst, ULONG uReason, LPVOID pRes )
{
    if( uReason == DLL_PROCESS_ATTACH )
        g_hInst = hInst;
    return TRUE;
}
//
// Every InProc server must support DllGetClassObject. The only
// object supported by this server is CLSID_CppExtension, all
// other requests are rejected. For our extension, a ClassFactory
// object is created, and a QIF is performed for the requested
// interface through the ClassFactory.
STDAPI DllGetClassObject(REFCLSID rcid, REFIID riid, LPVOID *ppv)
{
    HRESULT hr;
    *ppv = NULL; // Always clear the "out" parameter
    if( IsEqualCLSID( rcid, CLSID_CppExtension ) == FALSE )
        hr = ResultFromScode( CLASS_E_CLASSNOTAVAILABLE );
    else
    {
        CClassFactory* pFactory = new CClassFactory;
        if( pFactory == NULL )
            hr = ResultFromScode( E_OUTOFMEMORY );
        else
        {
            hr = pFactory->QueryInterface( riid, ppv );
        }
    }
    return hr;
}
//
// DllCanUnloadNow is called by the OS to determine if the inproc
// server can be unloaded. If the global reference count is greater
// than zero, S_FALSE is returned to prevent unloading.
STDAPI DllCanUnloadNow()
{
    HRESULT hr = S_FALSE;
    if( g_cRef == 0 )
        hr = S_OK;
    return hr;
}
// -------------------------------------------------------------
// IClassFactory implementation
// -------------------------------------------------------------
// The IClassFactory interface is responsible for creating an
// instance of the shell extension. The ctor and dtor increment
// and decrement the DLL's global reference count.
CClassFactory::CClassFactory()
{
    m_cRef = 0L;
    InterlockedIncrement( &g_cRef );
}
CClassFactory::~CClassFactory()
{
    InterlockedDecrement( &g_cRef );
}
// IUnknown interfaces for CClassFactory
STDMETHODIMP CClassFactory::QueryInterface( REFIID riid, LPVOID* ppv )
{
    *ppv = NULL;
    if( IsEqualIID( riid, IID_IUnknown ) == TRUE )
    {
        *ppv = (LPUNKNOWN)this;
        m_cRef++;
        return NOERROR;
    }
    else if( IsEqualIID( riid, IID_IClassFactory ) == TRUE )
    {
        *ppv = (LPCLASSFACTORY)this;
        m_cRef++;
        return NOERROR;
    }
    else
    {
        return ResultFromScode( E_NOINTERFACE );
    }
}
STDMETHODIMP_(ULONG) CClassFactory::AddRef()
{
    return ++m_cRef;
}
STDMETHODIMP_(ULONG) CClassFactory::Release()
{
    if( --m_cRef )
        return m_cRef;
    delete this;
    return 0L;
}
// IClassFactory interfaces - CreateInstance and LockServer.
//
// CreateInstance creates a CCppShellExt object, and returns
// the result of QIF on the requested interface.
STDMETHODIMP CClassFactory::CreateInstance( LPUNKNOWN punk,
                                            REFIID riid,
                                            LPVOID* ppv )
{
    *ppv = NULL;
    if( punk != NULL )
        return ResultFromScode( CLASS_E_NOAGGREGATION );

    CCppShellExt* pShellExt = new CCppShellExt;
    if( pShellExt == NULL )
        return ResultFromScode( E_OUTOFMEMORY );

    return pShellExt->QueryInterface( riid, ppv );
}
// Simple implementation of LockServer, this just increments and
// decrements the global reference count.
STDMETHODIMP CClassFactory::LockServer( BOOL f )
{
    if( f )
        InterlockedIncrement( &g_cRef );
    else
        InterlockedDecrement( &g_cRef );
    return NOERROR;
}
// -------------------------------------------------------------
// Shell extension implementation
// -------------------------------------------------------------
// There are three interfaces supported by a context menu extension
// - IContextMenu, IShellExtInit, and IUnknown. Additionally,there
// is one private member function, DoCppCount.
//
// The ctor and dtor for the CppShellExt class increment and decre-
// ment the global reference count for the DLL. The ctor also
// handles initialization of member variables.
CCppShellExt::CCppShellExt()
{
    m_cRef = 0L;
    m_szFile[0] = '\0';
    InterlockedIncrement( &g_cRef );
}
CCppShellExt::~CCppShellExt()
{
    InterlockedDecrement( &g_cRef );
}
// IUnknown interfaces for the Shell Interfaces
STDMETHODIMP CCppShellExt::QueryInterface( REFIID riid, LPVOID* ppv )
{
    if( IsEqualIID( riid, IID_IUnknown ) == TRUE )
    {
        *ppv = (LPUNKNOWN)(LPCONTEXTMENU)this;
        m_cRef++;
        return NOERROR;
    }
    else if( IsEqualIID( riid, IID_IContextMenu ) == TRUE )
    {
        *ppv = (LPCONTEXTMENU)this;
        m_cRef++;
        return NOERROR;
    }
    else if( IsEqualIID( riid, IID_IShellExtInit ) == TRUE )
    {
        *ppv = (LPSHELLEXTINIT)this;
        m_cRef++;
        return NOERROR;
    }
    else
    {
        *ppv = NULL;
        return ResultFromScode( E_NOINTERFACE );
    }
}
STDMETHODIMP_(ULONG) CCppShellExt::AddRef()
{
    return ++m_cRef;
}
STDMETHODIMP_(ULONG) CCppShellExt::Release()
{
    if (--m_cRef)
        return m_cRef;
    delete this;
    return 0L;
}
// -------------------------------------------------------------
// IShellExtInit interface
// -------------------------------------------------------------
// IShellExtInit only has one function - Initialize is called to
// prepare your shell extension for calls that will be made on
// other interfaces - in this case, through IContextMenu. The main
// point of interest for a context menu is the name of the file
// object receiving the mouse-click, which is collected using
// DragQueryFile.
STDMETHODIMP CCppShellExt::Initialize( LPCITEMIDLIST pidl,
                                       LPDATAOBJECT  pObj,
                                       HKEY          hKeyProgID )
{
    STGMEDIUM   stg;
    FORMATETC   fetc = { CF_HDROP,
                         NULL,
                         DVASPECT_CONTENT,
                         -1,
                         TYMED_HGLOBAL };
    if( pObj == NULL )
        return ResultFromScode( E_FAIL );
    HRESULT hr = pObj->GetData( &fetc, &stg );
    if( FAILED(hr) )
        return ResultFromScode( E_FAIL );

    UINT cFiles = DragQueryFile( (HDROP)stg.hGlobal,
                                 0xFFFFFFFF,
                                 NULL,
                                 0 );
    if( cFiles == 1 )
    {
        DragQueryFile( (HDROP)stg.hGlobal,
                       0,
                       m_szFile,
                       sizeof(m_szFile) );
        hr = NOERROR;
    }
    else
        hr = ResultFromScode( E_FAIL );

    ReleaseStgMedium( &stg );
    return hr;
}
// -------------------------------------------------------------
// IContextMenu interfaces
// -------------------------------------------------------------
//
// QueryContextMenu is called when the shell requests the extension
// to add its menu items to the context menu. It's possible to get
// a NULL menu handle here. Also note that the current (As of this
// writing) Win32 SDK documentation is wrong regarding the nFlags
// parameter. This function should always return the number of new
// items added to the menu.
STDMETHODIMP CCppShellExt::QueryContextMenu( HMENU hMenu,
                                             UINT  nMenuIndex,
                                             UINT  nFirstID,
                                             UINT  nLastID,
                                             UINT  nFlags )
{
    char szMenu[] = "Count Lines and Statements";
    BOOL fAppend = FALSE;
    if( (nFlags & 0x000F) == CMF_NORMAL )
    {
        fAppend = TRUE;
    }
    else if( nFlags & CMF_VERBSONLY )
    {
        fAppend = TRUE;
    }
    else if( nFlags & CMF_EXPLORE )
    {
        fAppend = TRUE;
    }
    if( fAppend && hMenu )
    {
        BOOL f = ::InsertMenu( hMenu,
                      nMenuIndex,
                      MF_STRING | MF_BYPOSITION,
                      nFirstID,
                      szMenu );
        return ResultFromScode( MAKE_SCODE(SEVERITY_SUCCESS,
                                           0,
                                           USHORT(1)) );
    }
    return NOERROR;
      
}
//
// InvokeCommand is the "Money Shot". This is a notification that
// the user has clicked on one of the selected items in the context
// menu. The name of the file is not passed to you in this function
// since it was passed in the IShellExtInit::Initialize  function.
STDMETHODIMP
CCppShellExt::InvokeCommand( LPCMINVOKECOMMANDINFO pInfo )
{
    if( HIWORD(pInfo->lpVerb) != 0 )
        return ResultFromScode( E_FAIL );

    if( LOWORD(pInfo->lpVerb) > OFFSET_COUNTLINES )
        return ResultFromScode( E_INVALIDARG );

    if( LOWORD(pInfo->lpVerb) == OFFSET_COUNTLINES )
        DoCountCppFile( pInfo );
    return NOERROR;
}
//
// GetCommandString is called by the shell to retrieve a string
// associated with a new menu item.
STDMETHODIMP CCppShellExt::GetCommandString( UINT  nItemID,
                                             UINT  nFlags,
                                             UINT* pReserved,
                                             LPSTR pszName,
                                             UINT  cchMax )
{
    if( nItemID == OFFSET_COUNTLINES )
    {
        switch( nFlags )
        {
        case GCS_HELPTEXT:
            lstrcpy( pszName,
                     "Counts lines and semicolons in a file");
            return NOERROR;
            break;
        case GCS_VALIDATE:
            return NOERROR;
            break;
        case GCS_VERB:
            lstrcpy( pszName, "Count" );
            break;
        }
    }
    return ResultFromScode(E_INVALIDARG);
}
//
// DoCountCppFile opens the file which is located under the mouse
// click. The name of this file was passed to us in the Initialize
// member function that is part of the IShellExtInit interface. The
// file name was stored in m_szFile. This function opens the file
// and counts the number of newlines and semicolons in the file.
void CCppShellExt::DoCountCppFile( LPCMINVOKECOMMANDINFO pInfo )
{
    HANDLE  hFile = CreateFile( m_szFile,
                                GENERIC_READ,
                                FILE_SHARE_READ,
                                NULL,
                                OPEN_EXISTING,
                                FILE_ATTRIBUTE_COMPRESSED,
                                NULL );
    if( hFile == INVALID_HANDLE_VALUE )
    {
        ::MessageBox( pInfo->hwnd,
                      m_szFile,
                      "Can't open file",
                      MB_ICONHAND );
        return;
    }
    BOOL  fRead;
    DWORD dwRead;
    DWORD cSemi = 0L;
    DWORD cLines = 0L;
    while(1)
    {
        char rgBuffer[1024];
        fRead = ReadFile( hFile,
                          rgBuffer,
                          sizeof(rgBuffer),
                          &dwRead,
                          NULL );
        if( fRead == FALSE || dwRead == 0 )
            break;
        for( DWORD dw = 0; dw < dwRead; dw++ )
        {
            if( rgBuffer[dw] == ';' )
                cSemi++;
            else if( rgBuffer[dw] == '\n' )
                cLines++;
        }
    }
    TCHAR szMsg[80];
    TCHAR szSemi[] = "Total Semicolons = ";
    TCHAR szLines[] = "Total Lines = ";
    wsprintf( szMsg,"%s%ld\n%s%ld",
              (LPCTSTR)szSemi,
              (DWORD)cSemi,
              (LPCTSTR)szLines,
              (DWORD)cLines );
    ::MessageBox( pInfo->hwnd, szMsg, "C++ File", MB_ICONINFORMATION );
    CloseHandle( hFile );
}



And finally for the Registry entries:
file CppExt.reg
REGEDIT4

[HKEY_CLASSES_ROOT\CLSID\{d3d834c0-9a3b-11cf-82a0-00608cca2a2a}]
   @="C++ Line Counter"
[HKEY_CLASSES_ROOT\CLSID\{d3d834c0-9a3b-11cf-82a0-00608cca2a2a}\InProcServer32]
   @="CppExt.dll"
     "ThreadingModel"="Apartment"


[HKEY_CLASSES_ROOT\.cpp]
   @="cpp_auto_file"
[HKEY_CLASSES_ROOT\cpp_auto_file]
   @="C++ File"
[HKEY_CLASSES_ROOT\cpp_auto_file\shellex\ContextMenuHandlers]
   @="CppLineCounter"
[HKEY_CLASSES_ROOT\cpp_auto_file\shellex\ContextMenuHandlers\CppLineCounter]
   @="{d3d834c0-9a3b-11cf-82a0-00608cca2a2a}"

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved]
   "{d3d834c0-9a3b-11cf-82a0-00608cca2a2a}"="C++ Line Counter"

0

Featured Post

What Security Threats Are You Missing?

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

Join & Write a Comment

Suggested Solutions

Title # Comments Views Activity
Clean Uninstall of VS Professional 2015 5 43
sumNumber challenge 16 99
fizzArray2 challenge 1 61
Is COM supported from Apache 1 38
Introduction: Hints for the grid button.  Nested classes, templated collections.  Squash that darned bug! Continuing from the sixth article about sudoku.   Open the project in visual studio. First we will finish with the SUD_SETVALUE messa…
Introduction: The undo support, implementing a stack. Continuing from the eigth article about sudoku.   We need a mechanism to keep track of the digits entered so as to implement an undo mechanism.  This should be a ‘Last In First Out’ collec…
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
This is Part 3 in a 3-part series on Experts Exchange to discuss error handling in VBA code written for Excel. Part 1 of this series discussed basic error handling code using VBA. http://www.experts-exchange.com/videos/1478/Excel-Error-Handlin…

743 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

11 Experts available now in Live!

Get 1:1 Help Now