Błąd "Out of memory" i Runtime error 213 w .dll

0
Adamek Adam napisał(a):

A w Delphi 2006 po stronie EXE wyjątki działają "po ludzku" ? , bo może zanim wywołasz funkcję z DLL sprawdź po stronie EXE ile możesz zajać

Nie rozważałeś Lazarus-a ? Zrobił byś aplikację 64 bit i problem magicznie sam by się rozwiązał

Nie lubię Lazarusa. Tylko Delphi.

Adamek Adam napisał(a):

Próbki kodu OP sugerują że "bug" mógł się łatwo ukryć w tym nieczytelnym gąszczu, a widziałem tylko mały wycinek.
Jak by OP zrobił prosty projekt który można uruchomić na jakimś nowszym Delphi to łatwo by było potwierdzić/zaprzeczyć że to stara wersja.

Adamek, zrobiłem.
dll:

library Project2;
{$R *.res}
var
p:array of pointer;
 function doit(n,m:cardinal):int64;stdcall;
 var i:integer;
 begin for i:=0 to length(p)-1 do if p[i]<>nil then freemem(p[i]);result:=0;p:=nil;
 try
 setlength(p,n);
 for i:=0 to length(p)-1 do  begin
 getmem(p[i],m);
 inc(result,m);
                             end;
except result:=0; end;
 end;
exports  doit;

begin
p:=nil;
end.

a tu exec testujący banalny:
unit Unit3;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TForm3 = class(TForm)
    Button1: TButton;
    procedure FormDestroy(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form3: TForm3;h:cardinal;
  fromdll:function(n,m:cardinal):int64; stdcall;
  procedure load;
  procedure unload;
  implementation

{$R *.dfm}
  procedure load;
begin
h:=loadlibrary('project2.dll');
if h<>0 then @fromdll:=getprocaddress(h,pchar('doit'));
end;
  procedure unload;
begin
  freelibrary(h);
end;
procedure TForm3.Button1Click(Sender: TObject);
begin
showmessage(inttostr(fromdll(100000,100000)));//powinno sie wyswietlić 0 gdyby się program nie wykrzaczył z błędem 203. A tak nie jest niestety.
end;
procedure TForm3.FormCreate(Sender: TObject);
begin
load;
end;
procedure TForm3.FormDestroy(Sender: TObject);
begin
unload;
end;
end.

I sprawdziłem to na Delphi 10.3. efekt ten sam jak na moim starym Turbo Delphi. Tym bardziej ponawiam prośbę do furous o instrukcję do korzystania z GlobalMemoryStatusEx bo nie umiem.

0
furious programming napisał(a):

Pytanie zasadnicze — dlaczego używasz Turbo Delphi? Musisz go używać, bo takie IDE macie w pracy czy w jakikolwiek sposób ktoś Cię zmusza do jego używania, czy może to Twój własny projekt, nad którym masz pełną władzę?

Używam Turbo Delphi bo jest darmowo legalne. No i już przez lata przyzwyczaiłem się do niego.

1
Marmar napisał(a):

Używam Turbo Delphi bo jest darmowo legalne.

Ok, w takim razie moje sugestie są jak najbardziej na miejscu. Dlatego też koniecznie zainteresuj się Lazarusem lub współczesnym Delphi, bo również są darmowe legalnie, a w dodatku Lazarus pozwala tworzyć aplikacje komercyjne, bez żadnych ograniczeń. Po drugie, możesz kompilować 64-bitowe aplikacje (w Lazarusie dodatkowo 32-bitowe, a jeśli potrzeba to i nawet 16-bitowe), więc jest pełna zgodność ze współczesną architekturą procesorów i systemów operacyjnych (bez żadnych sztuczek, fixów i bug wie czego jeszcze).

No i już przez lata przyzwyczaiłem się do niego.

To się od niego odzwyczaj, bo IDE to nie żona. Możesz się przyzwyczaić do Pascala (jako języka) i dłubać w nim jak długo tylko chcesz, ale nie przyzwyczajaj się do narzędzi, bo tylko robisz sobie krzywdę. Już teraz nie jesteś w stanie implementować tego co potrzebujesz, a w niedalekiej przyszłości może się okazać, że to Turbo Delphi przestanie generować pliki wykonywalne zgodne z OS-em/CPU (tak jak to miało miejsce w przypadku Delphi 7) i zostaniesz z palcem w nocniku. Nie dość, że wtedy będziesz musiał zmienić toolset, to w dodatku będziesz musiał portować kod napakowany trikami i hackami, którymi obchodziłeś najróżniejsze ograniczenia 32-bitowej architektury.


Temat w sumie się wyczerpał — nie ma nic więcej do dodania. Nie zaalokujesz więcej niż 2GB, bo więcej inty nie pomieszczą — nieważne czy masz 2GB RAM-u, czy dziesiątki gigabajtów. Tkwisz w przestarzałej technologii, choć nie musisz, bo masz sensowne, darmowe i w pełni legalne alternatywy. Możesz się tak kopać z koniem, albo w końcu pomyśleć racjonalnie i zmienić szkodliwe przyzwyczajenia.

Pobierz Lazarusa, pobaw się nim trochę, przeglądnij jakie ma możliwości i porównaj je z Turbo Delphi. Sam zobaczysz, z czego dobrowolnie i bezsensownie rezygnujesz.

0
Adamek Adam napisał(a):

A w Delphi 2006 po stronie EXE wyjątki działają "po ludzku" ? , bo może zanim wywołasz funkcję z DLL sprawdź po stronie EXE ile możesz zajać

Nie rozważałeś Lazarus-a ? Zrobił byś aplikację 64 bit i problem magicznie sam by się rozwiązał

Adamek, próbuję sprawdzać ale niewiele to nadaje choć nie nic. Jeśli chcę sprawdzić czy mogę zaalokować np. 10kb (try getmem except end) po stronie execa to muszę pytać o około 10MB więcej czyli czy mogę bez wyjątku zaalokować 10MB+10kb. to trochę lipne rozwiązanie ale jakieś. No i nic innego na tę chwile nie przychodzi mi do głowy. Stąd moja prośba do Was o pomoc. O coś lepszego.

0

@Marmar: we Free Pascalu da się spróbować zaalokować blok pamięci, a w przypadku gdy nie jest to możliwe, nie spowodować rzucenia ani wyjątku, ani runtime-błędu. Wystarczy ustawić globalną zmienną ReturnNilIfGrowHeapFails na True i to tyle — można próbować alokować ile się chce, bez kombinacji z wyjątkami:

var
  Block: Pointer;
begin
  // Zablokuj emitowanie runtime-błędów, a w ich następstwie również wyjątków "EOutOfMemory".
  ReturnNilIfGrowHeapFails := True; 

  // Spróbuj zaalokować blok pamięci o wymaganym rozmiarze.
  Block := GetMem({some_size});

  // Sprawdź czy alokacja się powiodła.
  if Block <> nil then
  begin
    // Alokacja powiodła się, można używać zmiennej "Block".

    // Zwolnienie bloku po jego wykorzystaniu.
    FreeMem(Block);
  end
  else
    // Nie ma na tyle pamięci, można tę sytuację obsłużyć tutaj.

W taki sposób możesz spokojnie próbować alokować ile tylko chcesz, a w przypadku skończenia się wolnej pamięci, możesz z poziomu kodu taki scenariusz obsłużyć, bez użerania się z wyjątkami. Sprawdź czy Turbo Delphi, którego używasz, ma coś w ten deseń.

0

@furious programming: to by było super i tak chyba powinno być, jednak ani w moim starym Turbo Delphi ani w dużo nowszym Delphi 10.3 tego nie ma. Jest tylko procedura (a nie funkcja) getmem(p:pointer; n:integer), która wywala wyjątek jeśli niepowodzenie, no a jak już pisałem w .dll to nie działa i, lipa. No ale dzięki za dobre chęci.

0

OK ! rozwiązanie problemu i błąd jest w tej linii:

for i:=0 to length(p)-1 do if p[i]<>nil then freemem(p[i]);result:=0;p:=nil;

jak poprawisz to wszystko działa "po ludzku" i wyjątek zostanie prawidłowo obsłużony w DLL, a przynajmniej u mnie działa :D

0
Adamek Adam napisał(a):

OK ! rozwiązanie problemu i błąd jest w tej linii:

for i:=0 to length(p)-1 do if p[i]<>nil then freemem(p[i]);result:=0;p:=nil;

jak poprawisz to wszystko działa "po ludzku" i wyjątek zostanie prawidłowo obsłużony w DLL, a przynajmniej u mnie działa :D

Adamek ja nie widzę w tej linii błędu. Jeśli widzisz, napisz proszę na czym polega i jak go naprawić.

1

Pomyliłem się !
Ta linijka z FOR jest OK , skopiowalem do domyslnego projektu DLL Twoja procedure i skasowałem wszystkie "p:=nil" i zaczelo dzialac :)
A dopiero potem doczytałem dokumentację że to nie to

Zacytuje samego siebie: "Może masz subtelne różnice" i rzeczywiście są subtelne

Braki w uses:

uses
  SysUtils,
  Classes;

jak dodasz do projektu DLL to działa

1
Adamek Adam napisał(a):

Pomyliłem się !
Ta linijka z FOR jest OK , skopiowalem do domyslnego projektu DLL Twoja procedure i skasowałem wszystkie "p:=nil" i zaczelo dzialac :)
A dopiero potem doczytałem dokumentację że to nie to

Zacytuje samego siebie: "Może masz subtelne różnice" i rzeczywiście są subtelne

Braki w uses:

uses
  SysUtils,
  Classes;

jak dodasz do projektu DLL to działa

Rzeczywiście, Adamek, masz rację. Dziękuję. Nawet samo classes wystarczy dodaćdo uses. Ale classes to wielki unit.Wiesz może co z unitu classes jest tu ważne by sobie zrobić mniejszy unit tylko z tym co potrzebne? Już wiem wystarczy zamiast classes dać unicik malutki:

unit classesexceptions;

interface     uses exceptions;
type
 EHeapException = class(Exception)
  private
    AllowFree: Boolean;
  public
    procedure FreeInstance; override;
  end;

  EOutOfMemory = class(EHeapException);

EOutOfResources = class(EOutOfMemory);

implementation
procedure EHeapException.FreeInstance;
begin
  if AllowFree then
    inherited FreeInstance;
end;
end.

I działa, super, jeszcze raz dziękuję.
...
Chyba się pośpieszyłem z tym małym unitem. To jednak nie działa. Więc ponawiam pytanie do Adamka: Wiesz może co z unitu classes jest tu ważne by sobie zrobić mniejszy unit tylko z tym co potrzebne?

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