Вызов управляемого делегата от обратного вызова x64? (параметр в rdx теряется) - PullRequest
1 голос
/ 17 ноября 2011

У меня есть библиотека, которая предоставляет функции обратного вызова и должна вызываться как из управляемого, так и из собственного кода.Я реализовал это, выполнив:

typedef struct { DWORD blah; } MY_STRUCT;

class ICallbackInterface
{
public:
    virtual HRESULT CallbackFunc1(const MY_STRUCT* pStruct) { return S_OK; }

    // helper for overriding the vtable (used later on by the managed code)
    class VTable
    {
    public:
        void* pfnCallbackFunc1;
    };
};

Собственный код получает указатель на ICallbackInterface и вызывает CallbackFunc1.

В коде C ++ / CLI я выделяю ICallbackInterface и переопределяюего vtable указывает на делегатов управляемых функций, которые я хочу вызвать.(Следующий фрагмент взят из конструктора):

public ref class MyManagedClass
{
...
        m_pCallbackClass = new ICallbackInterface;

        if (!m_pCallbackClass) 
            return E_OUTOFMEMORY;

        m_pNewCallbackVtable = new ICallbackInterface::VTable;

        if (!m_pNewCallbackVtable)
        {
            delete m_pCallbackClass;
            m_pCallbackClass = nullptr;

            return E_OUTOFMEMORY;
        }

        // Get the (hidden) pointer to the vtable

        ICallbackInterface::VTable** ppAddressOfInternalVtablePointer = 
            (ICallbackInterface::VTable**)m_pCallbackClass;

        ICallbackInterface::VTable* pOldVtable = *ppAddressOfInternalVtablePointer;

        // Copy all the functions from the old vtable that we don't want to override

        *m_pNewCallbackVtable = *pOldVtable;

        // Manually override the vtable entries with our delegate functions

        m_pNewCallbackVtable->pfnCallbackFunc1 = Marshal::GetFunctionPointerForDelegate(gcnew delCallbackFunc1(this, &MyManagedClass::CallbackFunc1)).ToPointer();
...

А вот функция обратного вызова и ее делегат

    [UnmanagedFunctionPointer(CallingConvention::StdCall)] 
    delegate HRESULT delCallbackFunc1(const MY_STRUCT* pMyStruct);
    HRESULT CallbackFunc1(const MY_STRUCT* pMyStruct)
    {
        // do something with pMyStruct.
    }
}

Когда я компилирую нативную библиотеку для x86, все работает хорошо.(Я не знаю, почему CallingConvention :: StdCall используется там, но альтернативы, кажется, вызывают проблемы с esp.)

Когда я компилирую это для x64, вызывается функция обратного вызова, и rsp хорош, когдаЯ возвращаюсь, но pMyStruct уничтожен.Похоже, нативный код любит передавать вещи в rdx, но где-то в нативном переходе -> управляемом (в который отладчик не пускает меня), rdx заполняется мусором.

Есть ли какой-нибудь атрибут, который я могу использовать в моем делегате, чтобы исправить это на x64?Или мне нужно сделать что-то менее приятное, например обернуть весь управляемый класс в нативный класс для выполнения обратных вызовов?Или я только что нашел ошибку управляемого кодагена?

Ответы [ 2 ]

0 голосов
/ 18 ноября 2011

Нашел проблему. Управляемый код имеет свой указатель «this», который появляется при инициализации делегата. Но у нативной функции есть свой собственный указатель this, который также передается. Изменение управляемых подписей на следующие полностью устраняет ошибку, и теперь функция получает доступ к обоим указателям «this».

[UnmanagedFunctionPointer(CallingConvention::ThisCall)]  
delegate HRESULT delCallbackFunc1(ICallbackInterface* NativeThis, const MY_STRUCT* pMyStruct); 
HRESULT CallbackFunc1(ICallbackInterface* NativeThis, const MY_STRUCT* pMyStruct) 
{ 
    // do something with pMyStruct. 
} 

Это работает на x86 и x64.

0 голосов
/ 17 ноября 2011

вы вызываете неопределенное bwhavior влево и вправо и полагаетесь на детали реализации, такие как макет vtable и соглашение о вызове виртуальных функций-членов.Неудивительно, что когда вы сменили платформу, все пошло не так.

вам нужно написать производный vl lass, если у вас собственный интерфейсреализация может напрямую вызывать указатели на функции или управляемые делегаты.и перестаньте связываться с указателем vtsble.

...