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)