65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 ATL 在 C++ 中托管 Silverlight 控件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.82/5 (20投票s)

2010 年 5 月 18 日

CPOL

2分钟阅读

viewsIcon

178142

downloadIcon

1493

使用 ATL 在 C++ 中托管 Silverlight 控件。

引言

本文解释了如何使用 ATL/WTL 在 C++ 应用程序中托管 Microsoft Silverlight 控件,而无需使用浏览器(Internet Explorer/Firefox)。

大多数人认为 Silverlight 控件不能在 C++ 应用程序中使用。在本文中,我们将介绍如何托管 Silverlight 控件并在其中加载 Silverlight 内容 (.xap) 文件。

使用这种方法,我们可以使用 Silverlight 编写基于桌面的应用程序(无需使用 Internet Explorer/Firefox),并使用 Microsoft Silverlight 运行时进行分发,与 .NET Framework 相比,它的体积非常轻巧。

为什么我们需要在 C++ 应用程序中托管 Silverlight 控件? 为什么我们不直接使用 WPF? 这种方法提供了一种使用 Silverlight 用户界面制作桌面应用程序的方法,而无需使用完整的 .NET Framework。 我们只需要 Silverlight 运行时。

我正在使用 Silverlight 4.0 作为此示例。

COM 参考

Microsoft 为 Silverlight 控件提供了 COM 参考。 请查看 此 URL

创建 ATL 应用程序

创建一个 ATL 进程外(可执行)项目。 我使用“AtlProject”作为我的示例名称。

步骤 1

实现 IXcpControlHost2 接口。

打开 AtlProject.idl 文件,并使用以下 .idl 文件来实现 IXcpControlHost 以托管 Silverlight ActiveX 控件。 此 .idl 由 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;
    };
};

第二步

CXcpControlHost 实现。

CAxHostWindowIXcpControlHost2(在 .idl 文件中定义)继承 CXcpControlHostCAxHostWindow 将提供托管服务,并且我们的应用程序将充当 Silverlight 控件宿主。

class CXcpControlHost :
    public CAxHostWindow,
    public IXcpControlHost2
{

步骤 3

我们应该重写 QueryService 并在宿主查询时返回 XcpControlHost/IXCPControlHost2/IXCPControlHost3 接口。 当 Silverlight Activex 控件被创建时,宿主会立即查询它。

// 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;
}

步骤 4

控件创建代码。

// 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;
}

步骤 5

Silverlight 控件的属性包类。 此类用于设置控件属性,例如背景颜色、设置 .xap 文件名等等。

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

步骤 6

实现 IXcpControl2::GetBaseUrl 以设置加载 .xap 文件和其他程序集的基目录。

// 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;
}

步骤 7

在您的 ATL 项目中插入一个对话框。 在新创建的对话框中放置一个 static 控件。 在 OnInitDialog() 中,通过传递 static 控件窗口的句柄来调用 CreateXcpControl(...)

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

我们已经完成了 Silverlight 控件的托管。 现在是时候创建一个 Silverlight 应用程序,我们将它加载到 C++ 应用程序中。

注意

如果有人使用的是旧版 Silverlight,请将代码中的以下行更改为正确的 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")

结论

在本文中,我向您展示了如何在 C++ 应用程序中托管 Silverlight 控件。

希望本文在您将 Silverlight 控件托管在 C++ 应用程序中时能对您有所帮助。 下一步将是 Silverlight 应用程序和 C++ 中的 Silverlight 宿主之间的通信。 动态加载程序集等...

如有任何问题、意见和建议,请使用本文底部的评论区。

历史

  • 2010 年 5 月 18 日:初始发布
© . All rights reserved.