char* gets lost on return from C DLL to js-ctypes

554 views Asked by At

I'm working on a Thunderbird extension that will call existing C# code via a C++/CLR intermediary. I've hit a snag that can be reproduced using just the C++/CLR DLL, or a straight C DLL.

My function is

 __declspec(dllexport)char* strTest2()
 {
    char *p = "Hello World";
    char buffer[200];
    char *q = buffer;

    strcpy_s(q,200,p);

    return p;
 }

If I return p, I get "Hello World" back. If I return q, I get garbage or a hang. Inspecting p and q in the debugger shows they both contain the same data.

I'm calling the function using this js;

 Components.utils.import("resource://gre/modules/ctypes.jsm");
 var lib = ctypes.open("<path to DLL>");

 var getStr = lib.declare("strTest2",
                      ctypes.default_abi,
                      ctypes.char.ptr);

 var str = getStr();
 alert(str.readStringReplaceMalformed());

 lib.close();   

In the Mozilla debugger, str is identified as an object of type CData, and digging down far enough shows it's containing a string in each case, though I'm unable to see what that string is.

The docs for js-ctype say if something's referenced directly by a CData then it'll be kept alive. But it looks to me like this isn't correctly happening.

If I specify a large 'static' buffer such as

 char *r = "\0....\0";

then use strcpy_s to copy the text into that buffer and return r then the string comes through. If I'm using a DLL project where it's straight C. But if I try that with the C++/CLR DLL project I need to use to be able to get at my existing C# code then attempts to write to the hardcoded buffer cause the program to crash.

So there's three ways I see of going forward;

  • get runtime-created strings to persist on switching back from C++/CLR to js-ctypes,
  • get C++/CLR to allow me to alter a static buffer- without that causing problems with multiple instances,
  • get the JS to provide a buffer that C++/CLR can populate.

Does anyone know how to get one of those to work?

2

There are 2 answers

1
nobody On BEST ANSWER

You can't return a pointer to a stack variable from a function in C - once the function returns, that part of the stack gets reclaimed and the pointer is no longer valid.

Alternatives that are valid include using a static global (caution, this is not thread-safe) or having the function allocate new memory from the heap, return a pointer to that, and provide a corresponding function for clients to use to free the memory when they are done with it.

0
Bukes On

If I return p, I get "Hello World" back. If I return q, I get garbage or a hang. Inspecting p and q in the debugger shows they both contain the same data.

The reason for this behavior is that p points to a string constant, which is stored at a fixed location within the DLL's data segment - this address remains valid as long as the DLL is loaded/mapped.

However, q points to stack allocated data, which will be reclaimed/reused at runtime...