Click here to Skip to main content
Click here to Skip to main content

Host Silverlight Control in C++ using ATL

By , 18 May 2010
Rate this:
Please Sign up or sign in to vote.

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.

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.

// 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.

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.

// 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.

// 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.

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.

// 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.

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.

#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)

About the Author

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

Comments and Discussions

 
Questiongot a problem while using Microsoft.Maps.MapControl Pinmemberczq_cn3-Apr-12 22:40 
QuestionDear Sir. Pingroupumarhussain15-Nov-11 20:47 
AnswerRe: Dear Sir. PinmemberSyed Aftab Naqvi8-Mar-13 13:21 
AnswerRe: Dear Sir. Pingroupumarhussain10-Mar-13 23:16 
GeneralRender to texture Pinmembertexel3d6-Mar-11 23:37 
GeneralactiveX control (.ocx) for browser PinmemberC party18-Feb-11 20:18 
GeneralTextBox control doesn't work Pinmembersergiobuonanno2-Jan-11 2:43 
GeneralRe: TextBox control doesn't work PinmemberSyed Aftab Naqvi22-Jan-11 16:32 
GeneralRe: TextBox control doesn't work Pinmembersergiobuonanno8-Nov-11 21:48 
GeneralMy vote of 5 PinmemberMBOL13-Dec-10 3:24 
Great article. Very useful!
GeneralISilverlightViewer::CreateRenderTargetBitmap PinmemberMBOL9-Dec-10 23:05 
GeneralVisual Studio 2010 Pinmembersmini19-Oct-10 23:40 
GeneralRe: Visual Studio 2010 Pinmembercrystalicetpmc21-Nov-10 19:21 
GeneralRe: Visual Studio 2010 Pinmembertesttesttestest19-May-11 22:12 
GeneralRe: Visual Studio 2010 Pinmembercrystalicetpmc24-May-11 1:12 
GeneralMy vote of 5 Pinmemberziade1-Sep-10 19:15 
GeneralRe: My vote of 5 PinmemberSyed Aftab Naqvi22-Jan-11 16:29 
GeneralMFC ActiveX Control Implementation PinmemberJancona2620-May-10 16:56 
GeneralRe: MFC ActiveX Control Implementation PinmemberSyed Aftab Naqvi20-May-10 19:28 
GeneralRe: MFC ActiveX Control Implementation PinmemberJancona2621-May-10 3:47 
QuestionWhat about HTTP stack? Pinmemberartsd18-May-10 8:55 
Generalcool! Pinmemberradioman.lt18-May-10 8:28 
GeneralSilverlight C++ Implementation PinmemberCristian Adam18-May-10 6:39 
GeneralRe: Silverlight C++ Implementation PinmemberSyed Aftab Naqvi28-Jun-10 13:15 

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

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

| Advertise | Privacy | Mobile
Web04 | 2.8.140415.2 | Last Updated 18 May 2010
Article Copyright 2010 by Syed Aftab Naqvi
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid