2009-11-25 14 views

risposta

6

La definizione SAFEARRAY (VARIANT *) non è del tutto corretta. È dichiarato in un IDL come SAFEARRAY (VARIANT), ma il puntatore disponibile per bloccare SAFEARRAY è in realtà un VARIANT *. Se ci pensi per un momento, dovrebbe avere più senso. Il puntatore dell'indice di un SAFEARRAY (il membro pvData) non può eventualmente contenere un VARIANT intero nella sua posizione fisica, quindi, per lo meno, dovrebbe essere in grado di memorizzare un puntatore che può essere utilizzato per indicizzare in un array di VARIANT.

Se si guarda <wtypes.h>, da qualche parte sulla riga 1110+ verranno visualizzate le definizioni di enumerazione VT_. Viene anche mostrato che VT_VARIANT implica in realtà VARIANT *. Sono anche pratici i tag [S] che notano quali elementi possono apparire in un SAFEARRAY.

/* 
* VARENUM usage key, 
* 
* * [V] - may appear in a VARIANT 
* * [T] - may appear in a TYPEDESC 
* * [P] - may appear in an OLE property set 
* * [S] - may appear in a Safe Array 
* 
* 
* VT_EMPTY   [V] [P]  nothing 
* VT_NULL    [V] [P]  SQL style Null 
* VT_I2    [V][T][P][S] 2 byte signed int 
* VT_I4    [V][T][P][S] 4 byte signed int 
* VT_R4    [V][T][P][S] 4 byte real 
* VT_R8    [V][T][P][S] 8 byte real 
* VT_CY    [V][T][P][S] currency 
* VT_DATE    [V][T][P][S] date 
* VT_BSTR    [V][T][P][S] OLE Automation string 
* VT_DISPATCH   [V][T] [S] IDispatch * 
* VT_ERROR   [V][T][P][S] SCODE 
* VT_BOOL    [V][T][P][S] True=-1, False=0 
* VT_VARIANT   [V][T][P][S] VARIANT * 
... (remaining definitions omittted) 
*/ 

Ecco un link a una copia del file di intestazione.

wtypes.h at DOC.DDART.NET

Procedendo da qui, si potrebbe semplicemente dichiarare un SAFEARRAY con un tipo di variante VT_VARIANT, poi trattare pvData come VARIANT * quando si blocca l'array. Ecco il codice sorgente per un'app di console win32 di esempio che dimostra ciò chiamando una funzione che corrisponde alla stessa dichiarazione della funzione.

#include "stdafx.h" 
#include "SFAComponent.h" 
#include "SFAComponent_i.c" 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    ::CoInitialize(NULL); 

    SAFEARRAYBOUND nameBounds; 
    nameBounds.cElements = 2; 
    nameBounds.lLbound = 0; 
    LPSAFEARRAY psaNames = SafeArrayCreate(VT_BSTR, 1, &nameBounds); 

    BSTR bstrApple = SysAllocString(L"apple"); 
    BSTR bstrOrange = SysAllocString(L"orange"); 

    SafeArrayLock(psaNames); 
    BSTR *nameArray = (BSTR *)psaNames->pvData; 
    nameArray[0] = bstrApple; 
    nameArray[1] = bstrOrange; 
    SafeArrayUnlock(psaNames); 

    SAFEARRAYBOUND valueBounds; 
    valueBounds.cElements = 2; 
    valueBounds.lLbound = 0; 
    LPSAFEARRAY psaValues = SafeArrayCreate(VT_VARIANT, 1, &valueBounds); 

    SafeArrayLock(psaValues); 
    VARIANT *valueArray = (VARIANT *)psaValues->pvData; 
    VariantClear(&valueArray[0]); 
    VariantClear(&valueArray[1]); 
    valueArray[0].vt = VT_BSTR; 
    valueArray[0].bstrVal = SysAllocString(L"hello"); 
    valueArray[1].vt = VT_I4; 
    valueArray[1].iVal = 42; 

    { 
    CComPtr<ITestReader> p; 
    p.CoCreateInstance(CLSID_TestReader); 
    p->Run(psaNames, psaValues); 
    p.Release(); // not explicitly necessary. 
    } 

    SafeArrayDestroy(psaValues); 
    SafeArrayDestroy(psaNames); 

    ::CoUninitialize(); 

    return 0; 
} 

Il componente chiamato da questa applicazione di test possono essere creati creando un progetto DLL ATL, e l'aggiunta di un oggetto ATL semplice chiamato 'TestReader'.

Ecco l'IDL di ITestReader.

[ 
    object, 
    uuid(832EF93A-18E8-4655-84CA-0BA847B52B77), 
    dual, 
    nonextensible, 
    helpstring("ITestReader Interface"), 
    pointer_default(unique), 
    oleautomation 
] 
interface ITestReader : IDispatch{ 
    [id(1), helpstring("method Run")] HRESULT Run([in] SAFEARRAY(BSTR) paramNames, [in] SAFEARRAY(VARIANT) paramValues); 
}; 

La funzione membro corrispondente alla dichiarazione IDL richiede solo SAFEARRAY * (o) LPSAFEARRAY argomenti.

public: 
    STDMETHOD(Run)(LPSAFEARRAY paramNames, LPSAFEARRAY paramValues); 

Ecco il corpo del metodo. È inclusa anche una funzione di supporto PrintVariant() per brevità.

void PrintVariant(VARIANT *pV) 
{ 
    switch(pV->vt) 
    { 
    case VT_BSTR: 
    wprintf(L" BSTR: %s\r\n", pV->bstrVal); 
    break; 
    case VT_I4: 
    wprintf(L" Integer: %d\r\n", pV->iVal); 
    break; 
    default: 
    wprintf(L" Unrecognized Type: vt=%d\r\n", pV->vt); 
    break; 
    } 
} 

STDMETHODIMP CTestReader::Run(LPSAFEARRAY paramNames, LPSAFEARRAY paramValues) 
{ 
    SafeArrayLock(paramNames); 
    SafeArrayLock(paramValues); 
    BSTR *nameArray = (BSTR *)paramNames->pvData; 
    VARIANT *valueArray = (VARIANT *)paramValues->pvData; 

    wprintf(L"Item 0 is %s, variant type %d\r\n", nameArray[0], valueArray[0].vt); 
    PrintVariant(&valueArray[0]); 
    wprintf(L"Item 1 is %s, variant type %d\r\n", nameArray[1], valueArray[1].vt); 
    PrintVariant(&valueArray[1]); 

    SafeArrayUnlock(paramNames); 
    SafeArrayUnlock(paramValues); 

    return S_OK; 
} 
+0

Come posso passare l'array di stringhe dallo script VB al metodo SAFEARRAY ? puoi per favore postare un esempio? –

8

Aggiungendo alle risposte sopra per riferimento per futuri lettori: In IDL, SAFEARRAY(...) significa un puntatore a un descrittore di matrice. Ma in C++, SAFEARRAY indica un descrittore di array. Quindi IDL's SAFEARRAY(...) è in realtà il C++ SAFEARRAY *. Questo mi ha confuso senza fine. Per rendere le cose ancora più interessanti, VB passa sempre le matrici per riferimento. Quindi V2 () As Long è SAFEARRAY<int32_t> ** in C++. (Non so se esiste un'intestazione comunemente usata che consente di specificare il tipo come parametro del modello, ma l'ho inserito per chiarezza.)

+0

+1 per commentare le differenze tra SAFEARRAY nel contesto IDL e la struttura SAFEARRAY C/C++ – meklarian

Problemi correlati