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