2010-08-03 4 views
1

У меня есть код, который отлично работал под Delphi 2007, но ломается под D2010. Он включает передачу строки, преобразование ее в PWideChar (в частности, указатель WideString, а не указатель UnicodeString), выполнение некоторой обработки, а затем вызов SysFreeString на нем. Он отлично работает до тех пор, пока не будет пропущена пустая строка, а затем разрывается SysFreeString. Он называет кучу вещей, которые в конечном итоге поднимают точку останова Int 3 внутри NTDLL.DLL. Продолжая мимо этой точки приводит кЧто может заставить SysFreeString попасть в точку останова Int 3?

проекта поднял класс исключение $ C0000005 с сообщением "Нарушение прав доступа на 0x7747206e: чтение из адреса 0x539b8dba.

Который, если вы внимательно присмотритесь, не является стандартным сообщением о нарушении прав доступа.

В верхней части трассировки стека, когда он попадает в Int 3 выглядит следующим образом:

:774e475d ; ntdll.dll 
:774afad0 ; ntdll.dll 
:774e5de9 ; ntdll.dll 
:774a6dff ; ntdll.dll 
:76fc1075 ; C:\Windows\system32\ole32.dll 
:770e443a ; C:\Windows\system32\oleaut32.dll 
:770e3ea3 oleaut32.SysFreeString + 0x4a 

Кто-нибудь есть какие-либо идеи, что здесь происходит?

Редактировать (с комментариями):

Это не WideString, хотя. Это PWideChar порождена StringToOleStr, и нет двойных свободных ошибок при непустой строки передаются. К сожалению, я действительно не могу опубликовать образец кода , потому что это третья сторона компонент, который находится под защитой авторских прав. (И я не могу просить у них поддержки, потому что это больше не поддерживается. В основном, один большой беспорядок все дело в.)

ответ

2

Это трудно диагностировать, не видя ваш фактический код, однако WideString автоматически вызывает SysFreeString(), когда он выходит за рамки. Похоже, ваш код может сделать второй вызов SysFreeString() в памяти, который уже был освобожден. Сам WideString совсем не изменился между D2007 и D2010, но есть и другие аспекты обработки строк Delphi. Возможно, вы неправильно управляете строками. Можете ли вы показать свой фактический код?

+0

Это не WideString. Это PWideChar, сгенерированный StringToOleStr, и нет никаких двойных ошибок при передаче непустой строки. К сожалению, я не могу опубликовать образец кода, потому что это сторонний компонент, который находится под защитой авторских прав. (И я не могу попросить их о поддержке, потому что она больше не поддерживается. В принципе, все это один большой беспорядок.) –

+0

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

3

Я собираюсь попробовать психическую отладку. У вас в вашем приложении какая-то куча коррупции, а SysFreeString - несчастная жертва (сложно сказать без символов ОС, возможно, вы должны установить пакеты символов MSFT для своей ОС).

Попробуйте включить верификатор приложения (в частности, перфоманс) для вашего приложения и посмотреть, не сработает ли он раньше.

0

+1 для ответа Ларри Остермана.

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

Вы можете установить крючки на SysAllocString/SysFreeString и перенаправить их в диспетчер памяти (который должен быть в полном режиме отладки) для сбора дополнительной информации. Или вы можете просто передать эти вызовы на оригинальные функции, установив только фильтр, который следит за действиями памяти.

И вы можете install debug symbols, чтобы получить больше информации (я не уверен, что отладчик Delphi может использовать его, но Process Explorer - может. Вы можете подключить его к вашему процессу и увидеть стек вызовов).

+0

Ваша ссылка мертва. – johnny

0

Простой тест показывает, что вам нужно быть очень внимательным к тому, что вы делаете в каком порядке.

Итак: даже если вы не можете опубликовать небольшой пример, можете ли вы указать, что вы делаете немного подробнее?

Плохая отладка; игнорировать вещи ниже; см. комментарий.

SysFreeString() вызывается в конце звонка Allocate(), даже если он возвращает PWideChar:

program ShowStringToOleStrBehaviourProject; 

{$APPTYPE CONSOLE} 

uses 
    SysUtils; 

function Allocate(const Value: UnicodeString): PWideChar; 
begin 
    Result := StringToOleStr(Value); 
// implicit SysFreeString(WideChars); 
end; 

procedure Run; 
var 
    WideChars: PWideChar; 
begin 
    WideChars := Allocate('Foo'); 
    Writeln(WideChars); 
end; 

begin 
    try 
    Run(); 
    except 
    on E: Exception do 
     Writeln(E.ClassName, ': ', E.Message); 
    end; 
end. 

Обратите внимание на консоль по-прежнему выводит «Foo», потому что память не была перезаписана еще.

--jeroen

+1

Существует вызов SysFreeString(), выполняемый внутри самого StringToOleStr() (в частности, внутри System._WStrFromPWCharLen()), а не в конце Allocate() (который не подразумевает ничего не вызывать после завершения StringToOleStr()), , Этот вызов SysFreeString() не передается WideString/BSTR, который выделяет StringToOleStr(). Проходит временный WideString/BSTR, который SysFreeString() использует внутри для своего результата, и это временное пустое, так что SysFreeString() фактически не работает. –

+0

@Remy: Вы правы, мой плохой, у меня были точки останова, один двинулся на одну линию из-за некоторых вырезать/вставить вне IDE. –

0

Это может быть различными причинами такого рода ошибки:

  1. Вы пытаетесь освободить с SysFreeString памятью, которая выделяется не с SysAllocString, но, например, с CoTaskMemAlloc.
  2. У вас есть куча правильная.

Недостатки кучи трудно локализовать. Функция HeapSetInformation может быть очень полезна. Например, вы можете использовать

HeapSetInformation(NULL,HeapEnableTerminationOnCorruption,NULL,0); 

Другой хороший способ использования HeapValidate функции. Например, вы можете определить функцию, которая проверить все отвалы пФ процесса (код C, который можно легко переписать в Delphi):

BOOL MyHeapValidate (void) 
{ 
    HANDLE hProcessHeaps[1024]; 
    DWORD i; 
    DWORD dwNumberOfHeaps; 
    BOOL bSuccess = FALSE; 

    dwNumberOfHeaps = GetProcessHeaps (sizeof(hProcessHeaps)/sizeof(hProcessHeaps[0]), 
             hProcessHeaps); 
    if (dwNumberOfHeaps > sizeof(hProcessHeaps)/sizeof(hProcessHeaps[0])) { 
     MessageBox(NULL, TEXT("GetProcessHeaps()"), 
        TEXT("Error in MyHeapValidate()"), MB_OK); 
     return FALSE; 
    } 

    for (i=0; i<dwNumberOfHeaps; i++) { 
     bSuccess = HeapValidate (hProcessHeaps[i], 0, NULL); 
     if (!bSuccess) 
      return bSuccess; 
    } 

    return bSuccess; 
} 

Использование этой функции может быть, как следующее:

void BadFunction(BSTR bstr) 
{ 
    LPOLESTR psz = OLESTR("Test12"); 
    lstrcpy (bstr, psz); 
} 

int main() 
{ 
    LPOLESTR psz = OLESTR("Test"); 

    BSTR bstr = SysAllocString (psz); 

    // verify that before call of BadFunction() all process heaps are OK! 
    if (!MyHeapValidate()) { 
     _tprintf(TEXT("heap is corrupted after the step 1.\n")); 
     return 1; 
    } 

    BadFunction(bstr); 

    if (!MyHeapValidate()) { 
     _tprintf(TEXT("heap is corrupted after the step 1.\n")); 
     return 1; 
    } 
    SysFreeString (bstr); 

    return 0; 
} 

Что касается вставки MyHeapValidate() в разных предполагаемых местах, вы можете очень быстро локализовать место коррупции.