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






4.82/5 (20投票s)
使用 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
实现。
从 CAxHostWindow
和 IXcpControlHost2
(在 .idl 文件中定义)继承 CXcpControlHost
。 CAxHostWindow
将提供托管服务,并且我们的应用程序将充当 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 日:初始发布