2010-06-29 25 views
11

Tôi là một ứng dụng phức tạp mà tôi vừa giới thiệu một số thay đổi, thêm một vài lớp mới với giao diện và xóa một số lớp khác. Chức năng tất cả đều hoạt động nhưng tôi bị vi phạm quyền truy cập ngay sau quy trình hủy của một lớp học:Cách tìm một giao diện lơ lửng gây ra AV trong Delphi

"Vi phạm truy cập tại địa chỉ 0040B984 trong mô-đun 'xxxx.exe'. Đọc địa chỉ 80808088".

Tôi biết điều này là trong mã 'Hoàn thành' của lớp và chắc chắn đủ nếu tôi bước vào việc tháo gỡ (Delphi 2010) tôi có thể thấy điểm của AV. Tôi không thể thấy một cách dễ dàng để tìm ra biến nào của tôi đang kích hoạt điều này. Có một thủ tục để được theo sau khi đi sâu này sẽ giúp tôi có được một đầu mối cho trường hợp đang được đề cập đến?

Cảm ơn Brian

+2

tôi đề nghị sử dụng miễn phí chứ không phải là tiêu diệt. – Bharat

Trả lời

13

Lỗi này có vẻ như bạn đang sử dụng FastMM cho quản lý bộ nhớ. Lỗi chỉ ra rằng bạn đang đề cập đến một con trỏ đã được xóa bởi FastMM với giá trị DebugFillDWord.

Điều đó có nghĩa là bạn đang sử dụng giao diện tham chiếu đến đối tượng đã được giải phóng.
Điều này cũng có nghĩa là bạn chưa bật CatchUseOfFreedInterfaces.

Để thay đổi chúng và để gỡ lỗi, bạn không thể thực hiện với chứng khoán FastMM đi kèm với Delphi.
Bạn sẽ cần tải xuống FastMM (phiên bản 4.94).

Sau khi tải về:

Giống như gabr đã đề cập, bên FastMM4Options.inc, hãy chắc chắn bạn kích hoạt FullDebugModeCatchUseOfFreedInterfaces (mà vô hiệu hóa CheckUseOfFreedBlocksOnShutdown, nhưng bạn không quan tâm đến sau này ngay bây giờ).
Bạn cũng có thể muốn bật RawStackTraces; điều đó phụ thuộc nếu dấu vết ngăn xếp hiện tại của bạn đủ tốt.

Khi bạn đã thực hiện các thiết lập này, sau đó chạy ứng dụng của bạn với FastMM qua debugger và đặt một breakpoint trên phương pháp này bên trong đơn vị FastMM4:

procedure TFreedObject.InterfaceError; 

tôi đã sửa đổi của tôi FastMM4 đơn vị một bit để có thêm thông tin ngữ cảnh; Tôi có thể chia sẻ điều đó với bạn (Tôi đã gửi nó cho nhóm FastMM4, nhưng nó chưa được đưa vào các nguồn chính thức).

Tôi đã viết khá dày đặc blog article on debugging using FastMM có thể giúp bạn.
Thả ghi chú ở đây nếu cần giải thích thêm :-)

Chúc bạn may mắn và cho chúng tôi biết nếu bạn cần hướng dẫn thêm.

--jeroen

Sửa: 20.100.701 - nhấn mạnh các bit nêu trong bình luận của Brian.

+0

Cảm ơn, tôi đánh giá cao chế độ thông tin ngữ cảnh mà bạn đề cập đến. Đã sử dụng FastMM 494 ở đây nhưng tôi sẽ làm theo các bước blog của bạn. Bri –

+0

@brian: thả cho tôi một ghi chú qua blog của tôi (hoặc e-mail; hầu hết mọi thứ ở pluimers.com đều hoạt động, đặc biệt khi bạn sử dụng tên của tôi trước biển hiệu) –

+1

@All: Điểm mấu chốt trong câu trả lời này là: "Khi bạn đã thực hiện các cài đặt này, hãy chạy ứng dụng của bạn với FastMM thông qua trình gỡ lỗi và đặt điểm ngắt trên phương thức này trong đơn vị FastMM4: thủ tục TFreedObject.InterfaceError". Một số cách để xác định thêm thông tin về kết quả TFreedObject (tức là loại lớp) sẽ là v.useful trong FastMM. –

13

Trong hầu hết các trường hợp sai sót như vậy có thể được đánh bắt bằng cách sử dụng FastMM và biên dịch ứng dụng với định nghĩa có điều kiện FullDebugModeCatchUseOfFreedInterfaces. Chỉ cần chắc chắn rằng bạn đặt FastMM4 ở vị trí đầu tiên trong danh sách 'sử dụng' của dpr.

+1

Gabr đề xuất tốt, tôi đang sử dụng FastMM nhưng chưa bật 'CatchUseOfFreedInterfaces'. Bây giờ điều này mang lại cho tôi một ngăn xếp gọn gàng của các bước tôi đã truy tìm trước đây, nhưng tôi không gần hơn nhận được một đầu mối để 'người đang nắm giữ' tài liệu tham khảo bất hợp pháp. Tôi đang làm ref đếm của riêng tôi (-1) bởi vì tôi đang giải phóng các lớp học của riêng tôi (như TComponent nào) vì vậy tôi cần phải có một đầu mối đến nơi con trỏ giao diện bất hợp pháp đã 'nhận từ'. Bri –

+0

Tôi đã có một vấn đề tương tự như một vài tuần trước đây, gây ra bởi giao diện trộn tham chiếu và không tham chiếu. Tôi không chắc tại sao điều này gây ra một vấn đề, nhưng không bao giờ ít hơn, nó đã làm. – Vegar

6

bước để tìm ra vấn đề:

  1. Sử dụng FastMM trong fulldebugmode như Gabr gợi ý (tôi nghĩ bạn đã làm, nhìn vào mô hình 808080).
  2. Đặt tất cả các giao diện bạn sử dụng trong lớp học của bạn một cách rõ ràng để nil trong Destroy bạn thủ tục
  3. Đặt một breakpoint vào lúc bắt đầu của bạn tiêu diệt thủ tục
  4. Bây giờ bước qua thủ tục tiêu diệt của bạn, khi nilling giao diện lủng lẳng bạn sẽ nhận được của bạn Vi phạm Truy cập và bạn sẽ biết đó là giao diện nào.
  5. Khi bạn vẫn còn AV sau khi bạn đã đổ đầy tất cả các giao diện mà không gặp sự cố, hãy thực hiện bước 2 - 5 cho lớp cha.

Tôi cũng gặp vấn đề này và phương pháp trên đã giúp tôi tìm thấy chúng. Các vấn đề của tôi là do các TComponents thực hiện các giao diện. Giả sử bạn có ComponentA và ComponentB, ComponentB triển khai một giao diện. Bạn gán ComponentB (hoặc giao diện của nó) cho ComponentA và lưu trữ tham chiếu giao diện. Bây giờ ComponentB bị phá hủy, nhưng ComponentA không biết về điều đó. Khi bạn phá hủy ComponentA nó nils giao diện, gọi phương thức _Release và bạn nhận được AV.

Giải pháp cho điều đó là làm việc với TComponent.FreeNotification. Khi bạn nhận được thông báo miễn phí từ ComponentB, bạn sẽ không có giao diện trong ComponentA. Tôi không biết gì về mã của bạn, nhưng nếu vấn đề của bạn tương tự, bạn cũng có thể làm việc với FreeNotifications.

Edit: thêm bước 5

+0

Fox: Tôi sẽ kiểm tra lại tôi đã có nils ở khắp mọi nơi! –

+0

@ Brian Frost: bạn có biết lớp nào đang sản xuất AV không? Thiết lập rõ ràng các giao diện cho nil là khá đơn giản, nhưng có thể cồng kềnh khi có nhiều giao diện. –

2

Một điều cần tìm kiếm trong mã của bạn là này

FInterfacedObject.GetInterface 

trong phạm vi tương tự như

FInterfacedObject := TInterfacedObjectClass.Create. 

nơi FInterfacedObject là một biến lớp.

Bạn có thể gọi GetInterface từ một hàm bên trong nếu bạn muốn, nhưng nếu bạn gọi GetInterface trong cùng phạm vi như bạn đã tạo FInterfacedObject, vì lý do nào đó, bạn sẽ giảm số tham chiếu xuống 0 và giải phóng mọi thứ. sẽ không phải là số không, vì vậy nếu bạn làm

if assigned(FInterfacedObject) then 
    FInterfacedObject.Free; 

bạn sẽ bị vi phạm quyền truy cập.

3

Lỗi tương tự đã cắn tôi là tham chiếu giao diện đã được đặt trên đối tượng hiện có, bộ đếm tham chiếu giao diện sẽ không tự động giảm khi đối tượng chủ sở hữu được giải phóng. Nó có thể được giải quyết với một if Assigned(FMyInterface) then FMyInterface := nil; trong destructor của đối tượng chủ sở hữu.

+0

Bài học khôn ngoan đã học: nếu bạn sử dụng giao diện, hãy sử dụng chúng một cách độc quyền và để cơ chế đếm ngược quản lý mọi thứ cho bạn. Cố gắng tránh các giao diện trên con cháu TComponent nếu có thể, vì mối quan hệ chủ sở hữu/thành phần sẽ cạnh tranh với cơ chế đếm ngược gây ra một nạn nhân: người cố gắng gỡ lỗi các vấn đề bộ nhớ. –

3

tôi làm một điều tương tự, và đoạn mã sau trong destructor của đối tượng của bạn sẽ giúp

Destructor TMyObjectThatIAmDoingManualRefCounting.Destroy; 
begin 
    if FMyRefCount<>0 then 
    messageDlg('You dork, you called Free on me when someone still had references to me'); 

    inherited; 
end; 

Sau đó, bạn ít nhất có thể tìm ra nơi bạn đang giải phóng các đối tượng innapropriately. Có lẽ bạn đang giải phóng nó quá sớm. Giải phóng các đối tượng quá sớm sẽ không thực sự gây ra một vấn đề, đó là khi bạn đang giải phóng đối tượng giữ giao diện tham chiếu đến đối tượng đã giải phóng mà bạn sẽ nhận được lỗi.

Điều khác bạn có thể làm là đặt điểm ngắt trong phương thức addref và release của bạn và theo dõi ai đang giữ tham chiếu giao diện và nếu những đối tượng đó đang giải phóng chúng sau đó.

Cũng là một vấn đề thường gặp như sau, nếu bạn nhận được một giao diện và giải phóng các đối tượng trong cùng một phương pháp

var 
    o:TSomeObject; 
begin 
    o:=TSomeObject.Create; 
    (o as ISomeInterface).DoSomething; 
    o.free 
end; 

Điều này sẽ gây ra một AV ở phần cuối của phương pháp này, bởi vì trình biên dịch tạo ra một giả biến giao diện được giải phóng ở cuối phương thức.

bạn sẽ cần phải làm điều này

var 
    o:TSomeObject; 
    i:ISomeInterface; 
begin 
    o:=TSomeObject.Create; 
    i:=(o as ISomeInterface); // or Supports or whatever 
    i.DoSomething; 
    i:=nil; 
    o.free 
end; 
Các vấn đề liên quan