Thread: Creating a central, global COM interface

  1. #1
    mustang benny bennyandthejets's Avatar
    Join Date
    Jul 2002
    Posts
    1,401

    Creating a central, global COM interface

    My goal is to have a globally accessible (ie from any running process) interface which a program can talk to using COM. What parts of COM does this involve?
    [email protected]
    Microsoft Visual Studio .NET 2003 Enterprise Architect
    Windows XP Pro

    Code Tags
    Programming FAQ
    Tutorials

  2. #2
    &TH of undefined behavior Fordy's Avatar
    Join Date
    Aug 2001
    Posts
    5,793
    That's a little general. If you want a single process that sits there and has other programs communicating with it via a com interface, then you can use ATL to create an object housed in an exe...or a service for that matter

  3. #3
    mustang benny bennyandthejets's Avatar
    Join Date
    Jul 2002
    Posts
    1,401
    I'd prefer to stay away from MFC and ATL, I'm not really too into them. Is it possible to not use them?
    [email protected]
    Microsoft Visual Studio .NET 2003 Enterprise Architect
    Windows XP Pro

    Code Tags
    Programming FAQ
    Tutorials

  4. #4
    &TH of undefined behavior Fordy's Avatar
    Join Date
    Aug 2001
    Posts
    5,793
    Quote Originally Posted by bennyandthejets
    I'd prefer to stay away from MFC and ATL, I'm not really too into them. Is it possible to not use them?
    Definately possible but a total pain in the butt! Loads of boilerplate code. Atl isnt like MFC anyway...it's pretty lightweight and efficient. And as you already have VC++ it would be a shame not to use the features that pretty much make it a must for com programming

    If you want to avoid class libraries full stop then get a decent basic com book and learn from that (I have Inside COM and found that pretty good http://www.amazon.com/exec/obidos/tg...books&n=507846)

  5. #5
    mustang benny bennyandthejets's Avatar
    Join Date
    Jul 2002
    Posts
    1,401
    Thanks I'll take that into consideration.

    Now that we're on the topic, do you think I should switch to MFC? I'm currently using my own set of base classes to create programs, but I might be missing out on functionality by not using MFC.
    [email protected]
    Microsoft Visual Studio .NET 2003 Enterprise Architect
    Windows XP Pro

    Code Tags
    Programming FAQ
    Tutorials

  6. #6
    &TH of undefined behavior Fordy's Avatar
    Join Date
    Aug 2001
    Posts
    5,793
    I cant really advise. I like MFC and always use it for normal window apps...but then there are people who never use it (a few people who know their stuff and just dont like MFC and a few people who dont know their ........ from their elbow but will bad mouth MFC without ever trying it)

    Best bet - give it a try. If it's for you then great, if not drop it and go back to your own classes

  7. #7
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    If you wish to implement multiple interfaces you will want to go with MFC or preferably ATL. However, the code for a single singleton interface in plain C++ is not too bad. I converted this from a plain C version I did a while ago. The C++ version is a lot neater.
    Code:
    #include <windows.h>
    #include <assert.h>
    #define ASSERT assert
    #include "ihello.h" // The midl generated header file
    
    class CHello : public IHello, public IClassFactory
    {
    private:
      ITypeInfo * m_ptinfo;
    
    public:
    
    /* --IUnknown methods-- */
      STDMETHODIMP QueryInterface(REFIID riid, void ** ppv)
      {
      	if (ppv == NULL) return E_INVALIDARG;
    
      	if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IDispatch) || IsEqualIID(riid, IID_IHello))
      	{
      		*ppv = (IHello *) this;
      	}
      	else if (IsEqualIID(riid, IID_IClassFactory))
      	{
      		*ppv = (IClassFactory *) this;
      	}
      	else
      	{
      		*ppv = NULL;
      		return E_NOINTERFACE;
      	}
    
    	AddRef();
    	return NOERROR;
      }
    
      STDMETHODIMP_(ULONG) AddRef(void)  { return 1; }
      STDMETHODIMP_(ULONG) Release(void) { return 1; }
    
    /* --IDispatch methods-- */
      STDMETHODIMP GetTypeInfoCount(unsigned int FAR*  pctinfo)
      {
      	if (!pctinfo) return E_INVALIDARG;
    
      	*pctinfo = 1;
      	return NOERROR;
      }
    
      STDMETHODIMP GetTypeInfo(unsigned int  iTInfo, LCID  lcid, ITypeInfo FAR* FAR*  ppTInfo)
      {
      	if (!ppTInfo) return E_INVALIDARG;
      	*ppTInfo = NULL;
    
      	if(iTInfo != 0) return DISP_E_BADINDEX;
    
      	m_ptinfo->AddRef();      // AddRef and return pointer to cached
    	                         // typeinfo for this object.
    	*ppTInfo = m_ptinfo;
    
      	return NOERROR;
      }
    
      STDMETHODIMP GetIDsOfNames(REFIID riid, OLECHAR ** rgszNames, UINT cNames, LCID lcid, DISPID * rgdispid)
      {
      	return DispGetIDsOfNames(m_ptinfo, rgszNames, cNames, rgdispid);
      }
    
      STDMETHODIMP Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS * pdispparams,
                          VARIANT * pvarResult, EXCEPINFO * pexcepinfo, UINT * puArgErr)
      {
      	return DispInvoke(this, m_ptinfo, dispidMember, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr);
      }
    
    /* --IClassFactory methods-- */
      STDMETHODIMP CreateInstance(LPUNKNOWN punkOuter, REFIID riid, void **ppv)
      {
      	if (!ppv) return E_INVALIDARG;
      	*ppv = NULL;
    
      	if (punkOuter != NULL) return CLASS_E_NOAGGREGATION;
    
      	return QueryInterface(riid, ppv);
      }
    
      STDMETHODIMP LockServer (BOOL fLock) { return NOERROR; }
    
    /* --Constructor and destuctor-- */
      CHello()
      {
      	ITypeLib * ptl;
    
      	ASSERT(SUCCEEDED(LoadTypeLib(L"hello.tlb", &ptl)));
      	ASSERT(SUCCEEDED(ptl->GetTypeInfoOfGuid(IID_IHello, &m_ptinfo)));
    
      	ptl->Release();
      }
    
      ~CHello()
      {
      	m_ptinfo->Release();
      }
    
    
    /* --IHello methods: Your custom interface goes here-- */     
      int m_x; // Could be private but let's keep everything together
    
      STDMETHODIMP put_x(/* [in] */ int Value)            { m_x = Value;   return NOERROR; }
      STDMETHODIMP get_x(/* [retval][out] */ int *retval) { *retval = m_x; return NOERROR; }
            
      STDMETHODIMP ShowMessage(/* [defaultvalue][in] */ BSTR msg = L"Hello World!")
      {
      	if (!msg) return E_INVALIDARG;
    
      	MessageBoxW(NULL, msg, L"Message", 0);
      	return NOERROR;
      }
    
    
    };
    
    CHello g_Hello;
    
    int main(void)
    {
    	HRESULT hr;
    	BOOL bRet;
    	DWORD dwClsFactoryCookie;
    	MSG msg;
    
    	OleInitialize(NULL);
    
    	hr = CoRegisterClassObject(CLSID_Hello, (IClassFactory *) &g_Hello,
    			           CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE, &dwClsFactoryCookie);
    
    	if (FAILED(hr)) { MessageBox(NULL, "Failed to register object", NULL, 0); return hr; }
    
    	while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0 && bRet != -1)
    	{
    		TranslateMessage(&msg);
    	        DispatchMessage(&msg);
    	}
    
    	CoRevokeClassObject(dwClsFactoryCookie);
    	OleUninitialize();
    
    	return 0;
    }
    It may look complicated but the only change you need to make is to replace the highlighted code wth your interface's methods(and replace "Hello" with the name of your interface).

    Before you get to the code however, you need to provide an idl version of your interface.
    Code:
    [
       uuid(3C591B20-1F13-101B-B826-00DD01103DE1), // LIBID_Hello
       helpstring("Hello 1.0 Type Library"),
       lcid(0x09),
       version(1.0)
    ]
    library Hello
    {
       importlib("stdole2.tlb");
       #define DISPID_NEWENUM -4
    
       [
          uuid(3C591B25-1F13-101B-B826-00DD01103DE1),  // IID_IHello.
          helpstring("Hello object."),
          oleautomation, dual
       ]
       interface IHello : IDispatch
       {
          [propget, helpstring("Returns and sets x coordinate.")]
          HRESULT x([out, retval] int* retval); 
          [propput, helpstring("Returns and sets x coordinate.")]
          HRESULT x([in] int Value);
    
          [helpstring("Displays a message.")]
          HRESULT ShowMessage([in, defaultvalue("Hello World!")] BSTR msg);
       }
    
       [
          uuid(FEB8C280-FD2D-11ce-87C3-00403321BFAC), // CLSID_Hello
          helpstring("Hello Class"),
          appobject
       ]
       coclass Hello
       {
          [default] interface IHello;
             interface IDispatch;
       }
    }
    Again, you only have to replace the highlighted section with the methods that make up your interface (and replace "Hello" and generate new uuids). This is compiled with midl.exe and provides a type library and a header file.
    Code:
    midl hello.idl /header ihello.h
    Finally, some entries must be made in the registry to register your class. Typically, this would be done on application install, but for simplicity we can do it with a ".reg" file:
    Code:
    REGEDIT4
    
    [HKEY_CLASSES_ROOT\HELLO.Application]
    ""="HELLO Application"
    
    [HKEY_CLASSES_ROOT\HELLO.Application\CLSID]
    ""="{FEB8C280-FD2D-11ce-87C3-00403321BFAC}"
    
    [HKEY_CLASSES_ROOT\CLSID\{FEB8C280-FD2D-11ce-87C3-00403321BFAC}]
    ""="HELLO Application"
    
    [HKEY_CLASSES_ROOT\CLSID\{FEB8C280-FD2D-11ce-87C3-00403321BFAC}\LocalServer32]
    ""="C:\\Full\\Path\\To\\Object\\HELLO.EXE /Automation"
    
    [HKEY_CLASSES_ROOT\CLSID\{FEB8C280-FD2D-11ce-87C3-00403321BFAC}\ProgId]
    ""="HELLO.Application"
    To end with here is some test code(a vbscript, test.vbs):
    Code:
    Set o = CreateObject("Hello.Application")
    
    o.ShowMessage
    o.ShowMessage "Goodbye Cruel World!"
    
    o.ShowMessage o.x
    o.x = 5
    o.ShowMessage o.x
    Being a COM object it can be used from C, C++, VB, C#, Perl, JScript, Delphi, or any other language that supports COM. Any out-of-process COM object is ready for DCOM so it can be run from the local computer or six blocks away(pending security, etc).

    EDIT: It should be pointed out that you need administrator access when you merge the ".reg" file and when you first run the program. This is because LoadTypLib() implicitly registers the type library the first time it is called. (This should be done on installation.)
    Last edited by anonytmouse; 05-08-2004 at 04:28 AM.

  8. #8
    mustang benny bennyandthejets's Avatar
    Join Date
    Jul 2002
    Posts
    1,401
    Thanks for the help. I'll try doing it both ways.
    [email protected]
    Microsoft Visual Studio .NET 2003 Enterprise Architect
    Windows XP Pro

    Code Tags
    Programming FAQ
    Tutorials

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Calling IRichEditOle interface methods
    By Niara in forum C Programming
    Replies: 2
    Last Post: 01-16-2009, 01:23 PM
  2. Global coordinates in OpenGL?
    By IcyDeath in forum C++ Programming
    Replies: 1
    Last Post: 11-25-2004, 06:29 PM
  3. Static global variable acting as global variable?
    By Visu in forum C Programming
    Replies: 2
    Last Post: 07-20-2004, 08:46 AM
  4. defining and using a global class
    By cjschw in forum C++ Programming
    Replies: 4
    Last Post: 03-05-2004, 09:51 PM
  5. Creating a User Friendly Interface.
    By jeeva in forum Linux Programming
    Replies: 5
    Last Post: 12-07-2001, 03:14 AM