Using COM with FOX
##################

gehriger@linkcad.com

This set of files contains the frame work for adding OLE to FOX. They don't 
represent a complete FOX application, so don't try to compile and link this 
stuff, it's just an example. They are taken from one of my FOX projects, and it 
works just fine. 

I'll create a full example (maybe with IE in a FOX Window?) as soon as I've got 
some time.

The following code is for an OCX w/o user interface, but it can easily be 
adapted for GUI OCX (ActiveX) controls: in the main() function, initialize OLE. 
Make CContainer and CEventSink member variables of the dialog class. Then set 
the OCX's parent with m_pContainer->setParent((HWND)id()) in the dialog's create 
member function.

main.h + main.cpp
=================

Main program entry function header + implementation. Also some COM helper 
functions. Note that the VisualC++ 6 #import statement is being used to create 
C++ wrapper classes for the OCX. Without #import, the COM functions have to be 
called in the "traditional" way (see any COM literature for details).

contnr.h + contnr.cpp 
===================== 

The COM container class header + implementation. Implements all COM interfaces 
required to host an OCX. I didn't actually implement everything, some of the 
rarely used interface functions return E_NOTIMPL.

ActiveX controls may load and save their state from / to memory / files etc. I 
therefore implemented the IStream interface Read and Write. Since the control
state is normally only saved during development, Write is only implemented in 
the debug version. It will write a byte array to a file called "props.log", which
will contain something like this:

static ULONG cbStateSize = 67;
BYTE bObjectState[67] = {
  0x00, 0x00, 0x01, 0x00, 0x06, 0x0a, 0x00, 0x00, 
  0x11, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x0d, 0x4c, 0x69, 0x6e, 0x6b, 0x43, 0x41, 0x44, 
  0x20, 0x33, 0x2e, 0x30, 0x2e, 0x31, 0x01, 0x0c, 
  0x25, 0x44, 0x81, 0xdb, 0x00, 0x00, 0x00, 0x00, 
  0x02, 0x31, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 
  0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 
  0x00, 0x00, 0x00
};

I only have a single OCX, so I just copy and paste this to contnr.cpp, and the 
Read method finds it. With more than one OCX, the container's parent should 
contain this array, and Read needs to be modified.

To get the container to save its state, use something like the code below, once 
the container has been initialized:

    if (SUCCEEDED(g_pSomeocx->QueryInterface(IID_IPersistStreamInit, (PVOID *)&ppsi)))
    {
      if (SUCCEEDED(g_pContainer->QueryInterface(IID_IStream, (PVOID *)&pps)))
      {
        ppsi->Save(pps, true);
        pps->Release();
      }
      ppsi->Release();
    }

In the release version, use this code to load the state:

    if (SUCCEEDED(g_pSomeocx->QueryInterface(IID_IPersistStreamInit, (PVOID *)&ppsi)))
    {
      if (SUCCEEDED(g_pContainer->QueryInterface(IID_IStream, (PVOID *)&pps)))
      {
        ppsi->Load(pps);
        g_pSomeocx->License(); // if needed...
        pps->Release();
      }
      
      ppsi->Release();
    }
  }
  
Of course, the control may also be configured by setting its properties manually.


evntsink.h + evntsink.cpp
=========================

These have to be "handcrafted" for every OCX. Get the method IDs from the IDL 
file / registry and write the appropriate handler in Invoke(). Best is to derive 
from CEventSink and override Invoke for each OCX. Then from Invoke(), call the 
appropriate members in the control's containing dialog.

