3

Let there be a structure

struct MyDataStructure
{
    int a;
    int b;
    string c;
};

Let there be a function in the interface exposed by a dll.

class IDllInterface
{
    public:
       void getData(MyDataStructure&) = 0;
};

From a client exe which loads the dll, would the following code be safe?

...
IDllInterface* dll = DllFactory::getInterface(); // Imagine this exists
MyDataStructure data;
dll->getData(data);
...

Assume, of course, that MyDataStructure is known to both the client and the dll. Also according to what I understand, as the code is compiled separately for the dll and exe, the MyDataStructure could be different for difference compilers/compiler versions. Is my understanding correct.

If so, how can you pass data between the dll boundaries safely when working with different compilers/compiler versions.

Abdus Khazi
  • 453
  • 3
  • 17
  • 1
    _"Is my understanding correct."_ Yes. – πάντα ῥεῖ May 13 '17 at 07:32
  • Is fine, this is basically how in process COM works. – Richard Critten May 13 '17 at 08:12
  • @Richard No, it's dangerous. The datastructure may significantly differ using different compiler versions, or even different compiler switches. That's why you have to import different DLL versions depending on MSVC's multi / single threading or debug / release models. – πάντα ῥεῖ May 13 '17 at 08:49
  • @πάνταῥεῖ oops sorry about that didn't notice it was not a pod type. `std::string` means exact match of ABI is required. – Richard Critten May 13 '17 at 11:19
  • 1
    Argh. You got that bounty set *just* before I could close this as a duplicate of https://stackoverflow.com/questions/5661738/how-can-i-use-standard-library-stl-classes-in-my-dll-interface-or-abi. – Cody Gray - on strike Jun 14 '17 at 10:35
  • @CodyGray, the duplicate question you pointed out is about stl class which you can't change. I have the source code of the structure and if required will change the code to make it portable. – Abdus Khazi Jun 14 '17 at 10:38
  • "If so, how can you pass data between the dll boundaries safely when working with compilers/compiler versions.", you can't. You must use the same compiler and version to be sure. – Stargateur Jun 14 '17 at 10:42
  • 2
    Your `MyDataStructure` contains a `std::string` object, so it is exactly the same issue as the proposed duplicate. – Cody Gray - on strike Jun 14 '17 at 10:44
  • @Stargateur, is there no way I can marshal and unmarshal my data. – Abdus Khazi Jun 14 '17 at 10:44
  • 1
    If you want portability across different compilers, you must throw away any C++ library type, and only rely on known size types (int16_t and int32_t instead of short and int). You could have some goodies provided by Windows API if you only target this platform. – Serge Ballesta Jun 14 '17 at 12:06

3 Answers3

2

You could use a "protocol" approach. For this, you could use a memory buffer to transfer the data and both sides just have to agree on the buffer layout.

The protocol agreement could be something like:

  1. We don't use a struct, we just use a memory buffer - (pass me a pointer or whatever means toolkit allows sharing a memory buffer.
  2. We clear the buffer to 0s before setting any data in it.
  3. All ints use 4 bytes in the buffer. This means each side uses whatever int type under their compiler is 4 bytes e.g. int/long.
  4. For the particular case of two ints, the first 8 bytes has the ints and after that it's the string data.

    #define MAX_STRING_SIZE_I_NEED 128

    // 8 bytes for ints.

    #define DATA_SIZE (MAX_STRING_SIZE_I_NEED + 8)

    char xferBuf[DATA_SIZE];

So Dll sets int etc. e.g.

void GetData(void* p);

// "int" is whatever type is known to use 4 bytes

(int*) p = intA_ValueImSending;
(int*) (p + 4) = intB_ValueImSending;
strcpy((char*) (p + 8), stringBuf_ImSending);

On the receving end it's easy enough to place the buffered values in the struct:

char buf[DATA_SIZE];
void* p =(void*) buf;
theDll.GetData(p);
theStrcuctInstance.intA = *(int*) p;
theStrcuctInstance.intB = *(int*) (p + 4);
...

If you want you could even agree on the endianness of the bytes per integer and set each of the 4 bytes of each integer in the buffer - but you probably wouldn't need to go to that extent.

For more general purpose both sides could agree on "markers" in the buffer. The buffer would look like this:

<marker>
<data>
<marker>
<data>
<marker>
<data>
...

Marker: 1st byte indicates the data type, the 2nd byte indicates the length (very much like a network protocol).

  • I understand what you mean.. Exchange data at the lowest level and on either side put it in the desired structs. Something along the lines of serialising and de-serialising data between the binaries. Accepting your answer :-) – Abdus Khazi Jun 15 '17 at 06:44
  • Is there any library out there that I can use for this instead of doing this myself? – Abdus Khazi Jun 15 '17 at 06:46
  • 1
    Because it is somewhat low level as you point out, libraries may not be too helpful because they would need to know your compiler environment etc., how many bytes per int, how are ints laid out etc. If you want to use something "built-in" to make this easier then the "protocol" could make a rule that all integers are transferred as string representation. So, you could "sprintf(p, "%x", myIntValue)" to put integers in buffer on sending side and atoi() or similar to convert back to integers. In fact this is probably a good technique because then it bypasses how each side represents integers. –  Jun 15 '17 at 16:13
1

If you want to pass a string in COM, you normally want to use a COM BSTR object. You can create one with SysAllocString. This is defined to be neutral between compilers, versions, languages, etc. Contrary to popular belief, COM does directly support the int type--but from its perspective, int is always a 32-bit type. If you want a 64-bit integer, that's a Hyper, in COM-speak.

Of course you could use some other format that both sides of your connection know/understand/agree upon. Unless you have an extremely good reason to do this, it's almost certain to be a poor idea. One of the major strengths of COM is exactly the sort of interoperation you seem to want--but inventing your own string formation would limit that substantially.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
0

Using JSON for communication.

I think I have found an easier way to do it hence answering my own question. As suggested in the answer by @Greg, one has to make sure that the data representation follows a protocol e.g. network protocol. This makes sure that the object representation between different binary components (exe and dll here) becomes irrelevant. If we think about it again this is the same problem that JSON solves by defining a simple object representation protocol.

So a simple yet powerful solution according to me would be to construct a JSON object from your object in the exe, serialise it, pass it across the dll boundary as bytes and deserialise it in the dll. The only agreement between the dll and exe would be that both use the same string encoding (e.g. UTF-8).

https://en.wikibooks.org/wiki/JsonCpp

One can use the above Jsoncpp library. The strings are encoded by UTF-8 by default in Jsoncpp library so that it convenient as well :-)

Abdus Khazi
  • 453
  • 3
  • 17