问题描述
此问题与问题,我之前已经问过。
@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 orI am doing something wrong.
执行<$ c之后$ c> 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将引发一个Exception,并且该异常会冒泡直至到达应用程序中的 可处理错误的位置。否则它会一直冒泡,直到到达应用程序的异常处理程序为止,然后该异常处理程序才向用户显示错误。
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
即使您 do 需要 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.
这篇关于我的类实例中的奇怪内存覆盖问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!