Неправильный vtable, сгенерированный компилятором C ++ (для COM-объекта) - PullRequest
2 голосов
/ 30 апреля 2020

У меня есть этот код C ++, который определяет IMyInterface COM-интерфейс. Единственная тонкость в том, что он определяет два метода с разными параметрами, но с одинаковым именем:

struct __declspec(uuid("bd8a0cfc-312e-4871-8f82-07adab05c6d0")) __declspec(novtable) IMyInterface : public IUnknown
{
  virtual HRESULT __stdcall SetValue(float value) = 0;
  virtual HRESULT __stdcall SetValue(int value) = 0;
};

Вот простая реализация:

class MyClass : public IMyInterface
{
public:
  // poor man's COM object impl
  HRESULT MyClass::QueryInterface(REFIID riid, void** ppvObject)
  {
    if (riid == __uuidof(IUnknown))
    {
      *ppvObject = (IUnknown*)this;
      return S_OK;
    }

    if (riid == __uuidof(IMyInterface))
    {
      *ppvObject = (IMyInterface*)this;
      return S_OK;
    }
    return E_NOINTERFACE;
  }

  ULONG MyClass::AddRef() { return 1; }
  ULONG MyClass::Release() { return 1; }

  HRESULT MyClass::SetValue(float value)
  {
    printf("float value called\n");
    return S_OK;
  }

  HRESULT MyClass::SetValue(int value)
  {
    printf("int value called\n");
    return S_OK;
  }
};

Вот два экспорта DLL, чтобы иметь возможность использовать его в C#:

static MyClass myc;

extern "C" {
  __declspec(dllexport) HRESULT __stdcall GetMyInterface(REFIID riid, void** pvInstance)
  {
    return myc.QueryInterface(riid, pvInstance);
  }
}

extern "C" {
  __declspec(dllexport) HRESULT __stdcall DoItInCPP()
  {
    IMyInterface* my;
    GetMyInterface(__uuidof(IMyInterface), (void**)&my);
    my->SetValue(.0f);
    my->SetValue(0);
    return S_OK;
  }
}

И теперь у меня есть эта C# программа, которая использует приведенный выше код:

public class Program
{
    static void Main()
    {
        var hr = GetMyInterface(typeof(IMyInterface).GUID, out var obj);
        var my = (IMyInterface)obj;
        my.SetValue(.0f);
        my.SetValue(0);

        DoItInCPP();
    }

    [DllImport("MyComProject.dll")]
    private static extern int GetMyInterface([MarshalAs(UnmanagedType.LPStruct)] Guid riid, [MarshalAs(UnmanagedType.IUnknown)] out object ppv);

    [DllImport("MyComProject.dll")]
    private static extern int DoItInCPP();

    [Guid("bd8a0cfc-312e-4871-8f82-07adab05c6d0"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    private interface IMyInterface
    {
        [PreserveSig]
        int SetValue(float value);

        [PreserveSig]
        int SetValue(int value);
    }
}

Вывод такой (???):

int value called
float value called

float value called
int value called

Даже если я изменю имя первого метода C# IMyInterface, результат будет тем же.

У меня такое ощущение, что из-за методов с одинаковым именем C / C ++ Компилятор создал vtable, который не соответствует ожидаемому макету. Это ошибка? Ожидается ли такое поведение? это может быть предопределено? Я использую Visual Studio 2019.

...