1

我有一个 c++ 结构,它具有如下所示的 char[20],并且它已打包。

#pragma pack(push, temp_aion_packed, 1)
struct temp
{
  char x[20];
  char y[20];
};
#pragma pack(pop, temp_aion_packed)

现在我如何在 c# 中编写这个结构,以便两者相同。我在c#中写过这样的

[DataContract]
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1),Serializable]
public class temp
{
   [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
     public string x;
   [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
     public string y;
}

下面是 c# 中的 pinvoke 声明

[DllImport("rmsCAPI.dll", CallingConvention = CallingConvention.StdCall, ExactSpelling = true, EntryPoint = "OrderRequirement")]
    public static extern int OrderRequirement(ref temp tmp);

将此结构作为参数调用的 c++ 函数

long __stdcall OrderRequirement(struct temp *tmp)
{
  string p="";
  string q="";
  p=temp->x;
  q=temp->y;
  char buff[2048];
  sprintf(buff,"p: %s\n q: %s\n x: %s\n y: %s\n",p,q,temp->x,temp->y);
 }

但是当我在 c# 中这样做时,它给了我 c++ 中的垃圾数据,当我在 c# 中为它们赋值时。任何人都可以帮忙。

谢谢大家对上述问题的帮助,但是现在我遇到了一个新问题,这是对此的扩展,我将在下面详细提供所有内容。

我在 C++ 中的结构

#pragma pack(push, temp_aion_packed, 1)
struct temp
{
  long req;
  struct type m_type;
  short id;
  char x[20];
  char y[20];
};
#pragma pack(pop, temp_aion_packed)

#pragma pack(push, type_aion_packed, 1)
struct type
{
  short i;
};
#pragma pack(pop, type_aion_packed)

我已经写了这样的等效 c# 结构

[DataContract]
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1),Serializable]
public struct temp
{
   [DataMember]
    public long req;
   [DataMember]
    [MarshalAs(UnmanagedType.Struct)]
     public type m_type;
   [DataMember]
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
     public string x;
   [DataMember]
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
     public string y;
}

[DataContract]
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1),Serializable]
public struct type
{
  [DataMember]
    public short i;
}

下面是我的 c# pinvoke

[DllImport("rmsCAPI.dll", CallingConvention = CallingConvention.StdCall, ExactSpelling = true, EntryPoint = "OrderRequirement")]
    public static extern int OrderRequirement(ref temp tmp);

下面是我的 c++ 方法,它调用 struct 作为参数

long __stdcall OrderRequirement(struct temp *tmp)
{
  char buff[2048];
  sprintf(buff,"req: %ld \n id: %d \n x: %s\n",tmp->req,tmp->id,tmp->x);
}

现在我遇到的问题是,我在 struct temp 中声明了结构变量 m_type(of struct "type"),之前声明的变量 (long req) 在我的 c++ 程序中打印良好,但之后声明的变量没有给出我的任何输出。所以我认为 c# 中的结构声明搞砸了,我无法弄清楚,所以任何人都可以帮忙。

4

1 回答 1

1

您在 C# 中将结构声明为类。这很好,但这意味着该类型的任何变量都已经是一个引用。所以你不需要路过ref。当你通过 ref 传递一个类时,你最终会传递一个指向对象指针的指针。那是太多的间接级别。

因此,C# 代码中的 P/invoke 应该是这样的:

public static extern int OrderRequirement(temp tmp);

修复它的另一种方法是将 保留ref在函数声明中,但将temp类型声明为 astruct而不是 a class。那是因为 astruct是一个值类型。类型为结构的变量是值而不是引用。

两种解决方案都有效,由您选择。


您的 C++ 代码中还有另一个问题。

sprintf(buff,"p: %s\n q: %s\n x: %s\n y: %s\n",p,q,temp->x,temp->y);

您正在传递pand q,它们的类型std::stringprintf并期望%s格式字符串打印它们。那是一个错误。您需要调用c_str()字符串。像这样:

sprintf(
  buff,
  "p: %s\n q: %s\n x: %s\n y: %s\n",
  p.c_str(),q.c_str(),temp->x,temp->y
);

您更新的问题在于,long 在 Windows C++ 中为 32 位,在 C# 中为 64 位。您需要像int在 C# 中一样声明它。你id完全错过了这个领域。

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1),Serializable]
public struct temp
{
    [DataMember]
    public int req;

    [DataMember]
    public type m_type;

    [DataMember]
    public short id;

    [DataMember]
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
    public string x;

    [DataMember]
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
    public string y;
}
于 2012-05-24T16:11:39.963 回答