Hosting Silverlight in C++
I'm a bit over my head here and would like some advice on how to go about.
Basicly what I want to do is to be able to render and control silverlight inside my C++ application. I would like something as:
class silverlight_host
{
public:
// Prio 1
silverlight_host(const std::string& filename); // Load a xap file
void draw(void* dest); // Draw with alpha to dest
std::pair<size_t, size_t> get_size(); // Need to know required size of dest
// Prio 2
bool is_dirty() const; // Check for dirty rect since last call to draw
void send_param(const std::string& param); // Send data to silverlight control or call functions. Alternative name maybe, call_function
void set_size(size_t width, size_t height); // Set the size of drawing.
// Prio 3
// Some more nice to have functions, although not as important as those above
double get_desired_frame_rate(); // The desired frame rate which is specified in xap
std::pair<size_t, size_t> get_desired_size(); // The desired size which is specified in xap
void tick(); // Tick a synchronous timeline, disable internal asynchronous timer
void set_param_callback(const std::function<void(const std::string&)>& callback); // Let the xap file call the application
};
Easier said than done. I have found the following articles Host Silverlight Contorl in C++ and Communication between C++ Silverlight Host and Silverlight Application. My problem with these are that the provided code doesn't seem to work when compiled in VS2010 and they create an actual window instead of a windowless control. Also what happens is not very well explained and I am somewhat lacking in COM and ATL knowledge.
I've also found this which seems to have a simpler way to implement the xcpcontrolhost than the articles above.
I have found some reference information on msdn. Where ISilverlightViewer seems quite interesting for my needs. In order to allow a windowless control I believe that I probably have to implement something like IOleInPlaceSiteWindowless?.
However, I am quite a bit over my head here and I'm unsure where to even begin. I'd like to ask some advice regarding where I should get started and if you have any general advice or experience with something like this?
EDIT: Something which would also be interesting, although quite secondary, would be if such an implementation could be made platform independent?
EDIT2: I have modified the code in "TestProject" from one of the articles above. I have tried to remove redundant code and fixed it so that it runs on VS2010 (according to answer below). You can find it here.
EDIT3:
I have tried to implement a windowless XcpControlHost class. I've looked at the code in CAxHostWindow and tried to re-create it. The reason I don't use CAxHostWindow to create a windowless control is that it does not support alpha.
It seems to compile fine, however when I call DrawControl I only get back a black frame.
XcpContorlHost.h XcpControlHost.cpp
Any ideas as to what might be wrong?
EDIT4: I'm taking a step back. I use the code from "TestProject" I want to modify CreateXcpControl(HWND hWnd) to be able to take hWnd == nullptr (windowless) and use OleDraw to draw the control to memory.
Soo what I tried to do is to simply bypass the call to "AttachControl" and directly call "ActivateXcpControl". And I've replaced GetClientRect by hard-coded values.
STDMETHODIMP XcpControlHost::AttachControl(IUnknown* pUnKnown, HWND hWnd)
{
assert(hWnd == nullptr);
ReleaseAll();
// Removed all hWnd related code
return ActivateXcpControl(pUnKnown);
}
HRESULT XcpControlHost::ActivateXcpControl(IUnknown* pUnKnown)
{
// Lots of code
// GetClientRect(&m_rcPos); // Remove this
m_rcPos.top = 0;
m_rcPos.left = 0;
m_rcPos.right = 720;
m_rcPos.bottom = 576;
// Lots of code
}
I create the XcpControlHost inside a thread with CoInitialize:
void run()
{
struct co_init
{
co_init(){CoInitialize(nullptr);}
~co_init(){CoUninitialize();}
} co;
if(FAILED(CComObject<XcpControlHost>::CreateInstance(&host_)))
throw std::exception("Failed to create XcpControlHost");
if(FAILED(host_->CreateXcpControl()))
throw std::exception("Failed to create XcpControl");
while(GetMessage(&msg, 0, 0, 0))
{
if(!is_running_)
PostQu开发者_运维技巧itMessage(0);
if(msg.message == WM_USER + 1) // Sent from app
OleDraw(host_->m_pUnKnown, DVASPECT_CONTENT, targetDC, 0);
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
And then I run the usual windows message handler loop with GetMessage, TranslateMessage and DispatchMessage.
However, still all I get is blackness. What am I doing wrong?
I seem to get E_FAIL from the following call in "ActivateXcpControl":
hr = m_spOleObject->DoVerb(OLEIVERB_INPLACEACTIVATE, NULL, spClientSite, 0, NULL, &m_rcPos);
I have tested the code project available here: http://www.codeproject.com/KB/atl/Host_Silverlight_In_ATL.aspx under Visual Studio 2010. Here is what's needed to make it work:
- load it under VS 2010 and convert it to VS 2010 (don't care about backupgs logs, etc...)
- remove the registration Post-Build Event ("$(TargetPath)" /RegServer), it's only used for registration of COM components inside the projet. You can also let that event and just forget about the error if you prefer.
- I had to do a small change in XcpControlHost.cpp.
Here is the change:
HRESULT CXcpControlHost::CreateXcpControl(HWND hWnd)
{
AtlAxWinInit();
CoInitialize(NULL); // add this line to initialize COM
...
}
And it works (I'm running Windows 7 with Silverlight 4).
This is really the way to go.
One remark though: In the sample, the author does not use the official xcpctrl.idl file available from here: http://msdn.microsoft.com/en-us/library/cc296246(VS.95).aspx . Instead he redefines all interfaces and GUID, and this is not needed. You can just add the xcpctrl.idl to a Visual Studio 2010 and compile, this will trigger the MIDL compiler that will create the 3 following files: xcpctrl_h.h, xcpctrl_i.c, xcpctrl_p.c. Once they are compiled, you can add them to the project.
精彩评论