在c#中使用pinvoke来调用64位的sprintf和朋友

我在C#中使用pinvoke来调用_snwprintf时遇到了一个有趣的问题。 它适用于整数类型,但不适用于浮点数。

这是在64位Windows上,它在32位上运行良好。

我的代码如下,请记住,这是一个人为的例子来展示我所看到的行为。

class Program { [DllImport("msvcrt.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] private static extern int _snwprintf([MarshalAs(UnmanagedType.LPWStr)] StringBuilder str, IntPtr length, String format, int p); [DllImport("msvcrt.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] private static extern int _snwprintf([MarshalAs(UnmanagedType.LPWStr)] StringBuilder str, IntPtr length, String format, double p); static void Main(string[] args) { Double d = 1.0f; Int32 i = 1; Object o = (object)d; StringBuilder str = new StringBuilder(32); _snwprintf(str, (IntPtr)str.Capacity, "%10.1lf", (Double)o); Console.WriteLine(str.ToString()); o = (object)i; _snwprintf(str, (IntPtr)str.Capacity, "%10d", (Int32)o); Console.WriteLine(str.ToString()); Console.ReadKey(); } } 

这个程序的输出是

  0.0 1 

它应该在第一行打印1.0而不是0.0,到目前为止我很难过。

我不确定为什么你的调用不起作用,但这些方法的安全版本在x86和x64中都能正常工作。

以下代码可以正常工作,如预期的那样:

 class Program { [DllImport("msvcrt.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] private static extern int _snwprintf_s([MarshalAs(UnmanagedType.LPWStr)] StringBuilder str, IntPtr bufferSize, IntPtr length, String format, int p); [DllImport("msvcrt.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] private static extern int _snwprintf_s([MarshalAs(UnmanagedType.LPWStr)] StringBuilder str, IntPtr bufferSize, IntPtr length, String format, double p); static void Main(string[] args) { // Preallocate this to a given length StringBuilder str = new StringBuilder(100); double d = 1.4; int i = 7; float s = 1.1f; // No need for box/unbox _snwprintf_s(str, (IntPtr)100, (IntPtr)32, "%10.1lf", d); Console.WriteLine(str.ToString()); _snwprintf_s(str, (IntPtr)100, (IntPtr)32, "%10.1f", s); Console.WriteLine(str.ToString()); _snwprintf_s(str, (IntPtr)100, (IntPtr)32, "%10d", i); Console.WriteLine(str.ToString()); Console.ReadKey(); } } 

可以使用未记录的__arglist关键字:

 using System; using System.Text; using System.Runtime.InteropServices; class Program { [DllImport("msvcrt.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] private static extern int _snwprintf(StringBuilder str, int length, String format, __arglist); static void Main(string[] args) { Double d = 1.0f; Int32 i = 1; String s = "nobugz"; StringBuilder str = new StringBuilder(666); _snwprintf(str, str.Capacity, "%10.1lf %d %s", __arglist(d, i, s)); Console.WriteLine(str.ToString()); Console.ReadKey(); } } 

请不要使用它。

uint是32位。 snprintf的长度参数是size_t,在64位进程中是64位。 将第二个参数更改为IntPtr,这是与size_t最接近的.NET等价物。

此外,您需要预先分配StringBuilder。 目前您有缓冲区溢出。

在第二个函数的最后一个参数上尝试MarshalAs R8(这是真实/浮动8(这是双))。