我正在实现我IDropTarget
的基础:如何允许表单在不处理 Windows 消息的情况下接受文件删除?
David的实现效果很好。但是IDropTarget
( TInterfacedObject
) 对象不会自动释放,即使设置为“nil”也不会。
部分代码是:
{ TDropTarget }
constructor TDropTarget.Create(AHandle: HWND; const ADragDrop: IDragDrop);
begin
inherited Create;
FHandle := AHandle;
FDragDrop := ADragDrop;
OleCheck(RegisterDragDrop(FHandle, Self));
//_Release;
end;
destructor TDropTarget.Destroy;
begin
MessageBox(0, 'TDropTarget.Destroy', '', MB_TASKMODAL);
RevokeDragDrop(FHandle);
inherited;
end;
...
procedure TForm1.FormShow(Sender: TObject);
begin
Assert(Panel1.HandleAllocated);
FDropTarget := TDropTarget.Create(Panel1.Handle, nil) as IDropTarget;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
FDropTarget := nil; // This should free FDropTarget
end;
var
NeedOleUninitialize: Boolean = False;
initialization
NeedOleUninitialize := Succeeded(OleInitialize(nil));
finalization
if (NeedOleUninitialize) then
OleUninitialize;
end.
哪里FDropTarget: IDropTarget;
。
当我单击该按钮时,不显示任何 MessageBox,并且该对象没有被破坏。
如果我在构造函数末尾_Release;
按照此处的建议FDropTarget
调用,则在单击按钮或程序终止时被破坏(我对此“解决方案”有疑问)。
如果我省略RegisterDragDrop(FHandle, Self)
,FDropTarget
则按预期销毁。
我认为引用计数由于某种原因被破坏了。我真的很困惑。我怎样才能TInterfacedObject
正确地免费?
编辑:
这是完整的代码:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
VirtualTrees, ExtCtrls, StdCtrls,
ActiveX, ComObj;
type
TDropTarget = class(TInterfacedObject, IDropTarget)
private
FHandle: HWND;
FDropAllowed: Boolean;
function GetTreeFromDataObject(const DataObject: IDataObject): TBaseVirtualTree;
procedure SetEffect(var dwEffect: Integer);
function DragEnter(const dataObj: IDataObject; grfKeyState: Integer; pt: TPoint; var dwEffect: Integer): HResult; stdcall;
function DragOver(grfKeyState: Longint; pt: TPoint; var dwEffect: Longint): HResult; stdcall;
function DragLeave: HResult; stdcall;
function Drop(const dataObj: IDataObject; grfKeyState: Longint; pt: TPoint; var dwEffect: Longint): HResult; stdcall;
public
constructor Create(AHandle: HWND);
destructor Destroy; override;
end;
TForm1 = class(TForm)
Panel1: TPanel;
VirtualStringTree1: TVirtualStringTree;
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure VirtualStringTree1DragAllowed(Sender: TBaseVirtualTree;
Node: PVirtualNode; Column: TColumnIndex; var Allowed: Boolean);
procedure Button1Click(Sender: TObject);
procedure FormShow(Sender: TObject);
private
FDropTarget: IDropTarget;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
{ TDropTarget }
constructor TDropTarget.Create(AHandle: HWND);
begin
inherited Create;
FHandle := AHandle;
OleCheck(RegisterDragDrop(FHandle, Self));
//_Release;
end;
destructor TDropTarget.Destroy;
begin
MessageBox(0, 'TDropTarget.Destroy', '', MB_TASKMODAL);
RevokeDragDrop(FHandle);
inherited;
end;
function TDropTarget.GetTreeFromDataObject(const DataObject: IDataObject): TBaseVirtualTree;
// Returns the owner/sender of the given data object by means of a special clipboard format
// or nil if the sender is in another process or no virtual tree at all.
var
Medium: TStgMedium;
Data: PVTReference;
formatetcIn: TFormatEtc;
begin
Result := nil;
if Assigned(DataObject) then
begin
formatetcIn.cfFormat := CF_VTREFERENCE;
formatetcIn.ptd := nil;
formatetcIn.dwAspect := DVASPECT_CONTENT;
formatetcIn.lindex := -1;
formatetcIn.tymed := TYMED_ISTREAM or TYMED_HGLOBAL;
if DataObject.GetData(formatetcIn, Medium) = S_OK then
begin
Data := GlobalLock(Medium.hGlobal);
if Assigned(Data) then
begin
if Data.Process = GetCurrentProcessID then
Result := Data.Tree;
GlobalUnlock(Medium.hGlobal);
end;
ReleaseStgMedium(Medium);
end;
end;
end;
procedure TDropTarget.SetEffect(var dwEffect: Integer);
begin
if FDropAllowed then begin
dwEffect := DROPEFFECT_COPY;
end else begin
dwEffect := DROPEFFECT_NONE;
end;
end;
function TDropTarget.DragEnter(const dataObj: IDataObject; grfKeyState: Integer; pt: TPoint; var dwEffect: Integer): HResult;
var
Tree: TBaseVirtualTree;
begin
Result := S_OK;
try
Tree := GetTreeFromDataObject(dataObj);
FDropAllowed := Assigned(Tree);
SetEffect(dwEffect);
except
Result := E_UNEXPECTED;
end;
end;
function TDropTarget.DragLeave: HResult;
begin
Result := S_OK;
end;
function TDropTarget.DragOver(grfKeyState: Integer; pt: TPoint; var dwEffect: Integer): HResult;
begin
Result := S_OK;
try
SetEffect(dwEffect);
except
Result := E_UNEXPECTED;
end;
end;
function TDropTarget.Drop(const dataObj: IDataObject; grfKeyState: Integer; pt: TPoint; var dwEffect: Integer): HResult;
var
Tree: TBaseVirtualTree;
begin
Result := S_OK;
try
Tree := GetTreeFromDataObject(dataObj);
FDropAllowed := Assigned(Tree);
if FDropAllowed then
begin
Alert(Tree.Name);
end;
except
Application.HandleException(Self);
end;
end;
{----------------------------------------------------------------------------------------------------------------------}
procedure TForm1.FormCreate(Sender: TObject);
begin
VirtualStringTree1.RootNodeCount := 10;
end;
procedure TForm1.VirtualStringTree1DragAllowed(Sender: TBaseVirtualTree;
Node: PVirtualNode; Column: TColumnIndex; var Allowed: Boolean);
begin
Allowed := True;
end;
procedure TForm1.FormShow(Sender: TObject);
begin
Assert(Panel1.HandleAllocated);
FDropTarget := TDropTarget.Create(Panel1.Handle) as IDropTarget;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
FDropTarget := nil; // This should free FDropTarget
end;
var
NeedOleUninitialize: Boolean = False;
initialization
NeedOleUninitialize := Succeeded(OleInitialize(nil));
finalization
if (NeedOleUninitialize) then
OleUninitialize;
end.
DFM:
object Form1: TForm1
Left = 192
Top = 114
Width = 567
Height = 268
Caption = 'Form1'
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'MS Shell Dlg 2'
Font.Style = []
OldCreateOrder = False
OnCreate = FormCreate
OnShow = FormShow
PixelsPerInch = 96
TextHeight = 13
object Panel1: TPanel
Left = 368
Top = 8
Width = 185
Height = 73
Caption = 'Panel1'
TabOrder = 0
end
object VirtualStringTree1: TVirtualStringTree
Left = 8
Top = 8
Width = 200
Height = 217
Header.AutoSizeIndex = 0
Header.Font.Charset = DEFAULT_CHARSET
Header.Font.Color = clWindowText
Header.Font.Height = -11
Header.Font.Name = 'MS Shell Dlg 2'
Header.Font.Style = []
Header.MainColumn = -1
Header.Options = [hoColumnResize, hoDrag]
TabOrder = 1
TreeOptions.SelectionOptions = [toMultiSelect]
OnDragAllowed = VirtualStringTree1DragAllowed
Columns = <>
end
object Button1: TButton
Left = 280
Top = 8
Width = 75
Height = 25
Caption = 'Button1'
TabOrder = 2
OnClick = Button1Click
end
end
结论: 来自文档:
RegisterDragDrop
函数还调用 IDropTarget 指针上的 IUnknown::AddRef 方法
我链接的答案中的代码是固定的。
请注意,TDropTarget 上的引用计数被抑制。这是因为当 RegisterDragDrop 被调用时,它会增加引用计数。这会创建一个循环引用,并且此代码会抑制引用计数会破坏它。这意味着您将通过类变量而不是接口变量来使用此类,以避免泄漏。