Странная проблема перезаписи памяти в экземпляре моего класса - PullRequest
2 голосов
/ 12 июля 2011

Эта проблема связана с этим вопросом, который я задавал ранее.Код, предоставленный @RRUZ, работает, но кажется, что это не совсем правильно, или я делаю что-то не так.

После выполнения GetSharedFiles происходит странная вещь в случае TMyObject.Поле FMyEvent, которое было (и должно быть) ноль, указывает на некоторые случайные данные.

То, что я обнаружил всего 5 минут назад, это то, что если я выключаю , оптимизация впараметры компилятора он прекрасно работает после перестройки.Так может это какая-то ошибка компилятора?

Вот снимок кода (Delphi 2009 Windows 7 64 bit):

unit Unit17;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm17 = class(TForm)
    btnetst: TButton;
    procedure btnTestClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

type
  TMyEvent = procedure(Sender: TObject) of object;

type
  TMyObject = class(TObject)
  private
    FMyEvent: TMyEvent;
    function GetSharedFiles: TStringList;
  public
    property OnEvent: TMyEvent read FMyEvent write FMyEvent;
    procedure DoSomething;
  end;

var
  Form17: TForm17;

implementation

uses
  ActiveDs_TLB,
  ActiveX;

function ADsGetObject(lpszPathName:WideString; const riid:TGUID; out ppObject):HRESULT; safecall; external 'activeds.dll';

{$R *.dfm}

procedure TForm17.btnTestClick(Sender: TObject);
var
  MyObject: TMyObject;
begin
  MyObject := TMyObject.Create;
  try
    MyObject.DoSomething;
  finally
    if Assigned(MyObject) then
      MyObject.Free;
  end;
end;

{ TMyObject }

procedure TMyObject.DoSomething;
var
  TmpList: TStringList;
begin
  try

    TmpList := GetSharedFiles; //something is overwritting the memory in object and puts random data to FMyEvent?
    if Assigned(FMyEvent) then
      ShowMessage('WTF'); //this should not be called, and if you comment out GetSharedFiles it won't.

  finally
    if Assigned(TmpList) then
      TmpList.Free;
  end;
end;


function TMyObject.GetSharedFiles: TStringList;
var
  FSO           : IADsFileServiceOperations;
  Resources     : IADsCollection;
  Resource      : OleVariant;
  pceltFetched  : Cardinal;
  oEnum         : IEnumvariant;
begin
  Result := TStringList.Create;
  //establish the connection to ADSI
  if ADsGetObject('WinNT://./lanmanserver', IADsFileServiceOperations, FSO) = S_OK then
  begin
    //get the resources interface
    Resources := FSO.Resources;
    //get the enumerator
    oEnum:= IUnknown(Resources._NewEnum) as IEnumVariant;
    while oEnum.Next(1, Resource, pceltFetched) = 0 do
    begin
      Result.Add(LowerCase(Format('%s%s%s',[Resource.Path,#9,Resource.User])));
      Resource:=Unassigned;
    end;
  end;
end;    
end.

Есть идеи, что происходит не так?Спасибо за ваше время.

Ответы [ 4 ]

2 голосов
/ 12 июля 2011

Соглашение о вызовах для этого должно быть, вероятно, stdcall, а не safecall:

function ADsGetObject(lpszPathName:WideString; const riid:TGUID; out ppObject):HRESULT; safecall; external 'activeds.dll';

Повтор

Типичные функции COM возвращают результат HRESULT;Они используют его для передачи кода ошибки или S_OK, если все прошло нормально.Используя этот тип функции, у вас обычно будет такой код:

if CallComFunction(parameters) = S_OK then
  begin
    // Normal processing goes here
  end
else
  begin
    // Error condition needs to be dealt with here.
  end

Поскольку с ошибками обычно невозможно справиться, Delphi предоставляет нам safecall соглашение о псевдо-вызове.Это не настоящее соглашение о вызовах, потому что на самом деле оно использует stdcall за кулисами.Он автоматически генерирует тест для S_OK и при ошибке выдает ошибку.Таким образом, типичный метод COM может быть объявлен как один из следующих:

function TypicalComFunction(Parameters): HRESULT; stdcall;
procedure TypicalComFunction(Parameters); safecall;

Если вы не намерены иметь дело с какими-либо потенциальными ошибками, используйте вторую форму (с safecall) и просто игнорируйте потенциальныеисключение.Если ошибка все же возникает, Delphi вызывает исключение, и это исключение будет всплывать, пока не достигнет точки в приложении, которая может устранить ошибку.Или он всплывает, пока не достигнет обработчика исключений приложения, и используется для отображения ошибки для пользователя.

При использовании safecall типичный код, приведенный выше, выглядит следующим образом:

TypicalComFunction(Parameters); // raises exception on error    
// Normal processing goes here

С другой стороны, если вам действительно нужен HRESUL, даже если он отличается от S_OK, используйте вариант stdcall.

0 голосов
/ 12 июля 2011

У меня была похожая ошибка в прошлом.Он появился ТОЛЬКО при включенной оптимизации компилятора.В остальном код работал без проблем около 2 лет.Это потому, что код, скомпилированный с оптимизацией ON, сильно отличается от кода, скомпилированного с оптимизацией OFF !!!!!!!Ошибка может проявиться (или нет) из-за этих шансов.

Подсказки:

  • Используйте FastMM (установлен в агрессивном режиме отладки)
  • ВСЕГДА используйте FreeAndNil вместоСвободно.Это может помочь вам МНОГО, так как это может заставить ошибку появляться в вашем коде раньше - возможно, также в коде, скомпилированном с отключенной оптимизацией.Это фактически продемонстрирует, что ошибка была там все время.Вы можете выполнить «поиск и замену» в своем коде для «.Free».

Эти два трюка помогли мне найти ошибку.

0 голосов
/ 12 июля 2011

Это не будет ошибкой компилятора. Установите точку прерывания данных на поле, и вы найдете код, который перезаписывает поле.

0 голосов
/ 12 июля 2011

Нет, это не означает ошибку компилятора как таковую.Изменение компилятора (Delphi <-> FPC), версии компилятора или параметров оптимизации может повлиять на генерацию кода и оптимизировать удаление счетчиков ссылок или освободить их ранее, или изменить используемые регистры и распределение регистров.

Это, в свою очередь, может сделать реальнымскрытые ошибки всплывают и выключаются.

Примером такой проблемы является то, что вы вызываете внешние функции.Если по какой-то причине их прототип (объявление в соответствующем модуле) неверен, регистры могут быть искажены, и опять-таки изменение параметров компилятора может вызвать поведение heisenbug.

Также пересчет проблем с автоматическими типами или изменение глобальных переменных, которыепередаются через CONST где-то может вызвать такие проблемы.О последней проблеме в основной рассылке рассылки FPC есть огромный поток.

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

...