// // axhostwnd.cpp: ActiveX control host window // (For ts activeX control) // // Copyright Microsoft Corportation 2000 // (nadima) // #include "stdafx.h" #define TRC_GROUP TRC_GROUP_UI #define TRC_FILE "axhostwnd.cpp" #include #define TSC_AX_HOSTWND_CLS TEXT("TSCAXHOST") #define TSC_CONTROL_DLL TEXT("mstscax.dll") #include "axhostwnd.h" #ifndef OS_WINCE #include "ntverp.h" #else #include "ceconfig.h" #endif CAxHostWnd::CAxHostWnd(CContainerWnd* pParentWnd) : _pParentWnd(pParentWnd) { DC_BEGIN_FN("CAxHostWnd"); _hWnd = NULL; _pTsc = NULL; _cRef = 1; _hLib = NULL; _piEventSink = NULL; _piOleClientSite = NULL; _piOleInPlaceSiteEx = NULL; _piOleObject = NULL; _piOleInPlaceActiveObject = NULL; _piOleInPlaceObject = NULL; _dwConCookie = 0; TRC_ASSERT(_pParentWnd,(TB,_T("_pParentWnd not set"))); DC_END_FN(); } CAxHostWnd::~CAxHostWnd() { DC_BEGIN_FN("~CAxHostWnd"); TRC_ASSERT(_piEventSink == NULL,(TB,_T("Exit without cleanup"))); TRC_ASSERT(_piOleClientSite == NULL,(TB,_T("Exit without cleanup"))); TRC_ASSERT(_piOleInPlaceSiteEx == NULL,(TB,_T("Exit without cleanup"))); TRC_ASSERT(_piOleObject == NULL,(TB,_T("Exit without cleanup"))); TRC_ASSERT(_piOleInPlaceActiveObject == NULL,(TB,_T("Exit without cleanup"))); TRC_ASSERT(_piOleInPlaceObject == NULL,(TB,_T("Exit without cleanup"))); TRC_ASSERT(_pTsc == NULL,(TB,_T("Exit without cleanup"))); DC_END_FN(); } STDMETHODIMP CAxHostWnd::QueryInterface( REFIID riid, void ** ppv ) { DC_BEGIN_FN("QueryInterface"); TRC_ASSERT(ppv != NULL,(TB,_T("ppv null"))); TRC_ASSERT(_piOleClientSite != NULL, (TB,_T("QI needs IOleClientSite object"))); TRC_ASSERT(_piOleInPlaceSiteEx != NULL, (TB,_T("QI needs _piOleInPlaceSiteEx object"))); if (ppv) { *ppv = NULL; } else { return E_INVALIDARG; } if (IID_IUnknown == riid) *ppv = this; else if (IID_IOleClientSite == riid) *ppv = (void *)_piOleClientSite; else if (IID_IOleInPlaceSiteEx == riid) *ppv = (void *)_piOleInPlaceSiteEx; if (NULL != *ppv) { ((LPUNKNOWN)*ppv)->AddRef(); return NOERROR; } DC_END_FN(); return ResultFromScode(E_NOINTERFACE); } STDMETHODIMP_(ULONG) CAxHostWnd::AddRef(void) { return InterlockedIncrement(&_cRef); } STDMETHODIMP_(ULONG) CAxHostWnd::Release(void) { if (0L != InterlockedDecrement(&_cRef)) { return _cRef; } delete this; return 0L; } CAxHostWnd::Init() { DC_BEGIN_FN("Init"); _piOleClientSite = new COleClientSite((IUnknown *)this); if (!_piOleClientSite) { Cleanup(); return FALSE; } _piOleClientSite->AddRef(); _piOleInPlaceSiteEx = new COleInPlaceSiteEx((IUnknown *)this); if (!_piOleInPlaceSiteEx) { Cleanup(); return FALSE; } _piOleInPlaceSiteEx->AddRef(); DC_END_FN(); return TRUE; } // // Create the child window that directly hosts the control // BOOL CAxHostWnd::CreateHostWnd(HWND hwndParent, HINSTANCE hInst) { DC_BEGIN_FN("CreateHostWnd"); #ifndef OS_WINCE WNDCLASSEX wndclass; #else WNDCLASS wndclass; #endif int hr = GetLastError(); #ifndef OS_WINCE wndclass.cbSize = sizeof (wndclass); #endif wndclass.style = 0; wndclass.lpfnWndProc = CAxHostWnd::StaticAxHostWndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = hInst; wndclass.hIcon = NULL; wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); wndclass.hbrBackground = (HBRUSH) GetStockObject(NULL_BRUSH); wndclass.lpszMenuName = NULL; wndclass.lpszClassName = TSC_AX_HOSTWND_CLS; #ifndef OS_WINCE wndclass.hIconSm = NULL; #endif #ifndef OS_WINCE if ((0 == RegisterClassEx(&wndclass)) && #else if ((0 == RegisterClass(&wndclass)) && #endif (ERROR_CLASS_ALREADY_EXISTS != GetLastError())) { TRC_ERR((TB,_T("RegisterClassEx failed: %d"),GetLastError())); return FALSE; } RECT rc; GetClientRect(hwndParent, &rc); _hWnd = CreateWindow(TSC_AX_HOSTWND_CLS, NULL, WS_CHILD, 0, 0, rc.right - rc.left, rc.bottom - rc.top, hwndParent, NULL, hInst, this); if (_hWnd) { // put a reference to the current object into the hwnd // so we can access the object from the WndProc SetWindowLongPtr(_hWnd, GWLP_USERDATA, (LONG_PTR)this); // put the newly created hwnd into the client site objects also TRC_ASSERT(_piOleInPlaceSiteEx, (TB,_T("Don't create window until container is created"))); TRC_ASSERT(_pTsc && _piEventSink, (TB,_T("Shouldn't create window until Control is instantiated"))); if (_piOleInPlaceSiteEx) { _piOleInPlaceSiteEx->SetHwnd(_hWnd); } // // Show the control // hr = _piOleObject->DoVerb(OLEIVERB_PRIMARY, NULL, _piOleClientSite, 0, _hWnd, &rc); TRC_ASSERT(SUCCEEDED(hr),(TB,_T("DoVerb OLEIVERB_PRIMARY failed: %d"),hr)); if(SUCCEEDED(hr)) { ShowWindow(_hWnd, SW_SHOWNORMAL); return TRUE; } else { return FALSE; } } else { TRC_ERR((TB,_T("CreateHost wnd failed"))); return FALSE; } DC_END_FN(); } // // return one of // AXHOST_SUCCESS, // ERR_AXHOST_DLLNOTFOUND, // ERR_AXHOST_VERSIONMISMATCH // ERR_AXHOST_ERROR // so caller can display appropriate error msg // INT CAxHostWnd::CreateControl(IMsRdpClient** ppTscCtl) { DC_BEGIN_FN("CreateControl"); LPFNGETTSCCTLVER pfnDllGetTscCtlVer = NULL; LPFNGETCLASSOBJECT pfnDllGetClassObject = NULL; IClassFactory *piClassFactory = NULL; IConnectionPointContainer *piConnectionPointContainer = NULL; IConnectionPoint *piConnectionPoint = NULL; TRC_ASSERT(ppTscCtl,(TB,_T("ppTscCtl param is null"))); if(!ppTscCtl) { return ERR_AXHOST_ERROR; } // get an interface to the control without CoCreateInstance _hLib = LoadLibrary(TSC_CONTROL_DLL); if (_hLib == NULL) { TRC_ABORT((TB, _T("LoadLibrary of the control failed"))); return ERR_AXHOST_DLLNOTFOUND; } // // First do a version check to ensure ctl and shell match // pfnDllGetTscCtlVer = (LPFNGETTSCCTLVER)GetProcAddress(_hLib, CE_WIDETEXT("DllGetTscCtlVer")); if(NULL == pfnDllGetTscCtlVer) { TRC_ABORT((TB, _T("GetProcAddress (DllGetTscCtlVer) failed"))); return ERR_AXHOST_ERROR; } DWORD dwCtlVer = pfnDllGetTscCtlVer(); #ifndef OS_WINCE DWORD dwShellVer = VER_PRODUCTVERSION_DW; #else DWORD dwShellVer = CE_TSC_BUILDNO; #endif TRC_ASSERT(dwShellVer == dwCtlVer, (TB,_T("Control and shell versions do not match"))); if(dwShellVer != dwCtlVer) { return ERR_AXHOST_VERSIONMISMATCH; } pfnDllGetClassObject = (LPFNGETCLASSOBJECT)GetProcAddress(_hLib, CE_WIDETEXT("DllGetClassObject")); if (NULL == pfnDllGetClassObject) { TRC_ABORT((TB, _T("GetProcAddress failed"))); return ERR_AXHOST_ERROR; } if (FAILED(pfnDllGetClassObject(CLSID_MsRdpClient, IID_IClassFactory, (void **)&piClassFactory))) { TRC_ABORT((TB, _T("pfnDllGetClassObject failed"))); return ERR_AXHOST_ERROR; } if (FAILED(piClassFactory->CreateInstance(NULL, IID_IMsRdpClient, (void **)&_pTsc))) { piClassFactory->Release(); TRC_ERR((TB, _T("CreateInstance failed"))); return ERR_AXHOST_ERROR; } piClassFactory->Release(); // set up our notification event sink if (FAILED(_pTsc->QueryInterface(IID_IConnectionPointContainer, (void **)&piConnectionPointContainer))) { TRC_ABORT((TB, _T("Couldn't find IConnectionPointContainer"))); return ERR_AXHOST_ERROR; } if (FAILED(piConnectionPointContainer->FindConnectionPoint( DIID_IMsTscAxEvents, &piConnectionPoint))) { piConnectionPointContainer->Release(); TRC_ABORT((TB, _T("Couldn't find ConnectionPoint for Event Sink"))); return ERR_AXHOST_ERROR; } piConnectionPointContainer->Release(); _piEventSink = new CEventSink(_pParentWnd); if (!_piEventSink) { piConnectionPoint->Release(); TRC_ABORT((TB, _T("Unable to create event sink"))); return ERR_AXHOST_ERROR; } _piEventSink->AddRef(); if (FAILED(piConnectionPoint->Advise((IUnknown *)_piEventSink, &_dwConCookie))) { piConnectionPoint->Release(); // // we have to release and clean up this pointer here in case of failure // because cleanup code assumes that existence of this pointer means // successful advise, and it tries to do the full Unadvise business in // that case _piEventSink->Release(); _piEventSink = NULL; TRC_ABORT((TB, _T("Unable to advise ConnectionPoint of Event Sink"))); return ERR_AXHOST_ERROR; } piConnectionPoint->Release(); if ((FAILED(_pTsc->QueryInterface(IID_IOleObject, (void **)&_piOleObject))) || (FAILED(_pTsc->QueryInterface(IID_IOleInPlaceActiveObject, (void **)&_piOleInPlaceActiveObject)))) { TRC_ABORT((TB, _T("QI for IOleObject and IOleInPlaceObject failed"))); return ERR_AXHOST_ERROR; } if((FAILED(_pTsc->QueryInterface(IID_IOleInPlaceObject, (void **)&_piOleInPlaceObject)))) { TRC_ABORT((TB, _T("QI for IID_IOleInPlaceObject failed"))); return ERR_AXHOST_ERROR; } if (FAILED(_piOleObject->SetClientSite(_piOleClientSite))) { TRC_ABORT((TB, _T("Couldn't Set Client Site"))); return ERR_AXHOST_ERROR; } *ppTscCtl = _pTsc; _pTsc->AddRef(); DC_END_FN(); return AXHOST_SUCCESS; } BOOL CAxHostWnd::Cleanup() { DC_BEGIN_FN("Cleanup"); if (_piEventSink) { TRC_ASSERT(_pTsc, (TB,_T("Event sink can't exist without tsc pointer!"))); if (_pTsc) { IConnectionPointContainer *piConnectionPointContainer = NULL; IConnectionPoint *piConnectionPoint = NULL; if (FAILED(_pTsc->QueryInterface(IID_IConnectionPointContainer, (void **)&piConnectionPointContainer))) { TRC_ABORT((TB,_T("Couldn't find IConnectionPointContainer"))); } else { if (FAILED(piConnectionPointContainer->FindConnectionPoint( DIID_IMsTscAxEvents, &piConnectionPoint))) { TRC_ABORT((TB,_T("Couldn't find connection point for ev sink"))); } else { if (FAILED(piConnectionPoint->Unadvise(_dwConCookie))) { TRC_ERR((TB,_T("Unadvise connection point failed!"))); } piConnectionPoint->Release(); } piConnectionPointContainer->Release(); } } _piEventSink->Release(); _piEventSink = NULL; } if (_piOleInPlaceActiveObject) _piOleInPlaceActiveObject->Release(); if (_piOleObject) _piOleObject->Release(); if (_pTsc) _pTsc->Release(); if (_piOleInPlaceSiteEx) _piOleInPlaceSiteEx->Release(); if (_piOleClientSite) _piOleClientSite->Release(); if(_piOleInPlaceObject) _piOleInPlaceObject->Release(); if (_hWnd && IsWindow(_hWnd)) DestroyWindow(_hWnd); _piEventSink = NULL; _piOleInPlaceSiteEx = NULL; _piOleClientSite = NULL; _pTsc = NULL; _piOleObject = NULL; _piOleInPlaceActiveObject = NULL; _piOleInPlaceObject = NULL; if (_hLib) { LPFNCANUNLOADNOW pfnDllCanUnloadNow = (LPFNCANUNLOADNOW) GetProcAddress(_hLib, CE_WIDETEXT("DllCanUnloadNow")); if (pfnDllCanUnloadNow == NULL) { TRC_ABORT((TB,_T("GetProcAddress failed"))); FreeLibrary(_hLib); } else { if (S_OK == pfnDllCanUnloadNow()) { FreeLibrary(_hLib); } else { TRC_ABORT((TB,_T("Library not ready for unloading @ cleanup time"))); } } } _hLib = NULL; DC_END_FN(); return TRUE; } HWND CAxHostWnd::GetHwnd() { DC_BEGIN_FN("GetHwnd"); DC_END_FN(); return _hWnd; } IMsRdpClient* CAxHostWnd::GetTscCtl() { DC_BEGIN_FN("GetTscCtl"); if (_pTsc) { _pTsc->AddRef(); return _pTsc; } DC_END_FN(); return NULL; } LRESULT CALLBACK CAxHostWnd::StaticAxHostWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { DC_BEGIN_FN("StaticAxHostWndProc"); // pull out the pointer to the container object associated with this hwnd CAxHostWnd *piAxHst = (CAxHostWnd *)GetWindowLongPtr(hwnd, GWLP_USERDATA); if(piAxHst) { return piAxHst->AxHostWndProc( hwnd, uMsg, wParam, lParam); } else { return DefWindowProc (hwnd, uMsg, wParam, lParam); } DC_END_FN(); } LRESULT CALLBACK CAxHostWnd::AxHostWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { DC_BEGIN_FN("AxHostWndProc"); // // Reflect appropriate messages to control // switch (uMsg) { #ifndef OS_WINCE case WM_ACTIVATEAPP: { _piOleInPlaceActiveObject->OnFrameWindowActivate((BOOL)wParam); } break; #endif case WM_SETFOCUS: { HWND hwndObj; _piOleInPlaceActiveObject->GetWindow(&hwndObj); SetFocus(hwndObj); } break; case WM_PALETTECHANGED: // intentional fallthru case WM_QUERYNEWPALETTE: // intentional fallthru case WM_SYSCOLORCHANGE: { HWND hwndObj; // // Forward the message directly to the control // if (_piOleInPlaceActiveObject) { _piOleInPlaceActiveObject->GetWindow(&hwndObj); SendMessage(hwndObj, uMsg, wParam, lParam); } return 1; } break; case WM_SIZE: { int nWidth = LOWORD(lParam); int nHeight = HIWORD(lParam); RECT rcPos; rcPos.bottom = nHeight; rcPos.right = nWidth; rcPos.left = 0; rcPos.top = 0; if(_piOleInPlaceObject) _piOleInPlaceObject->SetObjectRects(&rcPos,&rcPos); HWND hwndObj; _piOleInPlaceActiveObject->GetWindow(&hwndObj); SendMessage(hwndObj, WM_SIZE, wParam, lParam); return 0; } break; default: { return DefWindowProc(hwnd, uMsg, wParam, lParam); } break; } DC_END_FN(); return 0; }