Обращение аргумента указателя NULL в C # COM - PullRequest
3 голосов
/ 09 июля 2011

Я работаю с интерфейсом плагина COM, который имеет следующее определение функции:

HRESULT foo ( [out, ref] VARIANT* a, [out, ref] VARIANT* b );

При использовании tlbimp (tlbimp2.exe из codeplex) библиотека .NET имеет следующую интерфейсную функцию:

int foo ( out object a, out object b );

Проблема в том, что вызывающее приложение выполнит вызов функции:

VARIANT a;
::InitVariant( &a );

plugin->foo( &a, NULL );

А в C # я реализовал функцию:

int foo ( out object a, out object b )
{
    a = 1;
    b = 2;

    return 0; // S_OK
}

Когда все сказано и сделано, приложение фактически получает возврат E_POINTER, а не S_OK. Я предполагаю, что это из-за того, что NULL передал параметр out.

Есть ли способ проверить, равен ли указатель адреса NULL в реализации C #?

Примечание: выходные параметры не инициализируются, поэтому вы не можете использовать параметры вообще.

Я пытался реализовать интерфейс как [in, out, ref], чтобы заставить C # использовать (ref object a, ref object b), но это тоже не сработало.

Обновление

Ганс абсолютно прав, что мы должны были вызывать функцию с нулевым ptr, если мы объявили ее как [out, ref].

erurainon также был прав, что мы могли просто использовать IntPtr, чтобы добраться до варианта *.

Вот как это было исправлено:

int foo ([MarshalAs(UnmanagedType.Struct)]out object a, [MarshalAs(UnmanagedType.Struct)]out object b);

стал

int foo ([MarshalAs(UnmanagedType.Struct)]out object a, IntPtr b );

Теперь мы можем проверить на NULL случаи с:

if ( b == IntPtr.Zero )

Однако, поскольку мы имеем дело с Variant, вы не можете просто скопировать значение в IntPtr, например:

Marshal.StructureToPtr( myValue, b, false );

Итак, следуя этой записи , вам нужно создать класс struct:

[StructLayout(LayoutKind.Explicit, Size = 16)]
public struct PropVariant
{
    [FieldOffset(0)]
    public VarEnum variantType;
    [FieldOffset(8)]
    public IntPtr pointerValue;
    [FieldOffset(8)]
    public byte byteValue;
    [FieldOffset(8)]
    public long longValue;
    [FieldOffset(8)]
    public double dateValue;
    [FieldOffset(8)]
    public short boolValue;
}

И последняя функция выглядит следующим образом:

int foo( out object a, IntPtr b )
{
    a = 100;

    if ( b != IntPtr.Zero )
    {
        var time = new PropVariant();
        time.dateTime = DateTime.Now.ToOADate();
        time.variantType = VarEnum.VT_DATE;

        Marshal.StructureToPtr( time, b, false );
    }

    return 0; // S_OK
}

Надеюсь, это поможет кому-то еще в будущем

Ответы [ 2 ]

3 голосов
/ 09 июля 2011

Из документов MIDL :

The [ref] attribute identifies a reference pointer. It is used simply to represent a level of indirection
....
A reference pointer has the following characteristics:
 - Always points to valid storage; never has the value NULL. A reference pointer can always be dereferenced.

Код недействителен, прошел NULL. Прямое нарушение [ref] договора. На этом баксы прекращаются, у вас будет , чтобы исправить либо MIDL, либо собственный код.

1 голос
/ 09 июля 2011

Ганс прав.Либо вы не разрешаете своему API вызываться с параметрами NULL, либо реализуете свою функцию C # с IntPtr аргументами и проверяете на IntPtr.Zero.

...