1

I am fairly new to C++ and working with DLLs. I have a main application that aggregates results from different measurements. As the measurements are different from case to case I decided to put them into external DLLs so they can be loaded at runtime (they simply all export the same function). The idea is to just load them like this so the aggregator can be extended depending on the runtime needs:

typedef int (*measure)(measurement &dataHolder);

int callM() {
  [...]
  measurement dataHolder;
  lib = LoadLibraryA("measureDeviceTypeA.dll");
  measure measureFunc = (measure)GetProcAddress(lib, "measureFunc");
  pluginFunc(dataHolder);
  [...] // close the lib and load the next one depending on found Devices
}

This works pretty well for simple datatypes (depending on the actual definition of the struct "measurement") such as this:

typedef struct measurement {
    DWORD realPBS;
    DWORD imaginaryPBS;
    int a;
} measurement;

Now there also may be a string of arbitrary length (char representations of results). I would like to put them into the measurement struct as well and fill them inside the actual worker function inside the DLL. My first assumption was that it would be easy to just use std::string, which works sometimes and sometimes not (as it will reallocate memory on std::string().append() and this might break (access violation) depending on the actual runtime environment of the program and the dll). I read here and here that returning a string from a function is a bad idea.

So what would be the "proper" C++ way of returning arbitrary length strings from such a call? Is it helpful at all to pass a struct to the DLL or should I split it into separate calls? I don't want to have pointers dangling around or unfreed memory when I close the DLL again.

Jason Aller
  • 3,541
  • 28
  • 38
  • 38
Ben
  • 121
  • 4
  • 1
    C++ doesn’t support incompatible runtimes on any platform. You should avoid any C++ objects in your external interfaces if that’s the case. – Daniel Jul 02 '20 at 14:28
  • The problem is, that the string data is often allocated on the heap, so it has to be freed / managed somehow. – Bernd Jul 02 '20 at 15:13

2 Answers2

1

This won't work with std::string, as noted by Dani in the comments. The problem is that std::string is a type that belongs to your implementation, and different C++ implementations have different std::strings.

For DLL's specifically (Microsoft), you do have another alternative. COM is an ancient technology, but it still works today and is unlikely to go away ever. And it has its own string type, BSTR. Visual Studio provides a helper C++ class bstr_t for your own code, but on the interface you'd use the plain BSTR from _bstr_t::GetBSTR.

BSTR relies on the Windows allocator SysAllocString from OleAut32.dll

MSalters
  • 173,980
  • 10
  • 155
  • 350
1

The problem is, that the string data is often allocated on the heap, so it has to be freed / managed somehow. You could think, hey std::string is returned by value - so why I need to care about memory management. The problem is that usually only very small strings are stored "inside" the class. For larger strings the string class contains a pointer to some "heap-storage".

Dlls can be used from and with different programming languages - which is the reason that dlls do not share a "memory manager", freeing in the dll would fail.

To solve this you need to have two function calls, one which returns a pointer / handle to the data and one to free it. Or the caller could give the callee some pointer where it wants the data to be stored. You need for that a maximum-byte-count, too.

As you can see, there are some reasons why you should avoid these APIs - but it is not always possible. See for example the Windows API (there you can find both approaches).

Another approach would be to ensure a shared memory manager, but this is tricky somehow because it must be done really early!

Bernd
  • 2,113
  • 8
  • 22