PInvoke - 尝试读取或写入受保护的内存。这通常表明其他内存已经损坏。

4
我曾经见过“尝试读取或写入受保护的内存”的错误。通常当我没有正确设置C#结构时,就会出现这个错误。我已经成功使用其他调用,但是这一个没有配合起来。 我几乎确定这可能是我的函数调用和结构都导致了问题。
C语法
int CardTransaction(pTRequest req, char *ProductCodes)

请求结构(我进行了压缩,因为有重复的数据类型)

typedef struct _cardRequest
{
  unsigned short RedemptionNum
  long TotalAmount;
  unsigned char filler1[257];
  char CardNumber[80];
  unsigned char cardType;
} TRequest, *pTRequest;

C#函数调用

[DllImport("card.dll"), CallingConvention.Cdecl, CharSet = CharSet.Auto)]
public static extern int CardTransaction(ref CardRequest cardRequest, [MarshalAs(UnManagedType.LPStr)] StringBuilder productCodes);

ProductCodes为空,因此我只是实例化了一个空的StringBuilder对象并将其传递。这是我认为可能存在问题的地方之一。

C#结构

[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]    
public struct CardRequest
{
  public uint16 RedemptionNum

  public int TotalAmount;

  [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 257)]
  public string filler1;

  [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
  public string CardNumber;

  public byte cardType;

} 

char filler1[257] 不是 TStr,因为它的元素始终为 1 字节大小。 - usr
1
你的打包值可能是错误的,这可能会导致出现此类错误,但您需要告诉我们您用于C代码的编译器以及是否使用任何结构打包选项或编译指示。 - tinman
我没有关于C代码的任何信息。这就是提供给我的全部内容。而这一直是问题所在。至于C#方面,我尝试让它工作。由于缺乏足够的信息,这对我来说一直是一个巨大的挑战。当我最终陷入困境时,我会寄希望于这里的经验能够审查我所犯的错误 :) - Mike Stone
我一直认为是 char filler[257] 导致了问题。我不确定应该如何设置这个 unsigned char。 - Mike Stone
1个回答

2
明显的问题是C代码使用了一个对齐的结构体,但由于某些原因,您选择将C#结构体压缩。从C#代码中删除Pack = 1以使这两个结构匹配。
此外,填充数组看起来更像是一个字节数组而不是一个字符串。我会像这样声明它:
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 257)]
public byte[] filler1;

如果您想将“null”传递给“productCodes”参数,那么我希望您可以这样做。我自己不能回忆起来曾经这样做过,但通常当您将“null”传递给p/invoke时,编组器将向本机代码传递“NULL”。

你好David, 将pack=1去掉后,错误信息消失了。 然而,从DLL返回的结果是不正确的。所以它可能不喜欢byte[]。 但是,对我来说最重要的是,你帮助我摆脱了错误信息。 由于我很少得到有关结构和函数的信息......你说“明显的问题是C代码使用了一个对齐的结构体”。因为我正在学习和摸索......是什么让你知道它是对齐的?还有另一个结构体需要pack=1才能正常工作。 再次感谢。 - Mike Stone
结构体默认按对齐方式排列。 - David Heffernan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接