0

DLL: Visual Studio 2013 (VC12)/App: Visual Studio 2010 (VC10)Удаление массив указателей аварий приложения вызывающего

Я создаю библиотеку, которая позволяет клиенту доступ к нашей системе. Однако представляется, что при удалении массива указателей, возвращаемых из dll, клиентское приложение выходит из строя. При удалении нормального указателя (без массива) все работает нормально. Идеи? Я заметил, что при использовании VC12 в качестве клиентского приложения массив удаляется ОК, хотя при использовании VC10 я получаю сбой.

Итак, мой вопрос: почему приложение клиента падает при удалении массива указателей при возврате из dll, если возникла проблема с удалением памяти, выделенной dll, не будет удалять систему; быть проблемой?

struct IDevice { 
    virtual ~IDevice() {} 
    char name[64]; 
}; 

class Device : public IDevice { 
public: 
    Device() {} 
    ~Device() {} 
}; 

struct ISystem { 
    virtual ~IDevice() {} 
    virtual Result::Value GetDevices(Collection<IDevice**>* deviceCollection) = 0; 
}; 

class System : public ISystem { 
public: 
    Result::Value GetDevices(Collection<IDevice**>* deviceCollection) { 
     Result::Value result = Result::Success; 
     deviceCollection->collectionSize = 1; 
     deviceCollection->collection = new IDevice*[1]; 

     IDevice* device = new Device(); 
     Utilities::strcpy_safe(device->name, "blackey"); 
     deviceCollection->collection[0] = device; 
     return result; 
    } 
}; 

template<typename T> 
struct Collection { 
    T collection; 
    int collectionSize; 
}; 

///////// Main App /////////// 

LoginInfo loginInfo; 
string ip("192.168.1.2"); 
string u("admin"); 
string password("admin"); 
loginInfo.port = 443; 
loginInfo.ssl = true; 
strncpy_s(loginInfo.ipAddress, ip.c_str(), sizeof(loginInfo.ipAddress)-1); 
strncpy_s(loginInfo.uname, u.c_str(), sizeof(loginInfo.uname)-1); 
strncpy_s(loginInfo.password, password.c_str(), sizeof(loginInfo.password)-1); 

ISystem* system = nullptr; 
// SystemLogin calls into my dll which allocates memory for system OK 
Result::Value result = SystemLogin(&loginInfo, &system); 

Collection<IDevice**> devices; 
// If the line below is uncommented, and the dll is modified to not allocate memory, but instead use this memory 
// then below the delete[] devices.collection; works. 
//devices.collection = new IDevice*[50]; 

// Makes a call into the library which populates devices 
result = system->GetDevices(&devices); 

for (int i = 0; i < devices.collectionSize; i++) { 
    // Each item in the array is deleted OK 
    delete devices.collection[i]; 
} 

// Deleting this array of pointers crashes (see screenshots) when it's memory was allocated in the dll. 
delete[] devices.collection; 

// system deletes OK 
delete system; 

enter image description here

enter image description here

+0

Вы не указали достаточно кода. Как насчет [mcve]. Включите как распределение, так и освобождение. –

+0

Также вы не можете «удалить» что-то созданное с помощью malloc (в случае, если вы это сделали) – GameDeveloper

+0

Дополнительный код. Дайте мне знать, если что-нибудь еще поможет. – Blackey

ответ

1

Вы должны проверить, если клиентское приложение и библиотеки DLL скомпилирована с использованием тех же параметров. Чтобы избежать таких проблем лучше, чтобы либо:

  1. Выделяют/Освобождает память на стороне клиента
  2. Выделяют/Освобождает память на длл (сервер) стороне

Я бы порекомендовал # 1, но я вижу, вы используете # 2. Итак, теперь вам нужно добавить system->FreeDevices(&devices);, который освободит память, но внутри вашего dll.

[править]

Ссылка для чтения из MSDN: https://msdn.microsoft.com/en-us/library/ms235460.aspx

важная часть о различных кинескопы, с которой DLL и клиентское приложение может быть построен. Это может привести точные проблемы вы испытываете:

Кроме того, поскольку каждая копия библиотеки CRT имеет свой собственный менеджер кучи, выделение памяти в одной библиотеке CRT и передавая указатель через границу DLL, чтобы освободиться от других копия библиотеки CRT является потенциальной причиной повреждения кучи.

+0

Я много читал о том, как было бы лучше выделять/освобождать либо на клиенте, либо на dll, хотя для всего количества возвращаемых объектов я не хочу создавать миллионы бесплатных() методов. – Blackey

+0

@Blackey Я добавил несколько ссылок, я боюсь, у вас нет выбора, кроме как написать эти миллионы бесплатных() методов. По крайней мере, это то, что делает большинство API. – marcinj

+0

@Blackey вы также можете добавить обратный вызов к своей DLL, который должен быть предоставлен клиентским кодом, он будет отвечать за выделение памяти. Этот обратный вызов будет использоваться внутри вашей DLL для памяти, которая возвращается клиенту, которая затем будет освобождена кодом клиента. – marcinj

0

Обратите внимание, что в общем-то лучше, что материал создан из 1 DLL должны быть созданы и удалены DLL (только лучшего дизайна, если вы хотите, чтобы избегать вызовите пользовательские удалители, тогда вы должны передать свой собственный распределитель в DLL, называя «новым» сыном, которого вы можете просто вызвать «удалить» на возвращенные объекты.).

Однако код, который вы опубликовали, не может помочь воспроизвести проблему, потому что мы не видим, как объект создается в DLL, поэтому мы не можем помочь.

Однако, поскольку цель, похоже, использует указатель на указатель (довольно плохо, а как насчет std::vector< std::unique_ptr< IDevice> >?) Имеет ту же производительность и позволяет писать меньше кода).

Я предполагаю, что вы сделали следующее внутри GetDevices вызова

typdef IDevice * IDevicePtr; 
//using IDevicePtr = IDevice *; 
void GetDevices(Collection<IDevicePtr*> * devices) 
{ 
    devices->collectionSize = 10; 
    devices->collection = new IDevicePtr[10]; 
    for(int i =0; i<10;i++) 
     devices->collection[i] = new CDevice(); 
} 

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

Обратите внимание, что

  • IDevice должен иметь виртуальный деструктор реализован
  • ISystem должен иметь виртуальный деструктор реализован
+0

Причина, по которой я держусь в стороне от классов stl, я знаю, что они ломаются между разными версиями Visual Studio, поэтому я стараюсь не пропускать stl-объекты через границы dll. Также я хотел бы, чтобы код был совместим с VC10, который не предлагает полную поддержку C++ 11. – Blackey

+0

@Blackey: STL не является особенным. Все классы рискованны в границах DLL, когда разные части создаются с использованием разных компиляторов. Это связано с отсутствием стандартного C++ ABI, даже в версиях MSVC++. Чтобы быть в безопасности, придерживайтесь одного компилятора _or_, используйте только типы C в интерфейсе и распределите дескриптор плюс освобождение в той же DLL. – MSalters

+0

Каждая версия компилятора должна иметь свою собственную DLL @Blackey. Так что не бойтесь материала, разбивающего вещи, он все равно сломается. Если вы хотите сделать «неинверсивный» (он не будет универсальным, конечно) двоичным, вы должны обернуть его интерфейсом C, используя соглашение о смене имени и вызова имени C, поэтому забудьте о передаче параметров шаблона в функции, я боюсь, что это много более универсальное здание X разных DLLS для каждой версии компилятора вместо того, чтобы обертывать все в C-интерфейсе. – GameDeveloper