在C#中填充二进制缓冲区(1.5到2 kB),我在C DLL中引用它。一切顺利(通过C表示如此),直到我必须返回C#。在最后一刻,程序崩溃了(没有异常抛出,只是一次崩溃:"停止工作")。 Cr是DLL中的C例程。 我在Visual Studio Community 2017工作。 早些时候,我通过字符串和sscanf_s在C中传递数据,这很好,但是想要加快一些事情(并在途中学习不同的东西)。 缓冲区包含各种浮点数,整数和双精度数,它们被正确地拾取到C侧(踩到这样说)。 这是第一段C#代码:
double[,] MemberEndforces = new double[mbCount, 12];
double[,] Deflection = new double[mbCount + 1, 6];
//double[] Reactions = new double[6];
unsafe
{
using (BinaryWriter binWriter = new BinaryWriter(new MemoryStream()))
{
BuildExportBuffer(binWriter);
var buffer = ((MemoryStream)binWriter.BaseStream).GetBuffer();
info = NativeMethods.Cr(
ref buffer, NrOfMod, Eigenfreqs, ref error,
ref rms_resid, MemberEndforces, Deflection);
} // end using
if (info != 0)
throw new MyException(string.Format(ErrorMessages.SomeError, info));
bool converged = error < tolerance;
...
} // end unsafe
这里接下来是接收C程序的头部。只有在最后一个大括号,然后退回到C#,才会发生崩溃。它是这样做的,有或没有safe {}块。
extern "C"
{
char ** buffer;
//char * next_token;
__declspec(dllexport) int __cdecl Cr(
char **_buffer,
int n,
float * eigenFreq,
double * error,
double * rms_resid,
double * endforces,
double * deflection
)
{ buffer = _buffer;
...
// read stuff from buffer and process it...
return ExitCode;
}
这是DLL导入:
[DllImport(@"Cr.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "Cr",
CharSet = CharSet.Ansi, BestFitMapping =false,ThrowOnUnmappableChar =true)]
public static extern int Cr([In] ref byte[] buffer,
[In] int n,
float[] eigenfreq,
ref double error,
ref double rms_resid,
double[,] force,
double[,] defl);
所以:为什么?我做错了什么?如果C部分出现问题,它会比最后一个花括号更早出现,不是吗? C代码的最后一部分会释放大量的malloc-ed块(不是缓冲区!)。
答案 0 :(得分:0)
根据MSDN上的this post,在调用外部函数时,多维数组没有被编组,因此必须将数组转换为单维数组。
我看到这些数组是输出缓冲区,所以对于调用你可以这样做:
double[] MemberEndforces = new double[mbCount * 12];
double[] Deflection = new double[(mbCount + 1) * 6];
//...
info = NativeMethods.Cr(
ref buffer, NrOfMod, Eigenfreqs, ref error,
ref rms_resid, MemberEndforces, Deflection);
然后,在调用函数后,您需要将这些数组转换为多维数组:
double[,] mMemberEndforces = new double[mbCount, 12];
double[,] mDeflection = new double[mbCount + 1, 6];
for(int x = 0; x < mbCount; x++)
{
for(int y = 0; y < 12; y++)
mMemberEndforces[x,y] = MemberEndforces[x * 12 + y];
}
for(int x = 0; x < mbCount + 1; x++)
{
for(int y = 0; y < 6; y++)
mDeflection[x,y] = Deflection[x * 6 + y];
}
答案 1 :(得分:0)
我找到了一个解决方案: 我删除了不安全的{}。这不是必需的。 然后我在调用C例程时“取消引用”ref buffer:
using (BinaryWriter binWriter = new BinaryWriter(new MemoryStream()))
{
BuildExportBuffer(binWriter); // fill the buffer with doubles and floats ant ints
var buffer = ((MemoryStream)binWriter.BaseStream).GetBuffer(); // get the buffer's address
info = NativeMethods.Cr( buffer, NrOfMod, Eigenfreqs, ref error, ref rms_resid,
MemberEndforces, Deflection);
.....
etc.
当然,DLLImport也是这样。
同样在C函数中我改变了一些小东西:
extern "C"
{
char ** buffer;
__declspec(dllexport) int __cdecl Cr(
char *_buffer, // <== here and
int n,
float * eigenFreq,
double * error,
double * rms_resid,
double * endforces,
double * deflection
)
{
buffer = &_buffer; // <== here.
显示我如何从缓冲区C端读取双精度语:
double readd(char **buffer)
{
double *f = (double *) *buffer;
*buffer += 8;
return *f;
}
像魅力一样工作。我还是要弄明白为什么。