четверг, 6 августа 2009 г.

Про переводы строк

Писал сегодня получение информации о системе в windows-части диплома. Была задача получить при помощи Windows API несколько значений и запихнуть их в QString-и, которые используются далее по коду.
Казалось бы, что сложного? Написал вот такой код:

// Определяем имя компьютера
char computerName[200]={0};
DWORD dummy = sizeof(computerName)/sizeof(char);
GetComputerNameA(computerName, &dummy);
computer = QString::fromLocal8Bit(computerName);
// Определяем текущий каталог
char currentDir[400]={0};
GetCurrentDirectoryA(sizeof(currentDir)/sizeof(char), currentDir);
curdir = QString::fromLocal8Bit(currentDir);

Всё хорошо, всё компилируется... и ничего не показывает. В соответствующих строках - пусто.

Перечитываю код - вроде всё верно, должно работать. Значит начинаем отладку.
Отладка, как обычно, начинается с полной пересборки проекта. Rebuild - это просто обязательное действие. Оно гарантирует что мы не будем искать ошибку в неправильном коде, который слинковался в результате ошибки компоновки с приращением
(incremental linking) или неправильно разрешенными зависимостями между файлами.

Конечно же пересборка проблему не устранила. Попытка пройтись по шагам в построчном отладчике тоже ничего не дала - проект собран в release, код перемешан и указатель прыгает как бешенный по каждому F10 (Step over).

Зато просмотр окна disassemble (Alt+8) выявил, что функции наши не вызываются вовсе. Как будто и нет этого кода...
И действительно, получившийся EXE не зависит от функций GetComputerNameA , GetCurrentDirectoryA и прочих.

Чтобы убедиться что в эту ветку кода вообще заходит управление, добавил перед вышеприведённым кодом
искусственный бряк:

__asm int 3;

Эта инструкция предназначена для останова в отладчике.

Проект успешно брякнулся, значит код в этой ветке исполняется. Тут моё подозрение пало на компоновщик, и я некоторое время изучал его опции. Всё было верно.

Круг подозреваемых всё более сужался. Было ясно, что что-то происходит при компиляции. Первая мысль была про неверную оптимизацию,
но я её сразу отбросил.
Подумав некоторое время, я стёр строку комментария "// Определяем имя компьютера".
На моё счастье я забыл описать переменную computer выше и получил ошибку.
Но чудо: ошибка указывала на строку с другим номером!

После этого стало всё ясно:
  1. файл изначально писался под linux-ом и переводы строк были обычные для unix-систем (\n).
  2. эту часть файла я дописывал под windows, вставив из другого файла (\r\n).
  3. компилятор запутался в разных переводах строк и не посчитал за перевод строки символы после комментария. Таким образом, следующие строки считались продолжением однострочного комментария...

Проблема решилась в меню File->Advanced Save Options указанием Line Endings и сохранением файла.

Мораль:
  1. переводы строк тоже имеют значение и могут запутать компилятор.
  2. Умение пользоваться инструментарием вроде Dependency Walker или встроенного дисассемблера MSVS сильно ускоряет локализацию проблемы.

Теперь принудительная установка окончаний строк наряду с указанием кодировки в моём обязательном списке того, что надо сделать при таких необычных проблемах с компиляцией.

Комментариев нет:

Отправить комментарий