我的班级实例中出现奇怪的内存覆盖问题 [英] Strange memory overwrite problem in instance of my class

查看:20
本文介绍了我的班级实例中出现奇怪的内存覆盖问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这个问题与this 问题,我之前问过.@RRUZ 提供的代码正在运行,但似乎不太正确或我做错了什么.

This problem is related to this question, which I've asked earlier. The code provided by @RRUZ is working but it seems that not quite correctly or I am doing something wrong.

在执行GetSharedFiles 后,TMyObject 的实例发生了奇怪的事情.字段 FMyEvent 是(并且应该是)nil 指向一些随机数据.

After executing GetSharedFiles strange thing is happening in instance of TMyObject. The field FMyEvent which was (and it should be) nil points to some random data.

我在 5 分钟前发现的是,如果我关闭编译器选项中的优化,它会在重建后正常工作.所以也许这是一些编译器错误?

What I've discovered just 5 minutes ago is that if I turn off the optimization in compiler options it works fine after rebuild. So maybe this is some compiler bug?

这是代码快照(Delphi 2009 Windows 7 64 位):

Here is a code snapshot (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.

任何想法出了什么问题?感谢您抽出宝贵时间.

Any ideas what is going wrong? Thanks for your time.

推荐答案

对此的调用约定应该是 stdcall,而不是 safecall:

The calling convention on this should probably be stdcall, not safecall:

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


回顾

典型的 COM 函数返回一个 HRESULT 结果;如果一切正常,他们会使用它来传递错误代码或 S_OK.使用这种类型的函数,你通常会有这样的代码:


Recap

Typical COM functions return a HRESULT result; They use it to pass an error code or S_OK if everything went fine. Using this type of function, you'd usually have this kind of code:

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 方法可以声明为以下任一方法:

Since error conditions can't usually be dealt with, Delphi provides us with the safecall pseudo-calling-convention. It's not a true calling convention because in fact it uses stdcall behind the scenes. What it does is to automatically generate the test for S_OK and, on failure, raises an error. So the typical COM method can be declared as either one of this:

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

如果您不打算处理任何潜在的错误,请使用第二种形式(使用 safecall)并忽略潜在的异常.如果确实发生了错误,Delphi 将引发一个异常,并且该异常将冒泡,直到它到达应用程序中可以处理该错误的某个点.或者它一直冒泡,直到到达应用程序的异常处理程序,并用于向用户显示错误.

If you don't intend to deal with any potential errors use the second form (with safecall) and simply ignore the potential exception. If an error does occur, Delphi will raise an Exception, and that exception will bubble-up until it reaches a point in the application that can deal with the error. Or it bubbles up until it reaches Application's exception handler, and that's used to display the error for the user.

使用safecall,上面的典型代码如下所示:

Using safecall, the typical code above looks like this:

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

另一方面,如果您确实需要 HRESUL,即使它与 S_OK 不同,那么请使用 stdcall变体.

On the other hand if you do need the HRESUL even if it's different from S_OK, then use the stdcall variant.

这篇关于我的班级实例中出现奇怪的内存覆盖问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆