从C#调用C代码——遇到一些问题

7

我有一些C代码,想要从C#中使用。大部分的转换已经完成。但是还有几个问题。

其中一部分'C'代码看起来像这样。

typedef struct r_GetCPL {

  UInt8 Storage;
  UInt8 Key[16];             //(1) 

  UInt8 *Buff;               //(2)
  Array16 *CryptoKeyIDs;     //(3)

} REPLY_GETCPL;

'C'中定义的别名

typedef unsigned char      UInt8;
typedef unsigned short     UInt16;
typedef unsigned long      UInt32;
typedef unsigned long long UInt64;

typedef UInt8   Array8[8];
typedef UInt8   Array16[16];
typedef UInt8   Array32[32];
typedef UInt8   Array64[64];
typedef UInt8   Array128[128];

我假设我可以用直接的结构定义来替换typedef。这样可以吗?我定义的等价C#结构体是:

public struct REPLY_GETCPL 
{
  Byte Storage;
  Byte[16] Key;          //(1) Is this right?

  UInt8 *Buff;           //(2)  What is the equivalent?
  Array16 *CryptoKeyIDs; //(3)  What is the equivalent?
}

此外,我在几个导入方法上遇到了困难。
void hex_print(char* data, UInt32 length);
DRMKLVItem *NewDRMKLVItem(UInt32 lenBuff, UInt32 cmdId);
RESULT DRMKLVLengthDecode(const UInt8 *s, UInt32 *pLen);

C#

//I think this one is defined right
[DllImport("DoremiSource.dll")]
public static extern void hex_print([MarshalAs(UnmanagedType.LPStr)]string data, UInt32 length);

//(How to convert the function return type?)
[DllImport("DoremiSource.dll")]
public static extern DRMKLVItem *NewDRMKLVItem(UInt32 lenBuff, UInt32 cmdId);  //(4)

//(How do i convert the function parameters here)
[DllImport("DoremiSource.dll", CharSet = CharSet.Ansi)]
public static extern int DRMKLVLengthDecode(const UInt8 *s, ref UInt32 pLen);  //(5)

你似乎没有将代码转换,而是绑定了它,因为你正在从本地库调用函数,而不是重新实现它们。在这种情况下,你可能想要查看使用C++/CLI编写绑定的方法。它可以理解C结构,并定义可直接从C#使用的对象。 - Jan Hudec
1
你还应该使用StructLayout(LayoutKind.Explicit)和成员偏移来注释你的结构体布局。然而,根据这个问题,对于成员指针,你可能会运气不佳。 - DCoder
由于大部分代码已经使用C#绑定,我需要在C#本身中实现这一点。我该如何解决2、3、4和5个问题。如果您有一些关于C++/CLI方法的链接,现在阅读会很好。 - ShaQ.Blogs
有趣的问题...根据我的经验,C# 不喜欢指针。在托管代码中处理指针总是会引起问题。寻找像 IntPtr 结构这样的东西,它应该会帮助很多。 - Nevyn
5个回答

3

您可以使用复杂类型并在C和C#之间进行编组,只需要了解结构的编译选项,然后像以下方式装饰C#类型:

(来源于MSDN)
[StructLayout(LayoutKind.Explicit, Size=16, CharSet=CharSet.Ansi)]
public class MySystemTime 
{
   [FieldOffset(0)]public ushort wYear; 
   [FieldOffset(2)]public ushort wMonth;
   [FieldOffset(4)]public ushort wDayOfWeek; 
   [FieldOffset(6)]public ushort wDay; 
   [FieldOffset(8)]public ushort wHour; 
   [FieldOffset(10)]public ushort wMinute; 
   [FieldOffset(12)]public ushort wSecond; 
   [FieldOffset(14)]public ushort wMilliseconds; 
}

0

你的UInt8 *参数可以在C#中用数组表示,因为它就是一个数组。也就是说,调用C代码时应该使用UInt8[]类型。

生产代码示例:

[DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
public static extern void CCode(ushort[] data, ushort[] otherData, int width, int height);

dataotherData在C代码中映射到unsigned short *

关于返回指向已分配的struct指针的C代码,您可以始终将C#代码包装在unsafe区域中,并在C#中使用&*运算符。如果您构建了具有相同布局的有效C# struct,则此方法将按预期工作。


0

在C#中使用C的一种方法是将C代码封装在C++/CLI对象中。

C++/CLI非常适合处理安全和不安全代码。您可以做任何C#和C中可以做的事情。

通过创建托管对象并填充它以调用未管理的C代码的(静态)函数来定义接口。在每个函数中,您可以执行所需的编组,以将参数和结果从托管代码转换为未管理的代码,反之亦然。

这样,您就可以完全控制接口,并获得一个很好的调试访问点。


-1

如果你想要“转换”成C#,最好使用类而不是结构体。

例如,你的struct REPLY_GETCPL最好改为一个类,因为我看到你也在尝试使用指针。 此外,你可以将与结构体相关的某些方法放入一个类中,并在该类的对象内使用组合功能。


抱歉造成困惑。我正在尝试从C#中调用一些现有的C代码(并非实际转换它)。在这方面,我如何实现(2)、(3)、(4)和(5)? - ShaQ.Blogs
4
这是一个很糟糕的建议。REPLY_GETCPL 应该是一个结构体。 - Security Hound
如果你正在使用指针,为什么将其定义为类会更好?结构体也可以有方法。 - anton.burger

-1

如果你想使用指针,你需要一个不安全的块。看一下这个网站


我不明白为什么这个被踩了。有人能解释一下我错过了什么吗?因为ref在C代码中不起作用。你需要一个C指针,而我知道的唯一方法是在C#中使用unsafe块。 - DjSol

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