Click here to Skip to main content
15,860,861 members
Articles / Desktop Programming / ATL

Host Silverlight Control in C++ using ATL

Rate me:
Please Sign up or sign in to vote.
4.82/5 (20 votes)
18 May 2010CPOL2 min read 173.1K   1.4K   38   25
Hosting Silverlight control in C++ using ATL

Introduction

This article explains how we can host Microsoft Silverlight Control in a C++ application using ATL/WTL without using the browser (Internet Explorer/Firefox).

Mostly people think that Silverlight control can't be used in a C++ application. In this article, we will walk through hosting a Silverlight control and loading Silverlight contents (.xap) file in it.

Using this approach, we can write desktop based applications using Silverlight (without using Internet Explorer/Firefox) and distribute it using Microsoft Silverlight runtime which is very light weight as compared to the .NET Framework.

Why do we need to host the Silverlight Control in a C++ application? Why don't we use WPF right away? This approach gives you a way to make your desktop application with a Silverlight User Interface and without using the complete .NET Framework. We only need Silverlight runtime.

I am using Silverlight 4.0 for this sample.

Image 1

COM Reference

Microsoft provides COM reference for Silverlight control. Please take a look at this URL.

Create an ATL Application

Create an ATL out-of-process(executable) project. I used "AtlProject" name as my sample.

Step 1

Implement IXcpControlHost2 Interface.

Open AtlProject.idl file and use following .idl file in order to implement IXcpControlHost to host the Silverlight ActiveX control. This .idl is provided by Microsoft.

C++
// AtlProject.idl : IDL source for AtlProject
//
// This file will be processed by the MIDL tool to
// produce the type library (AtlProject.tlb) and marshalling code.

import "oaidl.idl";
import "ocidl.idl";
[
    object,
    uuid(2E340632-5D5A-427b-AC31-303F6E32B9E8),
    nonextensible,
    helpstring("IXcpControlDownloadCallback Interface"),
    pointer_default(unique)
]

interface IXcpControlDownloadCallback : IUnknown
{
    HRESULT OnUrlDownloaded(HRESULT hr, IStream* pStream);

};

[
    object,
    uuid(1B36028E-B491-4bb2-8584-8A9E0A677D6E),
    nonextensible,
    helpstring("IXcpControlHost Interface"),
    pointer_default(unique)
]

interface IXcpControlHost : IUnknown
{
    typedef enum
    {
        XcpHostOption_FreezeOnInitialFrame = 0x001,
        XcpHostOption_DisableFullScreen = 0x002,
        XcpHostOption_DisableManagedExecution = 0x008,
        XcpHostOption_EnableCrossDomainDownloads = 0x010,
        XcpHostOption_UseCustomAppDomain = 0x020,
        XcpHostOption_DisableNetworking = 0x040, 
        XcpHostOption_DisableScriptCallouts = 0x080,
        XcpHostOption_EnableHtmlDomAccess = 0x100,
        XcpHostOption_EnableScriptableObjectAccess = 0x200,
    } XcpHostOptions;

    HRESULT GetHostOptions([out, retval] DWORD* pdwOptions);
    HRESULT NotifyLoaded();
    HRESULT NotifyError([in] BSTR bstrError, [in] BSTR bstrSource, 
	[in] long nLine, [in] long nColumn);
    HRESULT InvokeHandler([in] BSTR bstrName, [in] VARIANT varArg1, 
	[in] VARIANT varArg2, [out, retval] VARIANT* pvarResult);
    HRESULT GetBaseUrl([out, retval] BSTR* pbstrUrl);
    HRESULT GetNamedSource([in] BSTR bstrSourceName, [out, retval] BSTR* pbstrSource);
    //
    // Called by Silverlight to allow a host to provide content for a specified URI. 
    // This is useful in cases where a resource would normally be loaded out of an 
    // XAP at runtime. At design time, no XAP exists, and
    // the host can provide content for that resource.
    //
    // This method can work synchronously or asynchronously. 
    // If the pCallback parameter is NULL, the host must
    // do the work synchronously and return the result in ppStream. 
    // If the pCallback parameter is non-NULL, the host
    // may do the work synchronously or asynchronously, 
    // invoking callback methods as defined by the 
    // IXcpControlDownloadCallback interface. 
    // If the host chooses to work asynchronously, the ppStream parameter is
    // ignored.
    //
    // The host should return S_FALSE if it cannot provide a resource 
    // for the requested URI, and S_OK on a
    // successful request.
    //
    HRESULT DownloadUrl([in] BSTR bstrUrl, 
	[in] IXcpControlDownloadCallback* pCallback, [out, retval] IStream** ppStream);
};

[
    object,
    uuid(fb3ed7c4-5797-4b44-8695-0c512ea27d8f),
    nonextensible,
    helpstring("IXcpControlHost2 Interface"),
    pointer_default(unique)
]

interface IXcpControlHost2 : IXcpControlHost
{
    HRESULT GetCustomAppDomain([out, retval] IUnknown** ppAppDomain);
    HRESULT GetControlVersion([out] UINT *puMajorVersion, [out] UINT *puMinorVersion);
};

[
    uuid(60074165-9D7F-43C9-B0A9-127FB2B8C267),
    version(1.0),
    helpstring("AtlProject 1.0 Type Library")
]

library AtlProjectLib
{
    importlib("stdole2.tlb");
    [
        uuid(1ADB6913-DD12-4F9E-95C8-46D4D01A8B65),
        helpstring("XcpControlHost Class")
    ]

    coclass XcpControlHost
    {
        [default] interface IXcpControlHost;
    };
};

Step 2

CXcpControlHost Implementation.

Inherit CXcpControlHost from CAxHostWindow and IXcpControlHost2 (defined in .idl file). CAxHostWindow will provide the hosting services and our application will act like a Silverlight control host.

C++
class CXcpControlHost :
    public CAxHostWindow,
    public IXcpControlHost2
{

Step 3

We should override QueryService and return the XcpControlHost/IXCPControlHost2/IXCPControlHost3 interface when host queries for it. Host queries for it the moment the Silverlight Activex Control is created.

C++
// XcpControlHost.h 
//IServiceProvider Implementation
STDMETHOD(QueryService)(REFGUID rsid, REFIID riid, void** ppvObj);

// XcpControlHost.cpp 
///////////////////////////////////////////////////////////////////////////////
// CXcpControlHost IServiceProvider Implementation
STDMETHODIMP CXcpControlHost::QueryService(REFGUID rsid, REFIID riid, void** ppvObj) 
{
    ATLASSERT(ppvObj != NULL);
    if (ppvObj == NULL)
        return E_POINTER;

    *ppvObj = NULL;
    HRESULT hr = E_NOINTERFACE;
    if ((rsid == IID_IXcpControlHost) && (riid == IID_IXcpControlHost)) 
    {
        ((IXcpControlHost*)this)->AddRef();
        *ppvObj = (IXcpControlHost*)this;
        hr = S_OK;
    }

    if ((rsid == IID_IXcpControlHost2) && (riid == IID_IXcpControlHost2)) 
    {
        ((IXcpControlHost2*)this)->AddRef();
        *ppvObj = (IXcpControlHost2*)this;
        hr = S_OK;
    }
    return hr;
}

Step 4

Control creation code.

C++
// XcpControlHost.h
// Infrastructure for control creation.
static HRESULT CreateXcpControl(HWND hwnd);
static HRESULT DestroyXcpControl();

// XcpControlHost.cpp
HRESULT CXcpControlHost::CreateXcpControl(HWND hWnd) 
{
    AtlAxWinInit();
    HRESULT hr;
    static const GUID IID_IXcpControl = 
    { 0x1FB839CC, 0x116C, 0x4C9B, { 0xAE, 0x8E, 0x3D, 0xBB, 0x64, 0x96, 0xE3, 0x26 }};
    static const GUID CLSID_XcpControl = 
    { 0xDFEAF541, 0xF3E1, 0x4c24, { 0xAC, 0xAC, 0x99, 0xC3, 0x07, 0x15, 0x08, 0x4A }};
    static const GUID IID_IXcpControl2 = 
    { 0x1c3294f9, 0x891f, 0x49b1, { 0xBB, 0xAE, 0x49, 0x2a, 0x68, 0xBA, 0x10, 0xcc }};
    
    hr = CoCreateInstance(CLSID_XcpControl, NULL, CLSCTX_INPROC_SERVER, 
	IID_IUnknown, (void**)&pUnKnown);
    if (SUCCEEDED(hr)) 
    {
        CComPtr<IUnknown> spUnkContainer;
        hr = CXcpControlHost::_CreatorClass::CreateInstance(NULL, 
		IID_IUnknown, (void**)&spUnkContainer);
        if (SUCCEEDED(hr)) 
        {
            CComPtr<IAxWinHostWindow> pAxWindow;
            spUnkContainer->QueryInterface(IID_IAxWinHostWindow, (void**)&pAxWindow);
            pUnKnown->QueryInterface(IID_IXcpControl2, (void**)&pControl);
            hr = pAxWindow->AttachControl(pUnKnown, hWnd); 
            hControlWindow = hWnd;
            IOleInPlaceActiveObject *pObj;
            hr = pControl->QueryInterface(IID_IOleInPlaceActiveObject, (void**)&pObj);
        }    
    }

    return hr;
}

HRESULT CXcpControlHost::DestroyXcpControl()
{
    HRESULT hr = S_OK;
    if (pControl)
    {
        pControl->Release();
    }
    if (pUnKnown)
    {
        pUnKnown->Release();
    }
    return hr;
}

Step 5

Property Bag class for Silverlight control. This class is used to set control properties like background color, setting up the .xap file name and many more.

C++
class CXcpPropertyBag:IPropertyBag
{
public:
	CXcpPropertyBag(){m_nRef=0;}
	~CXcpPropertyBag(){}
	HRESULT _stdcall QueryInterface(REFIID iid, void** ppvObject)
	{
		return S_OK;
	}

	ULONG _stdcall AddRef()
	{
		return ++m_nRef;
	}

	ULONG _stdcall Release()
	{
		if(--m_nRef == 0)
			delete this;
		return m_nRef;
	}
	ULONG m_nRef;
	STDMETHOD (Read)(LPCOLESTR pszPropName, VARIANT *pVar, IErrorLog *pErrorLog)
	{
		HRESULT hr = E_INVALIDARG;
		BSTR bstrValue = NULL;

		if (_wcsicmp(pszPropName, L"Source") == 0) 
		{
			bstrValue = SysAllocString(L"SilverlightTestApp.xap");
		} 
		else if (_wcsicmp(pszPropName, L"Background") == 0) 
		{
			bstrValue = SysAllocString(L"Gold");
		}
		else if (_wcsicmp(pszPropName, L"Windowless") == 0) 
		{
			V_VT(pVar) = VT_BOOL;
			V_BOOL(pVar) = VARIANT_FALSE;
			hr = S_OK;
		}
		if (bstrValue != NULL) 
		{
			V_VT(pVar) = VT_BSTR;
			V_BSTR(pVar) = bstrValue;
			hr = S_OK;
		}
		return hr;
	}

	STDMETHOD (Write)(LPCOLESTR pszPropName, VARIANT *pVar)
	{
		return S_OK;
	}
};

Step 6

Implement IXcpControl2::GetBaseUrl to set the base folder from where your .xap file and other assemblies will be loaded.

C++
// XcpControlHost.h 

STDMETHOD(GetBaseUrl)(BSTR* pbstrUrl);

// XcpControlHost.cpp
STDMETHODIMP CXcpControlHost::GetBaseUrl(BSTR* pbstrUrl) 
{
	CAtlString strPath;
	TCHAR pBuff[MAX_PATH];
	GetCurrentDirectory(MAX_PATH, pBuff);
	strPath = pBuff;
	strPath += "\\";
	*pbstrUrl = SysAllocString(strPath);
	return S_OK;
}

Step 7

Insert a dialog box in your ATL project. Place a static control in the newly created dialog. In OnInitDialog(), call CreateXcpControl(...) by passing the handle of static control window.

C++
CXcpControlHost::CreateXcpControl(GetDlgItem(IDC_STATIC_CONTROL).m_hWnd);

We are done with the hosting of Silverlight control. Now it's time to create a Silverlight app which we will load in a C++ application.

NOTE

If someone is using an older version of Silverlight, please change the following line in the code with the correct version of Silverlight.

C++
#import "libid:283C8576-0726-4DBC-9609-3F855162009A" version("4.0")
to
#import "libid:283C8576-0726-4DBC-9609-3F855162009A" version("3.0")
or 
#import "libid:283C8576-0726-4DBC-9609-3F855162009A" version("2.0")

Conclusion

In this article, I've shown you how to host a Silverlight control in a C++ application.

Hopefully, this article will help you a little bit when it comes to hosting Silverlight Control in C++ application. Next steps would be communication between a Silverlight application and a Silverlight host in C++. Loading assemblies dynamically, etc...

For questions, comments and remarks, please use the commenting section at the bottom of this article.

History

  • 18th May, 2010: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
Syed Aftab Hassan Naqvi

Comments and Discussions

 
Questiongot a problem while using Microsoft.Maps.MapControl Pin
czq_cn3-Apr-12 22:40
czq_cn3-Apr-12 22:40 
QuestionDear Sir. Pin
umarhussain15-Nov-11 20:47
umarhussain15-Nov-11 20:47 
AnswerRe: Dear Sir. Pin
Syed Aftab Naqvi8-Mar-13 13:21
Syed Aftab Naqvi8-Mar-13 13:21 
AnswerRe: Dear Sir. Pin
umarhussain10-Mar-13 23:16
umarhussain10-Mar-13 23:16 
GeneralRender to texture Pin
texel3d6-Mar-11 23:37
texel3d6-Mar-11 23:37 
GeneralactiveX control (.ocx) for browser Pin
C party18-Feb-11 20:18
C party18-Feb-11 20:18 
GeneralTextBox control doesn't work Pin
sergiobuonanno2-Jan-11 2:43
sergiobuonanno2-Jan-11 2:43 
GeneralRe: TextBox control doesn't work Pin
Syed Aftab Naqvi22-Jan-11 16:32
Syed Aftab Naqvi22-Jan-11 16:32 
Actually, C++ host blocking the keys in default message loop. Please introduce message loop in your C++ host and create your dialog using creat api instead of DoModal(). Hope it will work Smile | :)
GeneralRe: TextBox control doesn't work Pin
sergiobuonanno8-Nov-11 21:48
sergiobuonanno8-Nov-11 21:48 
GeneralRe: TextBox control doesn't work Pin
Member 1090024928-Jun-14 6:27
Member 1090024928-Jun-14 6:27 
GeneralMy vote of 5 Pin
MBOL13-Dec-10 3:24
MBOL13-Dec-10 3:24 
GeneralISilverlightViewer::CreateRenderTargetBitmap Pin
MBOL9-Dec-10 23:05
MBOL9-Dec-10 23:05 
GeneralVisual Studio 2010 Pin
smini19-Oct-10 23:40
smini19-Oct-10 23:40 
GeneralRe: Visual Studio 2010 Pin
crystalice543296121-Nov-10 19:21
crystalice543296121-Nov-10 19:21 
GeneralRe: Visual Studio 2010 Pin
testtesttestest19-May-11 22:12
testtesttestest19-May-11 22:12 
GeneralRe: Visual Studio 2010 Pin
crystalice543296124-May-11 1:12
crystalice543296124-May-11 1:12 
GeneralMy vote of 5 Pin
ziade1-Sep-10 19:15
ziade1-Sep-10 19:15 
GeneralRe: My vote of 5 Pin
Syed Aftab Naqvi22-Jan-11 16:29
Syed Aftab Naqvi22-Jan-11 16:29 
GeneralMFC ActiveX Control Implementation Pin
Jancona2620-May-10 16:56
Jancona2620-May-10 16:56 
GeneralRe: MFC ActiveX Control Implementation Pin
Syed Aftab Naqvi20-May-10 19:28
Syed Aftab Naqvi20-May-10 19:28 
GeneralRe: MFC ActiveX Control Implementation Pin
Jancona2621-May-10 3:47
Jancona2621-May-10 3:47 
QuestionWhat about HTTP stack? Pin
Arthur Dumas18-May-10 8:55
Arthur Dumas18-May-10 8:55 
Generalcool! Pin
radioman.lt18-May-10 8:28
radioman.lt18-May-10 8:28 
GeneralSilverlight C++ Implementation Pin
Cristian Adam18-May-10 6:39
Cristian Adam18-May-10 6:39 
GeneralRe: Silverlight C++ Implementation Pin
Syed Aftab Naqvi28-Jun-10 13:15
Syed Aftab Naqvi28-Jun-10 13:15 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.