【问题标题】:Why can't I get GetPrivateProfileString to work via pinvoke?为什么我不能通过 pinvoke 让 GetPrivateProfileString 工作?
【发布时间】:2009-12-11 22:28:29
【问题描述】:

运行我在 64 位 Vista 上编写的 c# 控制台应用程序。代码如下:

class Class1
{
    static void Main(string[] args)
    {
        Debug.Assert(File.Exists(@"c:\test.ini"));
        StringBuilder sb = new StringBuilder(500);
        uint res = GetPrivateProfileString("AppName", "KeyName", "", sb, sb.Capacity, @"c:\test.ini");
        Console.WriteLine(sb.ToString());
    }
    [DllImport("kernel32.dll")]
    static extern uint GetPrivateProfileString(string lpAppName, string lpKeyName, string lpDefault, StringBuilder lpReturnedString, int nSize, string lpFileName);
}

我相信我会得到一个大大的“DUH!”寻求答案,但我不明白为什么这不起作用。除了 Debug.Assert 之外,此代码是从 this page 的 c# 示例中截取的

【问题讨论】:

    标签: c# 64-bit pinvoke windows-vista


    【解决方案1】:

    这一天也让我大吃一惊。我想我找到了一种解决方法,我真的不想处理它:在 .ini 文件的第一节标题之前插入一个空行。现在运行您的应用,看看您是否开始看到您所期望的值。

    考虑到这个错误显然已经存在多年,我很惊讶微软现在还没有修复它。但是,.ini 文件应该在几年前就消失了。这当然很有趣,因为 MS 有很多地方使用 .ini 文件(例如 desktop.ini)。但我认为 MS 意识到了这个错误,因为我注意到我的 desktop.ini 文件包含一个前导空白行。嗯……

    【讨论】:

    • 我忘了说,我在 XP Pro 32 位上运行这个代码。
    • 我刚刚尝试了 GetPrivateProfileSectionW 和 GetPrivateProfileSectionNamesW。两者都以同样的方式失败。同样有趣的是,当我尝试检索部分名称时,只返回第一个部分,无论是使用 GetPrivateProfileStringW 还是 GetPrivateProfileSectionNamesW 获取。我已经玩够了这些。 CodePlex 看起来可能有几个替代品。如果这些都不起作用,我会自己动手。
    • 天哪!你是对的!我试图读取的部分不能是 INI 文件的第一行!难以置信的!谁会知道?非常感谢。
    • 快速澄清。问题是 VS 编辑器将 UTF-8 字节顺序标记 (EF BB BF) 放在文件的开头。 INI 读/写函数不处理 BOM。由于我正在阅读的部分位于第一行,GetPrivateProfileString 没有看到“[AppName]”而是“\xEF\xBB\xBF[AppName]”,因此它从未找到我正在寻找的部分。在文件的开头添加一个空行,将 BOM 放在第一行,将“[AppName]”放在第二行,以便可以找到它。大 DUH 在我身上。
    • 谢谢!我正在努力解决同样的问题。无法弄清楚为什么它可以与另一个应用程序一起使用,而不是我的新应用程序。在 ini 文件的顶部添加一个空行解决了这个问题。
    【解决方案2】:

    我看到的主要内容是您应该为 nSize 传递一个 uint 以及返回值。这是因为GetPrivateProfileString的return和nSize参数是DWORD的值,是无符号的32位整数。

    我个人使用过PInvoke.net上的语法:

    [DllImport("kernel32.dll", CharSet=CharSet.Unicode)]
    static extern uint GetPrivateProfileString(
       string lpAppName, 
       string lpKeyName,
       string lpDefault, 
       StringBuilder lpReturnedString, 
       uint nSize,
       string lpFileName);
    

    此外,您需要放置文件的完整路径,除非该文件位于 Windows 目录中。来自文档:

    如果此参数不包含文件的完整路径,则系统在Windows目录中搜索该文件。

    【讨论】:

    • 确实,似乎需要uint,而不是int
    【解决方案3】:

    根据 pinvoke.net,nSize 应该是一个 UINT。他们还在他们的示例中使用了绝对路径。

    除了这些差异,我看不到其他任何东西。

    如果它抛出了一个无效的格式异常,尝试将目标平台设置为 x86 来解决问题。

    pinvoke.net 的用法示例是

    [DllImport("kernel32.dll", CharSet=CharSet.Unicode)]
        static extern uint GetPrivateProfileString(
        string lpAppName, 
        string lpKeyName,
        string lpDefault, 
        StringBuilder lpReturnedString, 
        uint nSize,
        string lpFileName);
    
    
     static void Main(string[] args)
     {
       StringBuilder sb = new StringBuilder(500);
       uint res = GetPrivateProfileString("AppName", "KeyName", "", sb, (uint)sb.Capacity, @"c:\test.ini");
       Console.WriteLine(sb.ToString());
     }
    

    【讨论】:

    • 该页面的评论部分还指出,您可以在 size 参数上使用 int 而不是 union,以防止不得不大小写 sb.Capacity。
    • 您是否收到无效格式异常?或者抛出/返回什么异常/错误?
    • 函数看似成功,但返回零。当我检查 Marshal.GetLastWin32Error 时,它的值是 1008 == ERROR_NO_TOKEN
    • 对不起,这是一条红鲱鱼。 Marshal.GetLastWin32Error 在我调用 GetPrivateProfileString 之前是 1008。
    • @Charles:如果 DllImport 属性中没有 SetLastError=true,GetLastWin32Error() 可能会返回虚假值。
    【解决方案4】:

    如果未指定路径,GetPrivateProfileString 将在 Windows 目录中查找 Test.ini。

    GetPrivateProfileString 等旧 API 不能很好地处理 Unicode(即使有 GetPrivateProfileStringW 函数)。如果 Test.ini 包含 UTF 标头或 Unicode 字符,这可能足以阻止 GetPrivateProfileString 正常工作。

    此外,Vista 的 UAC 会使处理“特殊”位置(C:\、C:\Windows、C:\Program Files 等)中的文件变得棘手。尝试将 Test.ini 放在文件夹而不是 C: 驱动器的根目录中,或关闭 UAC。有一个 thread on CodeProject 讨论了 GetPrivateProfileString 在尝试从 UAC 控制的文件夹中读取 .ini 时静默失败。

    【讨论】:

    • 不。不是。我将它复制到 Windows 目录中,但没有运气。我还添加了一个 Debug.Assert(File.Exists("Test.ini")) - 我正在尝试读取当前工作目录中的那个。
    • 没有前导空行但文件保存为 ANSI(而不是 UTF8)GetPrivateProfileString 工作正常。
    【解决方案5】:

    也许您应该考虑寻找一种开源解决方案,该解决方案完全可以做到这一点,而您不必担心 p/Invoke 的问题。针对 .NET 的项目称为 nini 我在我正在处理的项目中使用它,我推荐它。

    希望这会有所帮助, 此致, 汤姆。

    【讨论】:

    • 谢谢,但我正在努力解决我的挫败感,而不是找到解决办法。
    • @Charles:好的……你看过这个pinvoke.net/default.aspx/kernel32/GetPrivateProfileString.html,其中包括如何使用它的示例?
    • @Charles:顺便说一句...我不禁注意到...您确实说过您是在 64 位 vista 上写的...api 会改变吗?只是想先问一些简单的问题来帮助你......
    • @Charles:在示例中,它是 uint res = GetPrivateProfileString(...) 并且在你的代码中你有 int?希望对您有所帮助...
    • 根据该页面上的 cmets,int 和 uint 可以互换。如果我按原样使用他们的 c# 示例(您必须修复编译器错误),它仍然无法正常工作。
    【解决方案6】:

    您能验证您的test.ini 文件的内容吗?鉴于您尝试过的所有步骤,我开始怀疑您的数据文件格式不正确(拼写错误等)。换句话说,GetPrivateProfileString 可能正在“工作”,但只是找不到您的字符串.根据您发布的代码,您的 test.ini 文件应如下所示:

    [应用名称]
    键名=foo

    【讨论】:

    • 不。 INI 在技术上是正确的,但 Lee 指出了修复 - 我正在阅读的部分不能从 INI 文件的第一行开始。
    • 我想你一定是在你发现 .ini 文件开头的字节顺序标记之前写了你的评论。因为基于这些信息,我会说 .ini 文件不是“技术上正确的”。 :-)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-27
    • 1970-01-01
    相关资源
    最近更新 更多