Figure 2   GetSupportedInterfaces


DWORD GetSupportedInterfaces(IUnknown *punk,
                             IID *iids,
                             DWORD nArraySize)
{
  DWORD result = 0;
  HKEY hkey;

// open the Interface (IID) key
  LONG r = RegOpenKeyEx( HKEY_CLASSES_ROOT,__TEXT("Interface"),
                          0, KEY_QUERY_VALUE, &hkey);

  if (r = = ERROR_SUCCESS) 
    { DWORD index = 0;
      TCHAR szGuid[128];
      // get each subkey
      while (ERROR_SUCCESS = = RegEnumKey(hkey, index, szGuid, sizeof(szGuid)))
        {
        // convert key name to GUID (note: IIDFromString is not const-correct)
        IID iid;
        IIDFromString(LPOLESTR(LPCOLESTR(OLESTRCVAR(szGuid))), &iid);

        // test the IID and append to array if supported
        if (IsInterfaceSupported(punk, iid) && result < nArraySize)
           iids[result++] = iid;
        index++;   }
      RegCloseKey(hkey);
    }
    return result;
}


Figure 3   GetInterfaceName


BOOL GetInterfaceName(REFIID riid,  // IID to map
                    LPTSTR szName,  // string
                    LONG cb)        // buf size
{
  BOOL result = FALSE;
  *szName = 0;
  HKEY hkey;

  // open the Interface (IID) key
  LONG r = RegOpenKeyEx( HKEY_CLASSES_ROOT,__TEXT("Interface"),
                         0, KEY_QUERY_VALUE, &hkey);

  if (r = = ERROR_SUCCESS) 
     {
     OLECHAR szGuid[64];
     // convert IID to a string (unicode)
     StringFromGUID2(riid, szGuid, sizeof(szGuid));

     // read value at corresponding key
     r = RegQueryValue(hkey, __TEXTCVAR(szGuid), szName, &cb);
     result = (r = = ERROR_SUCCESS);
     RegCloseKey(hkey);
     }

  return result;
}


Figure 4   QueryInterface


STDMETHODIMP
CoMyClass::QueryInterface(REFIID riid,
                          void **ppv) 
{
  *ppv = 0;
  if (riid = = IID_IUnknown || riid = = IID_IFoo)
    LPUNKNOWN(*ppv = LPFOO(this))->AddRef();
  else if (riid = = IID_IBar)
    LPUNKNOWN(*ppv = LPBAR(this))->AddRef();

  TCHAR szIfName[80];
  TCHAR szODS;
  if (!GetInterfaceName(riid, szIfName, 80))
    lstrcpy(szIfName, __TEXT("???"));

  wsprintf(szODS, 
           __TEXT("QueryInterface(%s) %s\n"),
           szIfName,
           *ppv ? __TEXT("succeeded")
           : __TEXT("failed"));

  OutputDebugString(szODS);
  return *ppv ? S_OK : E_NOINTERFACE;
}


Figure 5   IFDROP

IFDROP.RC


#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS
#include "windows.h"
#undef APSTUDIO_READONLY_SYMBOLS


IDD_DIALOG1 DIALOG DISCARDABLE  0, 0, 122, 158
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "Drop Something On Me!"
FONT 8, "MS Sans Serif"
BEGIN
    LISTBOX         IDC_LIST,3,20,116,132,LBS_SORT | LBS_NOINTEGRALHEIGHT | 
                    WS_VSCROLL | WS_TABSTOP
    EDITTEXT        IDC_EDIT,3,5,116,13,ES_AUTOHSCROLL | ES_READONLY
END

IFDROP.CPP


#define STRICT
#include <windows.h>
#include <windowsx.h>

#include "resource.h"

// include ANSI/UNICODE shims
#include <S816.h>


// reentrant thread-safe version of GetInterfaceName

BOOL GetInterfaceName(REFIID riid, 
                      LPTSTR szName, 
                      LONG cb)
{
  BOOL result = FALSE;
  *szName = 0;
  HKEY hkey;

// open the Interface (IID) key
  LONG r = RegOpenKeyEx( HKEY_CLASSES_ROOT, __TEXT("Interface"),
                         0, KEY_QUERY_VALUE, &hkey);

  if (r = = ERROR_SUCCESS) 
     {
     OLECHAR szGuid[64];
     // convert IID to a string (unicode)
     StringFromGUID2(riid, szGuid, sizeof(szGuid));

     // read value at corresponding key
     r = RegQueryValue(hkey, __TEXTCVAR(szGuid), szName, &cb);
     result = (r = = ERROR_SUCCESS);
     RegCloseKey(hkey);
     }

  return result;
}

// convenient non-tread-safe version (note static data)

LPCTSTR GetInterfaceName(REFIID riid)
{
   static TCHAR szName[128];
   if (!GetInterfaceName(riid, szName, sizeof(szName)))
       lstrcpy(szName, __TEXT("Unknown IID"));
   return szName;
}

// test for a single interface
BOOL IsInterfaceSupported(IUnknown *punk, 
                          REFIID riid)
{
  // try interface
  IUnknown *punkIf;
  HRESULT hr = punk->QueryInterface(riid, (void**)&punkIf);
  // clean up
  if (SUCCEEDED(hr))
    punkIf->Release();
                                 
  return SUCCEEDED(hr); 
}

// Test for all registered interfaces
DWORD GetSupportedInterfaces(IUnknown *punk,
                             IID *iids,
                             DWORD nArraySize)
{
  DWORD result = 0;
  HKEY hkey;

  // open the Interface (IID) key
  LONG r = RegOpenKeyEx( HKEY_CLASSES_ROOT, __TEXT("Interface"),
                         0, KEY_QUERY_VALUE, &hkey);

  if (r = = ERROR_SUCCESS) 
     {
     DWORD index = 0;
     TCHAR szGuid[128];
     // get each subkey
     while (ERROR_SUCCESS = = RegEnumKey(hkey, index, szGuid, sizeof(szGuid)))
       {
       // convert key name to GUID (note: IIDFromString is not const-correct)
       IID iid;
       IIDFromString(LPOLESTR(LPCOLESTR(OLESTRCVAR(szGuid))), &iid);

       // test the IID and append to array if supported
       if (IsInterfaceSupported(punk, iid) && result < nArraySize)
           iids[result++] = iid;
       index++;
       }
     RegCloseKey(hkey);
     }
  return result;
}


// a simple COM class to implement our drop target
class CoDrop : public IDropTarget {
       ULONG m_cRef;
       HWND m_hwndDlg;
public:
       CoDrop(HWND hwndDlg = 0)
              : m_cRef(1),  // note: no class factory
              m_hwndDlg(hwndDlg)
       {
       }

       void SetHwnd(HWND hwndDlg)
       {
          m_hwndDlg = hwndDlg;
       }
       
       STDMETHODIMP QueryInterface(REFIID riid, void**ppv)
          {
          if (riid = = IID_IUnknown || riid = = IID_IDropTarget)
              LPUNKNOWN(*ppv = LPDROPTARGET(this))->AddRef();
          else
              *ppv = 0;
          return ResultFromScode(*ppv ? S_OK : E_NOINTERFACE);
          }

       // since object's of this class will not be heap-based, 
       // we'll just cheat on AddRef/Release
       STDMETHODIMP_(ULONG) AddRef(void) { return 2; }
       STDMETHODIMP_(ULONG) Release(void) { return 1; }

       // DragEnter, DragOver and DragLeave are all no-ops
       STDMETHODIMP DragEnter(LPDATAOBJECT, DWORD, POINTL, DWORD *pdwEffect)
          {
          *pdwEffect = DROPEFFECT_COPY;
          return NOERROR;
          }

       STDMETHODIMP DragOver(DWORD, POINTL, DWORD *pdwEffect)
          {
          *pdwEffect = DROPEFFECT_COPY;
          return NOERROR;
          }

       STDMETHODIMP DragLeave(void)
          {
          return NOERROR;
          }

       // Drop is where we do the actual querying of the object
       STDMETHODIMP Drop(LPDATAOBJECT lpdo, DWORD, POINTL, DWORD *pdwEffect)
          {
          IUnknown *punkTarget = 0;
          IStorage *lpStg = 0;

          // We're about to make a lot of out-of-proc calls, 
          // so at least attempt to give the user some feedback
          HCURSOR hcur = GetCursor();
          SetCursor(LoadCursor(0, IDC_WAIT));

          // Try to get at an embedded object if possible
          if (NOERROR = = OleQueryCreateFromData(lpdo))
             {
             // create a dummy storage for the object(STGM_DELETEONRELEASE)
             StgCreateDocfile(0, STGM_DIRECT | STGM_READWRITE 
                              | STGM_SHARE_EXCLUSIVE | STGM_DELETEONRELEASE,
                              0, &lpStg);

             // attempt to create the embedding
             if (SUCCEEDED(OleCreateFromData(lpdo, IID_IUnknown, 
                           OLERENDER_NONE, 0, 0, 
                           lpStg, (void**)&punkTarget))) 
               {
               // put the object into the running state 
               // (otherwise, we're simply checking the handler)
               OleRun(punkTarget);

               // Get the ProgID for display
               CLSID clsid;
               IPersist *ppersist;
               punkTarget->QueryInterface(IID_IPersist, (void**)&ppersist);
               if (ppersist)
                  {
                  ppersist->GetClassID(&clsid);
                  LPOLESTR szProgID;
                  ProgIDFromCLSID(clsid, &szProgID);
                  SetDlgItemText(m_hwndDlg, IDC_EDIT, __TEXTCVAR(szProgID));
                  ppersist->Release();
                  CoTaskMemFree(szProgID);
                  }
               else
                  SetDlgItemText(m_hwndDlg, IDC_EDIT, 
                                 __TEXT("Unknown Embedding Type"));
               }
             }
          else
             {
             // there is not embedding on the cursor, 
             // so just inspect the IDataObject
             // that is on the cursor
             (punkTarget = lpdo)->AddRef();
             SetDlgItemText(m_hwndDlg, IDC_EDIT, 
                            __TEXT("Simple Dragged Data Object"));
             }

          // OK, now get the list of supported interfaces
          IID iids[32];
          DWORD count = GetSupportedInterfaces(punkTarget, iids, 32);

          // fill in the list box
          SetCursor(hcur);
          HWND hwndList = GetDlgItem(m_hwndDlg, IDC_LIST);
          ListBox_ResetContent(hwndList);

          for (DWORD i = 0; i < count; i++)
             {
             ListBox_AddString(hwndList, GetInterfaceName(iids[i]));
             }

          // release the storage and target objects
          if (lpStg) 
             lpStg->Release();

             punkTarget->Release();

             // we certainly don't want to accept this object for real!
             *pdwEffect = DROPEFFECT_NONE;
             return NOERROR;
          }

};

// declare a single instance of the DropTarget class
CoDrop codrop;

BOOL CALLBACK 
DlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
   switch (message)
      { 
      case WM_INITDIALOG:
        // bind the drop target to the dialog
        codrop.SetHwnd(hwnd);
        RegisterDragDrop(hwnd, &codrop);
        return TRUE;
      case WM_COMMAND:
        if (LOWORD(wParam) = = IDCANCEL)
           EndDialog(hwnd, IDCANCEL);
        return TRUE;
      case WM_DESTROY:
        // unbind the drop target from the dialog
        RevokeDragDrop(hwnd);
        return FALSE;
      }

   return FALSE;
}

// the standard WinMain for an Applet
int WINAPI 
WinMain(HINSTANCE hinstance, HINSTANCE, LPSTR, int)
{
   OleInitialize(0);
   DialogBox(hinstance, MAKEINTRESOURCE(IDD_DIALOG1), 0, DlgProc);
   OleUninitialize();
   return 0;
}


Figure 7   GetObject


LPDISPATCH GetObject(LPCOLESTR szFileName, 
                     LPCOLESTR szProgID)
{
  if (szFileName) {
    // implementation using file moniker
  }
  else {
    CLSID clsid;
    CLSIDFromProgID(szProgID, &clsid);
    LPUNKNOWN punk;
    LPDISPATCH pdisp = 0;
    HRESULT hr = GetActiveObject(clsid, 0, &punk);
    if (SUCCEEDED(hr)) {
      punk->QueryInterface(IID_IDispatch, 
                           (void**)&pdisp);
      punk->Release();
    }
    return pdisp;
  }
}


Figure 8   SharedSortedDWORDArray Interface

SSDA.H


#ifndef _SSDA_H
#define _SSDA_H

class SharedSortedDWORDArray {
public:
      SharedSortedDWORDArray(LPCTSTR szName);
      ~SharedSortedDWORDArray(void);

      BOOL Insert(DWORD id);
      void Remove(DWORD id);
      BOOL IsTop (DWORD id);

private:
      HANDLE m_hsection;
      HANDLE m_hmutex;
      DWORD *m_pdwCount;
      DWORD *m_pdwIDs; 
      enum { MAX_DWORDS = 1000 };
};

#endif

SSDA.CPP

#include "stdafx.h"
#include "SSDA.h"

SharedSortedDWORDArray::SharedSortedDWORDArray(LPCTSTR szName)
:     m_hsection(0),
      m_hmutex(0),
      m_pdwCount(0),
      m_pdwIDs(0)
{
  TCHAR szMutexName[64];
  TCHAR szSectionName[64];

// synthesize a mutex and section name
  wsprintf(szMutexName, __TEXT("%s_Mtx"), szName);
  wsprintf(szSectionName, __TEXT("%s_Scn"), szName);

// create/open the Mutex
      m_hmutex = CreateMutex(0, TRUE, szMutexName);
      BOOL bFirstApp = GetLastError() != ERROR_ALREADY_EXISTS;
      
// create/open the section object
      m_hsection = CreateFileMapping(HANDLE(0xFFFFFFFF),

0,
PAGE_READWRITE,
0, 
sizeof(DWORD) * (MAX_DWORDS + 1),
szSectionName);

// the first dword in the section will contain the array size
      m_pdwCount = (DWORD*)MapViewOfFile(m_hsection, FILE_MAP_ALL_ACCESS,
                                         0, 0, 0);

// dwords 2 - N will contain the array
      m_pdwIDs = m_pdwCount + 1;

// first thread inits the count to zero
      if (bFirstApp)
          {
           *m_pdwCount = 0;
           ReleaseMutex(m_hmutex);
          }
}

SharedSortedDWORDArray::~SharedSortedDWORDArray(void)
{
// release all objects alloced in constructor
      if (m_pdwCount)
            UnmapViewOfFile(m_pdwCount);
      if (m_hsection)
            CloseHandle(m_hsection);
      if (m_hmutex)
            CloseHandle(m_hmutex);
}

BOOL 
SharedSortedDWORDArray::Insert(DWORD id)
{
      BOOL result = FALSE;
// lock the array
      WaitForSingleObject(m_hmutex, INFINITE);

// remove id to avoid duplicates
      Remove(id);

// insert at end of array
      if (*m_pdwCount < MAX_DWORDS) 
         {
          m_pdwIDs[*m_pdwCount] = id;
          (*m_pdwCount)++;      
          result = TRUE;
            }

// unlock the array
      ReleaseMutex(m_hmutex);        
      return result;
}

void 
SharedSortedDWORDArray::Remove(DWORD id)
{
// lock the array
      WaitForSingleObject(m_hmutex, INFINITE);

// search array for id and remove if found
      for (DWORD i = 0; i < *m_pdwCount; i++)
          if (m_pdwIDs[i] = = id)
             {
              MoveMemory(m_pdwIDs + i, m_pdwIDs + i + 1, sizeof(DWORD) * 
                         (*m_pdwCount - 1 - i));
              (*m_pdwCount)--;
              break;
             }

// unlock the array
      ReleaseMutex(m_hmutex);        
}

BOOL 
SharedSortedDWORDArray::IsTop(DWORD id)
{
// lock the array
      WaitForSingleObject(m_hmutex, INFINITE);
      BOOL result = FALSE;

// test last element against id
      if (*m_pdwCount)
          result = (id = = m_pdwIDs[(*m_pdwCount) - 1]);

// unlock array
      ReleaseMutex(m_hmutex);        
      return result;
}


Figure 9   Arbitrator Interface

ARB.H


#ifndef _ARB_H
#define _ARB_H

#include "SSDA.h"

extern const UINT WM_ACTIVECHANGING;

class Arbitrator {
public:

      Arbitrator(REFCLSID rclsid, LPCTSTR szName);
      ~Arbitrator(void);

// register and revoke an object
      void RegisterObject(LPUNKNOWN punk);
      void RevokeObject(LPUNKNOWN punk);

// inform arbitrator when thread is going UI foreground/background
      void SuspendApp(void);
      void ResumeApp(void);

// inform arbitrator that WM_ACTIVECHANGING message
      void ActiveChanging(void);

private:
// used internally to broadcast WM_ACTIVECHANGING message
      void PostChangeMessage(void);

      DWORD m_dwReg;          // the key used by RegisterActiveObject
      BOOL  m_bIsRegistered;  // is our object actually registered?
      LPUNKNOWN m_punk;       // our object
      const CLSID m_clsid;    // our CLSID

      SharedSortedDWORDArray m_threadIds;  // the thread id array
};


#endif

ARB.CPP

#include "stdafx.h"
#include "Arb.h"

const UINT 
WM_ACTIVECHANGING = RegisterWindowMessage(__TEXT("WM_ACTIVECHANGING"));

Arbitrator::Arbitrator(REFCLSID rclsid, LPCTSTR szName)
    : m_dwReg(0),
      m_bIsRegistered(FALSE),
      m_punk(0),
      m_clsid(rclsid),
      m_threadIds(szName)
{
}

Arbitrator::~Arbitrator(void)
{
      RevokeObject(m_punk);
}

// internal function to broadcast change message
void 
Arbitrator::PostChangeMessage(void)
{
      PostMessage(HWND_BROADCAST,
                  WM_ACTIVECHANGING,
                  0, 0);
}

// called when UI code wants its object to become the active object
void 
Arbitrator::RegisterObject(LPUNKNOWN punk)
{
// revoke current object if registered
      if (m_bIsRegistered)
          RevokeActiveObject(m_dwReg, 0);

// cache punk as current object
      m_punk = punk;
      m_bIsRegistered = FALSE;

// we are assuming that our thread is the foreground thread, so it
// is safe to push ourselves to the head of the array and actually register

      if (m_threadIds.Insert(GetCurrentThreadId()))
          m_bIsRegistered = SUCCEEDED(::RegisterActiveObject(m_punk,m_clsid,0,
                                                             &m_dwReg));
      else
            m_bIsRegistered = FALSE;                     
}

// called when UI code wants its object no longer be active
void 
Arbitrator::RevokeObject(LPUNKNOWN punk)
{
// only revoke if punk is actually registered
      if (m_bIsRegistered && m_punk = = punk) 
         {
          RevokeActiveObject(m_dwReg, 0);
          m_bIsRegistered = FALSE;
          m_punk = 0;
          m_threadIds.Remove(GetCurrentThreadId());
          PostChangeMessage();
         }
}

// called when UI code when thread loses foreground status
void 
Arbitrator::SuspendApp(void)
{
// broadcast that the activation state has changed
      PostChangeMessage();
}

// called when UI code when thread gains foreground status
void 
Arbitrator::ResumeApp(void)
{
// promote this thread to the head of the array and
// broadcast that the activation state has changed
      m_threadIds.Insert(GetCurrentThreadId());
      PostChangeMessage();
}

// called when UI code receives notification that activation status has changed
void 
Arbitrator::ActiveChanging(void)
{
// if we are now the foreground thread, register ourselves
      if (m_threadIds.IsTop(GetCurrentThreadId()))
         {
          if (!m_bIsRegistered && m_punk)
              m_bIsRegistered = SUCCEEDED(RegisterActiveObject(m_punk,m_clsid,0,
                                                               &m_dwReg));
         }
// if we are not the foreground thread, we need to revoke our object
// to make way for the new foreground thread
      else 
         if (m_bIsRegistered)
            {
             RevokeActiveObject(m_dwReg, 0);
             m_bIsRegistered = FALSE;
            }
}


Figure 10   Text Data Types

  Normal OLE2ANSI UNICODE
CHAR char char char
WCHAR wchar_t wchar_t wchar_t
TCHAR char char wchar_t
OLECHAR wchar_t char wchar_t
LPSTR char* char* char*
LPWSTR wchar_t* wchar_t* wchar_t*
LPTSTR char* char* wchar_t*
LPOLESTR wchar_t* char* wchar_t*
LPCSTR const char* const char* const char*
LPCWSTR const wchar_t* const wchar_t* const wchar_t*
LPCTSTR const char* const char* const wchar_t*
LPCOLESTR const wchar_t* const char* const wchar_t*
__TEXT("x") "x" "x" L"x"
OLESTR("x") L"x" "x" L"x"


Figure 11   S816.H


#ifndef _S816_H
#define _S816_H

// String16 ////////////////////////////////////////////////////////

// Shim class that converts both 8-bit (foreign) and
// 16-bit (native) strings to 16-bit wideness

class String16 {
public:
// native and foreign constructors
      String16(const char *p8);
      String16(const wchar_t *p16);

// non-virtual destructor (this class is concrete)
  ~String16(void);

// native conversion operator
  operator const wchar_t * (void) const;

private:
// native wideness string
      wchar_t *m_sz;
// is foreign??
      BOOL m_bIsForeign;

// protect against assignment!
  String16(const String16&);
  String16& operator=(const String16&);
};

// native constructor is a pass-through
inline String16::String16(const wchar_t *p16) 
: m_sz((wchar_t *)p16), m_bIsForeign(FALSE) 
{ 
}

// simply give out the native wideness string 
inline String16::operator const wchar_t * (void) const 
{
  return m_sz;
}

// foreign constructor requires allocation of a native
// string and conversion
inline String16::String16(const char *p8)
: m_bIsForeign(TRUE) 
{
// calculate string length
  size_t len = strlen(p8);

// calculate required buffer size (some characters may
// already occupy 16-bits under DBCS)
  size_t size = mbstowcs(0, p8, len) + 1;

// alloc native string and convert
  if (m_sz = new wchar_t[size])
    mbstowcs(m_sz, p8, size);
}

// delete native string only if synthesized in foreign constructor
inline String16::~String16(void) {
  if (m_bIsForeign) 
    delete[] m_sz;
}


// String8 /////////////////////////////////////////////////////////

// Shim class that converts both 8-bit (native) and
// 16-bit (foreign) strings to 8-bit wideness

class String8 {
public:
// native and foreign constructors
      String8(const char *p8);
      String8(const wchar_t *p16);

// non-virtual destructor (this class is concrete)
  ~String8(void);

// native conversion operator
  operator const char * (void) const;

private:
// native wideness string
      char *m_sz;
// is foreign??
      BOOL m_bIsForeign;

// protect against assignment!
  String8(const String8&);
  String8& operator=(const String8&);
};

// native constructor is a pass-through
inline String8::String8(const char *p8) 
: m_sz((char *)p8), // casting away constness ONLY FOR CONVENIENCE!
  m_bIsForeign(FALSE) 
{ 
}

// simply give out the native wideness string 
inline String8::operator const char * (void) const 
{
  return m_sz;
}

// foreign constructor requires allocation of a native
// string and conversion
inline String8::String8(const wchar_t *p16)
: m_bIsForeign(TRUE) 
{
// calculate string length
  size_t len = wcslen(p16);

// calculate required buffer size (some characters may
// require more than one byte under DBCS)
  size_t size = wcstombs(0, p16, len) + 1;

// alloc native string and convert
  if (m_sz = new char[size])
    wcstombs(m_sz, p16, size);
}

// delete native string only if synthesized in foreign constructor
inline String8::~String8(void) {
  if (m_bIsForeign) 
    delete[] m_sz;
}

// Conditional Typedefs for Win32 and OLE Text Data Types ////////////////////

// typedef OLESTRCVAR to emulate the OLESTR 
// macro (converts any string at runtime instead 
// of simply changing layout of string literal at
// compile-time).

#ifdef OLE2ANSI
typedef String8 OLESTRCVAR;
#else
typedef String16 OLESTRCVAR;
#endif


// typedef __TEXTCVAR to emulate the __TEXT
// macro (converts any string at runtime instead 
// of simply changing layout of string literal at
// compile-time).


#ifdef UNICODE
typedef String16 __TEXTCVAR;
#else
typedef String8 __TEXTCVAR;
#endif

#endif