Invalid pointer operation

0

Tworzę w DLL wątek, który odwołuje się do interfejsu głównej aplikacji:

Kod DLL:

var
  OrderCount: string;
begin
OrderCount := '1';

    TThread.CreateAnonymousThread(procedure
    begin
      while not TThread.CheckTerminated do
      begin
          // AAP - interfejs głównej aplikacji
          if OrderCount = '' then
          begin
            AAP.UpdateNotifyBadgeButton('btnMobile', 'Lista zleceń mobilnych', '');
          end
          else
            AAP.UpdateNotifyBadgeButton('btnMobile', 'Liczba nowych zleceń mobilnych: ' + OrderCount, OrderCount);
       end;
    end).Start;

Kod aplikacji głównej:

function TfrmMain.UpdateNotifyBadgeButton(AButtonName, AHint,
  ABadgeValue: string): HRESULT;
var
  LNotifyButton: TAdvBadgeSpeedButton;
begin
  result := S_OK;

  LNotifyButton := pnlNotifications.FindComponent(AButtonName) as TAdvBadgeSpeedButton;
  if Assigned(LNotifyButton) then
  begin
    TThread.Synchronize(nil, procedure // próbowałem też bez tego - bez różnicy
      begin
        LNotifyButton.Hint := AHint; // tutaj za drugim razem Invalid Pointer (LNotifyButton jest przypisany)
        LNotifyButton.Badge := ABadgeValue;
      end);
  end;
end;

A tutaj tworzenie 1 raz przycisku LNotifyButton, przy inicjowaniu DLL

function TfrmMain.AddNotifyBadgeButton(AButtonName, AHint, ABadgeValue: string; ABitmapHandle: THandle; AOnClickProc: TNotifyProc): HRESULT;
var
  LMethod: TMethod;
  i: Integer;
begin
  result := S_OK;


  LMethod.Data := nil;
  LMethod.Code := @AOnClickProc;
  LNotifyButton := TAdvBadgeSpeedButton.Create(pnlNotifications);
  LNotifyButton.Parent := pnlNotifications;
  LNotifyButton.Name := AButtonName;
  LNotifyButton.ShowHint := AHint <> '';
  LNotifyButton.Hint := AHint;
  LNotifyButton.Align := alLeft;
  LNotifyButton.Flat := True;
  LNotifyButton.Width := 45;
  LNotifyButton.Badge := ABadgeValue;

  LNotifyButton.AlignWithMargins := True;
  LNotifyButton.Margins.Top := 10;
  LNotifyButton.Glyph.Handle := ABitmapHandle;
  LNotifyButton.OnClick := TNotifyEvent(LMethod);
end;

Za pierwszym razem działa, a za drugim dostaje Invalid Pointer Operation. Jak kliknę Break w oknie wyjątku to wyrzuca mnie miejsca jak w załączniku.

Dodam, że dzieje się tak, jeżeli przekazuję dodatkowy sklejony string ze zmiennej, natomiast jeżeli idzie stała wartość określona w apostrofach, to działa. Coś się dzieje z pamięcią.
Podejrzewam, ze to przez to, że przekazuję STRING z DLL, a powinienem PAnsiChar. Czy ktoś jest w stanie pokazać na moim przykładzie jak miałoby to wyglądać - muszę alokować pamięć dla PAnsiChar i ją zwalniać, czy tutaj manager pamięci zrobi to za mnie?
Proszę o pomoc.

1

Tak, nie powinieneś posługiwać się stringami w połączeniu między DLL, a aplikacją. Chyba, że masz dołączony do DLL specjalny moduł. Jest opisany zawsze przy tworzeniu dll, nie pamiętam jak się nazywa. Oczywiście tak się nie powinno robić.

Po drugie Twoim problemem jest LMethod. Coś tu strasznie nakombinowałeś, a potem robisz z LMethod - ze zmiennej lokalnej - procedurę zdarzeniową. LMethod po zakończeniu metody AddNotifyBadgeButton przestaje istnieć. W Twój guzik potem chce ją wywoływać. Ale nie ma co wywołać, bo ma w tym miejscu w pamięci śmieci.

0

Przy stringach dodawalem ShareMem do uses w DLL na 1 miejscu, ale to nie pomagało. Jak wrócę do domu to zmienię STRING na PAnsiChar.

z DLL przekazuję procedurę, którą chcę wywoływać w oknie głównym, w jaki inny sposób to wykonać? Tam gdzie się wywala, nie ma związku z LMethod, bo updateuje tylko string na przycisku, chyba że taka aktualizacja wiąże się ze sprawdzeniem wszystkich eventów i tam wywala Invalid Pointer.

0

Chłopaku, masz tak:

 LNotifyButton.OnClick := TNotifyEvent(LMethod);

Przy czym LMethod jest zmienną lokalną.
Powinieneś to zmienić na:

 LNotifyButton.OnClick := @AOnClickProc;
0

Gdybyś sprawidził to wiedziałbyś, że nie można przypisać pointera do OnClick, bo musi on być pochodną TNotifyEvent, o czym pisałem kilka postów wyżej.

0

Nie mam Delphi pod ręką, więc sam sprawdź, jak to zrobić dobrze. Ja Ci pokazałem, gdzie masz błąd.

0

Domyślałem się, że to może być przyczyną, natomiast nie znam rozwiązania na przekazanie procedury z DLL do EXE nie korzystając z LMethod

0

To nie przekazuj TNotifyProc, tylko TNotifyEvent i zrób z tego metodę w DLL.

0
Juhas:

Przy czym LMethod jest zmienną lokalną.

Problemem na pewno nie jest LMethod; Do właściwości obiektu nie jest wpisywany wskaźnik na zmienną lokalną, więc nie ma mowy o tym, aby właściwość wskazywała na martwą pamięć (jakieś śmieci);

To nie przekazuj TNotifyProc, tylko TNotifyEvent i zrób z tego metodę w DLL.

Aby bezpośrednie przypisanie było możliwe, musi zostać przekazane wskazanie na metodę klasy, o czym już dyskutowaliśmy w poprzednim wątku i rozwiązania nie znaleźliśmy;

user322:

Domyślałem się, że to może być przyczyną, natomiast nie znam rozwiązania na przekazanie procedury z DLL do EXE nie korzystając z LMethod

Do pobrania wskaźnika na procedurę z biblioteki użyj funkcji GetProcAddress i wtedy spróbuj z TMethod.

0

Poradziłem sobie już. Problemem było przekazywanie stringa zamiast PAnsiString z procedury. Po zmianie działa. LMethod nie był w ogóle problemem.

0
furious programming napisał(a):
Juhas:

Przy czym LMethod jest zmienną lokalną.

Problemem na pewno nie jest LMethod; Do właściwości obiektu nie jest wpisywany wskaźnik na zmienną lokalną, więc nie ma mowy o tym, aby właściwość wskazywała na martwą pamięć (jakieś śmieci);

No jak to nie? A to?
LNotifyButton.OnClick := TNotifyEvent(LMethod);

To nie przekazuj TNotifyProc, tylko TNotifyEvent i zrób z tego metodę w DLL.

Aby bezpośrednie przypisanie było możliwe, musi zostać przekazane wskazanie na metodę klasy, o czym już dyskutowaliśmy w poprzednim wątku i rozwiązania nie znaleźliśmy;

No jak to. Przecież wystarczy stworzyć klasę, która będzie miała zdarzenie typu TNotifyEvent. Nie trzeba nawet tworzyć własnego typu procedure.... of object

0

No jak to nie? A to?

LNotifyButton.OnClick := TNotifyEvent(LMethod);

A to proszę pana jest przekopiowanie danych z rekordu LMethod do pola FOnClick za pomocą właściwości OnClick, czyli przekopiowanie dwóch wskaźników, zawartych w polach Data i Code; Można by mówić o wskazywaniu na martwą przestrzeń, jeśli by do zmiennej poza metodą AddNotifyBadgeButton przypisać wskaźnik na zmienną lokalną, czyli w ten sposób:

LGlobalVar := @LMethod;

Po wyjściu z metody AddNotifyBadgeButton, zmienna LGlobalVar zawierałaby wskazanie na nieistniejącą w pamięci zmienną lokalną LMethod, więc wskazywała by na jakieś śmieci; Zresztą problemem nie był LMethod (o czym pisałem wcześniej i co potwierdził pytacz) i nie jest nadal, a przekazywanie ciągów znaków;

No jak to. Przecież wystarczy stworzyć klasę, która będzie miała zdarzenie typu TNotifyEvent. Nie trzeba nawet tworzyć własnego typu procedure.... of object

Nie klasę, a instancję klasy (obiekt), bo metody statyczne nie mogą być do tego celu użyte.

0
furious programming napisał(a):

No jak to nie? A to?

LNotifyButton.OnClick := TNotifyEvent(LMethod);

A to proszę pana jest przekopiowanie danych z rekordu LMethod do pola FOnClick za pomocą właściwości OnClick, czyli przekopiowanie dwóch wskaźników, zawartych w polach Data i Code;

A jak to się dzieje? Bo, patrząc na definicję TMethod, to jest po prostu record. Tutaj go rzutujemy na typ. Więc ja tu po prostu widzę rzutowanie rekordu na typ, czyli wg mnie OnClick powinien tak naprawdę mieć adres do LMethod. Więc w jaki sposób tu działa to kopiowanie?

No jak to. Przecież wystarczy stworzyć klasę, która będzie miała zdarzenie typu TNotifyEvent. Nie trzeba nawet tworzyć własnego typu procedure.... of object

Nie klasę, a instancję klasy (obiekt), bo metody statyczne nie mogą być do tego celu użyte.

No zgadza się, miałem na myśli np. jakiś singleton to przechwytywania zdarzeń.

1

@Juhas: poczytaj te artykuły - Procedural Types - Method Pointers oraz System.TMethod.

1 użytkowników online, w tym zalogowanych: 0, gości: 1