2010-03-19 39 views
2

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

这是在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,到目前为止我难倒。

+1

什么样的恶魔会开车送你到PInvoke的C运行时的printf家庭? C调用约定,可变参数,非操作系统部署的DLL,它像breweing完美的风暴! – 2010-03-19 17:10:01

+1

是不是string.Format足够你? – zneak 2010-03-19 17:12:50

+0

@Remus - 我意识到这并不是一件好事,而是在寻求麻烦,然而有时候我们都必须忍受现有的代码。此外,尽管可能会有多么可怕,但在我看来,它仍然应该起作用。 – WildCrustacean 2010-03-19 17:13:13

回答

5

我不完全确定您的呼叫无法正常工作,但secured versions of these methods在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(); 
    } 
} 
+0

这不起作用。如果我使用Single而不是Double,则会发生同样的问题。我会改变这个例子来使用lf。看到为什么我试图做到这一点的主要意见。 – WildCrustacean 2010-03-19 17:18:35

+0

@bde:我只是重复了我的答案。那里的代码工作在x86和x64,没有问题。 – 2010-03-19 17:43:29

+0

谢谢,这也适用于我。 – WildCrustacean 2010-03-19 17:48:59

0

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

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

+0

试过了,同样的问题。我会更新我的示例,但它应该使用IntPtr。 – WildCrustacean 2010-03-19 17:10:46

0

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

+0

也试过了,结果一样。 – WildCrustacean 2010-03-19 17:37:12

+0

我刚刚阅读msdn的帮助,第二个参数预期是在参数数组的形式,我不知道如何编组到c的参数数组,即使你发送你的参数为double,可能是运行时不期待它在同样的形式,我想知道int如何工作,double也应该工作。尝试将它作为最后一个参数的double数组中的第一项发送并尝试放置参数。 – 2010-03-19 17:50:25

+0

C和C++可变参数在.NET参数数组下面非常不同。 sprintf绝对使用传递值。 – 2010-03-19 18:19:22

3

有可能与无证__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(); 
    } 
} 

请不要使用。

+0

对不起复制后,没有看到你的__arglist时发布我的答案:) – 2010-03-19 17:52:30

+0

我其实已经尝试过,今天早些时候,我无法得到它的工作。所有这些示例代码对我来说都是打印格式字符串。也许我在这里错过了一些东西。我同意我宁愿不使用这种方法。 – WildCrustacean 2010-03-19 17:56:36

+0

@bde:Project + Properties,Application选项卡,Platform Target = x86。这是危险。 – 2010-03-19 18:00:02