2010-01-21 2 views
6

Utilizzo di ATL (VS2008) come è possibile enumerare i metodi disponibili disponibili su una determinata interfaccia IDispatch (IDispatch*)? Ho bisogno di cercare un metodo con un nome specifico e, una volta che ho il DISPID, invoco il metodo (conosco i parametri che il metodo richiede). Idealmente mi piacerebbe farlo usando puntatori intelligenti COM (CComPtr<>).Enumera i metodi dell'oggetto COM (IDispatch) utilizzando ATL?

È possibile?

+1

vedere questo strumento (codice sorgente): http://sourceforge.net/projects/axfuzz/files/ – lsalamon

+0

e questo: http://www.codeproject.com/KB/atl/ienum.aspx – lsalamon

+0

Sono andato alla ricerca di altri esempi e ho anche trovato http://spec.winprog.org/typeinf2/ –

risposta

7

Non è possibile enumerare tutti i metodi disponibili a meno che l'oggetto non implementi IDispatchEx.

Tuttavia, se si conosce il nome del metodo che si desidera chiamare, è possibile utilizzare GetIDsOfNames per associare il nome al DISPID corretto.

HRESULT hr; 
CComPtr<IDispatch> dispatch; 
DISPID dispid; 
WCHAR *member = "YOUR-FUNCTION-NAME-HERE"; 
DISPPARAMS* dispparams; 

// Get your pointer to the IDispatch interface on the object here. Also setup your params in dispparams. 

hr = dispatch->GetIDsOfNames(IID_NULL, &member, 1, LOCALE_SYSTEM_DEFAULT, &dispid); 
if (SUCCEEDED(hr)) { 
    hr = dispatch->Invoke(1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, dispparams, &varResult, NULL, NULL); 
} 

Edit: Per completezza, ho il sospetto c'è un modo per interrogare l'interfaccia ITypeInfo2 (presumere che vi sia una libreria di tipi per l'oggetto) che si ottiene da IDispatch :: GetTypeInfo per un elenco di metodi, ma io non l'ho fatto Vedi l'altra risposta.

+0

geniale! Proprio quello di cui avevo bisogno. Grazie mille. – Rob

+1

Credo di aver fatto ogni punto che hai appena fatto nel tuo commento nella mia risposta. Per favore leggilo di nuovo attentamente. Inoltre, il poster voleva solo essere in grado di invocare un metodo che conosceva già. Quindi la mia risposta ha fornito la soluzione a ciò che voleva veramente fare, non necessariamente a ciò che chiedeva. Ecco perché, sospetto, è stata contrassegnata come la risposta corretta. E infine, per favore calmati. Sono solo gli 1 e gli 0. –

15

È possibile enumerare i metodi uno IDispatch espone attraverso le informazioni sul tipo. Esistono due modi per ottenere le informazioni sul tipo:

Sfortunatamente, un'implementazione di IDispatch non è obbligata a fornire informazioni sul tipo dei metodi e delle proprietà che implementa.

Se lo fa, però, l'enumerazione di base coinvolge chiamando ITypeInfo::GetTypeAttr per ottenere il TYPEATTR per l'interfaccia e guardando il numero di metodi implementati (cFuncs) e variabili (cVars) e loop su questi e chiamando ITypeInfo::GetFuncDesc() o ITypeInfo::GetVarDesc(). Naturalmente, ci sono molti più dettagli che dovrete trattare come posso elencare qui, ma questo dovrebbe essere un buon punto di partenza per la vostra esplorazione.

Ecco un bel article explaining the process in more details con codice in VB.Net.

+0

Roba buona. Grazie per aver aggiunto questo. –

+0

@Franci, quando una proprietà è una matrice, VARDESC restituito ha un varkind = IDispatch. Come puoi sapere se una proprietà è una matrice e se una matrice - come posso accedere ai suoi membri? Quando si chiama Invoke per ottenere un array, il risultato è IDispatch. Questo IDispatch non supporta "Item" o "Length" o proprietà simili. – Uri

+1

@Uri - nota che le proprietà non sono campi e devono essere ispezionate tramite 'GetFuncDesc()' che ti dà un 'FUNCDESC', da cui devi andare a' elemdescFunc' (per il ritorno) o 'lprgelemdescParam' (per il parametri). Gli array di solito vengono restituiti come parametro out, quindi dovresti controllare quest'ultimo. In ogni caso, entrambi forniscono 'ELEMDESC', dove dovresti controllare' tdesk', che ti restituisce un 'TYPEDESC', che basato sul' VARTYPE vt' potrebbe rivelarsi in realtà un 'ARRAYDESC'. Se questo è il caso, hai un 'SAFEARRAY'. –

10

Ecco un codice che esegue l'enumerazione (inserisce le coppie [Dispatch ID] - [Nome metodo] in una mappa, ma è facile da modificare).

/// 
/// \brief Returns a map of [DispId, Method Name] for the passed-in IDispatch object 
/// 
HRESULT COMTools::GetIDispatchMethods(_In_ IDispatch * pDisp, 
             _Out_ std::map<long, std::wstring> & methodsMap) 
{ 
    HRESULT hr = S_OK; 

    CComPtr<IDispatch> spDisp(pDisp); 
    if(!spDisp) 
     return E_INVALIDARG; 

    CComPtr<ITypeInfo> spTypeInfo; 
    hr = spDisp->GetTypeInfo(0, 0, &spTypeInfo); 
    if(SUCCEEDED(hr) && spTypeInfo) 
    { 
     TYPEATTR *pTatt = nullptr; 
     hr = spTypeInfo->GetTypeAttr(&pTatt); 
     if(SUCCEEDED(hr) && pTatt) 
     { 
      FUNCDESC * fd = nullptr; 
      for(int i = 0; i < pTatt->cFuncs; ++i) 
      { 
       hr = spTypeInfo->GetFuncDesc(i, &fd); 
       if(SUCCEEDED(hr) && fd) 
       { 
        CComBSTR funcName; 
        spTypeInfo->GetDocumentation(fd->memid, &funcName, nullptr, nullptr, nullptr); 
        if(funcName.Length()>0) 
        { 
         methodsMap[fd->memid] = funcName; 
        } 

        spTypeInfo->ReleaseFuncDesc(fd); 
       } 
      } 

      spTypeInfo->ReleaseTypeAttr(pTatt); 
     } 
    } 

    return hr; 

} 
+0

Ottimo! questo codice funziona perfettamente – Elmue