2009-11-25 16 views

Respuesta

6

La definición SAFEARRAY (VARIANT *) no es del todo correcta. Se declara en un IDL como SAFEARRAY (VARIANT), pero el puntero disponible para bloquear el SAFEARRAY es en realidad un VARIANT *. Si piensas en esto por un momento, debería tener más sentido. El puntero de índice de un SAFEARRAY (el miembro pvData) posiblemente no puede adaptarse a un VARIANTE completo en su ubicación física, por lo que, al menos, debería ser capaz de almacenar un puntero que pueda usarse para indexar en una matriz de VARIANTES.

Si mira <wtypes.h>, en algún lugar sobre la línea 1110+ verá las definiciones de enumeración VT_. También se muestra allí que VT_VARIANT en realidad implica VARIANT *. También son útiles las etiquetas [S] que indican qué elementos pueden aparecer en una 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) 
*/ 

Aquí hay un enlace a una copia del archivo de encabezado.

wtypes.h at DOC.DDART.NET

Partiendo de aquí, sólo tendría que declarar un SAFEARRAY con un tipo de variante de VT_VARIANT, a continuación, tratar pvData como VARIANT * cuando se bloquea la matriz. Aquí está el código fuente de una aplicación de consola win32 de muestra que demuestra esto llamando a una función que coincide con la misma declaración que su función.

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

El componente llamado por esta aplicación de prueba se pueden crear mediante la creación de un proyecto DLL ATL, y la adición de un objeto ATL simple llamado 'TestReader'.

Aquí está el IDL para 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 función miembro correspondiente a la declaración IDL sólo se necesita SAFEARRAY * (o) LPSAFEARRAY argumentos.

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

Aquí está el cuerpo del método. También se incluye una función auxiliar PrintVariant() para abreviar.

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

¿Cómo puedo pasar una matriz de cadenas desde el script VB al método SAFEARRAY ? ¿puedes por favor publicar un ejemplo? –

8

Agregando a las respuestas anteriores como referencia para los futuros lectores: En IDL, SAFEARRAY(...) significa un puntero a un descriptor matriz. Pero en C++, SAFEARRAY significa un descriptor de matriz. Entonces SAFEARRAY(...) de IDL es realmente C++ SAFEARRAY *. Esto me confundió sin fin. Para hacer las cosas aún más interesantes, VB siempre pasa las matrices por referencia. Así que VB () As Long es SAFEARRAY<int32_t> ** en C++. (No sé si realmente hay un encabezado de uso común que le permite especificar el tipo como un parámetro de plantilla, pero lo inserté para mayor claridad)

+0

+1 para comentarios sobre las diferencias entre SAFEARRAY en contexto IDL y la estructura SAFEARRAY C/C++ – meklarian

Cuestiones relacionadas