2

In a dll, I'm doing

std::vector<foo*>* v = new std::vector<foo*>();
foo* f = new foo();
v->push_back(f);
// size of v is 1
process->Result = static_cast<void*>(v);  // Result is of type void*

and in a exe that uses the dll, I do

if (process->Result != NULL)
{
   std::vector<foo*>* v = static_cast<std::vector<foo*>*>(process->Result);
   int size = v->size(); // <-- size is garbage (221232 or something like that)   

}

Result has to be a void*. Does anyone know why I'm not able to pass back the vector properly ? Am I doing something wrong with the casting?

Thanks

Professor Chaos
  • 8,850
  • 8
  • 38
  • 54
  • 1
    Is the address the same on both sides? Are you compiling both sides with exactly the same runtime library *and* compile options? – Greg Hewgill Jun 21 '12 at 04:09
  • @GregHewgill the address is the same on both sides but the dll was created using vs2005 and the exe was created using vs2010. – Professor Chaos Jun 21 '12 at 04:18
  • Right, that definitely won't work then. Because the standard library data structures such as `std::vector` are implemented in header files, the actual data representation can be different between versions of the compiler. – Greg Hewgill Jun 21 '12 at 04:32
  • @GregHewgill Thanks! so what is the best way to return a collection of something from my dll to exe with this setup? – Professor Chaos Jun 21 '12 at 04:35

1 Answers1

4

Because the standard library data structures such as std::vector are implemented in header files, the actual data representation can be different between versions of the compiler. If you have some code compiled with VS2005 and other code with VS2010, you cannot reliably return a std::vector in this way.

Assuming foo is the exact same data layout between components, you could return an array of raw foo* pointers that has been allocated with HeapAlloc. Do not use malloc() because again, your components are using different runtime libraries and you can't free() something that has been allocated with a different allocator. By using HeapAlloc and HeapFree, you are using the Win32 standard allocator which is shared across your whole process.

By way of example, you could do something like:

v->push_back(f);
HANDLE heap = GetProcessHeap();
foo **r = (foo **)HeapAlloc(heap, 0, (v->size() + 1) * sizeof(foo *));
std::copy(v.begin(), v.end(), r);
r[v->size()] = NULL; // add a sentinel so you know where the end of the array is
process->Result = r;

and then in your calling module:

foo **v = static_cast<foo**>(process->Result);
for (int i = 0; v[i] != NULL; i++) {
    printf("index %d value %p\n", i, v[i]);
}
HANDLE heap = GetProcessHeap();
HeapFree(heap, 0, v);
Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
  • I have never used `HeapAlloc()`. Would you be able to show it's usage with my example from the question or point me to a resource that explains the usage. – Professor Chaos Jun 21 '12 at 05:06
  • Good answer about std::vector being in header-files, see also my question: http://stackoverflow.com/questions/5661738/common-practice-in-dealing-with-warning-c4251-class-needs-to-have-dll-inter, which had an excellent answer from John Dibling. – André Jun 21 '12 at 06:14
  • On your HeapAlloc example: Why would a simple array Foo*[] not work, combined with int numFoos? Ofcourse, all this presumes that a Foo class itself is safe to transport across dll-boundaries... – André Jun 21 '12 at 06:21
  • 1
    @Andre: I was assuming that the interface between modules was a single `void *`, and not anything else like an integer count. If the OP has space for a count, that would work equally well. – Greg Hewgill Jun 21 '12 at 06:22