我正在开发一个Thunderbird扩展,它将通过C ++ / CLR中介调用现有的C#代码。我遇到了一个只能使用C ++ / CLR DLL或直接C DLL重现的障碍。
我的功能是
__declspec(dllexport)char* strTest2()
{
char *p = "Hello World";
char buffer[200];
char *q = buffer;
strcpy_s(q,200,p);
return p;
}
如果我返回p,我会回到“Hello World”。如果我返回q,我会得到垃圾或挂起。检查调试器中的p和q表明它们都包含相同的数据。
我正在使用这个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();
在Mozilla调试器中,str被识别为CData类型的对象,并且挖掘得足够远,表明它在每种情况下都包含一个字符串,尽管我无法看到该字符串是什么。
js-ctype的文档说如果某个东西被CData直接引用,那么它将保持活着状态。但在我看来,这并没有正确发生。
如果我指定一个大的“静态”缓冲区,例如
char *r = "\0....\0";
然后使用strcpy_s将文本复制到该缓冲区并返回r然后字符串通过。如果我正在使用一个直接C的DLL项目。但是如果我尝试使用C ++ / CLR DLL项目,我需要使用它来获取我现有的C#代码然后尝试写入硬编码缓冲区导致程序崩溃。
所以我看到了前进的三种方式;
有谁知道如何让其中一个工作?
答案 0 :(得分:3)
你不能从C中的函数返回指向堆栈变量的指针 - 一旦函数返回,堆栈的那部分就会被回收并且指针不再有效。
有效的替代方法包括使用静态全局(注意,这不是线程安全的)或让函数从堆中分配新内存,返回指向它的指针,并为客户端提供相应的函数来释放完成它后的记忆。
答案 1 :(得分:3)
如果我返回p,我会回到“Hello World”。如果我回q,我会得到垃圾 或挂起。检查调试器中的p和q显示它们都包含 相同的数据。
此行为的原因是p
指向一个字符串常量,该常量存储在DLL数据段中的固定位置 - 只要DLL被加载/映射,该地址就会保持有效。
但是,q
指向堆栈分配的数据,这些数据将在运行时回收/重用...