forum.boolean.name

forum.boolean.name (http://forum.boolean.name/index.php)
-   C++ (http://forum.boolean.name/forumdisplay.php?f=22)
-   -   Передача указателя на интерфейс через DLL (http://forum.boolean.name/showthread.php?t=18186)

DarkMedveD 16.05.2013 05:09

Передача указателя на интерфейс через DLL
 
Есть класс, в котором есть некоторые методы и члены-интерфейсы, с которыми надо работать. Сами классы находятся в заголовочном файле, их реализация в исходнике. Все это собрано в библиотеку и подключено к другому проекту. Во время работы с проектом любое использование интерфейса из подключенной библиотеки приводит к ошибке доступа к памяти.

Может указатели на интерфейсы надо тоже как-то передавать по-особенному, как методы, например?

Код:

class Shader
{

public:
        ID3DXBuffer* shader;
        ID3DXBuffer* errorBuffer;

        IDirect3DVertexShader9* DiffuseShader;
        ID3DXConstantTable* DiffuseConstTable;

        D3DXMATRIX View;
        D3DXMATRIX Proj;

          __declspec(dllexport) HRESULT loadShader(LPCSTR,LPCSTR,LPCSTR);

};

Причем ошибка вылетает при использовании метода, который, казалось бы, к члену своего класса обращается.
Заранее огромное спасибо за помощь.

HolyDel 16.05.2013 12:40

Ответ: Передача указателя на интерфейс через DLL
 
для клиентского кода должно быть __declspec(dllimport).

определи дефайн для проекта своей библиотеки. например MY_SUPERLIB.

определи макрос типа:
Код:

#ifdef MY_SUPERLIB
#define SUPERLIB_API __declspec(dllexport)
#else
#define SUPERLIB_API __declspec(dllimport)
#endif

замени __declspec(dllexport) на SUPERLIB_API
Код:

SUPERLIB_API HRESULT loadShader(LPCSTR,LPCSTR,LPCSTR);
таким образом при компиляции либки у тебя там будет экспорт а при компиляции приложения, используещего либку - импорт.

DarkMedveD 16.05.2013 17:58

Ответ: Передача указателя на интерфейс через DLL
 
К сожалению, безрезультатно.
Абсолютно никакой реакции на данные манипуляции не возникло.

Только предупреждение выдает:
Код:

несовместимая компоновка dll
Может я что-то сделал не так?

В заголовок библиотеки добавил
Код:

#ifdef MY_SUPERLIB
#define SUPERLIB_API __declspec(dllexport)
#else
#define SUPERLIB_API __declspec(dllimport)
#endif

В классах спецификаторы заменил на SUPERLIB_API.

И все, никаких изменений в основном проекте я не производил.

UPD:
Добавил #define MY_SUPERLIB к проекту - никакой реакции.

HolyDel 16.05.2013 19:16

Ответ: Передача указателя на интерфейс через DLL
 
пока не понятно. такто код рабочий.

проверь на функции.
создай функцию тупо возвращающую инт и посмотри как она будет линковаться.

DarkMedveD 16.05.2013 20:59

Ответ: Передача указателя на интерфейс через DLL
 
Как проверить линкование функции?
По-моему дело в интерфейсах. С ними косяки, остальные функции нормально работали и раньше.

HolyDel 16.05.2013 21:37

Ответ: Передача указателя на интерфейс через DLL
 
хм. странно. как они работали без dllimport-а.

покажи как используешь.

DarkMedveD 16.05.2013 21:49

Ответ: Передача указателя на интерфейс через DLL
 
Проект библиотеки:
Файл заголовка:
Код:

class  Shader
{
public:

                ID3DXBuffer* shader;
                  ID3DXBuffer* errorBuffer;

            IDirect3DVertexShader9* DiffuseShader;
                ID3DXConstantTable* DiffuseConstTable;

                D3DXMATRIX View;
                D3DXMATRIX Proj;
                SUPERLIB_API Shader();
                  SUPERLIB_API  HRESULT loadShader(LPCSTR,LPCSTR,LPCSTR);
                SUPERLIB_API        void UpdateShader(D3DXMATRIX, D3DXMATRIX);
            SUPERLIB_API        void SetVector(LPCSTR, D3DXVECTOR4);
                SUPERLIB_API  void SetMatrix(LPCSTR, D3DXMATRIX);
            SUPERLIB_API        void SetFloat(LPCSTR, FLOAT);
            SUPERLIB_API        void SetInt(LPCSTR, INT);

};

Файл исходного кода:
Код:

HRESULT Shader::loadShader(LPCSTR sourse, LPCSTR point, LPCSTR version)
{
        HRESULT hr=0;

        hr = D3DXCompileShaderFromFile(
              sourse,
              0,
              0,
              point, // имя точки входа
                          version,
                          D3DXSHADER_AVOID_FLOW_CONTROL,
              &shader,
              &errorBuffer,
              &DiffuseConstTable);


    if(errorBuffer)
    {
          ::MessageBox(0, (char*)errorBuffer->GetBufferPointer(), 0, 0);
          Release<ID3DXBuffer*>(errorBuffer);
    }

    if(FAILED(hr))
    {
          ::MessageBox(0, "Ошибка компиляции шейдера", 0, 0);
          return false;
    }

        if(Device)
                Device->CreateVertexShader((DWORD*)shader->GetBufferPointer(), &DiffuseShader);

    if(FAILED(hr))
    {
          ::MessageBox(0, "CreateVertexShader - FAILED", 0, 0);
          return false;
    }

    Release<ID3DXBuffer*>(shader);



    DiffuseConstTable->SetDefaults(Device);

        return true;
}

После подключения библиотеки, в проекте пишу:

Код:

diffuse.loadShader("VertexShader.hlsl", "main","vs_1_1");
Результата, разумеется, нет.

Кстати, во время ошибки вылазит некоторая служебная информация от студии. Там указаны значения каждого из объектов. Что странно - вроде в объекте шейдера все в порядке, а вот объект Device всегда содержит 0x00000000.

impersonalis 17.05.2013 01:23

Ответ: Передача указателя на интерфейс через DLL
 
Хочется спать, потому же нет состояния разобраться в коде, но смазанные воспоминания и обронённое упоминание компоновки поднимает следующее (если ничего не даст - не серчать):
Соглашение вызова ("Во время работы с проектом любое использование интерфейса из подключенной библиотеки приводит к ошибке доступа к памяти.")?
например
HRESULT SUPERLIB_API __cdecl loadShader(LPCSTR,LPCSTR,LPCSTR);

coff omf ("несовместимая компоновка ")?
декорирование?

HolyDel 17.05.2013 01:34

Ответ: Передача указателя на интерфейс через DLL
 
один хидер - соглашение вызова одинаковое, + автор говорил что на обычных функциях это работает. хотя глянуть настройки компилятора в проекте таки можно.

один компилятор - совместимая компоновка и одинаковое декорирование.

поставь точку останова в начало loadShader, посмотри доходит ли твоя программа до туда.

DarkMedveD 17.05.2013 02:30

Ответ: Передача указателя на интерфейс через DLL
 
Да, доходит до начала загрузки шейдера.

Потом, после загрузки его из файла, значения интерфейсов никак не меняются.


Попробовал создавать указатель на интерфейс прямо в программе - работает.

Попробовал в тестирующем приложении обратиться к указателю на интерфейс Device, который содержится в заголовке, - не разрешает.

UPD: Господа, все прекрасно работает с некоторыми исправлениями. HolyDel, я преклоняюсь перед твоим опытом, но боюсь, что для корректной работы приложения нужно было написать в заголовке следующее:

Код:

#ifdef MY_SUPERLIB
#define SUPERLIB_API __declspec(dllimport)
#else
#define SUPERLIB_API __declspec(dllexport)
#endif

(Если есть define, то импорт, ибо в проекте-то я создаю эту константу)

А так же просто добавить спецификатор SUPERLIB_API к главному интерфейсу приложения. Как оказалось - это то, что было нужно.

Спасибо огромное за помощь. Что бы я без вас делал.

HolyDel 17.05.2013 03:24

Ответ: Передача указателя на интерфейс через DLL
 
чйорт! что ты понимаешь под указателями на интерфейс? что значит "значения интерфейсов никак не меняются. "?


раз метод вызывается - значит с линковкой все в порядке. this нормальный.
может метод кривой?

и почему у тебя shader - типа твой Shader, а не дх-ский?

Samodelkin 17.05.2013 17:47

Ответ: Передача указателя на интерфейс через DLL
 
Эй, DarkMedveD, твой главный косяк в отсутствии нормального конструктора класса Shader. Ты не зануляешь указатели, в частности errorBuffer. В итоге если функция D3DXCompileShaderFromFile завершается нормально, буфер по переданному ей указателю не создается. А после функции у тебя сразу стоит if ( errorBuffer ), который сработает если у тебя в errorBuffer будет мусор, и далее вызовется Release несуществующего буфера.

Я думаю в твоем проекте уместно разделить реализацию и интерфейс. Например сделать абстрактный класс и от него наследовать класс реализацию. В абстрактном классе сделать только методы ( чисто виртуальные ). А в реализацию уже добавить все данные-члены. Добавить функцию которая будет создавать объект типа Реализация, но возвращать указатель на класс типа Интерфейс. Так как в этом случае будет использоваться RTTI то указывать dllexport вообще не понадобиться.
Ну или найти другие паттерны которые делают интерфейс и реализацию раздельно. У тебя сейчас очень плохо - класс вообще не защищен, и пользователь может его испортить как угодно.

HolyDel 17.05.2013 21:04

Ответ: Передача указателя на интерфейс через DLL
 
Код:

Я думаю в твоем проекте уместно разделить реализацию и интерфейс.
я в своем движке так делал. очень удобно.

+ можно будет не подулючть дх к результирующему проекту (не надо будет ставить дхсдк)

Samodelkin 18.05.2013 00:40

Ответ: Передача указателя на интерфейс через DLL
 
Цитата:

я в своем движке так делал. очень удобно.
Да в моем движке тоже так сделано, но есть недостаток всетаки один - интерфейс должен быть хорошо продуман с самого начала, что бывает очень редко. Я вот смотрел паттерны и мне понравился паттерн типа мост - можно наследовать интерфейсы и реализации отдельно друг от друга, и каждый развивать по своему, еще можно несколько интерфейсов цеплять на одну реализацию. Я уже сделал несколько объектов таким образом: например существует реализация камеры, и для нее есть три интерфейса - один сделан для управления камерой в играх от третьего лица, второй для управления свободной камерой и первого лица, третий - для стратегий. У каждого интерфейса свои уникальные наборы методов, общие методы можно объеденить в абстрактный класс интерфейса. Вобщем один и тот же объект-реализация может нацеплять на себя разные интерфейсы в зависимости от контекста в котором он используется, объект при этом не нужно удалять/пересоздавать - интерфейсы меняются прямо в рантайме.

Цитата:

Так как в этом случае будет использоваться RTTI...
Тут я имел ввиду что функция будет вызываться не напрямую, а через vtable и например реализация msvs вызывает методы из dll даже если они не экспортируемые.

DarkMedveD 18.05.2013 16:07

Ответ: Передача указателя на интерфейс через DLL
 
У меня, парни, так и сделано.

Когда создается объект класса Shader все его переменные обнуляются, с чего вы взяли, что у меня нет конструктора? Так же есть и класс, от которого все наследуется, только он никак не относится к шейдерам. От него я наследую все объекты сцены.

А виртуальные методы, свои интерфейсы и все те вкусные штуки, о которых вы говорили - пока слишком сложно для меня.

Спасибо за советы, я постараюсь прислушаться. Если соображалки хватит.


Часовой пояс GMT +4, время: 19:26.

vBulletin® Version 3.6.5.
Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
Перевод: zCarot