2009-12-11 3 views
4

Запуск приложения консоли C# Я написал на 64-битной Vista. Вот код:Почему я не могу заставить GetPrivateProfileString работать с помощью pinvoke?

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, этот код был вырезан из образца C# в this page

ответ

4

Этот день тоже разорял мои отбивные. Я думаю, что нашел работу, с которой я действительно не хочу иметь дело: вставьте пустую строку перед заголовком первого раздела в вашем .ini-файле. Теперь запустите приложение и посмотрите, не начинаете ли вы видеть ожидаемые вами значения.

Учитывая, что эта ошибка, по-видимому, существует уже много лет, я удивлен, что MS не исправила ее. Но тогда файлы .ini должны были уйти много лет назад. Что, конечно, забавно, потому что существует много мест, в которых MS использует .ini-файл (например, desktop.ini). Но я думаю, что MS знает об ошибке, потому что я замечаю, что мои файлы desktop.ini содержат лидирующую пустую строку. Hmmm ...

+0

Я забыл упомянуть, я запускаю этот код на XP Pro 32-bit. – Lee

+0

Я просто попробовал GetPrivateProfileSectionW и GetPrivateProfileSectionNamesW. Оба не работают одинаково. Также интересно, когда я пытаюсь получить имена разделов, возвращается только первый раздел, независимо от того, извлекается ли с помощью GetPrivateProfileStringW или GetPrivateProfileSectionNamesW. У меня было достаточно удовольствия с ними. CodePlex выглядит так, будто у него может быть пара замен. Если никто из них не работает, я откажусь от своего. – Lee

+0

О, черт возьми! Ты прав! Раздел, который я пытаюсь прочитать, не может быть первой строкой INI-файла! Невероятно! Кто бы мог знать? Огромное спасибо. – Charles

0

Согласно 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()); 
} 
+0

Комментарий раздел этой страницы также указывается, вы можете использовать INT вместо union на аргументе размера, чтобы предотвратить необходимость использования sb.Capacity. – Charles

+0

Вы получаете недопустимое исключение формата? Или какое исключение/ошибка выбрасывается/возвращается? –

+0

Функция работает успешно, но возвращает ноль. Когда я проверяю Marshal.GetLastWin32Error, он имеет значение 1008 == ERROR_NO_TOKEN – Charles

1

Главное, что я вижу, что вы должны передать в UINT для nРазмер:, а также в качестве возвращаемого значения. Это связано с тем, что параметры return и nSize GetPrivateProfileString - это значения 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.

1

Если путь не указан, GetPrivateProfileString будет искать Test.ini в каталоге Windows.

Старые API, такие как GetPrivateProfileString, хорошо не обрабатывают Юникод (хотя есть функция GetPrivateProfileStringW). Если Test.ini содержит заголовок UTF или символы Unicode, этого может быть достаточно, чтобы предотвратить работу GetPrivateProfileString.

Кроме того, UAC Vista может обрабатывать файлы, находящиеся в «специальных» местах сложными (C: \, C: \ Windows, C: \ Program Files и т. Д.). Попробуйте поместить Test.ini в папку, а не в корень диска C: или выключите UAC. Существует thread on CodeProject, в котором обсуждается GetPrivateProfileString, когда он безуспешно пытается прочитать .ini из папки, управляемой UAC.

+0

Нет. Не так. Я скопировал его в каталог Windows и не повезло. Я также добавил Debug.Assert (File.Exists («Test.ini»)). Я пытаюсь прочитать его в текущем рабочем каталоге. – Charles

+0

Без ведущей пустой строки, но с файлом, сохраненным как ANSI (а не UTF8) GetPrivateProfileString работает правильно. –

0

Возможно, вам стоит взглянуть на решение с открытым исходным кодом, которое будет делать именно это, не беспокоясь о p/Invoke. Проект, предназначенный для .NET, называется nini. Я использую это в проекте, над которым я работаю, и рекомендую его.

Надеюсь, это поможет, С уважением, Tom.

+0

Спасибо, но я пытаюсь решить мое разочарование, а не найти работу. – Charles

+0

@Charles: Хорошо ... вы просмотрели этот http://pinvoke.net/default.aspx/kernel32/GetPrivateProfileString.html, который содержит образец того, как его использовать? – t0mm13b

+0

Вот где я вырезал 'n вставил мой код. – Charles

0

Вы можете проверить содержимое вашего файла test.ini? Учитывая все шаги, которые вы пробовали, я начинаю подозревать, что ваш файл данных не отформатирован правильно (орфографический и т. Д.). Другими словами, GetPrivateProfileString может быть «рабочим», но просто не нахожу вашу строку. На основе кода, который размещен ваш файл test.ini должен выглядеть примерно так:

[AppName]
KeyName = Foo

+0

Nope. INI был технически корректным, но Ли указал на исправление. Раздел, который я читаю, не может начинаться в первой строке INI-файла. – Charles

+0

Я думаю, вы должны написать свой комментарий, прежде чем обнаружите маркер байтового заказа в начале вашего .ini-файла. Потому что, основываясь на этой информации, я бы сказал, что файл .ini был * not * «технически корректным». :-) –

Смежные вопросы