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?
Printable View
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?
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
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 programmingQuote:
Originally Posted by bennyandthejets
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)
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.
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
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.
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).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;
}
Before you get to the code however, you need to provide an idl version of your interface.
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:[
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;
}
}
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:midl hello.idl /header ihello.h
To end with here is some test code(a vbscript, test.vbs):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"
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).Code:Set o = CreateObject("Hello.Application")
o.ShowMessage
o.ShowMessage "Goodbye Cruel World!"
o.ShowMessage o.x
o.x = 5
o.ShowMessage o.x
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.)
Thanks for the help. I'll try doing it both ways.