In the venerable tradition of simple C/C++ examples, this Win32 Automation server example from my book, Windows 98/NT Programming Unleashed, displays the string "Hello, World!". However, the string can be manipulated using the COM Automation interface. For example, the following Visual Basic lines can be attached to a button in a form; when the button is clicked, the automation server is invoked and its text is changed.
Sub Command1_Click () Dim hello As object Set hello = CreateObject("HELLO.Application") hello.text = "Hello from Visual Basic!" End Sub
Before using it, the automation server must be registered. Copying the following lines to a file named hello.reg and running the Registry editor (regedit hello.reg) shall accomplish this task. You may also want to change the LocalServer32 line to reflect the full path name of hello.exe.
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 = HELLO.EXE /Automation HKEY_CLASSES_ROOT\CLSID\{FEB8C280-FD2D-11ce-87C3-00403321BFAC}\ProgId = HELLO.Application
Finally, here is the real beast. These 300-odd lines of code can be compiled with Visual C++ using the following command line:
cl hello.cpp user32.lib gdi32.lib ole32.lib oleaut32.lib uuid.lib
The program contains no error-checking code, so if it fails for any reason, odd things will likely happen. As always with free example code of this kind, you are on your own... don't blame me if something goes wrong.
#include <windows.h> #include <initguid.h> #ifndef INITGUID #define INITGUID #endif DEFINE_GUID(CLSID_CHello, 0xfeb8c280, 0xfd2d, 0x11ce, 0x87, 0xc3, 0x0, 0x40, 0x33, 0x21, 0xbf, 0xac); static PARAMDATA rgpDataTEXT = { OLESTR("TEXT"), VT_BSTR }; enum IMETH_CTEXT { IMETH_SET = 0, IMETH_GET, }; enum IDMEMBER_CTEXT { IDMEMBER_TEXT = DISPID_VALUE }; static METHODDATA rgmdataCHello[] = { { OLESTR("TEXT"), &rgpDataTEXT, IDMEMBER_TEXT, IMETH_SET, CC_CDECL, 1, DISPATCH_PROPERTYPUT, VT_EMPTY }, { OLESTR("TEXT"), NULL, IDMEMBER_TEXT, IMETH_GET, CC_CDECL, 0, DISPATCH_PROPERTYGET, VT_BSTR } }; INTERFACEDATA idataCHello = { rgmdataCHello, 2 }; class CHello; class CText { public: STDMETHOD_(void, Set)(BSTR text); STDMETHOD_(BSTR, Get)(void); CText(CHello *pHello, char *p = NULL); ~CText(); void Paint(); HWND m_hwnd; private: char *m_text; CHello *m_pHello; }; class CHello : public IUnknown { public: static CHello *Create(char *p); STDMETHOD(QueryInterface)(REFIID riid, void **ppv); STDMETHOD_(unsigned long, AddRef)(void); STDMETHOD_(unsigned long, Release)(void); CHello(char *p = NULL); CText m_text; private: IUnknown *m_punkStdDisp; unsigned long m_refs; }; class CHelloCF : public IClassFactory { public: static IClassFactory *Create(); STDMETHOD(QueryInterface)(REFIID riid, void **ppv); STDMETHOD_(unsigned long, AddRef)(void); STDMETHOD_(unsigned long, Release)(void); STDMETHOD(CreateInstance)(IUnknown *punkOuter, REFIID riid, void **ppv); STDMETHOD(LockServer)(BOOL fLock); CHelloCF() { m_refs = 1; } private: unsigned long m_refs; }; CHello *pHello; CText::CText(CHello *pHello, char *p) { m_pHello = pHello; if (p != NULL) { m_text = new char[strlen(p) + 1]; strcpy(m_text, p); } else m_text = NULL; m_hwnd = NULL; } CText::~CText() { delete[] m_text; } STDMETHODIMP_(void) CText::Set(BSTR p) { char *bf; int size; size = WideCharToMultiByte(CP_ACP, NULL, p, -1, NULL, 0, NULL, NULL); bf = new char[size]; WideCharToMultiByte(CP_ACP, NULL, p, -1, bf, size, NULL, NULL); delete[] m_text; if (p != NULL) { m_text = new char[strlen(bf) + 1]; strcpy(m_text, bf); } else m_text = NULL; if (m_hwnd != NULL) InvalidateRect(m_hwnd, NULL, TRUE); } STDMETHODIMP_(BSTR) CText::Get() { static WCHAR *wbf; BSTR bbf; int size; size = MultiByteToWideChar(CP_ACP, 0, m_text, -1, NULL, 0); wbf = new WCHAR[size]; MultiByteToWideChar(CP_ACP, 0, m_text, -1, wbf, size); bbf = SysAllocString(wbf); delete[] wbf; return bbf; } void CText::Paint() { HDC hDC; PAINTSTRUCT paintStruct; RECT clientRect; if (m_text != NULL) { hDC = BeginPaint(m_hwnd, &paintStruct); if (hDC != NULL) { GetClientRect(m_hwnd, &clientRect); DPtoLP(hDC, (LPPOINT)&clientRect, 2); DrawText(hDC, m_text, -1, &clientRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE); EndPaint(m_hwnd, &paintStruct); } } } CHello *CHello::Create(char *p) { ITypeInfo *pTI; IUnknown *pUnk; CHello *pHello = new CHello(p); pHello->AddRef(); CreateDispTypeInfo(&idataCHello, LOCALE_SYSTEM_DEFAULT, &pTI); CreateStdDispatch(pHello, &(pHello->m_text), pTI, &pUnk); pTI->Release(); pHello->m_punkStdDisp = pUnk; return pHello; } STDMETHODIMP CHello::QueryInterface(REFIID riid, void **ppv) { if (IsEqualIID(riid, IID_IUnknown)) *ppv = this; else if (IsEqualIID(riid, IID_IDispatch)) return m_punkStdDisp->QueryInterface(riid, ppv); else { *ppv = NULL; return ResultFromScode(E_NOINTERFACE); } AddRef(); return NOERROR; } STDMETHODIMP_(unsigned long) CHello::AddRef() { return ++m_refs; } STDMETHODIMP_(unsigned long) CHello::Release() { if (--m_refs == 0) { if(m_punkStdDisp != NULL) m_punkStdDisp->Release(); PostQuitMessage(0); delete this; return 0; } return m_refs; } #pragma warning(disable:4355) CHello::CHello(char *p) : m_text(this, p) { m_refs = 0; } IClassFactory *CHelloCF::Create() { return new CHelloCF; } STDMETHODIMP CHelloCF::QueryInterface(REFIID riid, void **ppv) { if(IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IClassFactory)) { AddRef(); *ppv = this; return NOERROR; } *ppv = NULL; return ResultFromScode(E_NOINTERFACE); } STDMETHODIMP_(unsigned long) CHelloCF::AddRef() { return m_refs++; } STDMETHODIMP_(unsigned long) CHelloCF::Release() { if (--m_refs == 0) { delete this; return 0; } return m_refs; } STDMETHODIMP CHelloCF::CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv) { if(punkOuter != NULL) return ResultFromScode(CLASS_E_NOAGGREGATION); return pHello->QueryInterface(riid, ppv); } STDMETHODIMP CHelloCF::LockServer(BOOL fLock) { return NOERROR; } LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch(uMsg) { case WM_PAINT: pHello->m_text.Paint(); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hwnd, uMsg, wParam, lParam); } return 0; } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR d3, int nCmdShow) { MSG msg; HWND hwnd; WNDCLASS wndClass; IClassFactory *pHelloCF; unsigned long dwHelloCF = 0; unsigned long dwRegHello = 0; OleInitialize(NULL); pHello = CHello::Create("Hello, World!"); pHelloCF = CHelloCF::Create(); CoRegisterClassObject(CLSID_CHello, pHelloCF, CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE, &dwHelloCF); RegisterActiveObject(pHello, CLSID_CHello, NULL, &dwRegHello); pHelloCF->Release(); if (hPrevInstance == NULL) { memset(&wndClass, 0, sizeof(wndClass)); wndClass.style = CS_HREDRAW | CS_VREDRAW; wndClass.lpfnWndProc = WndProc; wndClass.hInstance = hInstance; wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wndClass.lpszClassName = "HELLO"; if (!RegisterClass(&wndClass)) return FALSE; } hwnd = CreateWindow("HELLO", "HELLO", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL); pHello->m_text.m_hwnd = hwnd; ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); while (GetMessage(&msg, NULL, 0, 0)) DispatchMessage(&msg); RevokeActiveObject(dwRegHello, NULL); CoRevokeClassObject(dwHelloCF); pHello->Release(); OleUninitialize(); return msg.wParam; }