Что-то не так с этим дизайном для инициализации массива в DLL?

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

struct Construction{
 public:
  Construction(){
   //do the initialization thing and read the needed data from the file
  }
  SomeType sTArray[100];
};

__declspec(dllexport) Construction obj();

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

SomeType (&arrayRef)[100]=obj.sTArray;

Теперь вы думаете, что я ошибаюсь в любом контексте?


person Pooria    schedule 30.11.2010    source источник


Ответы (2)


Да, в какой-то момент вы устроили себе очень неприятный сюрприз.

  1. Конструкторы глобальных объектов запускаются во время запуска среды выполнения C для библиотеки DLL.
  2. Код запуска среды выполнения C выполняется во время DLLMain.
  3. Во время DLLMain вы держите блокировку загрузчика DLL.
  4. Конструктор вашего объекта может включать вызовы Win32 API, которые загружают другие системные DLL.
  5. Попытка загрузить другую DLL, уже удерживая блокировку загрузчика DLL, приводит к быстрой смерти вашего процесса.

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

struct Construction{
public:
  Construction() : bInit(false) {};
  SomeType* GetArray()
  {
    if(!bInit)
    {
      //do the initialization thing and read the needed data from the file
      bInit = true;
    }
    return sTArray;
  };
private:
  SomeType sTArray[100];
  bool bInit;
};

__declspec(dllexport) Construction obj();

Конечно, это нужно разделить на отдельные заголовочные файлы и файлы реализации.

person Jon    schedule 30.11.2010
comment
+1. Правило: Не делайте ничего сложного в DLLMain. Чтение из файла, безусловно, считается сложным. Кроме того, если вы попытаетесь прочитать файл в DLLMain, нет хорошего способа обработать ошибку, если файл не существует. - person user9876; 30.11.2010

Поскольку CRT в DLL и в исполняемом файле могут быть разными, вы должны предоставить Construction метод release, который удалит выделенный объект. Таким образом вы гарантируете, что функция освобождения будет вызвана из соответствующей CRT. Также нужно вернуть Construction по указателю, чтобы исключить операции копирования. Следующий код иллюстрирует метод, как он может быть реализован:

// DLL export header
struct IConstruction {
protected:
  virtual ~IConstruction() {}
public:
  virtual void release() =0;
  virtual SomeType& get_array() =0;
};

__declspec(dllexport) IConstruction* obj();

-

// DLL implementation
struct Construction : public IConstruction {
  SomeType sTArray[100];

  Construction() { /* do initialization */ }
  virtual void release() { delete this; }
  virtual SomeType& get_array() { return sTArray; }
  virtual ~Construction() { /* do clean up */ }    
};

IConstruction* obj() { return new Construction; }
person Kirill V. Lyadvinsky    schedule 30.11.2010
comment
ОМГ, ты его скомпилировал? он не компилируется - person Pooria; 30.11.2010
comment
@Pooria, это не такой полный пример, как твой. Этот код здесь для иллюстрации техники. Например, я не знаю, как SomeType было объявлено в вашем коде. - person Kirill V. Lyadvinsky; 30.11.2010
comment
Я не понимаю, почему вы видите, что эти вещи нужны, хотя единственная причина, по которой я поместил массив в структуру, - это возможность инициализировать его конструктором во время сборки dll. - person Pooria; 30.11.2010
comment
Поскольку вы пытаетесь экспортировать экземпляр объекта, вам нужно беспокоиться о том, как этот экземпляр будет освобожден. Вы не можете вызвать new в одной DLL, а затем delete в другой (или в исполняемом файле), потому что вы не можете гарантировать, что функции освобождения будут одинаковыми в каждом модуле. Прочтите, например, этот вопрос. - person Kirill V. Lyadvinsky; 30.11.2010
comment
@Pooria: Если вы не сделаете что-то в этом роде, вы можете и, вероятно, испортите кучу. - person John Dibling; 30.11.2010