TChromium ChromeTabs не работает

Я хочу сделать вкладки для своего TChromium.
У меня есть это:

   Browsers: array[0..1000] of TChromium;

И это процедуры ChromeTabs:

    procedure TForm1.ChromeTabsActiveTabChanged(Sender: TObject; ATab: TChromeTab);
    var
    c:integer;
    begin
    for c := 0 to ChromeTabs.Tabs.Count do
       if browsers[c]<>NIL then
        if c=ChromeTabs.ActiveTabIndex then browsers[c].Visible:=true else browsers[c].visible:=false;
    end;



procedure TForm1.ChromeTabsButtonAddClick(Sender: TObject;
  var Handled: Boolean);
begin
browsers[ChromeTabs.ActiveTabIndex]:=TChromium.Create(Chromium);
browsers[ChromeTabs.ActiveTabIndex].OnAddressChange:=Chromium.OnAddressChange;
browsers[ChromeTabs.ActiveTabIndex].OnAfterCreated:=Chromium.OnAfterCreated;
browsers[ChromeTabs.ActiveTabIndex].OnBeforeContextMenu:=Chromium.OnBeforeContextMenu;
browsers[ChromeTabs.ActiveTabIndex].OnBeforePopup:=Chromium.OnBeforePopup;
browsers[ChromeTabs.ActiveTabIndex].OnLoadEnd:=Chromium.OnLoadEnd;
browsers[ChromeTabs.ActiveTabIndex].OnLoadError:=Chromium.OnLoadError;
browsers[ChromeTabs.ActiveTabIndex].OnLoadingStateChange:=Chromium.OnLoadingStateChange;
browsers[ChromeTabs.ActiveTabIndex].OnProcessMessageReceived:=Chromium.OnProcessMessageReceived;
browsers[ChromeTabs.ActiveTabIndex].OnStatusMessage:=Chromium.OnStatusMessage;
browsers[ChromeTabs.ActiveTabIndex].DefaultEncoding:=Chromium.DefaultEncoding;

browsers[ChromeTabs.ActiveTabIndex].parent:=Form1;
browsers[ChromeTabs.ActiveTabIndex].Align:=alClient;
browsers[ChromeTabs.ActiveTabIndex].Show;
browsers[ChromeTabs.ActiveTabIndex].Load(Chromium.DefaultUrl);
end;



procedure TForm1.ChromeTabsButtonCloseTabClick(Sender: TObject;
  ATab: TChromeTab; var Close: Boolean);
begin
browsers[ATab.Index].Destroy;
Close:=True;
end;

Объект Chromium - это мой браузер по умолчанию, который я разместил в своей форме.

Итак, когда я пытаюсь закрыть одну из вкладок, иногда возникает ошибка нарушения прав доступа.
Когда я пытаюсь загрузить URL-адрес из Adress_Line, появляется ошибка нарушения прав доступа.
Кроме того, когда я открываю 2 или более вкладок, они выглядят плохо, вот так - http://s43.radikal.ru/i101/1307/99/650e18d5e190.jpg

Пожалуйста, помогите мне решить все эти проблемы :(

Thanks.


person Priler    schedule 16.07.2013    source источник
comment
Вам нужно будет переиндексировать этот массив из TChromium объектов. Представьте, что у вас есть три вкладки: первая с индексом 0, вторая с индексом 1 и третья с индексом 2. Теперь вы закрываете вторую вкладку с индексом 1, уничтожаете объект TChromium (элемент Browsers с индексом 1), но этот элемент в массиве остается указывая на разрушенный объект. Затем вкладки переиндексируются, поэтому первая вкладка по-прежнему равна 0, но предыдущая третья получает индекс 1. И вы пытаетесь получить доступ к элементу с индексом 1, как и раньше, когда закрываете вторую вкладку. AV вы получаете, потому что вы вызываете Destroy метод на уничтоженном объекте.   -  person TLama    schedule 17.07.2013
comment
Но определенно лучше для этого будет использовать коллекцию TObjectList<TChromium> generics вместо этого массива.   -  person TLama    schedule 17.07.2013
comment
Как я могу переиндексировать этот массив? А что такое TObjectList?   -  person Priler    schedule 17.07.2013
comment
Помогите мне, пожалуйста, мне нужно заставить его работать   -  person Priler    schedule 17.07.2013
comment
Я внес некоторые изменения, и он точно работает, но я не знаю, что такое TObjectList :)   -  person Priler    schedule 17.07.2013
comment
TObjectList<T> - это коллекция объектов, которая не требует переиндексации, как это было бы с массивом. См. Справку для получения этой основной информации.   -  person TLama    schedule 17.07.2013
comment
Нравится ? - TChromeList = класс (TObjectList) общедоступная частная функция GetItems (индекс: целое число): TEdit; процедура SetItems (индекс: целое число; значение константы: TEdit); опубликованные общедоступные свойства Items [Index: Integer]: TEdit читать GetItems писать SetItems; дефолт; конец;   -  person Priler    schedule 17.07.2013


Ответы (1)


Проблема исключения нарушения прав доступа вызвана вызовом метода Destroy для уже уничтоженного объекта. Позвольте мне объяснить ситуацию.

Представьте, что у вас есть 3 вкладки со следующими индексами и массив Browsers со следующими экземплярами браузера:

ChromeTabs.Tabs            Browsers
----------------------     ----------------------
Index       Tab name       Index       Browser
----------  ----------     ----------  ----------
0           Tab 1          0           Browser 1
1           Tab 2          1           Browser 2
2           Tab 3          2           Browser 3

Теперь вы нажимаете кнопку закрытия средней вкладки (вкладка проиндексирована цифрой 1) и в OnButtonCloseTabClick запускаете это:

procedure TForm1.ChromeTabsButtonCloseTabClick(Sender: TObject;
  ATab: TChromeTab; var Close: Boolean);
begin
  // the ATab.Index equals 1
  Browsers[ATab.Index].Destroy;
  Close := True;
end;

Это приведет к уничтожению экземпляра браузера с именем Browser 2 из приведенной выше таблицы. Это не будет проблемой, если массив будет переиндексирован, как это делает коллекция Tabs. Давайте посмотрим, что происходит с вкладками и вашим массивом:

ChromeTabs.Tabs            Browsers
----------------------     ----------------------
Index       Tab name       Index       Browser
----------  ----------     ----------  ----------
0           Tab 1          0           Browser 1
1           Tab 3          1           ---        <-- dangling pointer
                           2           Browser 3

Как видите, коллекция Tabs была переиндексирована после закрытия второй вкладки, а ваш массив - нет. Вы только что уничтожили экземпляр объекта, но висячий указатель от этого уничтоженного объекта все еще существует, в том же индексе.

Теперь, если вы снова нажмете кнопку закрытия второй вкладки (вкладка с именем Tab 3), вы запустите тот же код, что и раньше, в своем обработчике событий:

procedure TForm1.ChromeTabsButtonCloseTabClick(Sender: TObject;
  ATab: TChromeTab; var Close: Boolean);
begin
  // as before, the ATab.Index equals 1, but this time you'll get AV
  // since object from element Browsers[1] has been destroyed before
  // and now that element contains just dangling pointer
  Browsers[ATab.Index].Destroy; // <-- 
  Close := True;
end;

Но на этот раз вы получите нарушение прав доступа, потому что объект из элемента Browsers[1] уже был уничтожен ранее.

Для вашей цели нет массива нужного типа коллекции. Я бы посоветовал вам использовать коллекцию списков общих объектов TObjectList<T> . Используя эту коллекцию, я бы переписал ваш код следующим образом:

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
  System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
  System.Generics.Collections, ChromeTabs, ChromeTabsClasses, cefvcl;

type
  TForm1 = class(TForm)
    ChromeTabs1: TChromeTabs;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure ChromeTabs1ButtonAddClick(Sender: TObject;
      var Handled: Boolean);
    procedure ChromeTabs1ButtonCloseTabClick(Sender: TObject;
      ATab: TChromeTab; var Close: Boolean);
    procedure ChromeTabs1ActiveTabChanging(Sender: TObject; AOldTab,
      ANewTab: TChromeTab; var Allow: Boolean);
  private
    FBrowsers: TObjectList<TChromium>;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  Tab_Closed:Boolean=False;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  // create instance of the object list and let it manage
  // lifetime of the inserted objects
  FBrowsers := TObjectList<TChromium>.Create;
  FBrowsers.OwnsObjects := True;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  // release the object list
  FBrowsers.Free;
end;

procedure TForm1.ChromeTabs1ButtonAddClick(Sender: TObject;
  var Handled: Boolean);
var
  ChromiumInstance: TChromium;
begin
  // create an instance of the browser component and
  // initiliaze its properties - here it's simplified
  ChromiumInstance := TChromium.Create(nil);
  ChromiumInstance.Parent := Self;
  ChromiumInstance.SetBounds(8, 8, 150, 150);
  // now add the new browser instance to the collection
  FBrowsers.Add(ChromiumInstance);
end;

procedure TForm1.ChromeTabs1ButtonCloseTabClick(Sender: TObject;
  ATab: TChromeTab; var Close: Boolean);
begin
  // delete the browser instance from the collection; since we've
  // assigned True to the OwnsObjects property of the collection,
  // we don't need to care of freeing the browser instance
  FBrowsers.Delete(ATab.Index);
  // allow the tab to close
  Close := True;
  //and fix tab close
  Tab_Closed:=True;
end;

procedure TForm1.ChromeTabs1ActiveTabChanging(Sender: TObject; AOldTab,
  ANewTab: TChromeTab; var Allow: Boolean);
begin
  // check if there's an "old tab" and if so, check also if we have its
  // index in the range of our collection; if so, then hide the browser
  if Assigned(AOldTab) and (AOldTab.Index < FBrowsers.Count) then
    FBrowsers[AOldTab.Index].Visible := False;
  // and show the activated tab browser
  If((ChromeTabs.Tabs.Count<>1)) Then
  Begin
  If((ANewTab.Index=(ChromeTabs.Tabs.Count-1)) AND Tab_Closed=True) Then
  FBrowsers[AOldTab.Index].Visible := True
  Else
  FBrowsers[ANewTab.Index].Visible := True;
  End
  Else FBrowsers[ANewTab.Index].Visible := True;
  //Now Tab is not closed
  Tab_Closed:=False;
end;

end.
person TLama    schedule 17.07.2013