gpt4 book ai didi

c# - 将字符串从 C++ 编码到 C# 时出现异常

转载 作者:太空宇宙 更新时间:2023-11-04 07:16:54 25 4
gpt4 key购买 nike

将固定大小的字符从 C 编码到 C# 时,一切似乎都运行良好。但是,当我尝试将动态分配的字符串数组从 C 编码到 C# 时,出现异常。

在第一种情况下,我有一个带有

的 C 结构
struct SyntaxTree {
int nodeType;
char TypeString[64];
char DataTypeString[64];
char *ValueString;
int NodeStart;
int NodeEnd;
int TokenStart;
int TokenEnd;
int Errno;
char ParameterBuffer[256];

int NodeHandle;
struct SyntaxTree* sibling; /* first sibling */
struct SyntaxTree* child; /* first child */

SyntaxTree(sn_Node* node) {
nodeType = node->Type;
strcpy(this->TypeString, "\0");
strcpy(this->DataTypeString, "\0");
/*strcpy(this->ValueString, "\0");*/
strcpy(this->ParameterBuffer, "\0");
sibling = 0;
child = 0;
}
};
typedef struct SyntaxTree SyntaxTree;

在 C# 中

namespace MISPLEditor
{
using System;
using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct ManagedTree
{
/// <summary>
/// The node type.
/// </summary>
[MarshalAs(UnmanagedType.U4)]
public int NodeType;

/// <summary>
/// The type string.
/// </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string TypeString;

/// <summary>
/// The data type string.
/// </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string DataTypeString;

/// <summary>
/// The value string.
/// </summary>
[MarshalAs(UnmanagedType.LPStr]
public string ValueString;

/// <summary>
/// The node start.
/// </summary>
[MarshalAs(UnmanagedType.U4)]
public int NodeStart;

/// <summary>
/// The node end.
/// </summary>
[MarshalAs(UnmanagedType.U4)]
public int NodeEnd;

/// <summary>
/// The token start.
/// </summary>
[MarshalAs(UnmanagedType.U4)]
public int TokenStart;

/// <summary>
/// The token end.
/// </summary>
[MarshalAs(UnmanagedType.U4)]
public int TokenEnd;

/// <summary>
/// The errno.
/// </summary>
[MarshalAs(UnmanagedType.U4)]
public int Errno;

/// <summary>
/// The parameter buffer.
/// </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string ParameterBuffer;

/// <summary>
/// The node handle.
/// </summary>
[MarshalAs(UnmanagedType.U4)]
public int NodeHandle;

/// <summary>
/// The sibling.
/// </summary>
public IntPtr Sibling;

/// <summary>
/// The child.
/// </summary>
public IntPtr Child;
}
}

我遇到的异常是:

An unhandled exception of type 'System.AccessViolationException' occurred in mscorlib.dll

Additional information: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

我在这里似乎没有得到任何额外的信息。 ValueString 中肯定有一个值。我更像是 C# 专家而不是 C 专家,所以我不知道 ValueString char 是否实际上以一种好的方式初始化。

case(SN__NOD_STRING):
strcpy(TypeString, "STRING");
char *PoolString;
PoolString = sn_GetPoolString(&SyntaxTree->StringPool,
Node->Value.Constant.Value.StringHandle);
ValueString = (char * ) CoTaskMemAlloc(strlen(PoolString) + 1);
strcpy(ValueString, PoolString);
break;

在 C# 中:

var unmanagedPtr = UnmanagedMisplApi.sn_parse(
builder, tableBuilder, sourcePrefix.Length, error, info, errorParams);
SyntaxTree root;

if (unmanagedPtr != IntPtr.Zero)
{
/* Marshal unmanagedPtr into managed struct */
var tree = (ManagedTree)Marshal.PtrToStructure(
unmanagedPtr, typeof(ManagedTree));

填充 C 结构是通过递归函数完成的。其实,首先lex和yacc是用来pare一串代码的。然后解析树被转换为 SyntaxTree 结构。在此代码中,sn_NodeToString 触发带有上述 case(SN__NOD_STRING) 的代码。递归函数的代码是:

SyntaxTree* BuildTree(sn_NodeHandle nodeHandle, sn_SyntaxTree* snSyntaxTree) {

SyntaxTree *first, *second, *third;
sn_Pool* pool = &snSyntaxTree->NodePool;
first = 0; second = 0; third = 0;

// ----------------------------------------------------------

/* NOTE (+ further uses): this operation is inherently not 64-bit safe! */
sn_Node* node = (sn_Node*) sn_GetPoolElement(
&snSyntaxTree->NodePool, nodeHandle);

if(!node) return (SyntaxTree*) NULL;

SyntaxTree* tree = new SyntaxTree(node);
tree->child = 0;
tree->sibling = 0;

/* sn_NodeToString triggers the code with the case(SN__NOD_STRING): */
sn_NodeToString(node, snSyntaxTree, tree->TypeString,
tree->DataTypeString, tree->ValueString);
tree->NodeStart = node->NodeSpan.NodeStart - SubtractCodeLength;
tree->NodeEnd = node->NodeSpan.NodeEnd - SubtractCodeLength;
tree->TokenStart = node->NodeSpan.TokenStart;
tree->TokenEnd = node->NodeSpan.TokenEnd;
tree->NodeHandle = nodeHandle;
tree->Errno = node->Value.ErrNo;
if(node->ErrorArgs)
strcpy(tree->ParameterBuffer, node->ErrorArgs);

int arity = sn_GetArity(node);
switch(arity) {
case 0:
break;
case 1:
first = BuildTree(UNOP(node), snSyntaxTree);
tree->child = first;
break;
case 2:
first = BuildTree(LEFTOP(node), snSyntaxTree);
second = BuildTree(RIGHTOP(node), snSyntaxTree);
if(first) { /* Todo Why wouldn't there be no first? */
first->sibling = second;
tree->child = first;
}
break;
case 3:
first = BuildTree(FIRSTOP(node), snSyntaxTree);
second = BuildTree(SECONDOP(node), snSyntaxTree);
third = BuildTree(THIRDOP(node), snSyntaxTree);
first->sibling = second;
second->sibling = third;
tree->child = first;
break;
case -1: /* Treated as variadic arity */
if(sn_GetPoolElement(pool, UNOP(node)), snSyntaxTree)
first = BuildTree(UNOP(node), snSyntaxTree);

if(sn_GetPoolElement(pool, LEFTOP(node)), snSyntaxTree)
first = BuildTree(LEFTOP(node), snSyntaxTree);

if(sn_GetPoolElement(pool, FIRSTOP(node)), snSyntaxTree)
first = BuildTree(FIRSTOP(node), snSyntaxTree);

if(sn_GetPoolElement(pool, RIGHTOP(node)), snSyntaxTree)
second = BuildTree(RIGHTOP(node), snSyntaxTree);

if(sn_GetPoolElement(pool, SECONDOP(node)), snSyntaxTree)
second = BuildTree(SECONDOP(node), snSyntaxTree);

if(sn_GetPoolElement(pool, THIRDOP(node)), snSyntaxTree)
third = BuildTree(THIRDOP(node), snSyntaxTree);

if(first)
first->sibling = second;
if(second)
second->sibling = third;
if(first)
tree->child = first;
break;
default:
break;
}

return tree;
}

最佳答案

这个问题的文档太少了,它看起来像一个结构成员,但 C 代码看起来肯定不像是在填充一个结构。有些暗示它实际上是 C++ 代码,您不能调用非静态 C++ 成员函数,this 指针将无效。并将触发 AVE。

C 代码中存在一个严重的错误,很可能会导致 AVE,但通常不会立即发生。您忘记了 C 字符串是零终止的,您必须为字符串分配 strlen()+1 个字节。 sprintf() 调用在写入 0 时会损坏堆。像这样的堆损坏非常难以调试,因为它的副作用可能需要一段时间才能变得明显,只有幸运时才能快速获得 AVE。

然后是内存管理问题,有人将不得不再次释放字符串缓冲区。 pinvoke 编码器通常在复制结构时承担该职责。并且会在 AVE 调用 CoTaskMemFree() 释放字符串缓冲区时死掉。字符串缓冲区未使用 CoTaskMemAlloc() 分配,pinvoke 编码器或您的 C# 代码没有希望调用正确的 free() 函数。

这就是我从发布的信息中看到的全部内容。 AVE的机会够多了。使用 C++/CLI 与此代码互操作通常是一个好主意,调用正确的 free() 实现的几率只有一半。当您给调用者分配内存的机会时,内存管理问题就消失了。

关于c# - 将字符串从 C++ 编码到 C# 时出现异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23867161/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com