Wywołanie okna PrintDlgEx na 64bit Windows w lazarusie

Wywołanie okna PrintDlgEx na 64bit Windows w lazarusie
srobert1000
  • Rejestracja:około 16 lat
  • Ostatnio:około 17 godzin
0

Podany poniżej kod działa gdy program jest skompilowany pod windows 10 32bit, natomiast przy kompilacji pod 64bit zwraca komunikat E_INVALIDARG.

Kopiuj
unit Unit1;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, windows;

const
  PD_ALLPAGES = $00000000;
  PD_SELECTION = $00000001;
  PD_PAGENUMS = $00000002;
  PD_NOSELECTION = $00000004;
  PD_NOPAGENUMS = $00000008;
  PD_COLLATE = $00000010;
  PD_PRINTTOFILE = $00000020;
  PD_PRINTSETUP = $00000040;
  PD_NOWARNING = $00000080;
  PD_RETURNDC = $00000100;
  PD_RETURNIC = $00000200;
  PD_RETURNDEFAULT = $00000400;
  PD_SHOWHELP = $00000800;
  PD_ENABLEPRINTHOOK = $00001000;
  PD_ENABLESETUPHOOK = $00002000;
  PD_ENABLEPRINTTEMPLATE = $00004000;
  PD_ENABLESETUPTEMPLATE = $00008000;
  PD_ENABLEPRINTTEMPLATEHANDLE = $00010000;
  PD_ENABLESETUPTEMPLATEHANDLE = $00020000;
  PD_USEDEVMODECOPIES = $00040000;
  PD_USEDEVMODECOPIESANDCOLLATE = $00040000;
  PD_DISABLEPRINTTOFILE = $00080000;
  PD_HIDEPRINTTOFILE = $00100000;
  PD_NONETWORKBUTTON = $00200000;
  PD_CURRENTPAGE = $00400000;
  PD_NOCURRENTPAGE = $00800000;
  PD_EXCLUSIONFLAGS = $01000000;
  PD_USELARGETEMPLATE = $10000000;

  START_PAGE_GENERAL = $ffffffff;

  PD_RESULT_CANCEL = 0;
  PD_RESULT_PRINT = 1;
  PD_RESULT_APPLY = 2;


type
  tagPDEXW = packed record
    lStructSize : DWORD;
    hWndOwner : HWND;
    hDevMode : HGLOBAL;
    hDevNames : HGLOBAL;
    hDC : HDC;
    Flags : DWORD;
    Flags2 : DWORD;
    ExclusionFlags : DWORD;
    nPageRanges : DWORD;
    nMaxPageRanges : DWORD;
    lpPageRanges : Pointer;
    nMinPage : DWORD;
    nMaxPage : DWORD;
    nCopies : DWORD;
    hInstance : HWND;
    lpPrintTemplateName : PWideChar;
    lpCallback : Pointer;
    nPropertyPages : DWORD;
    lphPropertyPages : PtrUInt;
    nStartPage : DWORD;
    dwResultAction : DWORD;
  end;

  TPrintDlgEx = tagPDEXW;
  PPrintDlgEx = ^TPrintDlgEx;

  tagPRINTPAGERANGE = packed record
    nFromPage, nToPage : DWord;
  end;

  TPageRange = tagPRINTPAGERANGE;

type TPrintDlgExFunc = function (lppd : PPrintDlgEx): DWORD; stdcall;

type

  { TForm1 }

  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private

  public

  end;

var
  Form1: TForm1;

implementation

{$R *.lfm}

{ TForm1 }

procedure TForm1.Button1Click(Sender: TObject);
var hComDlg32 : THandle;
    PrintDlgEx : TPrintDlgExFunc;
    PrintDlgExResult : integer;
    pdex : TPrintDlgEx;
    PageRangeArray : array[1..3] of TPageRange;
begin
  hComDlg32 := LoadLibrary('comdlg32.dll');
  if hComDlg32 <> 0 then
  begin
    Pointer(PrintDlgEx) := GetProcAddress(hComDlg32,'PrintDlgExW');
    if Assigned(PrintDlgEx) then
    begin
      ZeroMemory(@pdex, sizeof(pdex));
      pdex.lStructSize := sizeof(pdex);
      pdex.hWndOwner := Handle;
      pdex.Flags := PD_ALLPAGES  {or PD_NOSELECTION or PD_NOCURRENTPAGE};
      pdex.nPageRanges := 1;
      pdex.nMaxPageRanges := 3;
      PageRangeArray[1].nFromPage := 1;
      PageRangeArray[1].nToPage := 10;
      pdex.lpPageRanges := @PageRangeArray[1];
      pdex.nMinPage := 1;
      pdex.nMaxPage := 10;
      pdex.nCopies := 1;
      pdex.nStartPage := START_PAGE_GENERAL;

      PrintDlgExResult := PrintDlgEx(@pdex);
      case PrintDlgExResult of
        E_OUTOFMEMORY: showMessage('E_OUTOFMEMORY');
        E_INVALIDARG: showMessage('E_INVALIDARG');
        E_POINTER: showMessage('E_POINTER');
        E_HANDLE: showMessage('E_HANDLE');
        E_FAIL: showMessage('E_FAIL');
        S_OK: begin
                case pdex.dwResultAction of
                  PD_RESULT_CANCEL: ShowMessage('Cancel');
                  PD_RESULT_PRINT: ShowMessage('Print');
                  PD_RESULT_APPLY: ShowMessage('Applay');
                end;
              end;
      end;
    end;
    FreeLibrary(hComDlg32);
  end;
end;

end.

Jak zrobić aby ten kod działał nie tylko na win32, ale także na win64.

edytowany 1x, ostatnio: srobert1000
KA
  • Rejestracja:ponad 19 lat
  • Ostatnio:około 5 godzin
  • Lokalizacja:Gorlice
3

Przede wszystkim wywalić packed z tagPDEXW = packed record i prawie już powinno działać (przynajmniej w Delphi)
Inne sugestie:

Kopiuj
  PPageRange = ^TPageRange; //<-- dodać
  
  tagPDEXW = record
    lStructSize : DWORD;
    hWndOwner : HWND;
    hDevMode : HGLOBAL;
    hDevNames : HGLOBAL;
    hDC : HDC;
    Flags : DWORD;
    Flags2 : DWORD;
    ExclusionFlags : DWORD;
    nPageRanges : DWORD;
    nMaxPageRanges : DWORD;
    lpPageRanges : Pointer; //<-- zmienić na PPageRange 
    nMinPage : DWORD;
    nMaxPage : DWORD;
    nCopies : DWORD;
    hInstance : HWND;
    lpPrintTemplateName : PWideChar;
    lpCallback : Pointer;
    nPropertyPages : DWORD;
    lphPropertyPages : PtrUInt; //<-- nie wiem to FP odpowiednik UIntPtr? Jeżeli tak to OK
    nStartPage : DWORD;
    dwResultAction : DWORD;
  end;

Druga potrzebna zmiana to typ zmiennej PrintDlgExResult : integer; zmienić na PrintDlgExResult : NativeInt; jeżeli w FP jest coś takiego, bo jak się okazało Integer to może być za mało w aplikacji 64bit (w Delphi rzucało wyjątkiem ERangeError ale tylko jak apka miała zwrócić błąd czyli w tym przypadku przed wywaleniem, tego packed).


Nie odpowiadam na PW w sprawie pomocy programistycznej.
Pytania zadawaj na forum, bo:
od tego ono jest ;) | celowo nie zawracasz gitary | przeczyta to więcej osób a więc większe szanse że ktoś pomoże.
edytowany 1x, ostatnio: kAzek
flowCRANE
Moderator Delphi/Pascal
  • Rejestracja:ponad 13 lat
  • Ostatnio:3 minuty
  • Lokalizacja:Tuchów
  • Postów:12150
3

Spróbuj z taką deklaracją struktur:

Kopiuj
type
  tagPDEXW = record
    lStructSize:         DWORD;
    hWndOwner:           HWND;
    hDevMode:            HGLOBAL;
    hDevNames:           HGLOBAL;
    hDC:                 HDC;
    Flags:               DWORD;
    Flags2:              DWORD;
    ExclusionFlags:      DWORD;
    nPageRanges:         DWORD;
    nMaxPageRanges:      DWORD;
    lpPageRanges:        Pointer;
    nMinPage:            DWORD;
    nMaxPage:            DWORD;
    nCopies:             DWORD;
    hInstance:           HINST;
    lpPrintTemplateName: LPCWSTR;
    lpCallback:          Pointer;
    nPropertyPages:      DWORD;
    lphPropertyPages:    Pointer;
    nStartPage:          DWORD;
    dwResultAction:      DWORD;
  end;

type
  tagPRINTPAGERANGE = record
    nFromPage: DWORD;
    nToPage:   DWORD;
  end;

W razie dalszych problemów możesz wymusić wyrównanie pamięci struktur danych, tak aby było zgodne ze strukturami deklarowanymi w C (czyli tak jak to robi Win32 API). Do tego służy dyrektywa $PACKRECORDS oraz argument C:

Kopiuj
{$PACKRECORDS C}

Często można ją spotkać w nagłówkach automatycznie tłumaczonych z C na Free Pascala (jakimiś transpilerami).

Choć wątpię, aby pakowanie tych struktur miało jakiekolwiek znaczenie, skoro wszystkie ich pola są albo 32-bitowe, albo 64-bitowe. Żeby wyrównanie bajtowe było nieprawidłowe, musiałoby być zmienione w tym module na jakiekolwiek inne niż domyślne, przed deklaracjami tych struktur.

W razie gdybyś dla całego modułu potrzebował innego wyrównania niż domyślnego, ale dla struktur używanych w połączeniu z Win32 API chciał mieć to konkretne, możesz skorzystać z poniższej składni:

Kopiuj
// Zapamiętaj poprzednie wyrównanie i ustaw to dla struktur C.
{$PUSH PACKRECORDS}
{$PACKRECORDS C}

// Tu deklaracja struktur dla Win32 API.

// Przywróć poprzednie wyrównanie.
{$POP PACKRECORDS}

Jeśli masz problem z implementacją nagłówków, trzymaj się oryginalnych nazw oraz typów danych dedykowanych danemu API (tutaj: Win32 API). Po drugie, nigdy nie używaj pakowania struktur i macierzy, jeśli korzystasz z cudzego API, chyba że wyraźnie jest to napisane w dokumentacji. W przypadku Win32 API, zwykle nie używa się pakowanych struktur i macierzy, więc packed należy usunąć.

Przy okazji @kAzek wspomniał o kilku rzeczach wartych przyjrzenia się i skomentowania.

kAzek napisał(a):
Kopiuj
lpPageRanges : Pointer; //<-- zmienić na PPageRange

To nie jest zbyt istotne. Miałoby znaczenie, gdybyśmy potrzebowali konkretnego typu pointera na potrzeby jego dereferencji lub uniknięcia błędów kompilacji spowodowanych silnym typowaniem języka. A tak to typ wskaźnika jest obojętny, byle zgadzała się liczba jego poziomów (tutaj wymagany jest jednopoziomowy wskaźnik, więc typ Pointer jest prawidłowy).

Kopiuj
lphPropertyPages : PtrUInt; //<-- nie wiem to FP odpowiednik UIntPtr? Jeżeli tak to OK

To pole ma być jednopoziomowym wskaźnikiem, więc powinno być typu Pointer (w module Windows nie ma deklaracji typu LPPRINTPAGERANGE). Deklarowanie tego w formie liczby może się zemścić.


Pracuję nad własną, arcade'ową, docelowo komercyjną grą z gatunku action/adventure w stylu retro (pixel art), programując silnik i powłokę gry od zupełnych podstaw, przy użyciu Free Pascala i SDL3. Więcej informacji znajdziesz na moim mikroblogu.
edytowany 3x, ostatnio: flowCRANE
KA
Akurat widocznie packed jednak ma znaczenie po akurat zmiana tego powoduje że błędu nie ma (przynajmniej w Delphi), tamte pozostałe to sugestie... a ostanie z typem Integer to chyba tylko Delphi się ciska, bo pytający normalnie dostawał błąd E_INVALIDARG.
flowCRANE
Serio bez packed zadziałało? Muszę sprawdzić rozmiary tych struktur, spakowanej i nie, bo chyba coś mi umknęło, przez co teoria mija się z praktyką. :D
KA
W Delphi apka 32 bit z packed 84bajty bez też 84 bajty; apka 64 bit z packed 120 bajtów bez 136 bajtów (obojętne twoja czy jego deklaracja typu)
flowCRANE
O widzisz, dzięki za test (właśnie miałem się za niego zabierać). Czyli prawidłowo zdiagnozowałeś przyczynę problemu.
srobert1000
  • Rejestracja:około 16 lat
  • Ostatnio:około 17 godzin
0

Dzięki wszystkim za pomoc.

Mam jeszcze takie pytanie.

Kopiuj
const
  IID_IObjectWithSite = '{FC4801A3-2BA9-11CF-A229-00AA003D7352}';
  IID_IPrintDialogCallback = '{5852A2C3-6530-11D1-B6A3-0000F8757BF9}';

type
    IObjectWithSite = interface(IUnknown)
      [IID_IObjectWithSite]
      function SetSite(const pUnkSite: IUnknown): HResult; stdcall;
      function GetSite(const riid: TIID; out site: IUnknown): HResult; stdcall;
    end;

  type
    IPrintDialogCallback = interface(IUnknown)
      [IID_IPrintDialogCallback]
      function InitDone: HResult; stdcall;
      function SelectionChange: HResult; stdcall;
      function HandleMessage(hDlg: hWnd; uMsg: UINT; WParam: WParam; LParam:
        LParam; var pResult: lresult): HResult; stdcall;
    end;

  type
    TPrintDialogCallback = class(TInterfacedObject, IObjectWithSite, IPrintDialogCallback)
    private
      //FSite: IPrintDialogServices;
    public

      // IObjectWithSite
      function SetSite(const pUnkSite: IUnknown): HResult; stdcall;
      function GetSite(const riid: TIID; out site: IUnknown): HResult; stdcall;

      // IPrintDialogCallback
      function InitDone: HResult; stdcall;
      function SelectionChange: HResult; stdcall;
      function HandleMessage(hDlg: hWnd; uMsg: UINT; WParam: WParam; LParam:
        LParam; var pResult: lresult): HResult; stdcall;
    end;

implementation

{ TPrintDialogCallback }

{function TPrintDialogCallback.SetSite(const pUnkSite: IUnknown): HResult; stdcall;
begin
  Result := S_OK;
end;

function TPrintDialogCallback.GetSite(const riid: TIID; out site: IUnknown): HResult; stdcall;
begin
  Result := S_OK;
end;

function TPrintDialogCallback.HandleMessage(hDlg: hWnd; uMsg: UINT;
  WParam: WParam; LParam: LParam; var pResult: lresult): HResult; stdcall;
begin
  Result := S_FALSE;
end;

function TPrintDialogCallback.InitDone: HResult; stdcall;
begin
  Result := S_FALSE;
end;

function TPrintDialogCallback.SelectionChange: HResult; stdcall;
begin
  Result := S_FALSE;
end;

Jak prawidłowo zainicjować TPrintDialogCallback aby można było go przypisać do lpCallback.

edytowany 1x, ostatnio: srobert1000
srobert1000
  • Rejestracja:około 16 lat
  • Ostatnio:około 17 godzin
0

Mam jeszcze takie pytanie

Kopiuj
const
  IID_IObjectWithSite = '{FC4801A3-2BA9-11CF-A229-00AA003D7352}';
  IID_IPrintDialogCallback = '{5852A2C3-6530-11D1-B6A3-0000F8757BF9}';

type
    IObjectWithSite = interface(IUnknown)
      [IID_IObjectWithSite]
      function SetSite(const pUnkSite: IUnknown): HResult; stdcall;
      function GetSite(const riid: TIID; out site: IUnknown): HResult; stdcall;
    end;

  type
    IPrintDialogCallback = interface(IUnknown)
      [IID_IPrintDialogCallback]
      function InitDone: HResult; stdcall;
      function SelectionChange: HResult; stdcall;
      function HandleMessage(hDlg: hWnd; uMsg: UINT; WParam: WParam; LParam:
        LParam; var pResult: lresult): HResult; stdcall;
    end;

  type
    TPrintDialogCallback = class(TInterfacedObject, IObjectWithSite, IPrintDialogCallback)
    private
      //FSite: IPrintDialogServices;
    public

      // IObjectWithSite
      function SetSite(const pUnkSite: IUnknown): HResult; stdcall;
      function GetSite(const riid: TIID; out site: IUnknown): HResult; stdcall;

      // IPrintDialogCallback
      function InitDone: HResult; stdcall;
      function SelectionChange: HResult; stdcall;
      function HandleMessage(hDlg: hWnd; uMsg: UINT; WParam: WParam; LParam:
        LParam; var pResult: lresult): HResult; stdcall;
    end;

implementation

{ TPrintDialogCallback }

{function TPrintDialogCallback.SetSite(const pUnkSite: IUnknown): HResult; stdcall;
begin
  Result := S_OK;
end;

function TPrintDialogCallback.GetSite(const riid: TIID; out site: IUnknown): HResult; stdcall;
begin
  Result := S_OK;
end;

function TPrintDialogCallback.HandleMessage(hDlg: hWnd; uMsg: UINT;
  WParam: WParam; LParam: LParam; var pResult: lresult): HResult; stdcall;
begin
  Result := S_FALSE;
end;

function TPrintDialogCallback.InitDone: HResult; stdcall;
begin
  Result := S_FALSE;
end;

function TPrintDialogCallback.SelectionChange: HResult; stdcall;
begin
  Result := S_FALSE;
end;

Jak prawidłowo zainicjować TPrintDialogCallback aby można było go przypisać do lpCallback.

Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)