2011-01-22 24 views
17

Sono un po 'fuori di testa qui e vorrei qualche consiglio su come fare.Hosting Silverlight in C++

In linea di principio ciò che voglio fare è essere in grado di eseguire il rendering e il controllo di Silverlight all'interno dell'applicazione C++. Vorrei qualcosa come:

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

Più facile a dirsi che a farsi. Ho trovato i seguenti articoli Host Silverlight Contorl in C++ e Communication between C++ Silverlight Host and Silverlight Application. Il mio problema con questi è che il codice fornito non sembra funzionare quando compilato in VS2010 e creano una finestra effettiva invece di un controllo senza finestra. Inoltre, ciò che accade non è spiegato molto bene e mi manca un po 'di conoscenza di COM e ATL.

Ho anche trovato this che sembra avere un modo più semplice per implementare xcpcontrolhost rispetto agli articoli precedenti.

Ho trovato alcune informazioni di riferimento su msdn. Dove ISilverlightViewer sembra abbastanza interessante per le mie esigenze. Per consentire un controllo senza finestre credo che probabilmente dovrò implementare qualcosa come IOleInPlaceSiteWindowless?.

Tuttavia, sono un po 'sopra la mia testa e non so da dove iniziare. Vorrei chiedere qualche consiglio su dove dovrei iniziare e se hai qualche consiglio generale o esperienza con qualcosa del genere?

EDIT: Qualcosa che sarebbe anche interessante, anche se piuttosto secondario, sarebbe se tale implementazione potesse essere resa indipendente dalla piattaforma?

EDIT2: Ho modificato il codice in "TestProject" da uno degli articoli precedenti. Ho provato a rimuovere il codice ridondante e l'ho risolto in modo che venga eseguito su VS2010 (in base alla risposta di seguito). Lo puoi trovare here.

Edit3:

ho cercato di implementare una classe XcpControlHost senza finestre. Ho esaminato il codice in CAxHostWindow e ho provato a ricrearlo. Il motivo per cui non utilizzo CAxHostWindow per creare un controllo senza finestra è che non supporta l'alpha.

Sembra compilare bene, tuttavia quando chiamo DrawControl torno solo una cornice nera.

XcpContorlHost.h XcpControlHost.cpp

Tutte le idee su quello che potrebbe essere sbagliato?

EDIT4: Sto facendo un passo indietro. Uso il codice da "TestProject" Voglio modificare CreateXcpControl (HWND hWnd) per poter prendere hWnd == nullptr (senza finestre) e usare OleDraw per disegnare il controllo in memoria.

Quindi, ciò che ho cercato di fare è semplicemente bypassare la chiamata a "AttachControl" e chiamare direttamente "ActivateXcpControl". E ho sostituito GetClientRect con valori hardcoded.

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 
} 

ho creare il XcpControlHost all'interno di una discussione 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); 
    } 
} 

E poi ho eseguito il ciclo gestore di finestre messaggio al solito con GetMessage, TranslateMessage e DispatchMessage.

Tuttavia, tutto ciò che ottengo è l'oscurità. Che cosa sto facendo di sbagliato?

mi sembra di ottenere E_FAIL dal seguente chiamata in "ActivateXcpControl":

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

+1 per questo buon argomento. Ho anche bisogno di sapere questo ... come stavo cercando di fare queste cose: http://stackoverflow.com/questions/4744522/handle-to-silverlight-usercontrol ... e http://stackoverflow.com/questions/4758568/deploying-xbap-with-win32-dll – Nawaz

+0

Quale versione di Silverlight? –

+0

Il preferibilmente il più recente possibile. – ronag

risposta

8

Ho testato il progetto di codice disponibile qui: http://www.codeproject.com/KB/atl/Host_Silverlight_In_ATL.aspx in Visual Studio 2010. Ecco ciò che è necessario per farlo funzionare:

  • carico sotto VS 2010 e convertirlo in VS 2010 (non si preoccupano backupgs i registri, ecc ...)
  • rimuovere l'evento di registrazione post-generazione ("$ (TargetPath)"/RegServer) , è usato solo per la registrazione dei componenti COM all'interno del progetto. Puoi anche lasciare quell'evento e dimenticarti dell'errore se preferisci.
  • Ho dovuto eseguire una piccola modifica in XcpControlHost.cpp.

Qui è il cambiamento:

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

e funziona (Sono in esecuzione Windows 7 con Silverlight 4).

Questa è davvero la strada da percorrere.

Una osservazione però: nel campione, l'autore non utilizza il file ufficiale xcpctrl.idl disponibile da qui: http://msdn.microsoft.com/en-us/library/cc296246(VS.95).aspx. Invece ridefinisce tutte le interfacce e GUID, e questo non è necessario. Puoi semplicemente aggiungere xcpctrl.idl a Visual Studio 2010 e compilare, questo attiverà il compilatore MIDL che creerà i seguenti 3 file: xcpctrl_h.h, xcpctrl_i.c, xcpctrl_p.c. Una volta che sono stati compilati, è possibile aggiungerli al progetto.

+0

Buon lavoro. Ho aggiunto un collegamento a un file in cui ho risolto il progetto "TestProject" in base al tuo suggerimento. – ronag

+0

Un problema con questo esempio è che non è possibile inviare messaggi tastiera al controllo host Silverlight. Come risolvere questo? –

+0

@IslamYahiatene - Invia un'altra domanda –