четверг, 8 января 2009 г.

Перевод проекта на Delphi 2009 – первые грабли

Сегодня выдалось немного свободного времени и решил я поковырять свой проект на предмет совместимости с Delphi 2009. В одной из моих предыдущих заметок я уже говорил, что проект отказался компилироваться. Начнем разбираться почему.

Первым делом компилятор споткнулся о «мертвую» (нигде не использующуюся) процедуру RunDosAndWait которая запускала консольное приложение фоном и ждала его выполнения. Честно говоря, я намеренно не стал убирать эту процедуру и берег ее для такого случая, как она мне сегодня пригодится – речь пойдет немного позже.

Итак, не работает функция CreateProcess(nil, PChar(CommandLine), nil, nil, true, 0, nil, nil, StartupInfo, ProcessInfo). Попробовал поискать в инете, толком решения проблемы не нашел. На одном из форумов тоже сталкивались с этой проблемой, но решения предложено не было.

Пробуем просто создать процесс:

Получаем следующую картинку:



Поскольку новая Delphi 2009 это один большой Юникод, то думаю следует поискать причину в этом. Попробуем разобраться почему код отлично работавший в предыдущих версиях резко перестал работать. В предыдущих версиях (не Юникод) при вызове функции CreateProcess фактически вызывалась ANSI-версия этой функции – CreateProcessA, в новой Юникод-версии фактически вызывается CreateProcessW и по всей видимости в этом то и кроется основная проблема. А так как CreateProcess это самая что ни на есть WinAPI функция, попробуем поискать ответы на наши вопросы в MSDN.

Вот собственно, что и следовало доказать. Функции CreateProcessA и CreaterocessW ведут себя по-разному:

The Unicode version of this function, CreateProcessW, can modify the contents of this string. Therefore, this parameter cannot be a pointer to read-only memory (such as a const variable or a literal string). If this parameter is a constant string, the function may cause an access violation.

Что в вольном переводе означает, что эта функция может изменить содержание строки и поэтому параметр не может быть константой или строкой символов. Напрашивается самый простой вариант:


Но в этом случае программа так же откажется работать. Дело в том, что оптимизирующий компилятор Delphi вместо нашей переменной все равно воткнет константу, поскольку он видит, что значение нигде по коду не меняется. Чтобы проверить правильность наших выводов относительно функции CreateProcess слегка изменим код, чтобы обновить оптимизатор:


Теперь программа исполняется так как положено. Единственное, что мне необходимо было сделать, чтобы обойти этот баг – это изменить описание функции. Вместо:

function RunDosAndWait(const CommandLine: string; OutSrings: TStrings): boolean;

написать

function RunDosAndWait(var CommandLine: string; OutSrings: TStrings): boolean;.

Все - с этим разобрались, но в этой же процедуре осталась ошибка с процедурой OemToChar, переводящей кириллический текст из консоли в удобочитаемый вид. Но об этом – в другой раз.

8 комментариев:

  1. ух...спасибо за ответ.уже часа 2 сижу не могу понять в чем дело :)

    ОтветитьУдалить
  2. Анонимный8 июня 2009 г., 15:10

    А у меня при переходе с D7 на D2009 при использовании таблиц парадокса, неверно стали отображаться э... кажется их перфиксами зовут, сорри за безграмотность, а именно ранешь было "1 254,34 р." а сейчас "1 254,34 @=" - последний символ похож больше на знак как на клавише ентер.
    Изменение поля создание его вновь никчему не привели, все как есть так и осталось, и так везде гдеб я не использовал данные из таблицы в компонентах. Может подскажите знатоки в чем причина?

    ОтветитьУдалить
  3. Спасибо за совет.
    Владимир, Украина, г. Славянск

    ОтветитьУдалить
  4. А можно и так
    var
    si: STARTUPINFOA;
    pi: PROCESS_INFORMATION;
    begin
    // заполняем значения структуры STARTUPINFO по умолчанию
    ZeroMemory(@si, SizeOf(STARTUPINFOA));
    si.cb := SizeOf(STARTUPINFO);
    // запускаем процесс Notepad
    if not CreateProcessA(nil, // имя не задаем
    'Notepad.exe', // имя программы
    nil, // атрибуты защиты процесса устанавливаем по умолчанию
    nil, // атрибуты защиты первичного потока по умолчанию
    False, // дескрипторы текущего процесса не наследуются
    0, // по умолчанию NORMAL_PRIORITY_CLASS
    nil, // используем среду окружения вызывающего процесса
    nil, // текущий диск и каталог, как и в вызывающем процессе
    si, // вид главного окна - по умолчанию
    pi) then // информация о новом процессе

    Логвинов Владимир
    Украина, г.Славянск

    ОтветитьУдалить
  5. В принципе да - если явно вызвать "старую" не юникод версию этой функции - CreateProcessA - то проблем не будет.

    ОтветитьУдалить
  6. Очень интересует как получать читаемые строки из консоли. Никак не могу справиться с этой задачей.

    ОтветитьУдалить
  7. Анонимный5 марта 2010 г., 6:36

    Огромное спасибо! Я поначалу растерялся, увидев такую проблему.)

    ОтветитьУдалить
  8. Анонимный16 июня 2011 г., 10:00

    Я использую UniqueString для этого.

    Кстати, об этом писал ещё Рихтер в каком-то лохматом году.

    ОтветитьУдалить