Condividi tramite


Modifiche di marshalling

Nelle sezioni riportate di seguito viene fornito un insieme selezionato di modifiche che è possibile apportare a un assembly di interoperabilità per risolvere alcuni problemi specifici relativi all'output del processo di importazione:

  • Matrici di tipo C compatibili

  • Matrici di tipo C in/out

  • Matrici di tipo C multidimensionali

  • SAFEARRAY con limite diverso da zero

  • Mantenimento della firma

  • Passaggio di un valore null anziché del riferimento a un tipo di valore

In queste sezioni non viene fornita una rappresentazione completa dei casi per la modifica di un assembly di interoperabilità. È anche possibile ad esempio modificare un assembly di interoperabilità per semplificarne l'utilizzo. L'unico modo per determinare le personalizzazioni necessarie è quello di scrivere effettivamente il codice mediante l'assembly di interoperabilità. Per istruzioni sulla modifica di assembly di interoperabilità, vedere Procedura: modificare assembly di interoperabilità.

Il processo di marshalling è influenzato quando il client e il server sono in apartment incompatibili. Negli esempi riportati di seguito, la maggior parte dei parametri di cui è stato eseguito il marshalling non è compatibile con l'automazione e richiede pertanto l'esecuzione di una delle operazioni seguenti:

  • Confermare che client e server sono in apartment compatibili e che, di conseguenza, non è coinvolto alcun marshalling COM.

  • Registrare il proxy e lo stub generati da IDL (Interface Definition Language). La registrazione della libreria dei tipi non serve in questi casi, in quanto molte delle informazioni necessarie per il marshalling non sono distribuite da IDL alla libreria dei tipi.

Matrici di tipo C compatibili

Nella dichiarazione IDL riportata di seguito viene mostrata una matrice di tipo C.

HRESULT ConformantArray([in] int cElems, [in, size_is(cElems)] int 
aConf[]);

Poiché questo tipo non è compatibile con l'automazione, le informazioni sulla dimensione della matrice, come il collegamento tra il primo e il secondo parametro, non possono essere espresse nella libreria dei tipi. Mediante l'utilità di importazione della libreria dei tipi Tlbimp.exe, il secondo parametro viene importato non come matrice gestita, bensì come riferimento all'intero. È possibile regolare il parametro modificando il codice MSIL.

Cercare in MSIL quanto segue

method public hidebysig newslot virtual 
instance void  ConformantArray([in] int32 cElems,
[in] int32& aConf) runtime managed internalcall

Sostituire con

method public hidebysig newslot virtual 
instance void  ConformantArray([in] int32 cElems,
[in] int32[] marshal([]) aConf) runtime managed internalcall

Per chiamare dal codice gestito

int[] param1 = { 11, 22, 33 };
tstArrays.ConformantArray( 3, param1 );

Matrici di tipo C in/out

Nella dichiarazione IDL riportata di seguito viene mostrata una matrice di tipo C in/out.

HRESULT InOutArray([in, out] int* pcElems, [in, out, size_is(,*pcElems)] 
int** ppInOut);

In questo caso, la matrice può essere ridimensionata e la nuova dimensione può essere restituita. Poiché questo tipo non è compatibile con l'automazione, le informazioni sulla dimensione della matrice, come il collegamento tra il primo e il secondo parametro, non possono essere espresse nella libreria dei tipi. Mediante Tlbimp.exe il secondo parametro viene importato come un tipo IntPtr. Nonostante sia ancora possibile chiamare questo metodo dal codice gestito, per ridimensionare la matrice è necessario modificare MSIL e utilizzare i metodi della classe Marshal per gestire manualmente l'allocazione e la deallocazione di memoria.

Cercare in MSIL quanto segue

.method public hidebysig newslot virtual 
instance void  InOutArray([in][out] int32& pcElems,
[in][out] native int ppInOut) runtime managed internalcall

Sostituire con

.method public hidebysig newslot virtual 
instance void  InOutArray([in][out] int32& pcElems,
[in][out] native int& ppInOut) runtime managed internalcall

Per chiamare dal codice gestito

int[] inArray = { 11, 22, 33 };
int arraySize = inArray.Length;

IntPtr buffer = Marshal.AllocCoTaskMem( Marshal.SizeOf( typeof( int )) * inArray.Length );
Marshal.Copy( inArray, 0, buffer, inArray.Length );
tstArrays.InOutArray( ref arraySize, ref buffer );
if( arraySize > 0 )
{
int[] arrayRes = new int[ arraySize ];
Marshal.Copy( buffer, arrayRes, 0, arraySize );
Marshal.FreeCoTaskMem( buffer );
}

Matrici di tipo C multidimensionali

Nella dichiarazione IDL riportata di seguito viene mostrata una matrice di tipo C bidimensionale.

HRESULT TwoDimArray([in] int cDim, [in, size_is(cDim)] int aMatrix[][3]);

Poiché questo tipo non è compatibile con l'automazione, le informazioni sulla dimensione e il numero di dimensioni della matrice, come il collegamento tra il primo e il secondo parametro, non possono essere espresse nella libreria dei tipi. Mediante Tlbimp.exe, il secondo parametro viene importato non come una matrice multidimensionale gestita, bensì come un tipo IntPtr. È possibile regolare il parametro modificando il codice MSIL.

Cercare in MSIL quanto segue

.method public hidebysig newslot virtual 
instance void  TwoDimArray([in] int32 cDim,
[in] native int aMatrix) runtime managed internalcall

Sostituire con

.method public hidebysig newslot virtual 
instance void  TwoDimArray([in] int32 cDim,
[in] int32[,] marshal([]) aMatrix) runtime managed internalcall

Per chiamare dal codice gestito

int[,] param = {{ 11, 12, 13 }, { 21, 22, 23 }, { 31, 32, 33 }};
tstArrays.TwoDimArray( 3, param );

SAFEARRAY con limite diverso da zero

Nella dichiarazione IDL riportata di seguito viene mostrato un parametro SAFEARRAY.

HRESULT InSArray([in] SAFEARRAY(int) *ppsa);

Si tratta di un elemento SAFEARRAY con un limite diverso da zero. Nel codice gestito, tali matrici sono rappresentate dal tipo System.Array. Per impostazione predefinita, tuttavia, tutti i parametri SAFEARRAY vengono convertiti dall'utilità di importazione in riferimenti alle matrici gestite. Sono disponibili due opzioni per modificare il comportamento predefinito:

  • Importare tutte le matrici in una libreria dei tipi come tipi System.Array utilizzando Tlbimp.exe con l'opzione /sysarray.

  • Importare alcuni parametri come tipi System.Array modificando manualmente il codice MSIL, come illustrato nell'esempio che segue.

    Cercare in MSIL quanto segue

    .method public hidebysig newslot virtual 
    instance void  InSArray([in] int32[]&  marshal( safearray int) ppsa) runtime managed internalcall
    

    Sostituire con

    .method public hidebysig newslot virtual 
    instance void  InSArray(class [mscorlib]System.Array& marshal( safearray) 
    ppsa) runtime managed internalcall
    

    Chiamare dal codice gestito

    int[] lengthsArray = new int[1] { 3 };   
    int[] boundsArray = new int[1] { -1 };
    Array param2 = Array.CreateInstance( typeof(int), lengthsArray, boundsArray );
    for( int i = param2.GetLowerBound( 0 ); i <= param2.GetUpperBound( 0 ); i++ )
    param2.SetValue( i * 10, i ); 
    sum = tstArrays.InSArray( ref param2 );
    

Mantenimento della firma

Nella dichiarazione IDL riportata di seguito viene mostrata una firma di metodo COM.

HRESULT TestPreserveSig2([in] int inParam, [out,retval] int* outParam);

Mediante Tlbimp.exe, le firme di metodo COM vengono modificate. I parametri contrassegnati da [out, retval] in IDL diventano valori restituiti di metodi gestiti, mentre tutti i valori HRESULT che indicano errori sono trasformati in eccezioni gestite. È talvolta necessario mantenere la firma del metodo COM originale, ad esempio quando il metodo restituisce un risultato diverso da HRESULT corretti. Nella rappresentazione gestita riportata di seguito viene mostrato l'esempio di una firma che è possibile modificare.

Rappresentazione gestita in MSIL

.method public hidebysig newslot virtual 
instance int32 TestPreserveSig2([in] int32 inParam) runtime managed internalcall
{

Sostituire con

.method public hidebysig newslot virtual 
instance int32 TestPreserveSig2([in] int32 inParam, [out] int32& outParam) runtime managed internalcall preservesig

Per vedere quale HRESULT viene restituito

int hr = tst.TestPreserveSig2( -3, out retValue );
Console.WriteLine( "Return value is {0}", retValue );
if( hr == 0 )
Console.WriteLine( "HRESULT = S_OK" );
else if ( hr == 1 )
Console.WriteLine( "HRESULT = S_FALSE" );
else
Console.WriteLine( "HRESULT = {0}", hr );

Passaggio di un valore null anziché del riferimento a un tipo di valore

Nella dichiarazione IDL riportata di seguito viene mostrato un puntatore IDL a una struttura.

HRESULT TestPassingNull([in, unique] Point* refParam);

Mediante Tlbimp.exe, il parametro viene importato come riferimento al tipo di valore Point. In C# e Visual Basic 2005 non è possibile passare un riferimento null (Nothing in Visual Basic) come parametro quando è previsto un riferimento a un tipo di valore. Se per la funzione COM è necessario un parametro null (Nothing), è possibile alterare la firma modificando il codice MSIL.

Cercare in MSIL quanto segue

.method public hidebysig newslot virtual 
instance void  TestPassingNull(
[in] valuetype MiscSrv.tagPoint& refParam) 
runtime managed internalcall

Sostituire con

.method public hidebysig newslot virtual 
instance void  TestPassingNull([in] native int) runtime managed internalcall

Grazie alla firma modificata è possibile passare un valore null. Quando è necessario passare alcuni valori diversi da null, tuttavia, si devono utilizzare i metodi della classe Marshal, come illustrato nell'esempio riportato di seguito.

tagPoint p = new tagPoint();
p.x = 3;
p.y = 9;

IntPtr buffer = Marshal.AllocCoTaskMem( Marshal.SizeOf( p ));
Marshal.StructureToPtr( p, buffer, false );
tst.TestPassingNull( buffer );
Marshal.FreeCoTaskMem( buffer );
tst.TestPassingNull( IntPtr.Zero );

Vedere anche

Attività

Procedura: modificare assembly di interoperabilità

Procedura: creare wrapper manualmente

Riferimenti

Tlbimp.exe (utilità di importazione della libreria dei tipi)

Concetti

personalizzazione di wrapper di runtime richiamabili

Tipi di dati COM

personalizzazione di wrapper COM richiamabili