[Delphi] Forma w DLL w WinAPI

0

Witam,

Ostatnio interesuje się WinAPI, tak jak w przypadku exe jakoś mi to wychodziło to w DLL nie potrafię utworzyć czystej formy. Znalazłem jakieś przykłady na grupach dyskusyjnych, ale CreateWindow nic nie robi. To kod (pisany na szybkiego dla testów):

library Test;

uses
  Windows,
  Messages;

const
  DLLTestMsgStr: PChar = 'DLL Test Message';
  MAIN_FORM_CLASS = 'TMainForm';
  MAIN_FORM_WIDTH = 395;
  MAIN_FORM_HEIGHT = 500;
  ZERO = 0;
  JEDEN = 1;
  EMPTSTR='';

var
  Msg: TMsg;
  DLLTestMsg: UINT;
  MainFormClass: TWndClass;
  MainForm: HWND;

{$R *.res}

function DLLWindowProc(Hndl: HWnd; Msg: UINT; wParam: WPARAM;
    lParam: LPARAM): LRESULT; {$ifdef Win32} stdcall {$else} 
    export {$endif}; 
begin 
    { Look for and handle any custom messages posted or sent to the DLL 
      window } 
    if (Msg = DLLTestMsg) then
    begin 
        MessageBeep($ffff); 
        Result := 0; 
    end 
    else 
    begin 
        { Call the default window procedure for all non-custom 
          messages } 
        Result := DefWindowProc(Hndl, Msg, wParam, lParam); 
    end; { end if } 

end;

function ClassRegistered: Boolean;
begin
   Result := GetClassInfo(HInstance, MAIN_FORM_CLASS, MainFormClass);
end;

function RegisterDLLWindow: Boolean;
begin
  if ClassRegistered then
  begin
    Result := True;
    Exit;
  end;

 (* with MainFormClass do
  begin
    style := CS_NOCLOSE;           { Class style }
    lpfnWndProc := @DLLWindowProc; { Pointer to window
                                                      procedure }
    cbClsExtra := 0;               { Additional class
                                                      params }
    cbWndExtra := 0;               { Additional window
                                                      params }
    hInstance := hInstance;        { Instance handle }
    hCursor := LoadCursor(hInstance, IDC_ARROW);             { Cursor }
    hbrBackground := COLOR_BTNSHADOW;        { Background brush }
    lpszMenuName := nil;           { Menu name }
    lpszClassName := MAIN_FORM_CLASS;              { Class name }
  end;  *)

  with MainFormClass do
  begin
    lpfnWndProc := @DLLWindowProc; // funkcja okienkowa
    hInstance := hInstance; // uchwyt do zasobów
    lpszClassName := MAIN_FORM_CLASS; // klasa
    hbrBackground := COLOR_BTNSHADOW; // kolor tła
    hCursor := LoadCursor(hInstance, IDC_ARROW);
  end;

  Result := (Windows.RegisterClass(MainFormClass) <> ZERO);
end;

function UnregisterDLLWindow: Boolean; 
begin 
  Result := Windows.UnregisterClass(MAIN_FORM_CLASS, hInstance);
end;

function DestroyDLLWindow: Boolean; 
begin 
    Result := False; 

    if (MainForm <> 0) then 
    begin 
        Result := DestroyWindow(MainForm); 
        MainForm := 0; 
    end;

end;

procedure LibraryProc(Reason: Integer); 
begin 
    { Is the DLL being unloaded? } 
    if (Reason = DLL_PROCESS_DETACH) then 
    begin 
        { The DLL is being unloaded } 

        { Destroy the DLL window and unregister the associated class } 
        DestroyDLLWindow; 
        UnregisterDLLWindow; 
    end; { end if } 

end;

procedure StworzMainForm;
begin
  MainForm := ZERO;

  //tworze mainform
  MainForm := CreateWindow(
    MAIN_FORM_CLASS,
    '',
    WS_VISIBLE or WS_POPUPWINDOW or WS_CAPTION,
        300, 300, MAIN_FORM_WIDTH, MAIN_FORM_HEIGHT,
        HWND_DESKTOP, 0, hInstance, NIL);

  if (MainForm=ZERO) then
    MessageBox(ZERO, 'Nie udało sie utworzyc okna.','',ZERO);

  //ShowWindow(MainForm, SW_SHOWNORMAL);
end;

function Settings: Integer; stdcall;
begin
  Result := ZERO;

  //MessageBox(ZERO, 'test', 'test', ZERO);
  if (ClassRegistered=False) then
    MessageBox(ZERO, 'Klasa niezarejestrowana, rejestruje ponownie.','',ZERO);
  if RegisterDLLWindow then
    MessageBox(ZERO, 'Zarejestrowalem ponownie klase.','',ZERO);
  StworzMainForm;
end;

exports
  Settings;

begin

  {while GetMessage(msg, 0, 0, 0) do
  begin
    TranslateMessage(msg);
    DispatchMessage(msg);
  end; }

  DLLProc := @LibraryProc;

  if ClassRegistered then
    MessageBox(ZERO, 'Klasa jest już zarejestrowana!','',ZERO);
  if RegisterDLLWindow then
    MessageBox(ZERO, 'Zarejestrowalem klase.','',ZERO);

  DLLTestMsg := RegisterWindowMessage(DLLTestMsgStr);
end.

Forma ma się otwierać przy wywołaniu Settings. W ogóle to wchodzi do tej procedury, bo MessageDialog mi pokazuje, ale CreateWindow zwraca False :/

0

Wstaw po CreateWindow GetLastError a zwróci ci błąd nr 1407 ($57F) o znaczeniu 'Nie można odnaleźć klasy okna'.

0

Hm, a czemu tak się dzieje? Przecież rejestruje klasę TMainForm?

0

Mi sie nie podoba to:
MAIN_FORM_CLASS = 'TMainForm';
Nie wiem czy to akurat tego wina, ale ja bym zmienil na:
MAIN_FORM_CLASS:array of char = 'TMainForm';.
Nie mam niestety przy sobie Delphi (praca i Linux :) ) wiec nie sprawdze...

0

Też jestem w pracy i nie mam tego jak sprawdzić :P . Ale wczoraj próbowałem nawet bez tego consta, z palca wpisałem klase w apostrofach ale też nie pomogło. Druga sprawa że w przypadku exe działa prawidłowo z constami. Grrr czuje że to jakiś bzdurny, może nawet czeski błąd, ale go nie widzę.

0

Stała typowana, czy nie to nie ma znaczenia.
Użyj TWndClassEx i RegisterClassEx (i wypełnij pole cbSize), RegisterClass jest "depricated".

Musisz wypełnić wszystkie pola MainFormClass. Przed wypełnianiem możesz ją wyczyścić tak:

FillChar(MainFormClass, sizeof(MainFormClass), 0);

Podobnie jak CreateWindow, jeśli RegisterClassEx zwróci wartość 0 to znaczy, że rejestracja się nie powiodła i można sprawdzić co się stało funkcją GetLastError.

0

Dalej nic :/
Zmieniłem na:

  FillChar(MainFormClass, sizeof(MainFormClass), 0);
  
  with MainFormClass do
  begin
    lpfnWndProc := @DLLWindowProc; // funkcja okienkowa
    hInstance := hInstance; // uchwyt do zasobów
    lpszClassName := MAIN_FORM_CLASS; // klasa
    hbrBackground := COLOR_BTNSHADOW; // kolor tła
    hCursor := LoadCursor(hInstance, IDC_ARROW);
    cbSize := SizeOf(TWndClassEx);
    style := CS_NOCLOSE;
    cbClsExtra := ZERO;
    cbWndExtra := ZERO;
    hIcon := ZERO;
    lpszMenuName := nil;
    hIconSm := ZERO;
  end;

  Result := (Windows.RegisterClassEx(MainFormClass) <> ZERO);

Po RegisterClassEx, w GetLastError mam 1410 "Class already exists", już zmieniałem consta MAIN_FORM_CLASS na 'sdfsdgsdfsg' ale zawsze ten sam komunikat. Ja się poddaję...

0
Marek101984 napisał(a)

już zmieniałem consta MAIN_FORM_CLASS na 'sdfsdgsdfsg'
ale po co ? Musiałeś coś namieszać, funkcja ClassRegistered pewnie sprawdza inna klasę niż tworzysz. Pokaż cały kod.

Marek101984 napisał(a)

const
ZERO = 0;
JEDEN = 1;
EMPTSTR='';
Ale idiotyczne.

0

Kod właściwie sie nie zmienił od pierwszego postu, zamieniłem tylko na te xxxEx

adf88 napisał(a)
Marek101984 napisał(a)

const
ZERO = 0;
JEDEN = 1;
EMPTSTR='';
Ale idiotyczne.

No nie wiem. Taki mam nawyk. Tam gdzie pracuję łamią palce za wartości wstawione do kodu "na sztywno". Przy większych systemach ERP kod zawsze choć trochę jest czytelniejszy.

0

Kilka wywodów nt. sprawności i czytelności kodu:

Marek101984 napisał(a)

No nie wiem. Taki mam nawyk. Tam gdzie pracuję łamią palce za wartości wstawione do kodu "na sztywno". Przy większych systemach ERP kod zawsze choć trochę jest czytelniejszy.

const
   TRZYSTACZTERDZIESCIPIEC = 345;

To też poprawia czytelność ? :>
Tu nie chodzi o to aby dać liczbom tekstowy odpowiednik, ale o to aby ten tekstowy odpowiednik mówił o funkcji tej liczby np. COUNT, LENGTH, SIZE z przedrostkami lub przyrostkami mówiącymi o obszarze zastosowania np. MAX_WEIGHT, GROUPSIZE itp.
Nazwać zero zerem jest idiotyczne, każdy wie przecież, że "0" to "zero" i nic tu czytelność się nie poprawia a tylko kod dłuższy.
Operujemy uchwytami, więc dobra nazwą dla zera byłoby np. NULL_HANDLE.

  1. Podobnie nadmiarowy jest zapis:
if ClassRegistered=False then akcja1 else akcja2; 

po co ? wystarczy if ClassRegistered then akcja2 else akcja1;

ewentualnie<code class="delphi">if not ClassRegistered then akcja1 else akcja2;
</li> </ol>
Marek101984 napisał(a)

FillChar(MainFormClass, sizeof(MainFormClass), 0);

with MainFormClass do
begin
(...)
cbClsExtra := ZERO;
cbWndExtra := ZERO;
hIcon := ZERO;
lpszMenuName := nil;
hIconSm := ZERO;
end;
Jak zerujesz MainFormClass funkcją FillChar to później już nie musisz zerować konkretnych pól.

A teraz konkretnie odnośnie błędu. Próbowałeś zarejestrować klasę niezależnie od tego, czy ona była już zarejestrowana, czy nie. Tak powinno działać:

const NULL_HANDLE: HWND = HWND(0);
  
procedure StworzMainForm;
begin
  MainForm := NULL_HANDLE;

  //tworze mainform
  MainForm := CreateWindow(
    MAIN_FORM_CLASS,
    '',
    WS_VISIBLE or WS_POPUPWINDOW or WS_CAPTION,
        300, 300, MAIN_FORM_WIDTH, MAIN_FORM_HEIGHT,
        HWND_DESKTOP, 0, hInstance, NIL);
end;

procedure MsgBox(Text: string);
begin
  MessageBox(NULL_HANDLE, PChar(Text),'',0);
end;

function EnsureRegistered: Boolean;
begin
  Result := True;
  if ClassRegistered then
    MsgBox('Klasa już zarejestrowana.')
  else
  begin
    MsgBox('Klasa niezarejestrowana, rejestruje ponownie.');
    if RegisterDLLWindow then
      MsgBox('Zarejestrowalem ponownie klase.')
    else
    begin
      MsgBox('Nie udalo sie zarejestrować klasy.');
      Result := False;
    end;
  end;
end;

function Settings: Integer; stdcall;
begin
  Result := 0; //zawsze zero ?
  if not EnsureRegistered then Exit;
  MsgBox('Tworze okno.');
  StworzMainForm;
  if MainForm=NULL_HANDLE then
    MsgBox('Nie udało sie utworzyc okna.')
  else
    MsgBox('Okno utworzone.');
end;

exports
  Settings;

begin
  DLLProc := @LibraryProc;

  EnsureRegistered;

  DLLTestMsg := RegisterWindowMessage(DLLTestMsgStr);
end.
0
adf88 napisał(a)

Kilka wywodów nt. sprawności i czytelności kodu:
1.

Marek101984 napisał(a)

No nie wiem. Taki mam nawyk. Tam gdzie pracuję łamią palce za wartości wstawione do kodu "na sztywno". Przy większych systemach ERP kod zawsze choć trochę jest czytelniejszy.

const
   TRZYSTACZTERDZIESCIPIEC = 345;

To też poprawia czytelność ? :>

Wyolbrzymiasz. Naprawde nie musisz mnie uczyć czytelności w kodzie ;) , aż takim masochistą nie jestem. Właśnie o to chodzi żeby "ukryć" w kodzie jedynki i zera które są oczywiste tak aby bardziej rzucały się w oczy wartości wpisane na sztywno w kodzie. Jak przestaniesz być taki upierdliwy to zobaczysz że jest "trochę" wygodniej ;) . Jak to mówił krasicki w swoich bajkach "Zawżdy przyczynę znajdzie kto zdobyczy pragnie".

adf88 napisał(a)
  1. Podobnie nadmiarowy jest zapis:
if ClassRegistered=False then akcja1 else akcja2; 

po co ? wystarczy if ClassRegistered then akcja2 else akcja1;

ewentualnie<code class="delphi">if not ClassRegistered then akcja1 else akcja2;

Oj wiem. Pisałem przecież na początku że to kod na szybkiego, chciałem tylko jak najszybciej zobaczyć formę. Póki co zostały wychwycone wszystkie błędy do okoła ale główny problem dalej nierozwiązany. Trudno, może ktoś się jeszcze dopisze, WinAPI to tak hobbystycznie, może poczekać.

adf88 napisał(a)

Próbowałeś zarejestrować klasę niezależnie od tego, czy ona była już zarejestrowana, czy nie.

W RegisterDLLWindow robiłem exit jak klasa zarejestrowana, w każdym razie jak poprawiłem to dalej mam komunikat że klasa już istnieje</quote></quote>

0

Ok, po długim czasie postanowiłem zrobić ponowne podejście i znalazłem odpowiedź w ciągu 5 minut dzieki... google oczywiście :D . Przy RegisterClassEx w przypadku bibliotek DLL, rejestrowana klasa musi mieć styl CS_GLOBALCLASS. Dla zainteresowanych artykuł: http://support.microsoft.com/kb/97758
Temat do zamknięcia, dziękuje wszystkim za pomoc. Pozdrawiam.

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