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;
}
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;
}
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;
}
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;
}
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;
}
}
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;
}
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;
}
}
| 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" |
#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