1{%MainUnit win32int.pp}
2{ $Id$ }
3{******************************************************************************
4                 All GTK interface communication implementations.
5                   Initial Revision  : Sun Nov 23 23:53:53 2003
6
7
8  !! Keep alphabetical !!
9
10  Support routines go to gtkproc.pp
11
12 ******************************************************************************
13 Implementation
14 ******************************************************************************
15
16 *****************************************************************************
17  This file is part of the Lazarus Component Library (LCL)
18
19  See the file COPYING.modifiedLGPL.txt, included in this distribution,
20  for details about the license.
21 *****************************************************************************
22}
23
24//##apiwiz##sps##   // Do not remove
25
26function TWin32WidgetSet.AddEventHandler(AHandle: THandle; AFlags: dword;
27  AEventHandler: TWaitHandleEvent; AData: PtrInt): PEventHandler;
28var
29  listlen: dword;
30  lListIndex: pdword;
31begin
32  listlen := Length(FWaitHandles);
33  if FWaitHandleCount = listlen then
34  begin
35    inc(listlen, 16);
36    SetLength(FWaitHandles, listlen);
37    SetLength(FWaitHandlers, listlen);
38  end;
39  New(lListIndex);
40  FWaitHandles[FWaitHandleCount] := AHandle;
41  FWaitHandlers[FWaitHandleCount].ListIndex := lListIndex;
42  FWaitHandlers[FWaitHandleCount].UserData := AData;
43  FWaitHandlers[FWaitHandleCount].OnEvent := AEventHandler;
44  lListIndex^ := FWaitHandleCount;
45  Inc(FWaitHandleCount);
46{$ifdef DEBUG_ASYNCEVENTS}
47  DebugLn('Waiting for handle: ', IntToHex(AHandle, 8));
48{$endif}
49  Result := lListIndex;
50end;
51
52function TWin32WidgetSet.AddPipeEventHandler(AHandle: THandle;
53  AEventHandler: TPipeEvent; AData: PtrInt): PPipeEventHandler;
54var
55  lHandler: PPipeEventInfo;
56begin
57  if AEventHandler = nil then exit(nil);
58  New(lHandler);
59  lHandler^.Handle := AHandle;
60  lHandler^.UserData := AData;
61  lHandler^.OnEvent := AEventHandler;
62  lHandler^.Prev := nil;
63  lHandler^.Next := FWaitPipeHandlers;
64  if FWaitPipeHandlers <> nil then
65    FWaitPipeHandlers^.Prev := lHandler;
66  FWaitPipeHandlers := lHandler;
67  Result := lHandler;
68end;
69
70function TWin32WidgetSet.AddProcessEventHandler(AHandle: THandle;
71  AEventHandler: TChildExitEvent; AData: PtrInt): PProcessEventHandler;
72var
73  lProcessEvent: PProcessEvent;
74begin
75  if AEventHandler = nil then exit(nil);
76  New(lProcessEvent);
77  lProcessEvent^.Handle := AHandle;
78  lProcessEvent^.UserData := AData;
79  lProcessEvent^.OnEvent := AEventHandler;
80  lProcessEvent^.Handler := AddEventHandler(AHandle, 0,
81    @HandleProcessEvent, PtrInt(lProcessEvent));
82  Result := lProcessEvent;
83end;
84
85{------------------------------------------------------------------------------
86  Method:  ExtUTF8Out
87
88  As ExtTextOut except that Str is treated as UTF8
89 ------------------------------------------------------------------------------}
90function TWin32WidgetSet.ExtUTF8Out(DC: HDC; X, Y: Integer; Options: Longint; Rect: PRect;
91  Str: PChar; Count: Longint; Dx: PInteger): Boolean;
92begin
93  Result := ExtTextOut(DC, X, Y, Options, Rect, Str, Count, Dx);
94end;
95
96type
97  TFontIsMonoSpaceRec = record
98    Name: string;
99    Result: Boolean;
100  end;
101  PFontIsMonoSpaceRec = ^TFontIsMonoSpaceRec;
102
103function EnumFontsCallBack(
104  var LogFont: TEnumLogFontEx;
105  var Metric: TNewTextMetricEx;
106  FontType: Longint;
107  Data: LParam):LongInt; stdcall;
108var
109  R: PFontIsMonoSpaceRec;
110begin
111  R := PFontIsMonoSpaceRec(Data);
112  if ((logfont.elfLogFont.lfPitchAndFamily and FIXED_PITCH) = FIXED_PITCH)
113  and (CompareStr(R^.Name, LogFont.elfLogFont.lfFaceName) = 0) then
114  begin
115    R^.Result := True;
116    Result := 0 // we found it -> stop enumeration
117  end else
118    Result := 1;
119end;
120
121
122function TWin32WidgetSet.FontIsMonoSpace(Font: HFont): boolean;
123var
124  LF: LogFontA;
125  Res: LongInt;
126  DC: HDC;
127  Rec: TFontIsMonoSpaceRec;
128begin
129  Result := False;
130  FillChar(LF{%H-}, SizeOf(LogFontA), #0);
131  Res := GetObject(Font, SizeOf(LogFontA),@LF);
132  //writeln('TWin32WidgetSet.FontIsMonoSpace: Res = ',Res,' SizeOf(LogFont) = ',SizeOf(LogFontA));
133  //TWin32WidgetSet.GetObject uses LogFontW and converts back to LogFontA, so Res should be SizeOf(LogFontW)
134  if (Res <> SizeOf(LogFontW)) then
135    Exit;
136  LF.lfCharSet := DEFAULT_CHARSET;
137  LF.lfPitchAndFamily := 0;
138  Rec.Name := LF.lfFaceName;
139  Rec.Result := False;
140  DC := GetDC(0);
141  try
142    EnumFontFamiliesEX(DC, @LF, @EnumFontsCallback, LPARAM(@Rec), 0);
143  finally
144    ReleaseDC(0, DC);
145  end;
146  Result := Rec.Result;
147end;
148
149procedure TWin32WidgetSet.HandleProcessEvent(AData: PtrInt; AFlags: dword);
150var
151  lProcessEvent: PProcessEvent absolute AData;
152  exitcode: dword;
153begin
154  if not Windows.GetExitCodeProcess(lProcessEvent^.Handle, exitcode) then
155    exitcode := 0;
156  lProcessEvent^.OnEvent(lProcessEvent^.UserData, cerExit, exitcode);
157end;
158
159{------------------------------------------------------------------------------
160  Function: RawImage_QueryDescription
161  Params: AFlags:
162          ADesc:
163  Returns:
164
165 ------------------------------------------------------------------------------}
166function TWin32WidgetSet.RawImage_QueryDescription(AFlags: TRawImageQueryFlags; var ADesc: TRawImageDescription): Boolean;
167begin
168  if riqfAlpha in AFlags
169  then begin
170    //always return rgba description
171    if not (riqfUpdate in AFlags)
172    then ADesc.Init;
173
174    ADesc.Format := ricfRGBA;
175    ADesc.Depth := 32;
176    ADesc.BitOrder := riboReversedBits;
177    ADesc.ByteOrder := riboLSBFirst;
178    ADesc.LineOrder := riloTopToBottom;
179    ADesc.LineEnd := rileDWordBoundary;
180    ADesc.BitsPerPixel := 32;
181
182    ADesc.AlphaPrec := 8;
183    ADesc.AlphaShift := 24;
184
185    if riqfRGB in AFlags
186    then begin
187      ADesc.RedPrec := 8;
188      ADesc.GreenPrec := 8;
189      ADesc.BluePrec := 8;
190      ADesc.RedShift := 16;
191      ADesc.GreenShift := 8;
192      ADesc.BlueShift := 0;
193    end;
194
195    AFlags := AFlags - [riqfRGB, riqfAlpha, riqfUpdate];
196    if AFlags = [] then Exit(True);
197
198    // continue with default
199    Include(AFlags, riqfUpdate);
200  end;
201
202  Result := inherited RawImage_QueryDescription(AFlags, ADesc);
203  // reduce mem
204  if Result and (ADesc.Depth = 24)
205  then ADesc.BitsPerPixel := 24;
206end;
207
208procedure TWin32WidgetSet.RemoveProcessEventHandler(var AHandler: PProcessEventHandler);
209var
210  lProcessEvent: PProcessEvent absolute AHandler;
211begin
212  if AHandler = nil then exit;
213  RemoveEventHandler(lProcessEvent^.Handler);
214  Dispose(lProcessEvent);
215  AHandler := nil;
216end;
217
218procedure TWin32WidgetSet.SetRubberBandRect(const ARubberBand: HWND; const ARect: TRect);
219begin
220  with ARect do
221    SetWindowPos(ARubberBand, 0, Left, Top, Right - Left, Bottom - Top, SWP_NOZORDER or SWP_NOACTIVATE);
222end;
223
224{------------------------------------------------------------------------------
225  Function:
226  Params:
227
228  Returns:
229
230 ------------------------------------------------------------------------------}
231function TWin32WidgetSet.CreateStandardCursor(ACursor: SmallInt): hCursor;
232begin
233  Result := 0;
234  if ACursor < crLow then Exit;
235  if ACursor > crHigh then Exit;
236
237  case ACursor of
238    crSqlWait..crDrag, crNone:
239    begin
240      // TODO: load custom cursors here not in the LCL
241    end;
242  else
243    Result := Windows.LoadCursor(0, LclCursorToWin32CursorMap[ACursor]);
244  end;
245end;
246
247function DockWindowProc(Window: HWnd; Msg: UInt; WParam: Windows.WParam;
248    LParam: Windows.LParam): LResult; stdcall;
249begin
250  if (Msg = WM_ACTIVATE) and (LoWord(WParam) <> WA_INACTIVE) and (LParam <> 0) then
251    Windows.SendMessage(LParam, WM_NCACTIVATE, 1, 0);
252  Result := Windows.DefWindowProc(Window, Msg, WParam, LParam);
253end;
254
255function TWin32WidgetSet.CreateRubberBand(const ARect: TRect; const ABrush: HBrush = 0): HWND;
256var
257  WindowClass: Windows.WNDCLASS;
258  WndClassName: String;
259begin
260  WndClassName := 'LazRubberBand' + IntToStr(ABrush);
261
262  if not Windows.GetClassInfo(System.HInstance, PChar(WndClassName), WindowClass) then
263  begin
264    with WindowClass do
265    begin
266      Style := 0;
267      LPFnWndProc := @DockWindowProc;
268      CbClsExtra := 0;
269      CbWndExtra := 0;
270      hInstance := System.HInstance;
271      hIcon := Windows.LoadIcon(0, IDI_APPLICATION);
272      hCursor := Windows.LoadCursor(0, IDC_ARROW);
273      if ABrush = 0 then
274        hbrBackground := GetSysColorBrush(COLOR_HIGHLIGHT)
275      else
276        hbrBackground := ABrush;
277      LPSzMenuName := nil;
278      LPSzClassName := PChar(WndClassName);
279    end;
280    Windows.RegisterClass(@WindowClass);
281  end;
282
283  if WindowsVersion >= wv2000 then
284  begin
285    Result := CreateWindowEx(WS_EX_LAYERED or WS_EX_TRANSPARENT or WS_EX_TOPMOST or WS_EX_TOOLWINDOW,
286      PChar(WndClassName), PChar(WndClassName), WS_POPUP or WS_VISIBLE,
287      ARect.Left, ARect.Top, ARect.Right - ARect.Left, ARect.Bottom - ARect.Top, AppHandle, 0, System.HInstance, nil);
288
289    SetLayeredWindowAttributes(Result, 0, $30, LWA_ALPHA);
290  end
291  else
292    Result := CreateWindowEx(WS_EX_TOPMOST or WS_EX_TOOLWINDOW,
293      PChar(WndClassName), PChar(WndClassName), WS_POPUP or WS_VISIBLE,
294      ARect.Left, ARect.Top, ARect.Right - ARect.Left, ARect.Bottom - ARect.Top, AppHandle, 0, System.HInstance, nil);
295end;
296
297{------------------------------------------------------------------------------
298  Method: CallbackAllocateHWnd
299  Params:   None
300  Returns:  Nothing
301
302  Callback for the AllocateHWnd function
303 ------------------------------------------------------------------------------}
304procedure CallbackAllocateHWnd(Ahwnd: HWND; uMsg: UINT; wParam: WParam; lParam: LParam); stdcall;
305var
306  Msg: TLMessage;
307  PMethod: ^TLCLWndMethod;
308begin
309  FillChar(Msg{%H-}, SizeOf(Msg), #0);
310
311  Msg.msg := uMsg;
312  Msg.wParam := wParam;
313  Msg.lParam := lParam;
314
315  {------------------------------------------------------------------------------
316    Here we get the callback WndMethod associated with this window
317   ------------------------------------------------------------------------------}
318  PMethod := {%H-}Pointer(Widgetset.GetWindowLong(ahwnd, GWL_USERDATA));
319
320  if Assigned(PMethod) then PMethod^(Msg);
321
322  Windows.DefWindowProc(ahwnd, uMsg, wParam, lParam);
323end;
324
325{------------------------------------------------------------------------------
326  Method: TWin32WidgetSet.AllocateHWnd
327  Params:   Method  - The callback method for the window. Can be nil
328  Returns:  A window handle
329
330  Allocates a non-visible window that can be utilized to receive and send message
331
332  On Windows, you must call Windows.DefWindowProc(MyHandle, Msg.msg, Msg.wParam, msg.lParam);
333  in your callback function, if you provide one at all, of course.
334 ------------------------------------------------------------------------------}
335function TWin32WidgetSet.AllocateHWnd(Method: TLCLWndMethod): HWND;
336var
337  PMethod: ^TLCLWndMethod;
338begin
339  Result := Windows.CreateWindow(@ClsName[0],
340   '', WS_OVERLAPPED, 0, 0, 0, 0, 0, 0, MainInstance, nil);
341
342  {------------------------------------------------------------------------------
343    SetWindowLong has only space for 1 pointer on each slot, but a method is
344   referenced as a structure with 2 pointers, so here we allocate memory for
345   the structure before it can be used to transport data between the callback
346   and this function
347   ------------------------------------------------------------------------------}
348  if Assigned(Method) then
349  begin
350    Getmem(PMethod, SizeOf(TMethod));
351    PMethod^ := Method;
352
353    Self.SetWindowLong(Result, GWL_USERDATA, {%H-}PtrInt(PMethod));
354  end;
355
356  Self.SetWindowLong(Result, GWL_WNDPROC, {%H-}PtrInt(@CallbackAllocateHWnd))
357end;
358
359function TWin32WidgetSet.AskUser(const DialogCaption, DialogMessage: string;
360  DialogType: LongInt; Buttons: TDialogButtons; HelpCtx: Longint): LongInt;
361var
362  i: Integer;
363  Caption: String;
364  TaskConfig: TTASKDIALOGCONFIG;
365  DialogButtons: PTASKDIALOG_BUTTON;
366  State: TApplicationState;
367begin
368  //TaskDialogIndirect is available in Vista and up, but only if app was built with manifest.
369  //The check for the latter is done by checking for ComCtlVersionIE6 (which is set in the manifest)
370  //The availability of TaskDialogIndirect does not depend on the status of ThemeServices
371  //Issue #0027664
372  if (WindowsVersion >= wvVista) and (GetFileVersion(comctl32) >= ComCtlVersionIE6) then
373  begin
374    FillChar(TaskConfig{%H-}, SizeOf(TaskConfig), 0);
375    TaskConfig.cbSize := SizeOf(TaskConfig);
376    // if we skip hwndParent our form will be a root window - with the taskbar item and icon
377    // this is unwanted
378    if Assigned(Screen.ActiveCustomForm) then
379      TaskConfig.hwndParent := Screen.ActiveCustomForm.Handle
380    else
381    if Assigned(Application.MainForm) then
382      TaskConfig.hwndParent := Application.MainFormHandle
383    else
384      TaskConfig.hwndParent := AppHandle;
385    TaskConfig.hInstance := HInstance;
386    TaskConfig.dwFlags := TDF_ALLOW_DIALOG_CANCELLATION;
387    if DialogCaption <> '' then
388      Caption := DialogCaption
389    else
390    case DialogType of
391      idDialogConfirm,
392      idDialogInfo,
393      idDialogWarning,
394      idDialogError: Caption := GetDialogCaption(DialogType);
395    else
396      Caption := Application.Title;
397    end;
398    TaskConfig.pszWindowTitle := PWideChar(UTF8ToUTF16(Caption));
399
400    case DialogType of
401      idDialogConfirm:
402        begin
403          TaskConfig.hMainIcon := Windows.LoadIcon(0, IDI_QUESTION);
404          TaskConfig.dwFlags := TaskConfig.dwFlags or TDF_USE_HICON_MAIN;
405        end;
406      idDialogInfo: TaskConfig.pszMainIcon := TD_INFORMATION_ICON;
407      idDialogWarning: TaskConfig.pszMainIcon := TD_WARNING_ICON;
408      idDialogError: TaskConfig.pszMainIcon := TD_ERROR_ICON;
409      idDialogShield: TaskConfig.pszMainIcon := TD_SHIELD_ICON;
410    else
411      TaskConfig.dwFlags := TaskConfig.dwFlags or TDF_USE_HICON_MAIN;
412    end;
413
414    TaskConfig.pszContent := PWideChar(UTF8ToUTF16(DialogMessage));
415
416    // question dialog button magic :)
417
418    TaskConfig.cButtons := Buttons.Count;
419    GetMem(DialogButtons, SizeOf(TTASKDIALOG_BUTTON) * TaskConfig.cButtons);
420    for i := 0 to TaskConfig.cButtons - 1 do
421    begin
422      DialogButtons[i].nButtonID := Buttons[i].ModalResult;
423      DialogButtons[i].pszButtonText := UTF8StringToPWideChar(Buttons[i].Caption);
424    end;
425    TaskConfig.pButtons := DialogButtons;
426    if Assigned(Buttons.DefaultButton) then
427      TaskConfig.nDefaultButton := Buttons.DefaultButton.ModalResult;
428
429    State := SaveApplicationState;
430    try
431      Result := IDCANCEL;
432      TaskDialogIndirect(@TaskConfig, @Result, nil, nil);
433      if (Result = IDCANCEL) then
434      begin
435        if Assigned(Buttons.CancelButton) then
436          Result := Buttons.CancelButton.ModalResult
437        else
438          Result := mrCancel;
439      end;
440    finally
441      RestoreApplicationState(State);
442      for i := 0 to TaskConfig.cButtons - 1 do
443        FreeMem(DialogButtons[i].pszButtonText);
444      FreeMem(DialogButtons);
445    end;
446  end
447  else
448    Result := inherited AskUser(DialogCaption, DialogMessage, DialogType,
449      Buttons, HelpCtx);
450end;
451
452{------------------------------------------------------------------------------
453  Method: TWin32WidgetSet.DeallocateHWnd
454  Params:   Wnd   - A Window handle, that was created with AllocateHWnd
455  Returns:  Nothing
456 ------------------------------------------------------------------------------}
457procedure TWin32WidgetSet.DeallocateHWnd(Wnd: HWND);
458var
459  PMethod: ^TLCLWndMethod;
460begin
461  PMethod := {%H-}Pointer(Self.GetWindowLong(Wnd, GWL_USERDATA));
462
463  if Wnd <> 0 then Windows.DestroyWindow(Wnd);
464
465  {------------------------------------------------------------------------------
466    This must be done after DestroyWindow, otherwise a Access Violation will
467   happen when WM_CLOSE message is sent to the callback
468
469    This memory is for the TMethod structure allocated on AllocateHWnd
470   ------------------------------------------------------------------------------}
471  if Assigned(PMethod) then Freemem(PMethod);
472end;
473
474procedure TWin32WidgetSet.DestroyRubberBand(ARubberBand: HWND);
475var
476  WndClassName: array[0..255] of Char;
477begin
478  GetClassName(ARubberBand, @WndClassName, 255);
479  // preserve the brush or it will be deleted
480  SetClassLongPtr(ARubberBand, GCL_HBRBACKGROUND, 0);
481  DestroyWindow(ARubberBand);
482  Windows.UnRegisterClass(@WndClassName, System.HINSTANCE);
483end;
484
485procedure TWin32WidgetSet.DrawDefaultDockImage(AOldRect, ANewRect: TRect; AOperation: TDockImageOperation);
486const
487  LineSize = 4;
488
489  procedure DrawHorzLine(DC: HDC; x1, x2, y: integer); inline;
490  begin
491    PatBlt(DC, x1, y, x2 - x1, LineSize, PATINVERT);
492  end;
493
494  procedure DrawVertLine(DC: HDC; y1, y2, x: integer); inline;
495  begin
496    PatBlt(DC, x, y1, LineSize, y2 - y1, PATINVERT);
497  end;
498
499  procedure DefaultDockImage(ARect: TRect);
500  var
501    DC: HDC;
502    NewBrush, OldBrush: HBrush;
503  begin
504    DC := GetDCEx(0, 0, DCX_LOCKWINDOWUPDATE); // drawing during tracking
505    try
506      NewBrush := CreatePatternBrush(Win32WidgetSet.DotsPatternBitmap);
507      OldBrush := SelectObject(DC, NewBrush);
508      DrawHorzLine(DC, ARect.Left, ARect.Right, ARect.Top);
509      DrawVertLine(DC, ARect.Top + LineSize, ARect.Bottom - LineSize, ARect.Left);
510      DrawHorzLine(DC, ARect.Left, ARect.Right, ARect.Bottom - LineSize);
511      DrawVertLine(DC, ARect.Top + LineSize, ARect.Bottom - LineSize, ARect.Right - LineSize);
512      DeleteObject(SelectObject(DC, OldBrush));
513    finally
514      ReleaseDC(0, DC);
515    end;
516  end;
517var
518  WindowClass: WndClass;
519begin
520  if WindowsVersion >= wv2000 then
521  begin
522    case AOperation of
523      disShow:
524      begin
525        with WindowClass do
526        begin
527          Style := 0;
528          LPFnWndProc := @DockWindowProc;
529          CbClsExtra := 0;
530          CbWndExtra := 0;
531          hInstance := System.HInstance;
532          hIcon := Windows.LoadIcon(0, IDI_APPLICATION);
533          hCursor := Windows.LoadCursor(0, IDC_ARROW);
534          hbrBackground := GetSysColorBrush(COLOR_HIGHLIGHT);
535          LPSzMenuName := nil;
536          LPSzClassName := 'LazDockWnd';
537        end;
538        Windows.RegisterClass(@WindowClass);
539        FDockWndHandle := CreateWindowEx(WS_EX_LAYERED or WS_EX_TRANSPARENT or WS_EX_TOPMOST or WS_EX_TOOLWINDOW,
540          'LazDockWnd', 'LazDockWnd', WS_POPUP or WS_VISIBLE,
541          ANewRect.Left, ANewRect.Top, ANewRect.Right - ANewRect.Left, ANewRect.Bottom - ANewRect.Top, AppHandle, 0, System.HINSTANCE, nil);
542
543        SetLayeredWindowAttributes(FDockWndHandle, 0, $30, LWA_ALPHA);
544      end;
545      disHide:
546        begin
547          DestroyWindow(FDockWndHandle);
548          Windows.UnRegisterClass('LazDockWnd', System.HINSTANCE);
549        end;
550      disMove:
551        with ANewRect do
552          SetWindowPos(FDockWndHandle, 0, Left, Top, Right - Left, Bottom - Top, SWP_NOZORDER or SWP_NOACTIVATE);
553    end;
554  end
555  else
556  begin
557    if AOperation in [disMove, disHide] then
558      DefaultDockImage(AOldRect);
559    if AOperation in [disMove, disShow] then
560      DefaultDockImage(ANewRect);
561  end;
562end;
563
564procedure TWin32WidgetSet.DrawGrid(DC: HDC; const R: TRect; DX, DY: Integer);
565var
566  x, y: integer;
567  ALogPen: TLogPen;
568begin
569  GetObject(GetCurrentObject(DC, OBJ_PEN), SizeOf(ALogPen), @ALogPen);
570  x := R.Left;
571  while x <= R.Right do
572  begin
573    y := R.Top;
574    while y <= R.Bottom do
575    begin
576      DCSetPixel(DC, X, Y, ALogPen.lopnColor);
577      Inc(y, DY);
578    end;
579    Inc(x, DX);
580  end;
581end;
582
583{------------------------------------------------------------------------------
584  Function: GetAcceleratorString
585  Params: AVKey:
586          AShiftState:
587  Returns:
588
589 ------------------------------------------------------------------------------}
590function TWin32WidgetSet.GetAcceleratorString(const AVKey: Byte; const AShiftState: TShiftState): String;
591begin
592  //TODO: Implement
593  Result := '';
594end;
595
596{------------------------------------------------------------------------------
597  Function: GetControlConstraints
598  Params: Constraints: TObject
599  Returns: true on success
600
601  Updates the constraints object (e.g. TSizeConstraints) with interface specific
602  bounds.
603 ------------------------------------------------------------------------------}
604function TWin32WidgetSet.GetControlConstraints(Constraints: TObject): boolean;
605var
606  SizeConstraints: TSizeConstraints absolute Constraints;
607  SizeRect: TRect;
608  Height, Width: Integer;
609  FixedHeight, FixedWidth: boolean;
610  //MinWidth, MinHeight, MaxWidth, MaxHeight: Integer;
611begin
612  Result := True;
613
614  if Constraints is TSizeConstraints then
615  begin
616    if (SizeConstraints.Control=nil) then exit;
617
618    FixedHeight := false;
619    FixedWidth := false;
620    //MinWidth := 0;
621    //MinHeight := 0;
622    //MaxWidth := 0;
623    //MaxHeight := 0;
624
625    if SizeConstraints.Control is TCustomComboBox then
626    begin
627      // win32 combo (but not csSimple) has fixed height
628      FixedHeight := TCustomComboBox(SizeConstraints.Control).Style <> csSimple;
629    end;
630
631    if (FixedHeight or FixedWidth)
632      and TWinControl(SizeConstraints.Control).HandleAllocated then
633    begin
634      Windows.GetWindowRect(TWinControl(SizeConstraints.Control).Handle, @SizeRect);
635
636      if FixedHeight then
637        Height := SizeRect.Bottom - SizeRect.Top
638      else
639        Height := 0;
640      if FixedWidth then
641        Width := SizeRect.Right - SizeRect.Left
642      else
643        Width := 0;
644
645      SizeConstraints.SetInterfaceConstraints(Width, Height, Width, Height);
646    end;
647  end;
648end;
649
650function TWin32WidgetSet.GetDesignerDC(WindowHandle: HWND): HDC;
651var
652  OverlayWindow: HWND;
653  ARect: Windows.RECT;
654  WindowInfo, OverlayWindowInfo: PWin32WindowInfo;
655begin
656  WindowInfo := GetWin32WindowInfo(WindowHandle);
657  OverlayWindow := WindowInfo^.Overlay;
658  if OverlayWindow = {%H-}HWND(nil) then
659  begin
660    // create 'overlay' window
661    Windows.GetClientRect(WindowHandle, @ARect);
662    OverlayWindow := Windows.CreateWindowEx(WS_EX_TRANSPARENT,
663        @ClsName, '', WS_CHILD or WS_VISIBLE,
664        ARect.Left, ARect.Top, ARect.Right, ARect.Bottom,
665        WindowHandle, {%H-}HMENU(nil), HInstance, nil);
666    OverlayWindowInfo := AllocWindowInfo(OverlayWindow);
667    OverlayWindowInfo^.DefWndProc := {%H-}Windows.WNDPROC(SetWindowLong(
668        OverlayWindow, GWL_WNDPROC, {%H-}PtrInt(@OverlayWindowProc)));
669    OverlayWindowInfo^.WinControl := WindowInfo^.WinControl;
670    WindowInfo^.Overlay := OverlayWindow;
671  end;
672  // bring overlay window to front
673  Windows.SetWindowPos(OverlayWindow, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE or SWP_NOMOVE);
674  Result := Windows.GetDC(OverlayWindow);
675end;
676
677function TWin32WidgetSet.IsDesignerDC(WindowHandle: HWND; DC: HDC): Boolean;
678var
679  OverlayWindow: HWND;
680begin
681  OverlayWindow := GetWin32WindowInfo(WindowHandle)^.Overlay;
682  if OverlayWindow <> 0 then
683    Result := Windows.WindowFromDC(DC) = OverlayWindow
684  else
685    Result := False;
686end;
687
688function TWin32WidgetSet.PromptUser(const DialogCaption, DialogMessage: String;
689  DialogType: longint; Buttons: PLongint; ButtonCount, DefaultIndex,
690  EscapeResult: Longint): Longint;
691var
692  i: Integer;
693  Caption: String;
694  TaskConfig: TTASKDIALOGCONFIG;
695  DialogButtons: PTASKDIALOG_BUTTON;
696  State: TApplicationState;
697begin
698  //TaskDialogIndirect is available in Vista and up, but only if app was built with manifest.
699  //The check for the latter is done by checking for ComCtlVersionIE6 (which is set in the manifest)
700  //The availability of TaskDialogIndirect does not depend on the status of ThemeServices
701  //Issue #0027664
702  if (WindowsVersion >= wvVista) and (GetFileVersion(comctl32) >= ComCtlVersionIE6) then
703  begin
704    FillChar(TaskConfig, SizeOf(TaskConfig), 0);
705    TaskConfig.cbSize := SizeOf(TaskConfig);
706    // if we skip hwndParent our form will be a root window - with the taskbar item and icon
707    // this is unwanted
708    if Assigned(Screen.ActiveCustomForm) then
709      TaskConfig.hwndParent := Screen.ActiveCustomForm.Handle
710    else
711    if Assigned(Application.MainForm) then
712      TaskConfig.hwndParent := Application.MainFormHandle
713    else
714      TaskConfig.hwndParent := AppHandle;
715    TaskConfig.hInstance := HInstance;
716    TaskConfig.dwFlags := TDF_ALLOW_DIALOG_CANCELLATION;
717    if DialogCaption <> '' then
718      Caption := DialogCaption
719    else
720    case DialogType of
721      idDialogConfirm,
722      idDialogInfo,
723      idDialogWarning,
724      idDialogError: Caption := GetDialogCaption(DialogType);
725    else
726      Caption := Application.Title;
727    end;
728    TaskConfig.pszWindowTitle := PWideChar(UTF8ToUTF16(Caption));
729
730    case DialogType of
731      idDialogConfirm:
732        begin
733          TaskConfig.hMainIcon := Windows.LoadIcon(0, IDI_QUESTION);
734          TaskConfig.dwFlags := TaskConfig.dwFlags or TDF_USE_HICON_MAIN;
735        end;
736      idDialogInfo: TaskConfig.pszMainIcon := TD_INFORMATION_ICON;
737      idDialogWarning: TaskConfig.pszMainIcon := TD_WARNING_ICON;
738      idDialogError: TaskConfig.pszMainIcon := TD_ERROR_ICON;
739      idDialogShield: TaskConfig.pszMainIcon := TD_SHIELD_ICON;
740    else
741      TaskConfig.dwFlags := TaskConfig.dwFlags or TDF_USE_HICON_MAIN;
742    end;
743
744    TaskConfig.pszContent := PWideChar(UTF8ToUTF16(DialogMessage));
745
746    TaskConfig.cButtons := ButtonCount;
747    GetMem(DialogButtons, SizeOf(TTASKDIALOG_BUTTON) * ButtonCount);
748    for i := 0 to ButtonCount - 1 do
749    begin
750      DialogButtons[i].nButtonID := Buttons[i];
751      DialogButtons[i].pszButtonText := UTF8StringToPWideChar(GetButtonCaption(Buttons[i]));
752    end;
753    TaskConfig.pButtons := DialogButtons;
754    //we need idButtonXX value
755    if DefaultIndex < ButtonCount then
756      TaskConfig.nDefaultButton := Buttons[DefaultIndex]
757    else
758      TaskConfig.nDefaultButton := 0;
759
760    State := SaveApplicationState;
761    try
762      Result := IDCANCEL;
763      TaskDialogIndirect(@TaskConfig, @Result, nil, nil);
764      if Result = IDCANCEL then
765        Result := EscapeResult;
766    finally
767      RestoreApplicationState(State);
768      for i := 0 to ButtonCount - 1 do
769        FreeMem(DialogButtons[i].pszButtonText);
770      FreeMem(DialogButtons);
771    end;
772  end
773  else
774    Result := inherited PromptUser(DialogCaption, DialogMessage, DialogType,
775      Buttons, ButtonCount, DefaultIndex, EscapeResult);
776end;
777
778{------------------------------------------------------------------------------
779  Function: RawImage_CreateBitmaps
780  Params: ARawImage:
781          ABitmap:
782          AMask:
783          ASkipMask: When set there is no mask created
784  Returns:
785
786 ------------------------------------------------------------------------------}
787function TWin32WidgetSet.RawImage_CreateBitmaps(const ARawImage: TRawImage; out ABitmap, AMask: HBitmap; ASkipMask: Boolean): Boolean;
788var
789  ADesc: TRawImageDescription absolute ARawImage.Description;
790
791  function DoBitmap: Boolean;
792  var
793    DC: HDC;
794    Info: record
795      Header: Windows.TBitmapInfoHeader;
796      Colors: array[0..1] of Cardinal; // reserve extra color for mono bitmaps
797    end;
798    DstLinePtr, SrcLinePtr: PByte;
799    SrcPixelPtr, DstPixelPtr: PByte;
800    DstLineSize, SrcLineSize: PtrUInt;
801    x, y: Integer;
802    Ridx, Gidx, Bidx, Aidx, Align, SrcBytes, DstBpp: Byte;
803  begin
804    if (ADesc.BitsPerPixel = 1) and (ADesc.LineEnd = rileWordBoundary)
805    then begin
806      // default BW, word aligned bitmap
807      ABitmap := Windows.CreateBitmap(ADesc.Width, ADesc.Height, 1, 1, ARawImage.Data);
808      Exit(ABitmap <> 0);
809    end;
810
811    // for 24 bits images, BPP can be 24 or 32
812    // 32 shouldn't be use since we don't fill the alpha channel
813
814    if ADesc.Depth = 24
815    then DstBpp := 24
816    else DstBpp := ADesc.BitsPerPixel;
817
818    FillChar(Info, SizeOf(Info), 0);
819    Info.Header.biSize := SizeOf(Info.Header);
820    Info.Header.biWidth := ADesc.Width;
821    if ADesc.LineOrder = riloTopToBottom
822    then Info.Header.biHeight := -ADesc.Height // create top to bottom
823    else Info.Header.biHeight := ADesc.Height; // create bottom to top
824    Info.Header.biPlanes := 1;
825    Info.Header.biBitCount := DstBpp;
826    Info.Header.biCompression := BI_RGB;
827    {Info.Header.biSizeImage := 0;}
828    { first color is black, second color is white, for monochrome bitmap }
829    Info.Colors[1] := $FFFFFFFF;
830
831    DC := Windows.GetDC(0);
832    // Use createDIBSection, since only devicedepth bitmaps can be selected into a DC
833    // when they are created with createDIBitmap
834    //  ABitmap := Windows.CreateDIBitmap(DC, Info.Header, CBM_INIT, ARawImage.Data, Windows.PBitmapInfo(@Info)^, DIB_RGB_COLORS);
835    ABitmap := Windows.CreateDIBSection(DC, Windows.PBitmapInfo(@Info)^, DIB_RGB_COLORS, DstLinePtr, 0, 0);
836    Windows.ReleaseDC(0, DC);
837
838    if ABitmap = 0
839    then begin
840      DebugLn('Windows.CreateDIBSection returns 0. Reason = ' + GetLastErrorText(Windows.GetLastError));
841      Exit(False);
842    end;
843    if DstLinePtr = nil then Exit(False);
844
845    DstLineSize := Windows.MulDiv(DstBpp, ADesc.Width, 8);
846    // align to DWord
847    Align := DstLineSize and 3;
848    if Align > 0
849    then Inc(DstLineSize, PtrUInt(4 - Align));
850
851    SrcLinePtr := ARawImage.Data;
852    SrcLineSize := ADesc.BytesPerLine;
853
854    // copy the image data
855    if ADesc.Depth >= 24
856    then begin
857      // check if a pixel copy is needed
858      // 1) Windows uses alpha channel in 32 bpp modes, despite documentation statement that it is ignored. Tested under Windows XP SP3
859      // Wine also relies on this undocumented behaviour!
860      // So, we need to cut unused A-channel, otherwise we would get black image
861      //
862      // 2) incompatible channel order
863      ADesc.GetRGBIndices(Ridx, Gidx, Bidx, Aidx);
864
865      if ((ADesc.BitsPerPixel = 32) and (ADesc.Depth = 24))
866      or (Bidx <> 0) or (Gidx <> 1) or (Ridx <> 2)
867      then begin
868        // copy pixels
869        SrcBytes := ADesc.BitsPerPixel div 8;
870
871        for y := 0 to ADesc.Height - 1 do
872        begin
873          DstPixelPtr := DstLinePtr;
874          SrcPixelPtr := SrcLinePtr;
875          for x := 0 to ADesc.Width - 1 do
876          begin
877            DstPixelPtr[0] := SrcPixelPtr[Bidx];
878            DstPixelPtr[1] := SrcPixelPtr[Gidx];
879            DstPixelPtr[2] := SrcPixelPtr[Ridx];
880
881            Inc(DstPixelPtr, 3); //move to the next dest RGB triple
882            Inc(SrcPixelPtr, SrcBytes);
883          end;
884
885          Inc(DstLinePtr, DstLineSize);
886          Inc(SrcLinePtr, SrcLineSize);
887        end;
888
889        Exit(True);
890      end;
891    end;
892
893    // no pixelcopy needed
894    // check if we can move using one call
895    if ADesc.LineEnd = rileDWordBoundary
896    then begin
897      Move(SrcLinePtr^, DstLinePtr^, DstLineSize * ADesc.Height);
898      Exit(True);
899    end;
900
901    //Can't use just one move, as different alignment
902    for y := 0 to ADesc.Height - 1 do
903    begin
904      Move(SrcLinePtr^, DstLinePtr^, DstLineSize);
905      Inc(DstLinePtr, DstLineSize);
906      Inc(SrcLinePtr, SrcLineSize);
907    end;
908
909    Result := True;
910  end;
911
912begin
913  AMask := 0;
914  Result := DoBitmap;
915  if not Result then Exit;
916
917  //DbgDumpBitmap(ABitmap, 'CreateBitmaps - Image');
918  if ASkipMask then Exit;
919
920  AMask := Windows.CreateBitmap(ADesc.Width, ADesc.Height, 1, 1, ARawImage.Mask);
921  if AMask = 0 then
922    DebugLn('Windows.CreateBitmap returns 0. Reason = ' + GetLastErrorText(Windows.GetLastError));
923  Result := AMask <> 0;
924  //DbgDumpBitmap(AMask, 'CreateBitmaps - Mask');
925end;
926
927{------------------------------------------------------------------------------
928  Function: RawImage_DescriptionFromBitmap
929  Params: ABitmap:
930          ADesc:
931  Returns:
932
933 ------------------------------------------------------------------------------}
934function TWin32WidgetSet.RawImage_DescriptionFromBitmap(ABitmap: HBITMAP; out ADesc: TRawImageDescription): Boolean;
935var
936  ASize: Integer;
937  WinDIB: Windows.TDIBSection;
938begin
939  ASize := Windows.GetObject(ABitmap, SizeOf(WinDIB), @WinDIB);
940  Result := ASize > 0;
941  if Result then
942  begin
943    FillRawImageDescription(WinDIB.dsBm, ADesc);
944    // if it is not DIB then alpha in bitmaps is not supported => use 0 alpha prec
945    if ASize < SizeOf(WinDIB) then
946      ADesc.AlphaPrec := 0;
947  end;
948end;
949
950{------------------------------------------------------------------------------
951  Function: RawImage_DescriptionFromDevice
952  Params: ADC:
953          ADesc:
954  Returns:
955
956 ------------------------------------------------------------------------------}
957function TWin32WidgetSet.RawImage_DescriptionFromDevice(ADC: HDC; out ADesc: TRawImageDescription): Boolean;
958var
959  DC: HDC;
960begin
961  Result := True;
962
963  ADesc.Init;
964
965  if ADC = 0
966  then DC := Windows.GetDC(0)
967  else DC := ADC;
968
969  ADesc.Format := ricfRGBA;
970  ADesc.Width := Windows.GetDeviceCaps(DC, HORZRES);
971  ADesc.Height := Windows.GetDeviceCaps(DC, VERTRES);
972  ADesc.Depth := Windows.GetDeviceCaps(DC, BITSPIXEL) * Windows.GetDeviceCaps(DC, PLANES);
973  ADesc.BitOrder := riboReversedBits;
974  ADesc.ByteOrder := riboLSBFirst;
975  ADesc.LineOrder := riloTopToBottom;
976  ADesc.LineEnd := rileDWordBoundary;
977  ADesc.BitsPerPixel := ADesc.Depth;
978
979  if (Windows.GetDeviceCaps(DC, RASTERCAPS) and RC_PALETTE) <> 0
980  then begin
981    // has palette
982    ADesc.PaletteColorCount := Windows.GetDeviceCaps(DC, NUMCOLORS);
983  end;
984
985  if ADC = 0
986  then Windows.ReleaseDC(0, DC);
987
988  FillRawImageDescriptionColors(ADesc);
989
990  ADesc.MaskBitsPerPixel := 1;
991  ADesc.MaskShift := 0;
992  ADesc.MaskLineEnd := rileWordBoundary;
993  ADesc.MaskBitOrder := riboReversedBits;
994end;
995
996{------------------------------------------------------------------------------
997  Function: RawImage_FromBitmap
998  Params: ABitmap:
999          AMask:
1000          ARect:
1001          ARawImage:
1002  Returns:
1003
1004 ------------------------------------------------------------------------------}
1005function TWin32WidgetSet.RawImage_FromBitmap(out ARawImage: TRawImage; ABitmap, AMask: HBITMAP; ARect: PRect = nil): Boolean;
1006var
1007  WinDIB: Windows.TDIBSection;
1008  WinBmp: Windows.TBitmap absolute WinDIB.dsBm;
1009  ASize: Integer;
1010  R: TRect;
1011begin
1012  ARawImage.Init;
1013  FillChar(WinDIB, SizeOf(WinDIB), 0);
1014  ASize := Windows.GetObject(ABitmap, SizeOf(WinDIB), @WinDIB);
1015  if ASize = 0
1016  then Exit(False);
1017
1018  //DbgDumpBitmap(ABitmap, 'FromBitmap - Image');
1019  //DbgDumpBitmap(AMask, 'FromMask - Mask');
1020
1021  FillRawImageDescription(WinBmp, ARawImage.Description);
1022  // if it is not DIB then alpha in bitmaps is not supported => use 0 alpha prec
1023  if ASize < SizeOf(WinDIB) then
1024    ARawImage.Description.AlphaPrec := 0;
1025
1026  if ARect = nil
1027  then begin
1028    R := Rect(0, 0, WinBmp.bmWidth, WinBmp.bmHeight);
1029  end
1030  else begin
1031    R := ARect^;
1032    if R.Top > WinBmp.bmHeight then
1033      R.Top := WinBmp.bmHeight;
1034    if R.Bottom > WinBmp.bmHeight then
1035      R.Bottom := WinBmp.bmHeight;
1036    if R.Left > WinBmp.bmWidth then
1037      R.Left := WinBmp.bmWidth;
1038    if R.Right > WinBmp.bmWidth then
1039      R.Right := WinBmp.bmWidth;
1040  end;
1041
1042  ARawImage.Description.Width := R.Right - R.Left;
1043  ARawImage.Description.Height := R.Bottom - R.Top;
1044
1045  // copy bitmap
1046  Result := GetBitmapBytes(WinBmp, ABitmap, R, ARawImage.Description.LineEnd, ARawImage.Description.LineOrder, ARawImage.Data, ARawImage.DataSize);
1047
1048  // check mask
1049  if AMask <> 0 then
1050  begin
1051    if Windows.GetObject(AMask, SizeOf(WinBmp), @WinBmp) = 0
1052    then Exit(False);
1053
1054    Result := GetBitmapBytes(WinBmp, AMask, R, ARawImage.Description.MaskLineEnd, ARawImage.Description.LineOrder, ARawImage.Mask, ARawImage.MaskSize);
1055  end
1056  else begin
1057    ARawImage.Description.MaskBitsPerPixel := 0;
1058  end;
1059end;
1060
1061{------------------------------------------------------------------------------
1062  Function: RawImage_FromDevice
1063  Params: ADC:
1064          ARect:
1065          ARawImage:
1066  Returns:
1067
1068 ------------------------------------------------------------------------------}
1069function TWin32WidgetSet.RawImage_FromDevice(out ARawImage: TRawImage; ADC: HDC; const ARect: TRect): Boolean;
1070const
1071  FILL_PIXEL: array[0..3] of Byte = ($00, $00, $00, $FF);
1072var
1073  Info: record
1074    Header: Windows.TBitmapInfoHeader;
1075    Colors: array[0..1] of Cardinal; // reserve extra color for mono bitmaps
1076  end;
1077
1078  BitsPtr: Pointer;
1079
1080  copyDC, fillDC: HDC;
1081  bmp, copyOld, fillOld, copyBmp, fillBmp: HBITMAP;
1082  w, h: Integer;
1083
1084begin
1085  if Windows.GetObjectType(ADC) = OBJ_MEMDC
1086  then begin
1087    // we can use bitmap directly
1088    bmp := Windows.GetCurrentObject(ADC, OBJ_BITMAP);
1089    copyBmp := 0;
1090  end
1091  else begin
1092    // we need to copy the image
1093    // use a dibsection, so we can easily retrieve the bytes
1094    copyDC := Windows.CreateCompatibleDC(ADC);
1095
1096    w := Windows.GetDeviceCaps(ADC, DESKTOPHORZRES);
1097    if w = 0
1098    then w := Windows.GetDeviceCaps(ADC, HORZRES);
1099    h := Windows.GetDeviceCaps(ADC, DESKTOPVERTRES);
1100    if h = 0
1101    then h := Windows.GetDeviceCaps(ADC, VERTRES);
1102
1103    FillChar(Info, SizeOf(Info), 0);
1104    Info.Header.biSize := SizeOf(Info.Header);
1105    Info.Header.biWidth := w;
1106    Info.Header.biHeight := -h;
1107    Info.Header.biPlanes := 1;
1108    Info.Header.biBitCount := Windows.GetDeviceCaps(ADC, BITSPIXEL);
1109    Info.Header.biCompression := BI_RGB;
1110
1111    copyBmp := Windows.CreateDIBSection(copyDC, Windows.PBitmapInfo(@Info)^, DIB_RGB_COLORS, BitsPtr, 0, 0);
1112    copyOld := Windows.SelectObject(copyDC, copyBmp);
1113
1114    // prefill bitmap, to create an alpha channel in case of 32bpp bitmap
1115    if Info.Header.biBitCount > 24
1116    then begin
1117      // using a stretchblt is faster than filling the memory ourselves,
1118      // which is in its turn faster than using a 24bpp bitmap
1119      fillBmp := Windows.CreateBitmap(1, 1, 1, 32, @FILL_PIXEL);
1120      fillDC := Windows.CreateCompatibleDC(ADC);
1121      fillOld := Windows.SelectObject(fillDC, fillBmp);
1122
1123      Windows.StretchBlt(copyDC, 0, 0, w, h, fillDC, 0, 0, 1, 1, SRCCOPY);
1124
1125      Windows.SelectObject(fillDC, fillOld);
1126      Windows.DeleteDC(fillDC);
1127      Windows.DeleteObject(fillBmp);
1128
1129      Windows.BitBlt(copyDC, 0, 0, w, h, ADC, 0, 0, SRCPAINT);
1130    end
1131    else begin
1132      // copy image
1133      Windows.BitBlt(copyDC, 0, 0, w, h, ADC, 0, 0, SRCCOPY);
1134    end;
1135
1136    Windows.SelectObject(copyDC, copyOld);
1137    Windows.DeleteDC(copyDC);
1138
1139    bmp := copyBmp;
1140  end;
1141
1142  if bmp = 0 then Exit(False);
1143
1144  Result := RawImage_FromBitmap(ARawImage, bmp, 0, @ARect);
1145  if copyBmp <> 0
1146  then Windows.DeleteObject(copyBmp);
1147end;
1148
1149function TWin32WidgetSet.ReleaseDesignerDC(Window: HWND; DC: HDC): Integer;
1150var
1151  OverlayWindow: HWND;
1152begin
1153  OverlayWindow := GetWin32WindowInfo(Window)^.Overlay;
1154  if OverlayWindow <> 0 then
1155    Result := Windows.ReleaseDC(OverlayWindow, DC)
1156  else
1157    Result := 0;
1158end;
1159
1160procedure TWin32WidgetSet.RemoveEventHandler(var AHandler: PEventHandler);
1161var
1162  lListIndex: pdword absolute AHandler;
1163  I: dword;
1164begin
1165  if AHandler = nil then exit;
1166{$ifdef DEBUG_ASYNCEVENTS}
1167  DebugLn('Removing handle: ', IntToHex(FWaitHandles[lListIndex^], 8));
1168  if Length(FWaitHandles) > 0 then
1169    DebugLn(' WaitHandleCount=', IntToStr(FWaitHandleCount), ', WaitHandle[0]=', IntToHex(FWaitHandles[0], 8));
1170{$endif}
1171  // swap with last one
1172  if FWaitHandleCount >= 2 then
1173  begin
1174    I := lListIndex^;
1175    FWaitHandles[I] := FWaitHandles[FWaitHandleCount-1];
1176    FWaitHandlers[I] := FWaitHandlers[FWaitHandleCount-1];
1177    FWaitHandlers[I].ListIndex^ := I;
1178  end;
1179  Dec(FWaitHandleCount);
1180  Dispose(lListIndex);
1181  AHandler := nil;
1182end;
1183
1184procedure TWin32WidgetSet.RemovePipeEventHandler(var AHandler: PPipeEventHandler);
1185var
1186  lHandler: PPipeEventInfo absolute AHandler;
1187begin
1188  if AHandler = nil then exit;
1189  if lHandler^.Prev <> nil then
1190    lHandler^.Prev^.Next := lHandler^.Next
1191  else
1192    FWaitPipeHandlers := lHandler^.Next;
1193  if lHandler^.Next <> nil then
1194    lHandler^.Next^.Prev := lHandler^.Prev;
1195  Dispose(lHandler);
1196  AHandler := nil;
1197end;
1198
1199//##apiwiz##eps##   // Do not remove, no wizard declaration after this line
1200