1

Lets assume we've got a DLL and there should be an array stored globally in it that's going to be exported, the thing is we want to initialize it by reading some content from a file, so personally I find myself no other way than putting it in a struct to be able to initialize using constructor:

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

__declspec(dllexport) Construction obj();

Now where it's going to be used, the programmer can initialize a reference to it and then use the reference like below:

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

Now would you think I'm wrong in any context?

Pooria
  • 2,017
  • 1
  • 19
  • 37

2 Answers2

5

Yes, you've set yourself up for a very nasty surprise at some point.

  1. Global object constructors run during the startup of the C runtime for the DLL.
  2. The C runtime startup code runs during DLLMain.
  3. During DLLMain, you are holding the DLL loader lock.
  4. Your object constructor may involve calls to Win32 api's that load other system DLL's.
  5. Tring to load another DLL while already holding the DLL loader lock results in a swift death for your process.

I'd recommend that you hold off on initializing the array until the first attempt to access it, which will require that you expose the array indirectly as the result of a function call:

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

Of course, this would need to be split into separate header and implementation files.

Jon
  • 3,065
  • 1
  • 19
  • 29
  • 1
    +1. The rule is "Don't do anything complex in DLLMain". Reading from a file certainly counts as complex. Also, if you try to read a file in DLLMain, there's no nice way to handle the error if the file doesn't exist. – user9876 Nov 30 '10 at 15:47
3

Since CRT in the DLL and in the executable could be different you should supply Construction with a release method that will delete allocated object. In this way you will guarantee that the deallocation function will be called from the appropriate CRT. Also you need to return Construction by pointer to exclude copying operations. The following code illustrates the method how it could be implemented:

// 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; }
Kirill V. Lyadvinsky
  • 97,037
  • 24
  • 136
  • 212
  • OMG did you compile it? it doesn't get compiled – Pooria Nov 30 '10 at 15:32
  • @Pooria, this is not compilable full example as well as yours. This code here is to illustrate the technique. For instance, I don't know how `SomeType` was declared in your code. – Kirill V. Lyadvinsky Nov 30 '10 at 15:35
  • I don't get why you're seeing these stuff needed, though the only reason why I've put the array inside a struct is to be able to initialize it by constructor at dll build time. – Pooria Nov 30 '10 at 15:36
  • Since you are trying to export the instance of an object you need to worry about how this instance will be deallocated. You can't call `new` in one DLL and then `delete` in another(or in the executable), because you cannot guarantee that deallocation functions will be the same in each module. Read [this](http://stackoverflow.com/questions/1344126/memory-allocation-and-deallocation-across-dll-boundaries) question for example. – Kirill V. Lyadvinsky Nov 30 '10 at 15:42
  • @Pooria: If you don't do something along these lines, you could and probably will corrupt the heap. – John Dibling Nov 30 '10 at 15:47