Small is beautiful

At  the very beginning of this news-letter, we had denounced  the way the samples were written. Moreover, throughout this issue  we have  time and again expounded our faith in short programs  as  a teaching  aid.  (You must be tired of this rhetoric by  now).  We believe  that when we criticize somebody we must  have  something better.   This   article  is  a  representative   of   the   code we use to learn. Bill Gates wouldn't own up to such code. Just imagine no error checks, no Hungarian, no UI....
We  came  to these "to the point" programs  by  the method  of systematic elimination. We had two  Visual  C++ samples  that we could start with: SIMPCNTR, the  simplest container  or SIMPSVR, the simplest server. We decided  to begin  with the container. Why? Don't ask us. It wasn't meant  to be a choice based on logic.
We   kept   SIMPSVR   as  is.   In   every   function   of SIMPCNTR, we used file(), our by now  famous ide-de-camp. file() would merely write the name of the  function  onto a file on disk. We  ran  SIMPCNTR  and SIMPSVR  together  testing no more than one feature  at  a time. The first time all we did was to get in SIMPSVR into our container.
If  file() was our beacon, then this file  on  disk with the listing of all functions that are executed in the  process  was our blue-print. We used it to trace the path of  program execution. Having determined the necessary functions, we create a new file which contains only these functions. This file now  acts as our container.
Just because a function is called does not mean that all code  in it  gets  executed. So we go through the code with a  fine  tooth comb,  eliminating step by painful step every bit of  superfluous code  cutting  the code to a fraction of it's original  size.  It merely brought in SIMPSVR as an embedded object. Even this shortened  version  of the program wasn't the easiest  to  understand.  It was full of helper functions. The problem with  helper functions is that they in turn call several functions.
For  example, consider OleUIInsertObject(). This is  a  UI helper  function written to ease programming and not an OLE  API. It does a whole lot of things, all of which will remain transparent  to  the user. This function is not even  documented  in  the help.  We  went through it's source code and  realised  that  the function of interest to us is an OLE API OleCreate().
Alas!  OleCreate() calls as many as five other  functions. Of  these  only the use of the OLE  API  function  CoCreateInstance()  is critical. On reading the help, much to our  dismay, we realised that this too was a helper function. The  OLE 2.0 documentation says that CoCreateInstance() can  be split into CoGetClassObject() + CreateInstance()  combination. So  there we went, back to the drawing board, to  replace  CoCreateInstance()  with  these two. At long last, we  had  our shortest  container  devoid of all UI and helper  functions  that would bring in SIMPSVR as an embedded object.
Next, we turned our attention to SIMPSVR. We repeated  the entire  process for the server. This time the full-featured  container  SIMPCNTR is retained as is and  SIMPSVR  is cut down progressively to get to the heart of the matter. Eventually,  we  came  to  the shortest  server  that  will  work  with SIMPCNTR.
At  this  point, we realised that more code could  be  purged  on either   side  if  we  used  our   container   with our  server. So back to the job it was. By  now  we were  past masters at cutting down code. With constant  trimming, we came to the shortest possible programs. Horror of horrors, the whittled down container does not have a single interface or class implementation.

The Container

#include < windows.h> 
#include < ole2.h>
WNDCLASS a; UINT b; MSG c; IOleObject* j; CLSID g; IClassFactory *f; long _pascal _export zzz(UINT w,UINT x,UINT y,long z) { if(x == WM_COMMAND && y == 100) { CLSIDFromString("SIMPSVR",&g); CoGetClassObject(g,4,0,IID_IClassFactory,(void**)&f); f->>CreateInstance(0,IID_IOleObject,(void**)&j); j->>DoVerb(0,0,0,0,0,0); } if(x == WM_DESTROY) PostQuitMessage(0); return DefWindowProc(w,x,y,z); } int _pascal WinMain(UINT i,UINT j,char* k,int l) { OleInitialize(0); a.lpfnWndProc = zzz; a.hInstance = i; a.hbrBackground = GetStockObject(WHITE_BRUSH); a.lpszMenuName = "mmm"; a.lpszClassName = "abc"; RegisterClass(&a); b=CreateWindow("abc","Hello",WS_OVERLAPPEDWINDOW,5,5,200,300,0,0,i,0); ShowWindow(b,3); while(GetMessage(&c,0,0,0)) DispatchMessage(&c); OleUninitialize(); return (c.wParam); }
The  container will need absolutely no explanation to  those  who have been through the first OCX container program. What's not  to understand in this program.

The Server

#include < windows.h> 
#include < ole2.h>
class iii { public: virtual void* _export _cdecl QueryInterface(REFIID,void**); virtual DWORD _export _cdecl i2(){return 0;} virtual DWORD _export _cdecl i3(){return 0;} virtual void*_export _cdecl i4 (){return 0;} virtual void i5(){} virtual void i6(){} virtual void i7(){} virtual void i8(){} virtual void i9(){} virtual void i10(){} virtual void i11(){} virtual void* _export _cdecl  DoVerb(long,MSG*, IOleClientSite*,long,UINT,RECT*); virtual void i13(){} virtual void* _export _cdecl i14(){return 0;}; virtual void i15(){} virtual void i16(){} virtual void i17(){} virtual void i18(){} virtual void i19(){} virtual void * _export _cdecl i20(){return 0;} }; class ccc { public: virtual void* _export _cdecl QueryInterface(REFIID,void**); virtual DWORD _export _cdecl c2(){ return 0;} virtual DWORD _export _cdecl c3(){return 0;} virtual  void* _export _cdecl  CreateInstance(IUnknown*,REFIID, void**); }; WNDCLASS a; UINT b; MSG c; DWORD e; int x1,y1; iii *i; ccc *d; CLSID g; void* _export _cdecl iii::QueryInterface(REFIID r,void**p) { if(r==IID_IOleObject) { *p=this; return 0; } return ResultFromScode(E_NOINTERFACE); } void* _export _cdecl iii::DoVerb(long,MSG*,IOleClientSite*,long,UINT,RECT*) { ShowWindow (b,1); return 0; } void* _export _cdecl ccc::QueryInterface(REFIID r,void** p) { if(r == IID_IClassFactory) { *p=this; return 0; } return ResultFromScode(E_NOINTERFACE); } void*  _export  _cdecl  ccc::CreateInstance(IUnknown*,REFIID,void **p) { i=new iii; *p=i; return 0; } long _export _pascal zzz(UINT w,UINT x,UINT y,long z) { if(x==WM_LBUTTONDOWN) { x1=LOWORD(z); y1=HIWORD(z); InvalidateRect(w,0,1); } if(x==WM_PAINT) { PAINTSTRUCT p; HDC h = BeginPaint(w,&p); TextOut(h,x1,y1,"Hello",5); EndPaint(h,&p); } if(x==WM_DESTROY) PostQuitMessage(0); return DefWindowProc(w,x,y,z); } int _pascal WinMain(UINT i,UINT j,char* k,int l) { OleInitialize(0); a.hInstance=i; a.lpfnWndProc=zzz; a.lpszClassName="abc"; a.hbrBackground=GetStockObject(WHITE_BRUSH); RegisterClass(&a); d=new ccc; CLSIDFromString("SIMPSVR",&g); CoRegisterClassObject(g,(IUnknown*)d,4,0,&e); b=CreateWindow("abc","Server",WS_OVERLAPPEDWINDOW,0,0,400,400,0,0,i,0); while(GetMessage(&c,0,0,0)) DispatchMessage(&c); OleUninitialize(); return(c.wParam); }
Very little of the server requires some explanation. WinMain() as we know is the start of all Windows EXEs. The only function that requires explanation is the OLE API  CoRegisterClassObject(). This function is used by every EXE server  to indicate which pointer is to be returned to  the  container's  call of CoGetClassObject(). The first  parameter to CoRegisterClassObject() is the CLSID that  identifies  the server. The second is the pointer to be  returned  to the  container. We pass the pointer to the class ccc;  our IClassFactory stand-in as this parameter. The third parameter indicates that this is an EXE server while the fourth stipulates  that  only one user can access the server at a  time.  The fifth and final parameter is of no importance to us at this point in time.
CoGetClassObject() of the container will load and run  the server.  Thus, WinMain() of the server will get  executed. Here we create a window in memory, but no ShowWindow()  is used. The container uses the pointer returned to it by  CoGetClassObject()  to call the method CreateInstance()  in the server. In this function, we create an instance of the  class iii,  our  IOleObject  stand-in,  and  return  it's pointer to the container.
The  container uses this pointer to call DoVerb(). In  our implementation  of the function DoVerb(), we do not  refer to any of the parameters passed. Thus, from the container's  side we set all parameters to 0. DoVerb() of  the server  does  no more than a ShowWindow()  on  the  window created  in  WinMain().  Hey Presto!  The  server  appears "embedded" in the container. Simple. But this never comes  across in the Microsoft samples.
These  are  the  programs  that taught  us  the  fundamentals  of OLE.  These programs will work only with each  other.  But aren't  they easy on the eye? Anyone who looks at this code  will never  feel intimidated. Most of it is just basic C  code. In  arriving  at  this code, progress at times  was  slower  than snail's  pace but finally we had the smallest program. A  program infinitely more simpler to understand.
OLE 2.0 is a complex technology. Not surprising, considering all that it is meant to achieve. In learning OLE  2.0, one  has  to  grasp it's essence. Once the  concepts  are  clear, coding to implement a feature is incredibly facile.
No one can learn OLE 2.0 from the Microsoft samples unless  they  are willing to forgo all else in life  for  a  long time.  Like  we did. It took us nearly six months  to  understand OLE 2.0 inside-out. In this time we breathed, ate,  drank, slept,  dreamt  OLE.  We  just  stopped  short  of   using Olé as a greeting or did we?