2011-01-22 12 views
17

Estoy un poco loco y quisiera recibir algunos consejos sobre cómo hacerlo.Hosting Silverlight en C++

Básicamente lo que quiero hacer es poder renderizar y controlar Silverlight dentro de mi aplicación C++. Me gustaría algo como:

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

Es más fácil decirlo que hacerlo. He encontrado los siguientes artículos Host Silverlight Contorl in C++ y Communication between C++ Silverlight Host and Silverlight Application. Mi problema con estos es que el código provisto no parece funcionar cuando se compila en VS2010 y crean una ventana real en lugar de un control sin ventana. Además, lo que sucede no está muy bien explicado y me falta cierto conocimiento de COM y ATL.

También encontré this que parece tener una forma más simple de implementar el xcpcontrolhost que los artículos anteriores.

He encontrado información de referencia en msdn. Donde ISilverlightViewer parece bastante interesante para mis necesidades. Para permitir un control sin ventanas, creo que probablemente tenga que implementar algo como IOleInPlaceSiteWindowless?.

Sin embargo, estoy un poco sobre mi cabeza aquí y no estoy seguro de por dónde empezar. Me gustaría pedir algunos consejos sobre dónde debo comenzar y si tienes algún consejo general o experiencia con algo como esto.

EDITAR: ¿Algo que también sería interesante, aunque bastante secundario, sería si tal implementación pudiera hacerse independiente de la plataforma?

EDIT2: He modificado el código en "TestProject" de uno de los artículos anteriores. Intenté eliminar el código redundante y lo corrigí para que funcione en VS2010 (según la respuesta a continuación). Puede encontrarlo here.

Edit3:

he tratado de implementar una clase XcpControlHost sin ventanas. Miré el código en CAxHostWindow e intenté volver a crearlo. La razón por la que no uso CAxHostWindow para crear un control sin ventana es que no admite alfa.

Parece compilar bien, sin embargo, cuando llamo a DrawControl solo obtengo un marco negro.

XcpContorlHost.h XcpControlHost.cpp

Alguna idea de lo que podría estar mal?

EDIT4: Estoy dando un paso atrás. Uso el código de "TestProject" Deseo modificar CreateXcpControl (HWND hWnd) para poder tomar hWnd == nullptr (sin ventana) y usar OleDraw para dibujar el control en la memoria.

Soo lo que traté de hacer es simplemente omitir la llamada a "AttachControl" y llamar directamente a "ActivateXcpControl". Y reemplacé GetClientRect por valores codificados.

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 
} 

Crío XcpControlHost el interior de un hilo con 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_) 
      PostQuitMessage(0); 

     if(msg.message == WM_USER + 1) // Sent from app 
      OleDraw(host_->m_pUnKnown, DVASPECT_CONTENT, targetDC, 0); 

     TranslateMessage(&msg); 
     DispatchMessage(&msg); 
    } 
} 

Y entonces corro el mensaje de ventanas bucle habitual manejador con GetMessage, TranslateMessage y DispatchMessage.

Sin embargo, todavía todo lo que obtengo es negrura. ¿Qué estoy haciendo mal?

Me parece conseguir E_FAIL de la siguiente llamada en "ActivateXcpControl":

hr = m_spOleObject->DoVerb(OLEIVERB_INPLACEACTIVATE, NULL, spClientSite, 0, NULL, &m_rcPos); 
+1

+1 para este buen tema. También necesito saber sobre esto ... cuando intentaba hacer estas cosas: http://stackoverflow.com/questions/4744522/handle-to-silverlight-usercontrol ... y http://stackoverflow.com/questions/4758568/deploying-xbap-with-win32-dll – Nawaz

+0

¿Qué versión de Silverlight? –

+0

Lo más preferiblemente posible. – ronag

Respuesta

8

me han puesto a prueba el proyecto de código disponible aquí: http://www.codeproject.com/KB/atl/Host_Silverlight_In_ATL.aspx en Visual Studio 2010. Esto es lo que se necesita para hacer que funcione:

  • carga en los términos de VS 2010 y convertirlo en VS 2010 (no se preocupan por backupgs registros, etc ...)
  • eliminar el evento de registro posterior a la generación ("$ (TargetPath)"/RegServer) , solo se usa para el registro de componentes COM dentro del proyecto. También puede dejar ese evento y simplemente olvidarse del error si lo prefiere.
  • Tuve que hacer un pequeño cambio en XcpControlHost.cpp.

Aquí es el cambio:

HRESULT CXcpControlHost::CreateXcpControl(HWND hWnd) 
{ 
    AtlAxWinInit(); 
    CoInitialize(NULL); // add this line to initialize COM 
    ... 
} 

y funciona (Estoy utilizando Windows 7 con Silverlight 4).

Este es realmente el camino a seguir.

Sin embargo, una observación: en el ejemplo, el autor no utiliza el archivo oficial xcpctrl.idl disponible desde aquí: http://msdn.microsoft.com/en-us/library/cc296246(VS.95).aspx. En su lugar, redefine todas las interfaces y GUID, y esto no es necesario. Simplemente puede agregar xcpctrl.idl a Visual Studio 2010 y compilar, esto activará el compilador MIDL que creará los 3 archivos siguientes: xcpctrl_h.h, xcpctrl_i.c, xcpctrl_p.c. Una vez que se compilan, puede agregarlos al proyecto.

+0

Buen trabajo. He agregado un enlace a un archivo donde he arreglado el proyecto "TestProject" de acuerdo con su sugerencia. – ronag

+0

Un problema con este ejemplo es que no puede enviar mensajes de teclado al control de host de Silverlight. ¿Cómo solucionar esto? –

+0

@IslamYahiatene - Publicar otra pregunta –