【问题标题】:Passing char * parameter to C++ dll function from C# GUI从 C# GUI 将 char * 参数传递给 C++ dll 函数
【发布时间】:2017-06-20 18:28:21
【问题描述】:

我有一个作为 DLL 文件的 cpp 函数,可以从某个文件路径读取文件,如果成功则返回“0”,如果失败则返回其他数字:

short __stdcall ReadPx(char *filePath, MAP *map, int *num);

这个函数在我的 C# 中定义为:

[DllImport("lib.dll", EntryPoint = "ReadPx", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern System.Int16 ReadPx([MarshalAs(UnmanagedType.LPStr)] string filePath, ref MAP Map, ref Int32 numE);

它在main函数中被调用为:

var pix = new MAP();
int num = 1;
string path = "C:/Users/Visual Studio 2015/Projects/testWrapper2/Map\0";
System.Int16 Output = ReadPx(path, ref pix, ref num);
Console.WriteLine(Output);

该函数运行正常,但给出了无效的文件路径错误。我认为问题可能是在 C# 代码中将“String filePath”定义为 Unicode(每个字符 2 个字节),而 ReadPx 需要一个指向简单 ASCII 字符串的指针。这就是为什么我尝试了如下所示的一些修改,但文件路径错误仍然存​​在。

[DllImport("lib.dll", EntryPoint = "ReadPx", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern System.Int16 ScReadPixMap(IntPtr filePath, ref PIXMAPENTRY pixMap, ref Int32 numEntries);

IntPtr ptrCString = (IntPtr)Marshal.StringToHGlobalAnsi(path);
System.Int16 output = ReadPx(ptrCString, ref pix, ref num);
Marshal.FreeHGlobal(ptrCString);

感谢您的一些想法和建议。谢谢。

【问题讨论】:

  • 调试代码的时候,filePath这个参数有什么作用?
  • 您使用了错误的调用约定。如果您有 c++ 接口,则必须使用 CDecl 而不是 StdCall,后者是 Windows 调用约定
  • 原版正确传递字符串。还有什么不对劲的。做一些调试。
  • @ThomasFlinkow 在我调试时,filepath 包含完整路径。
  • @fins 它没有显示任何奇怪的字符吗?所以也许这不是编码

标签: c# c++ pointers dll dllimport


【解决方案1】:

C++ 声明是:

short __stdcall ReadPx(char *filePath, MAP *map, int *num);

您的 C# 声明是:

[DllImport("lib.dll", EntryPoint = "ReadPx", 
    CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern System.Int16 ReadPx(
    [MarshalAs(UnmanagedType.LPStr)] string filePath, 
    ref MAP Map, 
    ref Int32 numE
);

鉴于我们现有的信息,这没有错误。但是请注意,我们不知道MAP 是如何声明的,并且那里可能存在错误。我想,第二个和第三个参数可能是数组。这些细节只有你自己知道。

p/invoke 声明过于冗长。如果是我,我会这样写:

[DllImport("lib.dll")]
public static extern short ReadPx(string filePath, ref MAP Map, ref int numE);

在您的问题中,您继续声明您这样调用函数:

string path = "C:/Users/Visual Studio 2015/Projects/testWrapper2/Map\0";
System.Int16 Output = ReadPx(ref path, ref pix, ref num);

您添加的显式空终止符毫无意义。该框架确保编组后的字符串具有空终止符。明确添加一个并没有什么坏处,但它没有任何意义。删除它。

更大的问题是如何传递字符串参数。代码说:

ref path

嗯,这不会编译,因为您的 p/invoke 将第一个参数声明为值参数,确实应该这样做。

显然,问题中提供的信息是错误的。也许您的 p/invoke 声明错误地将第一个参数声明为 ref 参数,这与您的问题中所述相矛盾。

总之,鉴于我们掌握的信息,您在问题中提供的 p/invoke 声明是准确的。您的实际代码没有使用该声明,而是使用了错误的声明。

【讨论】:

  • 是的,我实际代码中的 ref 参数是一个错字。真正的问题是我的第二个参数应该首先用初始值声明。谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-01-29
  • 1970-01-01
  • 2012-12-02
  • 2011-11-18
  • 2012-07-26
  • 2011-05-22
  • 1970-01-01
相关资源
最近更新 更多