Что-то не так с этим дизайном для инициализации массива в DLL? - PullRequest
1 голос
/ 30 ноября 2010

Предположим, что у нас есть 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;

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

Ответы [ 2 ]

5 голосов
/ 30 ноября 2010

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

  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();

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

3 голосов
/ 30 ноября 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; }
...