unit MPCommonUtilities; // Version 1.7.0 // // The contents of this file are subject to the Mozilla Public License // Version 1.1 (the "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ // // Alternatively, you may redistribute this library, use and/or modify it under the terms of the // GNU Lesser General Public License as published by the Free Software Foundation; // either version 2.1 of the License, or (at your option) any later version. // You may obtain a copy of the LGPL at http://www.gnu.org/copyleft/. // // Software distributed under the License is distributed on an "AS IS" basis, // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the // specific language governing rights and limitations under the License. // // The initial developer of this code is Jim Kueneman // Special thanks to the following in no particular order for their help/support/code // Danijel Malik, Robert Lee, Werner Lehmann, Alexey Torgashin, Milan Vandrovec // //---------------------------------------------------------------------------- // The following are implemented in Win 95: // EnumResourceLanguagesW // EnumResourceNamesW // EnumResourceTypesW // ExtTextOutW // FindResourceW // FindResourceExW // GetCharWidthW // GetCommandLineW // GetTextExtentPoint32W // GetTextExtentPointW // lstrlenW // MessageBoxExW // MessageBoxW // MultiByteToWideChar // TextOutW // WideCharToMultiByte {$IFDEF TNTSUPPORT} // IMPORTANT - PLEASE READ then comment this line out. // If using TNT you MUST include the TNT package for your specific compiler in the // Requires section of this package. It may be possible to compile without doing // this but you WILL eventually have strange crashes in your application that will // be difficult to understand. The best way to do this in my opinion is to create // a new folder in the package install directory called "Delphi_TNT" (or CBuilder_TNT) // and copy all the files from the Delphi (or CBuilder) folder into it. Now open the // MPCommonLibDx.dpk (or bpk) file in the "Delphi_TNT" (or CBuilder_TNT) based on your compiler // version in a text editor. In the "Requires" section add "TNTUnicodeVcl_Rx0", where // the "x" is the version of Delphi you are using. Open the dpk (bpk) file in your // IDE. Select the menu option Projects>Options>Directories/Conditionals>Conditional // and enter TNTSUPPORT. Compile the package, then open the MPCommonLibDxD.dpk (or bpk) // and compile and press the Install button. // Now when you update the packages you won't have to redo all this. Just install // the update then compile the packages in the "Delphi_TNT" (or CBuilder_TNT) folders // an you are done. {$ENDIF} {$B-} {$I Compilers.inc} {$I Options.inc} {$I ..\Include\Debug.inc} {$I ..\Include\Addins.inc} interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Math, ActiveX, ShlObj, {$IFDEF COMPILER_6_UP} Variants, RTLConsts, {$ELSE} Consts, {$ENDIF COMPILER_6_UP} {$IFNDEF COMPILER_6_UP} Menus, {$ENDIF} {$IFDEF TNTSUPPORT} TntSysUtils, TntClasses, {$ENDIF} ShellAPI, ComCtrls, ComObj, CommCtrl, MPShellTypes, MPResources; const WideNull = WideChar(#0); WideCR = WideChar(#13); WideLF = WideChar(#10); WideLineSeparator = WideChar(#2028); WideSpace = WideChar(#32); WidePeriod = WideChar('.'); {$IFNDEF COMPILER_6_UP} NullAsStringValue: string = ''; {$ENDIF} Shlwapi = 'shlwapi.dll'; var SEasyNSEMsg_Caption: WideString = 'Shell extension registration'; SEasyNSEMsg_CannotRegister: WideString = 'Cannot register shell extension.'; SEasyNSEMsg_CannotUnRegister: WideString = 'Cannot unregister shell extension.'; SEasyNSEMsg_CannotFindRegSvr: WideString = 'Unable to find RegSvr32.exe executable.'; SEasyNSEMsg_CannotFindDLL: WideString = 'Unable to find extension DLL.'; type TCommonWideCharArray = array of WideChar; TCommonPWideCharArray = array of PWideChar; TCommonWideStringDynArray = array of WideString; TCommonIntegerDynArray = array of Integer; {$IFNDEF COMPILER_6_UP} TValueRelationship = -1..1; TSeekOrigin = (soBeginning, soCurrent, soEnd); {$ENDIF} TCommonDropEffect = ( cdeNone, // No drop effect (the circle with the slash through it cdeCopy, // Copy the dropped object cdeMove, // Move the dropped object cdeLink, // Make a shortcut to the dropped object cdeScroll // The dragging is in the middle of a scroll ); TCommonDropEffects = set of TCommonDropEffect; TCommonOLEDragResult = ( cdrDrop, // The drag resulted in a drop cdrCancel, // The drag resulted in being canceled cdrError // The drag resulted in an unknown error ); TCommonKeyState = ( cksControl, // Control Key is down cksLButton, // Left Mouse is down cksMButton, // Middle Mouse is down cksRButton, // Right Mouse is down cksShift, // Shift Key is down cksAlt, // Alt Key is down cksButton // One of the mouse buttons are down ); TCommonKeyStates = set of TCommonKeyState; TCommonMouseButton = ( cmbNone, // No Button cmbLeft, // Left Button cmbRight, // Right Button cmbMiddle // Middle Button ); TCommonVAlignment = ( cvaTop, // The vertical alignment of the text is at the top of the object cvaBottom, // The vertical alignment of the text is at the bottom of the object cvaCenter // The vertical alignment of the text is at the center of the object ); // Flags for the DrawTextWEx function TCommonDrawTextWFlag = ( dtSingleLine, // Put Caption on one line dtLeft, // Aligns Text Left dtRight, // Aligns Text Right dtCenter, // Aligns Text Center dtTop, // Vertical Align Text to Bottom of Rect dtBottom, // Vertical Align Text to Bottom of Rect // Only valid with: dtSingleLine dtVCenter, // Vertical Align Text to Center of Rect // Only valid with: dtSingleLine dtCalcRect, // Modifies the Rectangle to the size required for the Text does // not draw the text. By default it does not modify the right // edge of the rectangle, it only changes the height to fit // the text, see dtCalcRectAdjR dtCalcRectAdjR, // Modifies the Rectangles right edge for a best fit of the text // Does not increase the width only shortens it, // Only valid with: dtCalcRect dtCalcRectAlign, // Modifies the rectangle by aligning it with the original // rectangle based on the dtLeft, dtRight, dtCenter flag. // In other words it ensures that if the text won't fit that // only the end of the text is clipped. For instance if // the text is horz centered the calculation could clip // both ends of the text. Just using the dtCalcRectAdjR // flag will only stretch the Right edge and the left will // still be clipped. Using this flag will shift the rect // to the edge of the passed rect so that the beginning // of the text is always shown. // Only valid with: dtCalcRect and dtCalcRectAdjR dtEndEllipsis, // Adds a "..." to the end of the string if it will not fit in // the passed rectangle dtWordBreak, // Breaks the passed string to best fit in the rectangle // The default Characters to break the line are: // WideSpace ( WideChar(#32) ) // WideCR/WideLF sequence or individually ( WideChar(#13 or #10) ) // WideLineSeparator ( WideChar(#2028) ) dtUserBreakChars, // The UserBreakChars parameters should be used for defining // what to use to break the passed string into lines // Only valid with: dtWordBreak dtRTLReading, // Right to Left reading dtNoClip // Do not clip the text in the rectangle ); TCommonDrawTextWFlags = set of TCommonDrawTextWFlag; // Describes the mode how to blend pixels. TCommonBlendMode = ( cbmConstantAlpha, // apply given constant alpha cbmPerPixelAlpha, // use alpha value of the source pixel cbmMasterAlpha, // use alpha value of source pixel and multiply it with the constant alpha value cbmConstantAlphaAndColor // blend the destination color with the given constant color und the constant alpha value ); TShortenStringEllipsis = ( sseEnd, // Ellipsis on end of string sseFront, // Ellipsis on begining of string sseMiddle, // Ellipsis in middle of string sseFilePathMiddle // Ellipsis is in middle of string but tries to show the entire filename ); // RGB (red, green, blue) color given in the range [0..1] per component. TCommonRGB = record R, G, B: Double; end; // Hue, luminance, saturation color with all three components in the range [0..1] // (so hue's 0..360° is normalized to 0..1). TCommonHLS = record H, L, S: Double; end; // Enhanced library loading functions that reference counts the loading to make // sure that libraries are freed when using cool controls in COM applications function CommonLoadLibrary(LibraryName: string): THandle; function CommonUnloadLibrary(LibraryName: string): Boolean; procedure CommonUnloadAllLibraries; function FlipReverseCopyRect(const Flip, Reverse: Boolean; const Bitmap: TBitmap): TBitmap; overload; procedure FlipReverseCopyRect(const Flip, Reverse: Boolean; R: TRect; const Canvas: TCanvas); overload; procedure DrawRadioButton(Canvas: TCanvas; Pos: TPoint; Size: Integer; clBackground, clHotBkGnd, clLeftOuter, clRightOuter, clLeftInner, clRightInner: TColor; Checked, Enabled, Hot: Boolean); procedure DrawCheckBox(Canvas: TCanvas; Pos: TPoint; Size: Integer; clBackground, clHotBkGnd, clLeftOuter, clRightOuter, clLeftInner, clRightInner: TColor; Checked, Enabled, Hot: Boolean); function CheckBounds(Size: Integer): TRect; {$IFNDEF COMPILER_6_UP} function VarToWideStrDef(const V: Variant; const ADefault: WideString): WideString; function VarToWideStr(const V: Variant): WideString; function BoolToStr(B: Boolean; UseBoolStrs: Boolean = False): string; function CompareTime(const A, B: TDateTime): TValueRelationship; function WideCompareText(S1, S2: WideString): Integer; function ExcludeTrailingBackslash(Path: WideString): WideString; function IncludeTrailingBackslash(Path: WideString): WideString; function GUIDToString(const GUID: TGUID): string; {$ENDIF} function AbsRect(ARect: TRect): TRect; function AddContextMenuItem(Menu: HMenu; ACaption: WideString; Index: Integer; MenuID: UINT = $FFFF; hSubMenu: UINT = 0; Enabled: Boolean = True; Checked: Boolean = False; Default: Boolean = False): Integer; function AddCommas(NumberString: WideString): WideString; function CalcuateFolderSize(FolderPath: WideString; Recurse: Boolean): Int64; function CenterRectHorz(OuterRect, InnerRect: TRect): TRect; function CenterRectInRect(OuterRect, InnerRect: TRect): TRect; function CenterRectVert(OuterRect, InnerRect: TRect): TRect; function CommonSupports(const Instance: IUnknown; const IID: TGUID): Boolean; overload; function CommonSupports(const Instance: IUnknown; const IID: TGUID; out Intf): Boolean; overload; function CommonSupports(const Instance: TObject; const IID: TGUID): Boolean; overload; function CommonSupports(const Instance: TObject; const IID: TGUID; out Intf): Boolean; overload; procedure CopyToNullBufferA(S: WideString; Buffer: PChar; CharCount: Cardinal); procedure CopyToNullBufferW(S: WideString; Buffer: PWideChar; CharCount: Cardinal); procedure CreateProcessMP(ExeFile, Parameters, InitalDir: WideString); function DiffRectHorz(Rect1, Rect2: TRect): TRect; function DiffRectVert(Rect1, Rect2: TRect): TRect; {$IFNDEF COMPILER_6_UP} function DirectoryExists(const Directory: string): Boolean; {$ENDIF} function DiskInDrive(C: Char): Boolean; function DragDetectPlus(Handle: HWND; Pt: TPoint): Boolean; function DrawTextWEx(DC: HDC; Text: WideString; var lpRect: TRect; Flags: TCommonDrawTextWFlags; MaxLineCount: Integer): Integer; function DropEffectToStr(DropEffect: DWORD): WideString; function EqualWndMethod(A, B: TWndMethod): Boolean; function FileExistsW(const FileName: WideString): Boolean; function FileIconInit(FullInit: BOOL): BOOL; stdcall; function FindUniqueMenuID(AMenu: HMenu): Cardinal; function GetMyDocumentsVirtualFolder: PItemIDList; function HasMMX: Boolean; function IncludeTrailingBackslashW(const S: WideString): WideString; function IsAnyMouseButtonDown: Boolean; function IsFTPPath(Path: WideString): Boolean; function IsMappedDrivePath(const Path: WideString): Boolean; function IsRectNull(ARect: TRect): Boolean; function IsTextTrueType(Canvas: TCanvas): Boolean; overload; function IsTextTrueType(DC: HDC): Boolean; overload; function IsUNCPath(const Path: WideString): Boolean; function IsUnicode: Boolean; // OS supports Unicode functions (basiclly means IsWinNT or XP) function IsWin2000: Boolean; function IsWin95_SR1: Boolean; function IsWinME: Boolean; function IsWinNT: Boolean; function IsWinNT4: Boolean; function IsWinXP: Boolean; function IsWinXPOrUp: Boolean; function IsWinVista: Boolean; function IsWinVistaOrUp: Boolean; procedure MakeFindDataW(const FindFileDataA: TWIN32FindDataA; var FindFileDataW: TWIN32FindDataW); function ModuleFileName(PathOnly: Boolean = True): WideString; function PIDLToPath(PIDL: PItemIDList): WideString; function SHGetImageList(iImageList: Integer; const RefID: TGUID; out ppvOut): HRESULT; stdcall; function ShiftStateToKeys(Keys: TShiftState): LongWord; function ShiftStateToStr(Keys: TShiftState): WideString; function ShortenStringEx(DC: HDC; const S: WideString; Width: Integer; RTL: Boolean; EllipsisPlacement: TShortenStringEllipsis): WideString; function ShortenTextW(DC: hDC; TextToShorten: WideString; MaxSize: Integer): WideString; function ShortFileName(const FileName: WideString): WideString; function ShortPath(const Path: WideString): WideString; function Size(cx, cy: Integer): TSize; function SplitTextW(DC: hDC; TextToSplit: WideString; MaxWidth: Integer; var Buffer: TCommonWideCharArray; MaxSplits: Integer): Integer; function StrRetToStr(StrRet: TStrRet; APIDL: PItemIDList): WideString; function SystemDirectory: WideString; function SysMenuFont: HFONT; function SysMenuHeight: Integer; function TextExtentW(Text: PWideChar; Canvas: TCanvas): TSize; overload; function TextExtentW(Text: PWideChar; DC: hDC): TSize; overload; function TextExtentW(Text: WideString; Canvas: TCanvas): TSize; overload; function TextExtentW(Text: WideString; Font: TFont): TSize; overload; function TextTrueExtentsW(Text: WideString; DC: HDC): TSize; function TNTConditionallyDefined: Boolean; function UnicodeStringLists: Boolean; function UniqueDirName(const ADirPath: WideString): WideString; function UniqueFileName(const AFilePath: WideString): WideString; function VariantToCaption(const V: Variant): WideString; function WideCreateDir(Path: WideString): Boolean; function WideDirectoryExists(const Name: WideString): Boolean; function WideExcludeTrailingBackslash(Path: WideString): WideString; function WideExpandEnviromentString(EnviromentString: WideString): WideString; function WideExtractFileDir(Path: WideString): WideString; function WideExtractFileDrive(Path: WideString): WideString; function WideExtractFileExt(Path: WideString): WideString; function WideExtractFileName(Path: WideString): WideString; function WideExtractFilePath(Path: WideString): WideString; function WideFileExists(Path: WideString): Boolean; function WideFindFirstFileEx(FileName: WideString; var lpFindFileData: TWIN32FindDataW; Mask: WideString; CaseSensitive: Boolean): THandle; function WideFindFirstFileExExists: Boolean; function WideGetCurrentDir: WideString; function WideGetTempDir: WideString; function WideIncludeTrailingBackslash(Path: WideString): WideString; function WideIncrementalSearch(CompareStr, Mask: WideString): Integer; function WideIntToStr(Value: integer): WideString; function WideIsDrive(Drive: WideString): Boolean; function WideIsFloppy(FileFolder: WideString): boolean; function WideIsPathDelimiter(const S: WideString; Index: Integer): Boolean; function WideLowerCase(const Str: WideString): WideString; function WideMessageBox(Window: HWND; const ACaption, AMessage: WideString; uType: integer): integer; function WideNewFolderName(ParentFolder: WideString; SuggestedFolderName: WideString = ''): WideString; function WidePathMatchSpec(Path, Mask: WideString): Boolean; function WidePathMatchSpecExists: Boolean; function WideShellExecute(hWnd: HWND; Operation, FileName, Parameters, Directory: WideString; ShowCmd: Integer = SW_NORMAL): HINST; function WideStrComp(Str1, Str2: PWideChar): Integer; function WideStrLower(Str: PWideChar): PWideChar; function WideStrIComp(Str1, Str2: PWideChar): Integer; function WideStringReplace(const S, OldPattern, NewPattern: WideString; Flags: TReplaceFlags; WholeWord: Boolean = False): WideString; function WideStripExt(AFile: WideString): WideString; function WideStripLeadingBackslash(const S: WideString): WideString; function WideStripRemoteComputer(const UNCPath: WideString): WideString; function WideStripTrailingBackslash(const S: WideString; Force: Boolean = False): WideString; function WideStrMove(Dest, Source: PWideChar; Count: Cardinal): PWideChar; function WideStrPos(Str, SubStr: PWideChar): PWideChar; function WideStrRScan(Str: PWideChar; Chr: WideChar): PWideChar; function WideStrScan(Str: PWideChar; Chr: WideChar): PWideChar; function WideUpperCase(const S: WideString): WideString; function WindowsDirectory: WideString; procedure AlphaBlend(Source, Destination: HDC; R: TRect; Target: TPoint; Mode: TCommonBlendMode; ConstantAlpha, Bias: Integer); procedure ConvertBitmapEx(Image32: TBitmap; var OutImage: TBitmap; const BackGndColor: TColor); procedure ShadowBlendBits(Bits: TBitmap; BackGndColor: TColor); procedure FillWideChar(var Dest; count: Integer; Value: WideChar); procedure FreeMemAndNil(var P: Pointer); procedure LoadWideString(S: TStream; var Str: WideString); procedure MinMax(var A, B: Integer); procedure SaveWideString(S: TStream; Str: WideString); procedure WideInsert(Source: WideString; var S: WideString; Index: Integer); procedure WideShowMessage(Window: HWND; ACaption, AMessage: WideString); procedure WideStrLCopy(Str1, Str2: PWideChar; Count: Integer); // Rectangle functions function ProperRect(Rect: TRect): TRect; function RectHeight(R: TRect): Integer; function RectToStr(R: TRect): string; function RectToSquare(R: TRect): TRect; function RectWidth(R: TRect): Integer; function ContainsRect(OuterRect, InnerRect: TRect): Boolean; function IsRectProper(Rect: TRect): Boolean; // WideString routines (many borrowed from Mike Liscke) function StrCopyW(Dest, Source: PWideChar): PWideChar; function KeyDataToShiftState(KeyData: Longint): TShiftState; function KeyToKeyStates(Keys: Word): TCommonKeyStates; function KeyStatesToMouseButton(Keys: Word): TCommonMouseButton; function KeyStatesToKey(Keys: TCommonKeyStates): Longword; function DropEffectToDropEffectStates(Effect: Integer): TCommonDropEffects; function DropEffectStatesToDropEffect(Effect: TCommonDropEffects): Integer; function DropEffectToDropEffectState(Effect: Integer): TCommonDropEffect; function DropEffectStateToDropEffect(Effect: TCommonDropEffect): Integer; function KeyStateToDropEffect(Keys: TCommonKeyStates): TCommonDropEffect; function KeyStateToMouseButton(KeyState: TCommonKeyStates): TCommonMouseButton; // Color Functions function RGBToHLS(const RGB: TCommonRGB): TCommonHLS; function HLSToRGB(const HLS: TCommonHLS): TCommonRGB; function BrightenColor(const RGB: TCommonRGB; Amount: Double): TCommonRGB; function DarkenColor(const RGB: TCommonRGB; Amount: Double): TCommonRGB; function MakeTRBG(Color: TColor): TCommonRGB; function MakeTColor(RGB: TCommonRGB): TColor; function MakeColorRef(RGB: TCommonRGB; Gamma: Double = 1): COLORREF; procedure GammaCorrection(var RGB: TCommonRGB; Gamma: Double); function MakeSafeColor(var RGB: TCommonRGB): Boolean; function UpsideDownDIB(Bits: TBitmap): Boolean; procedure FixFormFont(AFont: TFont); // Window Manipulation procedure ActivateTopLevelWindow(Child: HWND); // Graphics procedure FillGradient(X1, Y1, X2, Y2: integer; fStartColor, fStopColor: TColor; StartPoint, EndPoint: integer; fDrawCanvas: TCanvas); // Helpers to create a callback function out of a object method function CreateStub(ObjectPtr: Pointer; MethodPtr: Pointer): Pointer; procedure DisposeStub(Stub: Pointer); {$IFNDEF COMPILER_6_UP} function Supports(const Instance: IUnknown; const IID: TGUID): Boolean; overload; procedure ClearMenuItems(Menu: TMenu); {$ENDIF} {$IFNDEF COMPILER_5_UP} function Supports(const Instance: IUnknown; const IID: TGUID; out Intf): Boolean; overload; function Supports(const Instance: TObject; const IID: TGUID; out Intf): Boolean; overload; procedure FreeAndNil(var Obj); type TWMContextMenu = packed record Msg: Cardinal; hWnd: HWND; case Integer of 0: ( XPos: Smallint; YPos: Smallint); 1: ( Pos: TSmallPoint; Result: Longint); end; {$ENDIF} // **************************************************************************** // Registration function for Shell and Namespace Extensions // Donated by by Alexey Torgashin // Note: we assume that paths to both RegSvr32.exe and extension DLL are ANSI (not Unicode). // **************************************************************************** type TEasyNSERegMessages = set of (enseMsgShowErrors, enseMsgRegSvr); //True to register or False to unregister function RegUnregNSE(const AFileName: WideString; DoRegister: boolean; AMessages: TEasyNSERegMessages = [enseMsgShowErrors]): boolean; function RegisterNSE(const AFileName: WideString; AMessages: TEasyNSERegMessages = [enseMsgShowErrors]): boolean; function UnregisterNSE(const AFileName: WideString; AMessages: TEasyNSERegMessages = [enseMsgShowErrors]): boolean; function ExecShellEx(const Cmd, Params, Dir: WideString; ShowCmd: integer; DoWait: boolean): boolean; // // Dynamically linked Unicode functions that do not have stubs on Win9x // var GetDriveTypeW_MP: function(lpRootPathName: PWideChar): UINT; stdcall; DrawTextW_MP: function(hDC: HDC; lpString: PWideChar; nCount: Integer; var lpRect: TRect; uFormat: UINT): Integer; stdcall; SHGetFileInfoW_MP: function(pszPath: PWideChar; dwFileAttributes: DWORD; var psfi: TSHFileInfoW; cbFileInfo, uFlags: UINT): DWORD; stdcall; CreateFileW_MP: function(lpFileName: PWideChar; dwDesiredAccess, dwShareMode: DWORD; lpSecurityAttributes: PSecurityAttributes; dwCreationDisposition, dwFlagsAndAttributes: DWORD; hTemplateFile: THandle): THandle; stdcall; SHGetDataFromIDListW_MP: function(psf: IShellFolder; pidl: PItemIDList; nFormat: Integer; ptr: Pointer; cb: Integer): HResult; stdcall; FindFirstFileW_MP: function(lpFileName: PWideChar; var lpFindFileData: TWIN32FindDataW): THandle; stdcall; FindNextFileW_MP: function(hFindFile: THandle; var lpFindFileData: TWIN32FindDataW): BOOL; stdcall; GetDiskFreeSpaceW_MP: function(lpRootPathName: PWideChar; var lpSectorsPerCluster, lpBytesPerSector, lpNumberOfFreeClusters, lpTotalNumberOfClusters: DWORD): BOOL; stdcall; GetCurrentDirectoryW_MP: function(nBufferLength: DWORD; lpBuffer: PWideChar): DWORD; stdcall; GetTempPathW_MP: function(nBufferLength: DWORD; lpBuffer: PWideChar): DWORD; stdcall; lstrcmpiW_MP: function(lpString1, lpString2: PWideChar): Integer; stdcall; lstrcmpW_MP: function(lpString1, lpString2: PWideChar): Integer; stdcall; lstrcpynW_MP: function(lpString1, lpString2: PWideChar; iMaxLength: Integer): PWideChar; stdcall; lstrcpyW_MP: function(lpString1, lpString2: PWideChar): PWideChar; stdcall; CharLowerBuffW_MP: function(lpsz: PWideChar; cchLength: DWORD): DWORD; stdcall; CharUpperBuffW_MP: function(lpsz: PWideChar; cchLength: DWORD): DWORD; stdcall; CreateDirectoryW_MP: function(lpPathName: PWideChar; lpSecurityAttributes: PSecurityAttributes): BOOL; stdcall; GetFullPathNameW_MP: function(lpFileName: PWideChar; nBufferLength: DWORD; lpBuffer: PWideChar; var lpFilePart: PWideChar): DWORD; stdcall; ShellExecuteExW_MP: function(lpExecInfo: PShellExecuteInfoW):BOOL; stdcall; ShellExecuteW_MP: function(hWnd: HWND; Operation, FileName, Parameters, Directory: PWideChar; ShowCmd: Integer): HINST; stdcall; FindFirstChangeNotificationW_MP: function(lpPathName: PWideChar; bWatchSubtree: BOOL; dwNotifyFilter: DWORD): THandle; stdcall; GetCharABCWidthsW_MP: function(DC: HDC; FirstChar, LastChar: UINT; const ABCStructs): BOOL; stdcall; GetFileAttributesW_MP: function(lpFileName: PWideChar): DWORD; stdcall; GetSystemDirectoryW_MP: function(lpBuffer: PWideChar; uSize: UINT): UINT; stdcall; GetWindowsDirectoryW_MP: function(lpBuffer: PWideChar; uSize: UINT): UINT; stdcall; SetWindowTextW_MP: function(hWnd: HWND; lpString: PWideChar): BOOL; stdcall; // Robert SHMultiFileProperties_MP: function(pdtobj: IDataObject; dwFlags: DWORD): HResult; stdcall; SHDoDragDrop_MP: function(wnd : HWND; dtObj : IDataObject; dsrc : IDropSource; OKEffect : DWORD; var Effect : Integer) : HResult; stdcall; GetDiskFreeSpaceExA_MP: function(lpDirectoryName: PAnsiChar; var lpFreeBytesAvailableToCaller, lpTotalNumberOfBytes; lpTotalNumberOfFreeBytes: PLargeInteger): BOOL; stdcall; GetDiskFreeSpaceExW_MP: function(lpDirectoryName: PWideChar; var lpFreeBytesAvailableToCaller, lpTotalNumberOfBytes; lpTotalNumberOfFreeBytes: PLargeInteger): BOOL; stdcall; GetNumberFormatW_MP: function(Locale: LCID; dwFlags: DWORD; lpValue: PWideChar; lpFormat: PNumberFmtW; lpNumberStr: PWideChar; cchNumber: Integer): Integer; stdcall; CDefFolderMenu_Create2_MP: function(pidlFolder: PItemIdList; wnd: HWnd; cidl: uint; var apidl: PItemIdList; psf: IShellFolder; lpfn: TFNDFMCallback; nKeys: UINT; ahkeyClsKeys: PHKEY; var ppcm: IContextMenu): HRESULT; stdcall; CDefFolderMenu_Create_MP: function(pidlFolder: PItemIdList): HRESULT; stdcall; // THIS IS NOT THE RIGHT PROTOTYPE DO NOT USE FOR TEST ONLY RegOpenKeyW_MP: function(hKey: HKEY; lpSubKey: PWideChar; var phkResult: HKEY): Longint; stdcall; RegOpenKeyExW_MP: function(hKey: HKEY; lpSubKey: PWideChar; ulOptions: DWORD; samDesired: REGSAM; var phkResult: HKEY): Longint; stdcall; RegQueryValueW_MP: function(hKey: HKEY; lpSubKey: PWideChar; lpValue: PWideChar; var lpcbValue: Longint): Longint; stdcall; WritePrivateProfileStringW_MP: function(lpAppName, lpKeyName, lpString, lpFileName: PWideChar): BOOL; stdcall; GetPrivateProfileStringW_MP: function(lpAppName, lpKeyName, lpDefault: PWideChar; lpReturnedString: PWideChar; nSize: DWORD; lpFileName: PWideChar): DWORD; stdcall; TryEnterCriticalSection_MP: function(var lpCriticalSection: TRTLCriticalSection): BOOL; stdcall; CreateFontIndirectW_MP: function(const p1: TLogFontW): HFONT; stdcall; SendMessageW_MP: function(hWnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall; InsertMenuItemW_MP: function(p1: HMENU; p2: UINT; p3: BOOL; const p4: TMenuItemInfoW): BOOL; stdcall; SetFileAttributesW_MP: function(lpFileName: PWideChar; dwFileAttributes: DWORD): BOOL; stdcall; SystemParametersInfoW_MP: function(uiAction, uiParam: UINT; pvParam: Pointer; fWinIni: UINT): BOOL; stdcall; SHBrowseForFolderW_MP: function(var lpbi: TBrowseInfoW): PItemIDList; stdcall; SHGetPathFromIDListW_MP: function(pidl: PItemIDList; pszPath: PWideChar): BOOL; stdcall; SHFileOperationW_MP: function(const lpFileOp: TSHFileOpStructW): Integer; stdcall; PathMatchSpecA_MP: function(const pszFileParam, pszSpec: PAnsiChar): Bool; stdcall; PathMatchSpecW_MP: function(const pszFileParam, pszSpec: PWideChar): Bool; stdcall; CreateProcessW_MP: function(lpApplicationName: PWideChar; lpCommandLine: PWideChar; lpProcessAttributes, lpThreadAttributes: PSecurityAttributes; bInheritHandles: BOOL; dwCreationFlags: DWORD; lpEnvironment: Pointer; lpCurrentDirectory: PWideChar; const lpStartupInfo: TStartupInfoW; var lpProcessInformation: TProcessInformation): BOOL; stdcall; DeleteVolumeMountPoint_MP: function(lpszVolumeMountPoint: LPCSTR): BOOL; stdcall; GetVolumeNameForVolumeMountPoint_MP: function(lpszVolumeMountPoint: LPCSTR; lpszVolumeName: LPSTR; cchBufferLength: DWORD): BOOL; stdcall; GetVolumePathName_MP: function(lpszFileName: LPCSTR; lpszVolumePathName: LPTSTR; cchBufferLength: DWORD): BOOL; stdcall; SetVolumeMountPoint_MP: function(lpszVolumeMountPoint: LPCSTR; lpszVolumeName: LPCSTR): BOOL; FindFirstVolume_MP: function(lpszVolumeName: LPTSTR; cchBufferLength: DWORD): THandle; stdcall; FindNextVolume_MP: function(hFindVolume: THandle; lpszVolumeName: LPTSTR; cchBufferLength: DWORD): BOOL; stdcall; FindVolumeClose_MP: function(hFindVolume: THandle): BOOL; stdcall; FindFirstVolumeMountPoint_MP: function(lpszRootPathName: LPTSTR; lpszVolumeMountPoint: LPTSTR; cchBufferLength: DWORD): THandle stdcall; FindNextVolumeMountPoint_MP: function(hFindVolumeMountPoint: THandle; lpszVolumeMountPoint: LPTSTR; cchBufferLength: DWORD): BOOL; stdcall; FindVolumeMountPointClose_MP: function(hFindVolumeMountPoint: THandle): BOOL; stdcall; SHGetKnownFolderPath_MP: function(const rfid: TGUID; dwFlags: DWord; hToken: THandle; out ppszPath: PWideChar): HRESULT; stdcall; FindFirstFileExW_MP: function(lpFileName: PWideChar; fInfoLevelId: DWORD; var lpFindFileData: TWIN32FindDataW; fSearchOp: DWORD; lpSearchFilter: Pointer; dwAdditionalFlags: DWORD): THandle; stdcall; FindFirstFileExA_MP: function(lpFileName: PChar; fInfoLevelId: DWORD; var lpFindFileData: TWIN32FindDataA; fSearchOp: DWORD; lpSearchFilter: Pointer; dwAdditionalFlags: DWORD): THandle; stdcall; ExpandEnvironmentStringsW_MP: function(lpSrc: PWideChar; lpDst: PWideChar; nSize: DWORD): DWORD; stdcall; TrackMouseEvent_MP: function(var EventTrack: TTrackMouseEvent): BOOL; stdcall; // Usage of this flag makes the SumFolder Function not thread safe SumFolderAbort: Boolean = False; implementation uses MPCommonObjects; type PLibRec = ^TLibRec; TLibRec = packed record LibraryName: string; ReferenceCount: Integer; Handle: THandle; end; var FLibList: TList; PIDLMgr: TCommonPIDLManager; Shell32Handle, Kernel32Handle, User32Handle, GDI32Handle, AdvAPI32Handle, ShlwapiHandle: THandle; function ShiftStateToKeys(Keys: TShiftState): LongWord; begin Result := 0; if ssShift in Keys then Result := Result or MK_SHIFT; if ssCtrl in Keys then Result := Result or MK_CONTROL; if ssLeft in Keys then Result := Result or MK_LBUTTON; if ssRight in Keys then Result := Result or MK_RBUTTON; if ssMiddle in Keys then Result := Result or MK_MBUTTON; if ssAlt in Keys then Result := Result or MK_ALT; end; function ShiftStateToStr(Keys: TShiftState): WideString; begin Result := '['; if ssShift in Keys then Result := Result + 'ssShift, '; if ssCtrl in Keys then Result := Result + 'ssCtrl, '; if ssLeft in Keys then Result := Result + 'ssLeft, '; if ssRight in Keys then Result := Result + 'ssRight, '; if ssMiddle in Keys then Result := Result + 'ssMiddle, '; if ssAlt in Keys then Result := Result + 'ssAlt, '; if ssDouble in Keys then Result := Result + 'ssDouble, '; if Length(Result) > 1 then SetLength(Result, Length(Result) - 2); Result := Result + ']'; end; function RegSvrPath: WideString; const ExeName: WideString = 'RegSvr32.exe'; var Path: WideString; begin Result:= ''; //Look in System dir Path := SystemDirectory + '\' + ExeName; if FileExistsW(Path) then Result := Path else begin //Look in Windows dir Path:= WindowsDirectory + '\' + ExeName; if FileExistsW(Path) then Result:= Path; end end; function ExecShellEx(const Cmd, Params, Dir: WideString; ShowCmd: integer; DoWait: boolean): boolean; var InfoA: TShellExecuteInfoA; InfoW: TShellExecuteInfoW; CmdA, ParamsA, DirA: string; begin if Assigned(ShellExecuteExW_MP) then begin FillChar(InfoW, SizeOf(InfoW), 0); InfoW.cbSize := SizeOf(InfoW); InfoW.fMask := SEE_MASK_FLAG_NO_UI or SEE_MASK_NOCLOSEPROCESS; InfoW.lpFile := PWideChar(Cmd); InfoW.lpParameters := PWideChar(Params); InfoW.lpDirectory := PWideChar(Dir); InfoW.nShow := ShowCmd; Result := ShellExecuteExW_MP(@InfoW); if Result and DoWait then begin WaitForSingleObject(InfoW.hProcess, INFINITE); CloseHandle(InfoW.hProcess) end; end else begin CmdA := Cmd; ParamsA := Params; DirA := Dir; FillChar(InfoA, SizeOf(InfoA), 0); InfoA.cbSize := SizeOf(InfoA); InfoA.fMask := SEE_MASK_FLAG_NO_UI or SEE_MASK_NOCLOSEPROCESS; InfoA.lpFile := PChar(CmdA); InfoA.lpParameters := PChar(ParamsA); InfoA.lpDirectory := PChar(DirA); InfoA.nShow := ShowCmd; Result := ShellExecuteEx(@InfoA); if Result and DoWait then begin WaitForSingleObject(InfoA.hProcess, INFINITE); CloseHandle(InfoA.hProcess) end; end; end; function RegUnregNSE(const AFileName: WideString; DoRegister: boolean; AMessages: TEasyNSERegMessages = [enseMsgShowErrors]): boolean; var Fn, Exe, Msg: WideString; begin Result:= false; if not FileExistsW(AFileName) then begin if enseMsgShowErrors in AMessages then begin if DoRegister then Msg := SEasyNSEMsg_CannotRegister else Msg := SEasyNSEMsg_CannotUnRegister; WideMessageBox(Application.Handle, PWideChar( Msg + #13 + SEasyNSEMsg_CannotFindDLL), PWideChar( SEasyNSEMsg_Caption), MB_OK or MB_ICONERROR); end; Exit end; Fn := AFileName; // If includes spaces then " " it if Pos(' ', Fn) > 0 then Fn := '"' + Fn + '"'; Exe := RegSvrPath; if not FileExistsW(Exe) then begin if enseMsgShowErrors in AMessages then begin if DoRegister then Msg := SEasyNSEMsg_CannotRegister else Msg := SEasyNSEMsg_CannotUnRegister; MessageBoxW(Application.Handle, PWideChar( Msg + #13 + SEasyNSEMsg_CannotFindRegSvr), PWideChar( SEasyNSEMsg_Caption), MB_OK or MB_ICONERROR); end; Exit end; if DoRegister then Msg := '' else Msg := '/U '; if not (enseMsgRegSvr in AMessages) then Msg := Msg + '/S '; Result:= ExecShellEx(exe, Msg + Fn, '', SW_SHOW, True); end; function RegisterNSE(const AFileName: WideString; AMessages: TEasyNSERegMessages = [enseMsgShowErrors]): boolean; begin Result:= RegUnregNSE(AFileName, True, AMessages); end; function UnregisterNSE(const AFileName: WideString; AMessages: TEasyNSERegMessages = [enseMsgShowErrors]): boolean; begin Result:= RegUnregNSE(AFileName, False, AMessages); end; {$IFNDEF COMPILER_6_UP} function DirectoryExists(const Directory: string): Boolean; var Code: Integer; begin Code := GetFileAttributes(PChar(Directory)); Result := (Code <> -1) and (FILE_ATTRIBUTE_DIRECTORY and Code <> 0); end; {$ENDIF} {$IFNDEF COMPILER_6_UP} function GUIDToString(const GUID: TGUID): string; var P: PWideChar; begin Result := ''; if Succeeded(StringFromCLSID(GUID, P)) then begin Result := P; CoTaskMemFree(P); end end; {$ENDIF} procedure MakeFindDataW(const FindFileDataA: TWIN32FindDataA; var FindFileDataW: TWIN32FindDataW); // // Makes TWIN32FindDataW from a TWIN32FindDataA structure // var i: Integer; WS: WideString; begin FindFileDataW.dwFileAttributes := FindFileDataA.dwFileAttributes; FindFileDataW.ftCreationTime := FindFileDataA.ftCreationTime; FindFileDataW.ftLastAccessTime := FindFileDataA.ftLastAccessTime; FindFileDataW.ftLastWriteTime := FindFileDataA.ftLastWriteTime; FindFileDataW.nFileSizeHigh := FindFileDataA.nFileSizeHigh; FindFileDataW.nFileSizeLow := FindFileDataA.nFileSizeLow; FindFileDataW.dwReserved0 := FindFileDataA.dwReserved0; FindFileDataW.dwReserved1 := FindFileDataA.dwReserved1; FillChar(FindFileDataW.cAlternateFileName, SizeOf(FindFileDataW.cAlternateFileName), #0); FillChar(FindFileDataW.cFileName, SizeOf(FindFileDataW.cFileName), #0); i := 0; while FindFileDataA.cAlternateFileName[i] <> #0 do begin WS := FindFileDataA.cAlternateFileName[i]; FindFileDataW.cAlternateFileName[i] := WS[1]; Inc(i) end; i := 0; while FindFileDataA.cFileName[i] <> #0 do begin WS := FindFileDataA.cFileName[i]; FindFileDataW.cFileName[i] := WS[1]; Inc(i) end end; function WideDirectoryExists(const Name: WideString): Boolean; begin {$IFDEF TNTSUPPORT} Result := TntSysUtils.WideDirectoryExists(Name); {$ELSE} Result := DirectoryExists(Name) {$ENDIF} end; function WideCreateDir(Path: WideString): Boolean; begin {$IFDEF TNTSUPPORT} Result := TntSysUtils.WideCreateDir(Path); {$ELSE} Result := CreateDir(Path) {$ENDIF} end; function WideExcludeTrailingBackslash(Path: WideString): WideString; begin {$IFDEF TNTSUPPORT} Result := TntSysUtils.WideExcludeTrailingBackslash(Path); {$ELSE} {$IFDEF COMPILER_6_UP} {$WARN SYMBOL_PLATFORM OFF} {$ENDIF} Result := ExcludeTrailingBackslash(Path) {$IFDEF COMPILER_6_UP} {$WARN SYMBOL_PLATFORM ON} {$ENDIF} {$ENDIF} end; function WideExpandEnviromentString(EnviromentString: WideString): WideString; var Length: Integer; EnviromentStringA, ResultA: string; begin Result := EnviromentString; if Assigned(ExpandEnvironmentStringsW_MP) then begin Length := ExpandEnvironmentStringsW_MP(PWideChar( EnviromentString), nil, 0); if Length > 0 then begin SetLength(Result, Length - 1); // Includes the null ExpandEnvironmentStringsW_MP( PWideChar( EnviromentString), PWideChar( @Result[1]), Length); end end else begin EnviromentStringA := EnviromentString; Length := ExpandEnvironmentStringsA(PChar( EnviromentStringA), nil, 0); if Length > 0 then begin SetLength(ResultA, Length - 2); // There is a magic 1 per the MSDN docs for the ANSI version ExpandEnvironmentStringsA( PChar( EnviromentStringA), PChar( @ResultA[1]), Length); Result := ResultA end end end; function WideExtractFilePath(Path: WideString): WideString; begin {$IFDEF TNTSUPPORT} Result := TntSysUtils.WideExtractFilePath(Path); {$ELSE} Result := ExtractFilePath(Path); {$ENDIF} end; function WideExtractFileName(Path: WideString): WideString; begin {$IFDEF TNTSUPPORT} Result := TntSysUtils.WideExtractFileName(Path); {$ELSE} Result := ExtractFileName(Path); {$ENDIF} end; function WideFileExists(Path: WideString): Boolean; begin {$IFDEF TNTSUPPORT} Result := TntSysUtils.WideFileExists(Path); {$ELSE} Result := FileExists(Path); {$ENDIF} end; function WideExtractFileDir(Path: WideString): WideString; begin {$IFDEF TNTSUPPORT} Result := TntSysUtils.WideExtractFileDir(Path); {$ELSE} Result := ExtractFileDir(Path); {$ENDIF} end; function WideExtractFileDrive(Path: WideString): WideString; begin {$IFDEF TNTSUPPORT} Result := TntSysUtils.WideExtractFileDrive(Path); {$ELSE} Result := ExtractFileDrive(Path); {$ENDIF} end; function WideExtractFileExt(Path: WideString): WideString; begin {$IFDEF TNTSUPPORT} Result := TntSysUtils.WideExtractFileExt(Path); {$ELSE} Result := ExtractFileExt(Path); {$ENDIF} end; function WideFindFirstFileEx(FileName: WideString; var lpFindFileData: TWIN32FindDataW; Mask: WideString; CaseSensitive: Boolean): THandle; var CaseFlag: DWORD; FindFileDataA: TWIN32FindDataA; begin Result := 0; if CaseSensitive then CaseFlag := FIND_FIRST_EX_CASE_SENSITIVE else CaseFlag := 0; if Assigned(FindFirstFileExW_MP) then Result := FindFirstFileExW_MP(PWideChar(WideString( FileName + '\' + Mask)), FINDEX_INFO_STANDARD, lpFindFileData, FINDEX_SEARCH_NAMEMATCH, nil, CaseFlag) else if Assigned(FindFirstFileExA_MP) then begin Result := FindFirstFileExA_MP(PChar( String( FileName + '\' + Mask)), FINDEX_INFO_STANDARD, FindFileDataA, FINDEX_SEARCH_NAMEMATCH, nil, CaseFlag); MakeFindDataW(FindFileDataA, lpFindFileData) end end; function WideFindFirstFileExExists: Boolean; begin Result := Assigned(FindFirstFileExA_MP) or Assigned(FindFirstFileExW_MP) end; procedure FixFormFont(AFont: TFont); var LogFont: TLogFont; begin if SystemParametersInfo(SPI_GETICONTITLELOGFONT, SizeOf(LogFont), @LogFont, 0) then AFont.Handle := CreateFontIndirect(LogFont) else AFont.Handle := GetStockObject(DEFAULT_GUI_FONT); end; procedure FillGradient(X1, Y1, X2, Y2: integer; fStartColor, fStopColor: TColor; StartPoint, EndPoint: integer; fDrawCanvas: TCanvas); // X1, Y1, X2, Y2: TopLeft and BottomRight coordinates of fill area... //fStartColor: color to begin the gradient fill with //FStopColor: color to end the gradient fill with //StartPoint: the first point between X1 and X2 to draw (useful for faster updating of // specific areas instead of redrawing the entire gradient fill area such as progress bars) //EndPoint: the last point between X1 and X2 to draw //fDrawCanvas: the canvas to draw the gradient on var y: integer; tmpColor: TColor; begin fStartColor := ColorToRGB(fStartColor); fStopColor := ColorToRGB(fStopColor); tmpColor := fDrawCanvas.Pen.Color; for y := Y1 to Y2 do begin fDrawCanvas.MoveTo(X1, y); if (EndPoint > 0) and (y <= EndPoint) and (y >= StartPoint) then begin fDrawCanvas.Pen.Color := RGB(Round(GetRValue(fStartColor) + (((GetRValue(fStopColor) - GetRValue(fStartColor)) / (Y2 - Y1)) *Abs(y - Y1))), Round(GetGValue(fStartColor) + (((GetGValue(fStopColor) - GetGValue(fStartColor)) / (Y2 - Y1)) * Abs(y - Y1))), Round(GetBValue(fStartColor) + (((GetBValue(fStopColor) - GetBValue(fStartColor)) / (y2 - Y1)) * Abs(y - Y1)))); fDrawCanvas.LineTo(X2, y); end; end; fDrawCanvas.Brush.Color := tmpColor; end; function DropEffectToStr(DropEffect: DWORD): WideString; begin Result := ''; if DropEffect and DROPEFFECT_COPY <> 0 then Result := Result + 'DROPEFFECT_COPY|'; if DropEffect and DROPEFFECT_MOVE <> 0 then Result := Result + 'DROPEFFECT_MOVE|'; if DropEffect and DROPEFFECT_LINK <> 0 then Result := Result + 'DROPEFFECT_LINK|'; if DropEffect and DROPEFFECT_SCROLL <> 0 then Result := Result + 'DROPEFFECT_SCROLL|'; if Length(Result) > 0 then SetLength(Result, Length(Result) - 1) else Result := '(none)'; end; function EqualWndMethod(A, B: TWndMethod): Boolean; begin Result := (TMethod(A).Code = TMethod(B).Code) and (TMethod(A).Data = TMethod(B).Data) end; function FindRootToken(const Path: WideString): PWideChar; const RootToken = WideString(':\'); begin Result := WideStrPos(PWideChar(Path), RootToken); end; function ExtractFileDirW(const FileName: WideString): WideString; var WP: PWideChar; begin Result := ''; if (Length(FileName) < 3) and (Length(FileName) > 0) then begin if (((FileName[1] >= 'A') and (FileName[1] >= 'Z')) or ((FileName[1] >= 'a') and (FileName[1] >= 'z'))) then begin Result := WideString(FileName[1]) + ':\'; end end else begin WP := FindRootToken(FileName); if Assigned(WP) then begin { Find the last '\' } WP := WideStrRScan(PWideChar( FileName), WideChar( '\')); if Assigned(WP) then begin { The stripped file name leaves just the root directory } if (Length(FileName) > 1) and ( (WP - 1)^ = ':') then WP := WP + 1; // Tack on the '\' SetLength(Result, WP - @FileName[1]); WideStrMove(PWideChar(Result), PWideChar(FileName), WP - @FileName[1]); end end end end; function DirExistsW(const FileName: PWideChar): Boolean; var ErrorCode: LongWord; S: string; begin if FileName <> '' then begin if (Win32Platform = VER_PLATFORM_WIN32_NT) then ErrorCode := GetFileAttributesW_MP(FileName) else begin S := FileName; ErrorCode := GetFileAttributesA(PChar(S)) end; Result := (Integer(ErrorCode) <> -1) and (FILE_ATTRIBUTE_DIRECTORY and ErrorCode <> 0); end else Result := False end; function FlipReverseCopyRect(const Flip, Reverse: Boolean; const Bitmap: TBitmap): TBitmap; var Bottom, Left, Right, Top: integer; begin Result := TBitmap.Create; Result.Width := Bitmap.Width; Result.Height := Bitmap.Height; Result.PixelFormat := Bitmap.PixelFormat; // Flip Top to Bottom if Flip then begin // Unclear why extra "-1" is needed here. Top := Bitmap.Height-1; Bottom := -1 end else begin Top := 0; Bottom := Bitmap.Height end; // Reverse Left to Right if Reverse then begin // Unclear why extra "-1" is needed here. Left := Bitmap.Width-1; Right := -1; end else begin Left := 0; Right := Bitmap.Width; end; Result.Canvas.CopyRect(Rect(Left,Top, Right,Bottom), Bitmap.Canvas, Rect(0,0, Bitmap.Width,Bitmap.Height)); end; procedure FlipReverseCopyRect(const Flip, Reverse: Boolean; R: TRect; const Canvas: TCanvas); overload; var Bottom, Left, Right, Top: integer; begin // Flip Top to Bottom if Flip then begin // Unclear why extra "-1" is needed here. Top := RectHeight(R)-1; Bottom := -1 end else begin Top := 0; Bottom := RectHeight(R) end; // Reverse Left to Right if Reverse then begin // Unclear why extra "-1" is needed here. Left := RectWidth(R)-1; Right := -1; end else begin Left := 0; Right := RectWidth(R); end; Canvas.CopyRect(Rect(Left, Top, Right, Bottom), Canvas, Rect(0,0, RectWidth(R), RectHeight(R))); end; function FileExistsW(const FileName: WideString): Boolean; var Handle: THandle; FindData: TWin32FindData; FindDataW: TWin32FindDataW; FileNameA: string; begin Result := True; if Win32Platform = VER_PLATFORM_WIN32_NT then Handle := FindFirstFileW_MP(PWideChar(FileName), FindDataW) else begin FileNameA := FileName; Handle := FindFirstFileA(PChar(FileNameA), FindData) end; if Handle <> INVALID_HANDLE_VALUE then Windows.FindClose(Handle) else Result := False; end; function IsMappedDrivePath(const Path: WideString): Boolean; var WS: WideString; begin WS := Path; SetLength(WS, 3); if IsWinNT then Result := GetDriveTypeW_MP(PWideChar(WS)) = DRIVE_REMOTE else Result := GetDriveType(PChar(string(WS))) = DRIVE_REMOTE; end; { Searchs through the passed menu looking for an item identifer that is not } { currently being used. } function FindUniqueMenuID(AMenu: HMenu): Cardinal; function RunMenu(AMenu: HMenu; var ID: Cardinal): Boolean; var MenuInfoW: TMenuItemInfoW; MenuInfoA: TMenuItemInfoA; i, ItemCount: Integer; Reset, IsDuplicate: Boolean; begin Reset := False; IsDuplicate := False; ItemCount := GetMenuItemCount(AMenu); i := 0; while (i < ItemCount) and not IsDuplicate do begin if IsUnicode then begin FillChar(MenuInfoW, SizeOf(MenuInfoW), #0); MenuInfoW.cbSize := SizeOf(MenuInfoW); MenuInfoW.fMask := MIIM_SUBMENU or MIIM_ID; GetMenuItemInfoW(AMenu, i, True, MenuInfoW); if MenuInfoW.hSubMenu <> 0 then Reset := RunMenu(MenuInfoW.hSubMenu, ID); IsDuplicate := MenuInfoW.wID = ID end else begin FillChar(MenuInfoA, SizeOf(MenuInfoA), #0); MenuInfoA.cbSize := SizeOf(MenuInfoA); MenuInfoA.fMask := MIIM_SUBMENU or MIIM_ID; GetMenuItemInfoA(AMenu, i, True, MenuInfoA); if MenuInfoA.hSubMenu <> 0 then Reset := RunMenu(MenuInfoA.hSubMenu, ID); IsDuplicate := MenuInfoA.wID = ID end; Inc(i); end; Result := IsDuplicate and not Reset end; begin Result := 1000; while RunMenu(AMenu, Result) do Inc(Result) end; function AddContextMenuItem(Menu: HMenu; ACaption: WideString; Index: Integer; MenuID: UINT = $FFFF; hSubMenu: UINT = 0; Enabled: Boolean = True; Checked: Boolean = False; Default: Boolean = False): Integer; // // Pass '-' for a separator // -1 to add to the end // if MenuID = -1 then the function will create a unique ID // if hSubMenu > 0 then the item contain sub-items // Returns ID of new Item // var InfoA: TMenuItemInfoA; InfoW: TMenuItemInfoW; begin if IsUnicode and Assigned(InsertMenuItemW_MP) then begin FillChar(InfoW, SizeOf(InfoW), #0); InfoW.cbSize := SizeOf(InfoW); InfoW.fMask := MIIM_TYPE or MIIM_ID or MIIM_STATE; if Enabled or (ACaption = '-') then InfoW.fState := InfoW.fState or MFS_ENABLED else InfoW.fState := InfoW.fState or MFS_DISABLED; if Checked and (ACaption <> '-') then InfoW.fState := InfoW.fState or MFS_CHECKED; if Default and (ACaption <> '-') then InfoW.fState := InfoW.fState or MFS_DEFAULT; if ACaption = '-' then InfoW.fType := MFT_SEPARATOR else begin InfoW.fType := MFT_STRING; if hSubMenu > 0 then begin InfoW.fMask := InfoW.fMask or MIIM_SUBMENU; InfoW.hSubMenu := hSubMenu end end; InfoW.dwTypeData := PWideChar(ACaption); InfoW.cch := Length(ACaption); if InfoW.fType = MFT_STRING then begin if MenuID = $FFFF then InfoW.wID := FindUniqueMenuID(Menu) else if InfoW.fMask and MIIM_SUBMENU <> 0 then InfoW.wID := $FFFF // Sub-Item Parents don't get an unique ID else InfoW.wID := MenuID; end else InfoW.wID := $FFFF; // Separators don't get an unique ID Result := InfoW.wID; if Index < 0 then InsertMenuItemW_MP(Menu, GetMenuItemCount(Menu), True, InfoW) else InsertMenuItemW_MP(Menu, Index, True, InfoW); // Inserts by Position end else begin FillChar(InfoA, SizeOf(InfoA), #0); InfoA.cbSize := SizeOf(InfoA); InfoA.fMask := MIIM_TYPE or MIIM_ID or MIIM_STATE; if Enabled or (ACaption = '-') then InfoA.fState := InfoA.fState or MFS_ENABLED else InfoA.fState := InfoA.fState or MFS_DISABLED; if Checked and (ACaption <> '-') then InfoA.fState := InfoA.fState or MFS_CHECKED; if Default and (ACaption <> '-') then InfoA.fState := InfoA.fState or MFS_DEFAULT; if ACaption = '-' then InfoA.fType := MFT_SEPARATOR else begin InfoA.fType := MFT_STRING; if hSubMenu > 0 then begin InfoA.fMask := InfoA.fMask or MIIM_SUBMENU; InfoA.hSubMenu := hSubMenu end end; InfoA.dwTypeData := PChar( string(ACaption)); InfoA.cch := Length(ACaption); if InfoA.fType = MFT_STRING then begin if MenuID = $FFFF then InfoA.wID := FindUniqueMenuID(Menu) else if InfoA.fMask and MIIM_SUBMENU <> 0 then InfoA.wID := $FFFF // Sub-Item Parents don't get an unique ID else InfoA.wID := MenuID; end else InfoA.wID := $FFFF; // Separators don't get an unique ID Result := InfoA.wID; if Index < 0 then InsertMenuItemA(Menu, GetMenuItemCount(Menu), True, InfoA) else InsertMenuItemA(Menu, Index, True, InfoA); end end; procedure ShadowBlendBits(Bits: TBitmap; BackGndColor: TColor); begin if Assigned(Bits) and (Bits.PixelFormat = pf32Bit) then begin AlphaBlend(Bits.Canvas.Handle, Bits.Canvas.Handle, Rect(0, 0, Bits.Width,Bits.Height), Point(0, 0), cbmConstantAlphaAndColor, 0, ColorToRGB(BackGndColor)); ConvertBitmapEx(Bits, Bits, BackGndColor) end end; procedure SumFolder(FolderPath: WideString; Recurse: Boolean; var Size: Int64); { Returns the size of all files within the passed folder, including all } { sub-folders. This is recurcive don't initialize Size to 0 in the function! } var Info: TWin32FindData; InfoW: TWin32FindDataW; FHandle: THandle; FolderPathA: string; begin if IsUnicode then begin FHandle := FindFirstFileW_MP(PWideChar( FolderPath + '\*.*'), InfoW); if FHandle <> INVALID_HANDLE_VALUE then try if Recurse and (InfoW.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY <> 0) then begin if (lstrcmpiW_MP(InfoW.cFileName, '.') <> 0) and (lstrcmpiW_MP(InfoW.cFileName, '..') <> 0) and (InfoW.dwFileAttributes and FILE_ATTRIBUTE_REPARSE_POINT = 0) then SumFolder(FolderPath + '\' + InfoW.cFileName, Recurse, Size) end else Size := Size + InfoW.nFileSizeHigh * MAXDWORD + InfoW.nFileSizeLow; while FindNextFileW_MP(FHandle, InfoW) and not SumFolderAbort do begin if Recurse and (InfoW.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY <> 0) then begin if (lstrcmpiW_MP(InfoW.cFileName, '.') <> 0) and (lstrcmpiW_MP(InfoW.cFileName, '..') <> 0) and (InfoW.dwFileAttributes and FILE_ATTRIBUTE_REPARSE_POINT = 0) then SumFolder(FolderPath + '\' + InfoW.cFileName, Recurse, Size) end else Size := Size + InfoW.nFileSizeHigh * MAXDWORD + InfoW.nFileSizeLow; end; finally Windows.FindClose(FHandle) end end else begin FolderPathA := FolderPath; FHandle := FindFirstFile(PChar( FolderPathA + '\*.*'), Info); if FHandle <> INVALID_HANDLE_VALUE then try if Recurse and (Info.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY <> 0) then begin if (lstrcmpi(Info.cFileName, '.') <> 0) and (lstrcmpi(Info.cFileName, '..') <> 0) and (InfoW.dwFileAttributes and FILE_ATTRIBUTE_REPARSE_POINT = 0) then SumFolder(FolderPathA + '\' + Info.cFileName, Recurse, Size) end else Size := Size + Info.nFileSizeHigh * MAXDWORD + Info.nFileSizeLow; while FindNextFile(FHandle, Info) and not SumFolderAbort do begin if Recurse and (Info.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY <> 0) then begin if (lstrcmpi(Info.cFileName, '.') <> 0) and (lstrcmpi(Info.cFileName, '..') <> 0) and (InfoW.dwFileAttributes and FILE_ATTRIBUTE_REPARSE_POINT = 0) then SumFolder(FolderPathA + '\' + Info.cFileName, Recurse, Size) end else Size := Size + Info.nFileSizeHigh * MAXDWORD + Info.nFileSizeLow; end; finally Windows.FindClose(FHandle) end end end; function InternalTextExtentW(Text: PWideChar; DC: HDC): TSize; var S: string; begin if IsUnicode then GetTextExtentPoint32W(DC, PWideChar(Text), Length(Text), Result) else begin S := WideString( Text); GetTextExtentPoint32(DC, PChar(S), Length(S), Result) end; end; function WideStrMove(Dest, Source: PWideChar; Count: Cardinal): PWideChar; // Copies the specified number of characters to the destination string and returns Dest // also as result. Dest must have enough room to store at least Count characters. asm PUSH ESI PUSH EDI MOV ESI, EDX MOV EDI, EAX MOV EDX, ECX CMP EDI, ESI JG @@1 JE @@2 SHR ECX, 1 REP MOVSD MOV ECX, EDX AND ECX, 1 REP MOVSW JMP @@2 @@1: LEA ESI, [ESI + 2 * ECX - 2] LEA EDI, [EDI + 2 * ECX - 2] STD AND ECX, 1 REP MOVSW SUB EDI, 2 SUB ESI, 2 MOV ECX, EDX SHR ECX, 1 REP MOVSD CLD @@2: POP EDI POP ESI end; function WideStrRScan(Str: PWideChar; Chr: WideChar): PWideChar; // returns a pointer to the last occurance of Chr in Str asm PUSH EDI MOV EDI, Str MOV ECX, 0FFFFFFFFH XOR AX, AX REPNE SCASW NOT ECX STD SUB EDI, 2 MOV AX, Chr REPNE SCASW MOV EAX, 0 JNE @@1 MOV EAX, EDI ADD EAX, 2 @@1: CLD POP EDI end; function WideStrScan(Str: PWideChar; Chr: WideChar): PWideChar; // returns a pointer to first occurrence of a specified character in a string asm PUSH EDI PUSH EAX MOV EDI, Str MOV ECX, 0FFFFFFFFH XOR AX, AX REPNE SCASW NOT ECX POP EDI MOV AX, Chr REPNE SCASW MOV EAX, 0 JNE @@1 MOV EAX, EDI SUB EAX, 2 @@1: POP EDI end; function WideUpperCase(const S: WideString): WideString; begin Result := S; if IsUnicode then CharUpperBuffW_MP(PWideChar(Result), Length(Result)) else CharUpperBuffA(PChar(string(Result)), Length(Result)) end; function LibList: TList; begin if not Assigned(FLibList) then FLibList := TList.Create; Result := FLibList end; function CommonLoadLibrary(LibraryName: string): THandle; var i: Integer; Found: Boolean; LibRec: PLibRec; begin Result := 0; Found := False; i := 0; while (i < LibList.Count) and not Found do begin LibRec := PLibRec(LibList[i]); if lstrcmpi(PChar(LibRec.LibraryName), PChar(LibraryName)) = 0 then begin Inc(LibRec.ReferenceCount); Result := LibRec.Handle; Found := True end; Inc(i) end; if not Found then begin New(LibRec); LibRec.Handle := LoadLibrary(PChar(LibraryName)); if LibRec.Handle <> 0 then begin LibRec.LibraryName := LibraryName; LibRec.ReferenceCount := 1; LibList.Add(LibRec); Result := LibRec.Handle end else Dispose(LibRec) end end; function CommonUnloadLibrary(LibraryName: string): Boolean; var i: Integer; LibRec: PLibRec; begin Result := False; i := 0; while (i < LibList.Count) and not Result do begin LibRec := PLibRec(LibList[i]); if lstrcmpi(PChar(LibRec.LibraryName), PChar(LibraryName)) = 0 then begin Dec(LibRec.ReferenceCount); FreeLibrary(LibRec.Handle); if LibRec.ReferenceCount = 0 then begin LibList.Delete(i); Dispose(LibRec); end; Result := True end; Inc(i) end; if LibList.Count = 0 then FreeAndNil(FLibList) end; procedure CommonUnloadAllLibraries; var i, LibIndex: Integer; LibRec: PLibRec; begin i := 0; while i < LibList.Count do begin LibRec := PLibRec(LibList[i]); for LibIndex := 0 to LibRec.ReferenceCount - 1 do FreeLibrary(LibRec.Handle); LibList.Delete(i); Dispose(LibRec); Inc(i) end; FreeAndNil(FLibList); end; procedure DrawRadioButton(Canvas: TCanvas; Pos: TPoint; Size: Integer; clBackground, clHotBkGnd, clLeftOuter, clRightOuter, clLeftInner, clRightInner: TColor; Checked, Enabled, Hot: Boolean); begin MarlettFont.Size := Size; Canvas.Brush.Style := bsClear; Canvas.Font.Assign(MarlettFont); // Draw the background if Hot then Canvas.Font.Color := clHotBkGnd else Canvas.Font.Color := clBackground; Canvas.TextOut(Pos.X, Pos.Y, 'n'); Canvas.Brush.Style := bsClear; // Draw the Outer Circle Canvas.Font.Color := clLeftOuter; Canvas.TextOut(Pos.X, Pos.Y, 'j'); Canvas.Font.Color := clRightOuter; Canvas.TextOut(Pos.X, Pos.Y, 'k'); // Draw the Inner Circle Canvas.Font.Color := clLeftInner; Canvas.TextOut(Pos.X, Pos.Y, 'l'); Canvas.Font.Color := clRightInner; Canvas.TextOut(Pos.X, Pos.Y, 'm'); if Checked then begin if Enabled then Canvas.Font.Color := clBlack else Canvas.Font.Color := clBtnShadow; Canvas.TextOut(Pos.X, Pos.Y, 'i'); end end; procedure DrawCheckBox(Canvas: TCanvas; Pos: TPoint; Size: Integer; clBackground, clHotBkGnd, clLeftOuter, clRightOuter, clLeftInner, clRightInner: TColor; Checked, Enabled, Hot: Boolean); begin MarlettFont.Size := Size; Canvas.Brush.Style := bsSolid; Canvas.Font.Assign(MarlettFont); // Draw the background if Hot then Canvas.Font.Color := clHotBkGnd else Canvas.Font.Color := clBackground; Canvas.TextOut(Pos.X, Pos.Y, Char($67)); Canvas.Brush.Style := bsClear; // Draw the Outer Frame Canvas.Font.Color := clLeftOuter; Canvas.TextOut(Pos.X, Pos.Y, Char($63)); Canvas.Font.Color := clRightOuter; Canvas.TextOut(Pos.X, Pos.Y, Char($64)); // Draw the Inner Frame Canvas.Font.Color := clLeftInner; Canvas.TextOut(Pos.X, Pos.Y, Char($65)); Canvas.Font.Color := clRightInner; Canvas.TextOut(Pos.X, Pos.Y, Char($66)); if Checked then begin if Enabled then Canvas.Font.Color := clBlack else Canvas.Font.Color := clBtnShadow; Canvas.TextOut(Pos.X, Pos.Y, Char($62)); end end; function CheckBounds(Size: Integer): TRect; var Canvas: TCanvas; begin Result := Rect(0, 0, 0, 0); Canvas := TCanvas.Create; try Canvas.Handle := GetDC(0); Canvas.Font.Name := 'Marlett'; Canvas.Font.Size := Size; // Use the background for the Checkbox for the size, the Radio will be this // size or a bit smaller Result.Right := Canvas.TextWidth(Char($67)); Result.Bottom := Canvas.TextHeight(Char($67)); finally if Assigned(Canvas) then begin ReleaseDC(0, Canvas.Handle); Canvas.Handle := 0 end; Canvas.Free end; end; function HasMMX: Boolean; // Helper method to determine whether the current processor supports MMX. asm PUSH EBX XOR EAX, EAX // Result := False PUSHFD // determine if the processor supports the CPUID command POP EDX MOV ECX, EDX XOR EDX, $200000 PUSH EDX POPFD PUSHFD POP EDX XOR ECX, EDX JZ @1 // no CPUID support so we can't even get to the feature information PUSH EDX POPFD MOV EAX, 1 DW $A20F // CPUID, EAX contains now version info and EDX feature information MOV EBX, EAX // free EAX to get the result value XOR EAX, EAX // Result := False CMP EBX, $50 JB @1 // if processor family is < 5 then it is not a Pentium class processor TEST EDX, $800000 JZ @1 // if the MMX bit is not set then we don't have MMX INC EAX // Result := True @1: POP EBX end; procedure AlphaBlendLineConstant(Source, Destination: Pointer; Count: Integer; ConstantAlpha, Bias: Integer); // Blends a line of Count pixels from Source to Destination using a constant alpha value. // The layout of a pixel must be BGRA where A is ignored (but is calculated as the other components). // ConstantAlpha must be in the range 0..255 where 0 means totally transparent (destination pixel only) // and 255 totally opaque (source pixel only). // Bias is an additional value which gets added to every component and must be in the range -128..127 // // EAX contains Source // EDX contains Destination // ECX contains Count // ConstantAlpha and Bias are on the stack asm PUSH ESI // save used registers PUSH EDI MOV ESI, EAX // ESI becomes the actual source pointer MOV EDI, EDX // EDI becomes the actual target pointer // Load MM6 with the constant alpha value (replicate it for every component). // Expand it to word size. MOV EAX, [ConstantAlpha] DB $0F, $6E, $F0 /// MOVD MM6, EAX DB $0F, $61, $F6 /// PUNPCKLWD MM6, MM6 DB $0F, $62, $F6 /// PUNPCKLDQ MM6, MM6 // Load MM5 with the bias value. MOV EAX, [Bias] DB $0F, $6E, $E8 /// MOVD MM5, EAX DB $0F, $61, $ED /// PUNPCKLWD MM5, MM5 DB $0F, $62, $ED /// PUNPCKLDQ MM5, MM5 // Load MM4 with 128 to allow for saturated biasing. MOV EAX, 128 DB $0F, $6E, $E0 /// MOVD MM4, EAX DB $0F, $61, $E4 /// PUNPCKLWD MM4, MM4 DB $0F, $62, $E4 /// PUNPCKLDQ MM4, MM4 @1: // The pixel loop calculates an entire pixel in one run. // Note: The pixel byte values are expanded into the higher bytes of a word due // to the way unpacking works. We compensate for this with an extra shift. DB $0F, $EF, $C0 /// PXOR MM0, MM0, clear source pixel register for unpacking DB $0F, $60, $06 /// PUNPCKLBW MM0, [ESI], unpack source pixel byte values into words DB $0F, $71, $D0, $08 /// PSRLW MM0, 8, move higher bytes to lower bytes DB $0F, $EF, $C9 /// PXOR MM1, MM1, clear target pixel register for unpacking DB $0F, $60, $0F /// PUNPCKLBW MM1, [EDI], unpack target pixel byte values into words DB $0F, $6F, $D1 /// MOVQ MM2, MM1, make a copy of the shifted values, we need them again DB $0F, $71, $D1, $08 /// PSRLW MM1, 8, move higher bytes to lower bytes // calculation is: target = (alpha * (source - target) + 256 * target) / 256 DB $0F, $F9, $C1 /// PSUBW MM0, MM1, source - target DB $0F, $D5, $C6 /// PMULLW MM0, MM6, alpha * (source - target) DB $0F, $FD, $C2 /// PADDW MM0, MM2, add target (in shifted form) DB $0F, $71, $D0, $08 /// PSRLW MM0, 8, divide by 256 // Bias is accounted for by conversion of range 0..255 to -128..127, // doing a saturated add and convert back to 0..255. DB $0F, $F9, $C4 /// PSUBW MM0, MM4 DB $0F, $ED, $C5 /// PADDSW MM0, MM5 DB $0F, $FD, $C4 /// PADDW MM0, MM4 DB $0F, $67, $C0 /// PACKUSWB MM0, MM0, convert words to bytes with saturation DB $0F, $7E, $07 /// MOVD [EDI], MM0, store the result @3: ADD ESI, 4 ADD EDI, 4 DEC ECX JNZ @1 POP EDI POP ESI end; procedure AlphaBlendLinePerPixel(Source, Destination: Pointer; Count, Bias: Integer); // Blends a line of Count pixels from Source to Destination using the alpha value of the source pixels. // The layout of a pixel must be BGRA. // Bias is an additional value which gets added to every component and must be in the range -128..127 // // EAX contains Source // EDX contains Destination // ECX contains Count // Bias is on the stack asm PUSH ESI // save used registers PUSH EDI MOV ESI, EAX // ESI becomes the actual source pointer MOV EDI, EDX // EDI becomes the actual target pointer // Load MM5 with the bias value. MOV EAX, [Bias] DB $0F, $6E, $E8 /// MOVD MM5, EAX DB $0F, $61, $ED /// PUNPCKLWD MM5, MM5 DB $0F, $62, $ED /// PUNPCKLDQ MM5, MM5 // Load MM4 with 128 to allow for saturated biasing. MOV EAX, 128 DB $0F, $6E, $E0 /// MOVD MM4, EAX DB $0F, $61, $E4 /// PUNPCKLWD MM4, MM4 DB $0F, $62, $E4 /// PUNPCKLDQ MM4, MM4 @1: // The pixel loop calculates an entire pixel in one run. // Note: The pixel byte values are expanded into the higher bytes of a word due // to the way unpacking works. We compensate for this with an extra shift. DB $0F, $EF, $C0 /// PXOR MM0, MM0, clear source pixel register for unpacking DB $0F, $60, $06 /// PUNPCKLBW MM0, [ESI], unpack source pixel byte values into words DB $0F, $71, $D0, $08 /// PSRLW MM0, 8, move higher bytes to lower bytes DB $0F, $EF, $C9 /// PXOR MM1, MM1, clear target pixel register for unpacking DB $0F, $60, $0F /// PUNPCKLBW MM1, [EDI], unpack target pixel byte values into words DB $0F, $6F, $D1 /// MOVQ MM2, MM1, make a copy of the shifted values, we need them again DB $0F, $71, $D1, $08 /// PSRLW MM1, 8, move higher bytes to lower bytes // Load MM6 with the source alpha value (replicate it for every component). // Expand it to word size. DB $0F, $6F, $F0 /// MOVQ MM6, MM0 DB $0F, $69, $F6 /// PUNPCKHWD MM6, MM6 DB $0F, $6A, $F6 /// PUNPCKHDQ MM6, MM6 // calculation is: target = (alpha * (source - target) + 256 * target) / 256 DB $0F, $F9, $C1 /// PSUBW MM0, MM1, source - target DB $0F, $D5, $C6 /// PMULLW MM0, MM6, alpha * (source - target) DB $0F, $FD, $C2 /// PADDW MM0, MM2, add target (in shifted form) DB $0F, $71, $D0, $08 /// PSRLW MM0, 8, divide by 256 // Bias is accounted for by conversion of range 0..255 to -128..127, // doing a saturated add and convert back to 0..255. DB $0F, $F9, $C4 /// PSUBW MM0, MM4 DB $0F, $ED, $C5 /// PADDSW MM0, MM5 DB $0F, $FD, $C4 /// PADDW MM0, MM4 DB $0F, $67, $C0 /// PACKUSWB MM0, MM0, convert words to bytes with saturation DB $0F, $7E, $07 /// MOVD [EDI], MM0, store the result @3: ADD ESI, 4 ADD EDI, 4 DEC ECX JNZ @1 POP EDI POP ESI end; procedure AlphaBlendLineMaster(Source, Destination: Pointer; Count: Integer; ConstantAlpha, Bias: Integer); // Blends a line of Count pixels from Source to Destination using the source pixel and a constant alpha value. // The layout of a pixel must be BGRA. // ConstantAlpha must be in the range 0..255. // Bias is an additional value which gets added to every component and must be in the range -128..127 // // EAX contains Source // EDX contains Destination // ECX contains Count // ConstantAlpha and Bias are on the stack asm PUSH ESI // save used registers PUSH EDI MOV ESI, EAX // ESI becomes the actual source pointer MOV EDI, EDX // EDI becomes the actual target pointer // Load MM6 with the constant alpha value (replicate it for every component). // Expand it to word size. MOV EAX, [ConstantAlpha] DB $0F, $6E, $F0 /// MOVD MM6, EAX DB $0F, $61, $F6 /// PUNPCKLWD MM6, MM6 DB $0F, $62, $F6 /// PUNPCKLDQ MM6, MM6 // Load MM5 with the bias value. MOV EAX, [Bias] DB $0F, $6E, $E8 /// MOVD MM5, EAX DB $0F, $61, $ED /// PUNPCKLWD MM5, MM5 DB $0F, $62, $ED /// PUNPCKLDQ MM5, MM5 // Load MM4 with 128 to allow for saturated biasing. MOV EAX, 128 DB $0F, $6E, $E0 /// MOVD MM4, EAX DB $0F, $61, $E4 /// PUNPCKLWD MM4, MM4 DB $0F, $62, $E4 /// PUNPCKLDQ MM4, MM4 @1: // The pixel loop calculates an entire pixel in one run. // Note: The pixel byte values are expanded into the higher bytes of a word due // to the way unpacking works. We compensate for this with an extra shift. DB $0F, $EF, $C0 /// PXOR MM0, MM0, clear source pixel register for unpacking DB $0F, $60, $06 /// PUNPCKLBW MM0, [ESI], unpack source pixel byte values into words DB $0F, $71, $D0, $08 /// PSRLW MM0, 8, move higher bytes to lower bytes DB $0F, $EF, $C9 /// PXOR MM1, MM1, clear target pixel register for unpacking DB $0F, $60, $0F /// PUNPCKLBW MM1, [EDI], unpack target pixel byte values into words DB $0F, $6F, $D1 /// MOVQ MM2, MM1, make a copy of the shifted values, we need them again DB $0F, $71, $D1, $08 /// PSRLW MM1, 8, move higher bytes to lower bytes // Load MM7 with the source alpha value (replicate it for every component). // Expand it to word size. DB $0F, $6F, $F8 /// MOVQ MM7, MM0 DB $0F, $69, $FF /// PUNPCKHWD MM7, MM7 DB $0F, $6A, $FF /// PUNPCKHDQ MM7, MM7 DB $0F, $D5, $FE /// PMULLW MM7, MM6, source alpha * master alpha DB $0F, $71, $D7, $08 /// PSRLW MM7, 8, divide by 256 // calculation is: target = (alpha * master alpha * (source - target) + 256 * target) / 256 DB $0F, $F9, $C1 /// PSUBW MM0, MM1, source - target DB $0F, $D5, $C7 /// PMULLW MM0, MM7, alpha * (source - target) DB $0F, $FD, $C2 /// PADDW MM0, MM2, add target (in shifted form) DB $0F, $71, $D0, $08 /// PSRLW MM0, 8, divide by 256 // Bias is accounted for by conversion of range 0..255 to -128..127, // doing a saturated add and convert back to 0..255. DB $0F, $F9, $C4 /// PSUBW MM0, MM4 DB $0F, $ED, $C5 /// PADDSW MM0, MM5 DB $0F, $FD, $C4 /// PADDW MM0, MM4 DB $0F, $67, $C0 /// PACKUSWB MM0, MM0, convert words to bytes with saturation DB $0F, $7E, $07 /// MOVD [EDI], MM0, store the result @3: ADD ESI, 4 ADD EDI, 4 DEC ECX JNZ @1 POP EDI POP ESI end; procedure AlphaBlendLineMasterAndColor(Destination: Pointer; Count: Integer; ConstantAlpha, Color: Integer); // Blends a line of Count pixels in Destination against the given color using a constant alpha value. // The layout of a pixel must be BGRA and Color must be rrggbb00 (as stored by a COLORREF). // ConstantAlpha must be in the range 0..255. // // EAX contains Destination // EDX contains Count // ECX contains ConstantAlpha // Color is passed on the stack asm // The used formula is: target = (alpha * color + (256 - alpha) * target) / 256. // alpha * color (factor 1) and 256 - alpha (factor 2) are constant values which can be calculated in advance. // The remaining calculation is therefore: target = (F1 + F2 * target) / 256 // Load MM3 with the constant alpha value (replicate it for every component). // Expand it to word size. (Every calculation here works on word sized operands.) DB $0F, $6E, $D9 /// MOVD MM3, ECX DB $0F, $61, $DB /// PUNPCKLWD MM3, MM3 DB $0F, $62, $DB /// PUNPCKLDQ MM3, MM3 // Calculate factor 2. MOV ECX, $100 DB $0F, $6E, $D1 /// MOVD MM2, ECX DB $0F, $61, $D2 /// PUNPCKLWD MM2, MM2 DB $0F, $62, $D2 /// PUNPCKLDQ MM2, MM2 DB $0F, $F9, $D3 /// PSUBW MM2, MM3 // MM2 contains now: 255 - alpha = F2 // Now calculate factor 1. Alpha is still in MM3, but the r and b components of Color must be swapped. MOV ECX, [Color] BSWAP ECX ROR ECX, 8 DB $0F, $6E, $C9 /// MOVD MM1, ECX // Load the color and convert to word sized values. DB $0F, $EF, $E4 /// PXOR MM4, MM4 DB $0F, $60, $CC /// PUNPCKLBW MM1, MM4 DB $0F, $D5, $CB /// PMULLW MM1, MM3 // MM1 contains now: color * alpha = F1 @1: // The pixel loop calculates an entire pixel in one run. DB $0F, $6E, $00 /// MOVD MM0, [EAX] DB $0F, $60, $C4 /// PUNPCKLBW MM0, MM4 DB $0F, $D5, $C2 /// PMULLW MM0, MM2 // calculate F1 + F2 * target DB $0F, $FD, $C1 /// PADDW MM0, MM1 DB $0F, $71, $D0, $08 /// PSRLW MM0, 8 // divide by 256 DB $0F, $67, $C0 /// PACKUSWB MM0, MM0 // convert words to bytes with saturation DB $0F, $7E, $00 /// MOVD [EAX], MM0 // store the result ADD EAX, 4 DEC EDX JNZ @1 end; procedure EMMS; // Reset MMX state to use the FPU for other tasks again. asm DB $0F, $77 /// EMMS end; function GetBitmapBitsFromDeviceContext(DC: HDC; var Width, Height: Integer): Pointer; // Helper function used to retrieve the bitmap selected into the given device context. If there is a bitmap then // the function will return a pointer to its bits otherwise nil is returned. // Additionally the dimensions of the bitmap are returned. var Bitmap: HBITMAP; DIB: TDIBSection; begin Result := nil; Width := 0; Height := 0; Bitmap := GetCurrentObject(DC, OBJ_BITMAP); if Bitmap <> 0 then begin if GetObject(Bitmap, SizeOf(DIB), @DIB) = SizeOf(DIB) then begin Assert(DIB.dsBm.bmPlanes * DIB.dsBm.bmBitsPixel = 32, 'Alpha blending error: bitmap must use 32 bpp.'); Result := DIB.dsBm.bmBits; Width := DIB.dsBmih.biWidth; Height := DIB.dsBmih.biHeight; end; end; Assert(Result <> nil, 'Alpha blending DC error: no bitmap available.'); end; function CalculateScanline(Bits: Pointer; Width, Height, Row: Integer): Pointer; // Helper function to calculate the start address for the given row. begin if Height > 0 then // bottom-up DIB Row := Height - Row - 1; // Return DWORD aligned address of the requested scanline. Integer(Result) := Integer(Bits) + Row * ((Width * 32 + 31) and not 31) div 8; end; procedure ConvertBitmapEx(Image32: TBitmap; var OutImage: TBitmap; const BackGndColor: TColor); var I, N: Integer; LongColor: DWORD; SourceRed, SourceGreen, SourceBlue, BkGndRed, BkGndGreen, BkGndBlue, RedTarget, GreenTarget, BlueTarget, Alpha: Byte; Target, Mask: TBitmap; LineDeltaImage32, PixelDeltaImage32, LineDeltaTarget, PixelDeltaTarget, LineDeltaMask, PixelDeltaMask: Integer; PLineImage32, PLineTarget, PLineMask, PPixelImage32, PPixelTarget, PPixelMask: PByte; begin // Algorithm only works for bitmaps with a height > 1 pixel, should not be a limitation // as it would then be a line! if (Image32.PixelFormat = pf32Bit) and (Image32.Height > 1) then begin Target := TBitmap.Create; Mask := TBitmap.Create; try Target.PixelFormat := pf32Bit; Target.Width := Image32.Width; Target.Height := Image32.Height; Target.Assign(Image32); Mask.PixelFormat := pf32Bit; Mask.Width := Image32.Width; Mask.Height := Image32.Height; Mask.Canvas.Brush.Color := BackGndColor; Mask.Canvas.FillRect(Mask.Canvas.ClipRect); LineDeltaImage32 := DWORD( Image32.ScanLine[1]) - DWORD( Image32.ScanLine[0]); LineDeltaTarget := DWORD( Target.ScanLine[1]) - DWORD( Target.ScanLine[0]); LineDeltaMask := DWORD( Mask.ScanLine[1]) - DWORD( Mask.ScanLine[0]); PixelDeltaImage32 := SizeOf(TRGBQuad); PixelDeltaTarget := SizeOf(TRGBQuad); PixelDeltaMask := SizeOf(TRGBQuad); PLineImage32 := Image32.ScanLine[0]; PLineTarget := Target.ScanLine[0]; PLineMask := Mask.ScanLine[0]; for I := 0 to Image32.Height - 1 do begin PPixelImage32 := PLineImage32; PPixelTarget := PLineTarget; PPixelMask := PLineMask; for N := 0 to Image32.Width - 1 do begin // Source GetColorValues ; Profiled = ~24-30% of time LongColor := PDWORD( PPixelImage32)^; SourceBlue := LongColor and $000000FF; SourceGreen := (LongColor and $0000FF00) shr 8; SourceRed := (LongColor and $00FF0000) shr 16; Alpha := (LongColor and $FF000000) shr 24; // Mask GetColorValues ; Profiled = ~24-30% of time LongColor := PDWORD( PPixelMask)^; BkGndBlue := LongColor and $000000FF; BkGndGreen := (LongColor and $0000FF00) shr 8; BkGndRed := (LongColor and $00FF0000) shr 16; if Alpha < High(Byte) then begin // displayColor = sourceColor×alpha / 255 + backgroundColor×(255 – alpha) / 255 // Profiled = ~15-24% of time RedTarget := SourceRed*Alpha shr 8 + BkGndRed*(255-Alpha) shr 8; GreenTarget := SourceGreen*Alpha shr 8 + BkGndGreen*(255-Alpha) shr 8; BlueTarget := SourceBlue*Alpha shr 8 + BkGndBlue*(255-Alpha) shr 8; end else begin // skip non-blended pixels RedTarget := SourceRed; GreenTarget := SourceGreen; BlueTarget := SourceBlue; end; // Create the RGB DWORD color ; Profiled = ~8%-9%% of time // Mask out all but the alpha channel then build the backwards stored RGB preserving the alpha channel bits PDWORD(PPixelTarget)^ := ((BlueTarget) or (GreenTarget shl 8)or (RedTarget shl 16)); Inc(PPixelImage32, PixelDeltaImage32); Inc(PPixelTarget, PixelDeltaTarget); Inc(PPixelMask, PixelDeltaMask); end; Inc(PLineImage32, LineDeltaImage32); Inc(PLineTarget, LineDeltaTarget); Inc(PLineMask, LineDeltaMask); end; OutImage.Assign(Target); finally FreeAndNil(Target); FreeAndNil(Mask); end; end else OutImage.Assign(Image32) end; procedure AlphaBlend(Source, Destination: HDC; R: TRect; Target: TPoint; Mode: TCommonBlendMode; ConstantAlpha, Bias: Integer); // NOTE::::::::::::: // AlphaBlend does not respect any clipping in the DC!!!!!!! // // Optimized alpha blend procedure using MMX instructions to perform as quick as possible. // For this procedure to work properly it is important that both source and target bitmap use the 32 bit color format. // R describes the source rectangle to work on. // Target is the place (upper left corner) in the target bitmap where to blend to. Note that source width + X offset // must be less or equal to the target width. Similar for the height. // If Mode is bmConstantAlpha then the blend operation uses the given ConstantAlpha value for all pixels. // If Mode is bmPerPixelAlpha then each pixel is blended using its individual alpha value (the alpha value of the source). // If Mode is bmMasterAlpha then each pixel is blended using its individual alpha value multiplied by ConstantAlpha. // If Mode is bmConstantAlphaAndColor then each destination pixel is blended using ConstantAlpha but also a constant // color which will be obtained from Bias. In this case no offset value is added, otherwise Bias is used as offset. // Blending of a color into target only (bmConstantAlphaAndColor) ignores Source (the DC) and Target (the position). // CAUTION: This procedure does not check whether MMX instructions are actually available! Call it only if MMX is really // usable. var Y: Integer; SourceRun, TargetRun: PByte; SourceBits, DestBits: Pointer; SourceWidth, SourceHeight, DestWidth, DestHeight: Integer; begin if not IsRectEmpty(R) then begin // Note: it is tempting to optimize the special cases for constant alpha 0 and 255 by just ignoring soure // (alpha = 0) or simply do a blit (alpha = 255). But this does not take the bias into account. case Mode of cbmConstantAlpha: begin // Get a pointer to the bitmap bits for the source and target device contexts. // Note: this supposes that both contexts do actually have bitmaps assigned! SourceBits := GetBitmapBitsFromDeviceContext(Source, SourceWidth, SourceHeight); DestBits := GetBitmapBitsFromDeviceContext(Destination, DestWidth, DestHeight); if Assigned(SourceBits) and Assigned(DestBits) then begin for Y := 0 to R.Bottom - R.Top - 1 do begin SourceRun := CalculateScanline(SourceBits, SourceWidth, SourceHeight, Y + R.Top); Inc(SourceRun, 4 * R.Left); TargetRun := CalculateScanline(DestBits, DestWidth, DestHeight, Y + Target.Y); Inc(TargetRun, 4 * Target.X); AlphaBlendLineConstant(SourceRun, TargetRun, R.Right - R.Left, ConstantAlpha, Bias); end; end; EMMS; end; cbmPerPixelAlpha: begin SourceBits := GetBitmapBitsFromDeviceContext(Source, SourceWidth, SourceHeight); DestBits := GetBitmapBitsFromDeviceContext(Destination, DestWidth, DestHeight); if Assigned(SourceBits) and Assigned(DestBits) then begin for Y := 0 to R.Bottom - R.Top - 1 do begin SourceRun := CalculateScanline(SourceBits, SourceWidth, SourceHeight, Y + R.Top); Inc(SourceRun, 4 * R.Left); TargetRun := CalculateScanline(DestBits, DestWidth, DestHeight, Y + Target.Y); Inc(TargetRun, 4 * Target.X); AlphaBlendLinePerPixel(SourceRun, TargetRun, R.Right - R.Left, Bias); end; end; EMMS; end; cbmMasterAlpha: begin SourceBits := GetBitmapBitsFromDeviceContext(Source, SourceWidth, SourceHeight); DestBits := GetBitmapBitsFromDeviceContext(Destination, DestWidth, DestHeight); if Assigned(SourceBits) and Assigned(DestBits) then begin for Y := 0 to R.Bottom - R.Top - 1 do begin SourceRun := CalculateScanline(SourceBits, SourceWidth, SourceHeight, Y + R.Top); Inc(SourceRun, 4 * Target.X); TargetRun := CalculateScanline(DestBits, DestWidth, DestHeight, Y + Target.Y); AlphaBlendLineMaster(SourceRun, TargetRun, R.Right - R.Left, ConstantAlpha, Bias); end; end; EMMS; end; cbmConstantAlphaAndColor: begin // Source is ignore since there is a constant color value. DestBits := GetBitmapBitsFromDeviceContext(Destination, DestWidth, DestHeight); if Assigned(DestBits) then begin for Y := 0 to R.Bottom - R.Top - 1 do begin TargetRun := CalculateScanline(DestBits, DestWidth, DestHeight, Y + R.Top); Inc(TargetRun, 4 * R.Left); AlphaBlendLineMasterAndColor(TargetRun, R.Right - R.Left, ConstantAlpha, Bias); end; end; EMMS; end; end; end; end; function DrawTextWEx(DC: HDC; Text: WideString; var lpRect: TRect; Flags: TCommonDrawTextWFlags; MaxLineCount: Integer): Integer; // Creates and extented version of DrawTextW that works in Win9x as well as // NT. If MaxLineCount is -1 then the line count will depend on the Text. All // lines that are extracted from the text are drawn or calcuated in the rectangle // // The result is the number of lines actually drawn, note if the CalcRect flags are // used the result will be the number of lines that would be drawn var TextMetrics: TTextMetric; Size: TSize; TextPosX, TextPosY, i, NewLineTop: Integer; TextOutFlags: Longword; LineRect, OldlpRect: TRect; Buffer: TCommonWideCharArray; BufferIndex: PWideChar; ShortText: WideString; VOffset, SplitCount: Integer; begin OldlpRect := lpRect; GetTextMetrics(DC, TextMetrics); TextOutFlags := 0; if dtRTLReading in Flags then TextOutFlags := TextOutFlags or ETO_RTLREADING; if not (dtNoClip in Flags) then TextOutFlags := TextOutFlags or ETO_CLIPPED; if dtSingleLine in Flags then begin Result := 1; // Easy one! // Set up the LineRect in the Vertical Direction // Default to the top LineRect := Rect(lpRect.Left, lpRect.Top, lpRect.Right, lpRect.Top + TextMetrics.tmHeight); if dtVCenter in Flags then OffsetRect(LineRect, 0, (RectHeight(lpRect) - RectHeight(LineRect)) div 2) else if dtBottom in Flags then OffsetRect(LineRect, 0, RectHeight(lpRect) - RectHeight(LineRect)); TextPosX := LineRect.Left; TextPosY := LineRect.Top; if dtEndEllipsis in Flags then Text := ShortenTextW(DC, Text, RectWidth(LineRect)); GetTextExtentPoint32W(DC, PWideChar(Text), Length(Text), Size); if Flags * [dtCenter, dtRight] <> [] then begin if dtCenter in Flags then TextPosX := TextPosX + (RectWidth(LineRect) - Size.cx) div 2 else TextPosX := LineRect.Right - Size.cx end; { if dtCenter in Flags then SetTextAlign(DC, TA_CENTER) else if dtLeft in Flags then SetTextAlign(DC, TA_LEFT) else if dtRight in Flags then SetTextAlign(DC, TA_RIGHT); } // See if the caller wants to only calculate the rectangle for the text if dtCalcRect in Flags then begin // Assume that the text will fit in the calulated line/rect lpRect.Left := TextPosX; lpRect.Top := TextPosY; lpRect.Bottom := LineRect.Bottom; lpRect.Right := lpRect.Left + Size.cx; // If it does not then we have to do some adjusting if Size.cx > RectWidth(OldlpRect) then begin if dtCalcRectAlign in Flags then begin lpRect.Left := OldlpRect.Left; lpRect.Right := OldlpRect.Right; end; if dtCalcRectAdjR in Flags then lpRect.Right := lpRect.Left + Size.cx; end end else ExtTextOutW(DC, TextPosX, TextPosY, TextOutFlags, @LineRect, PWideChar(Text), Length(Text), nil); end else begin // It is multi-line SplitCount := SplitTextW(DC, Text, lpRect.Right-lpRect.Left, Buffer, MaxLineCount); i := 0; if Length(Buffer) > 0 then begin // We call ourselves recursivly one line at a time to draw the multi line text Include(Flags, dtSingleLine); BufferIndex := @Buffer[0]; // Calculate where the center of the text block is with respect to the // rectangle { if dtVCenter in Flags then begin if (SplitCount > MaxLineCount) and (MaxLineCount > -1) then VOffset := (RectHeight(OldlpRect) - (TextMetrics.tmHeight * MaxLineCount)) div 2 else VOffset := (RectHeight(OldlpRect) - (TextMetrics.tmHeight * SplitCount)) div 2; if VOffset < 0 then VOffset := 0; end else if dtBottom in Flags then begin VOffset := (RectHeight(OldlpRect) - (TextMetrics.tmHeight * MaxLineCount)) end else VOffset := 0; } // Fix for multitext vertical alignment from Solerman Kaplon 11.9.04 if (dtVCenter in Flags) or (dtBottom in Flags) then begin if (SplitCount > MaxLineCount) and (MaxLineCount > -1) then VOffset := (RectHeight(OldlpRect) - (TextMetrics.tmHeight * MaxLineCount)) else VOffset := (RectHeight(OldlpRect) - (TextMetrics.tmHeight * SplitCount)); if VOffset < 0 then VOffset := 0 else if dtVCenter in Flags then VOffset := VOffset shr 1; end else VOffset := 0; while ((i < MaxLineCount) or (MaxLineCount < 0)) and (BufferIndex^ <> WideNull) do begin // Calculate where the top of a single line of text starts NewLineTop := OldlpRect.Top + (i * TextMetrics.tmHeight) + VOffset; LineRect := Rect(OldlpRect.Left, NewLineTop, OldlpRect.Right, NewLineTop + TextMetrics.tmHeight); if (dtEndEllipsis in Flags) {and not(dtCalcRect in Flags)} then begin ShortText := ShortenTextW(DC, WideString(BufferIndex), RectWidth(OldlpRect)); DrawTextWEx(DC, ShortText, LineRect, Flags, MaxLineCount); end else DrawTextWEx(DC, WideString(BufferIndex), LineRect, Flags, MaxLineCount); if dtCalcRect in Flags then begin if i = 0 then lpRect := LineRect else UnionRect(lpRect, lpRect, LineRect); end; Inc(BufferIndex, lStrLenW(BufferIndex) + 1); Inc(i) end; if (SplitCount = 0) and (dtCalcRect in Flags) then begin if dtCalcRectAdjR in Flags then begin lpRect.Right := lpRect.Left; lpRect.Bottom := lpRect.Top + TextMetrics.tmHeight end else begin lpRect.Bottom := lpRect.Top; lpREct.Right := lpRect.Left + TextMetrics.tmAveCharWidth end end; if SplitCount > MaxLineCount then Result := MaxLineCount else Result := SplitCount end else Result := 0; end end; // Helpers to create a callback function out of a object method { ----------------------------------------------------------------------------- } { This is a piece of magic by Jeroen Mineur. Allows a class method to be used } { as a callback. Create a stub using CreateStub with the instance of the object } { the callback should call as the first parameter and the method as the second } { parameter, ie @TForm1.MyCallback or declare a type of object for the callback } { method and then use a variable of that type and set the variable to the } { method and pass it: } { } { type } { TEnumWindowsFunc = function (AHandle: hWnd; Param: lParam): BOOL of object; stdcall; } { } { TForm1 = class(TForm) } { private } { function EnumWindowsProc(AHandle: hWnd; Param: lParam): BOOL; stdcall; } { end; } { } { var } { MyFunc: TEnumWindowsFunc; } { Stub: pointer; } { begin } { MyFunct := EnumWindowsProc; } { Stub := CreateStub(Self, MyFunct); } { .... } { or } { } { var } { Stub: pointer; } { begin } { MyFunct := EnumWindowsProc; } { Stub := CreateStub(Self, TForm1.EnumWindowsProc); } { .... } { Now Stub can be passed as the callback pointer to any windows API } { Don't forget to call Dispose Stub when not needed } { ----------------------------------------------------------------------------- } const AsmPopEDX = $5A; AsmMovEAX = $B8; AsmPushEAX = $50; AsmPushEDX = $52; AsmJmpShort = $E9; type TStub = packed record PopEDX: Byte; MovEAX: Byte; SelfPointer: Pointer; PushEAX: Byte; PushEDX: Byte; JmpShort: Byte; Displacement: Integer; end; { ----------------------------------------------------------------------------- } function CreateStub(ObjectPtr: Pointer; MethodPtr: Pointer): Pointer; var Stub: ^TStub; begin // Allocate memory for the stub // 1/10/04 Support for 64 bit, executable code must be in virtual space Stub := VirtualAlloc(nil, SizeOf(TStub), MEM_COMMIT, PAGE_EXECUTE_READWRITE); // Pop the return address off the stack Stub^.PopEDX := AsmPopEDX; // Push the object pointer on the stack Stub^.MovEAX := AsmMovEAX; Stub^.SelfPointer := ObjectPtr; Stub^.PushEAX := AsmPushEAX; // Push the return address back on the stack Stub^.PushEDX := AsmPushEDX; // Jump to the 'real' procedure, the method. Stub^.JmpShort := AsmJmpShort; Stub^.Displacement := (Integer(MethodPtr) - Integer(@(Stub^.JmpShort))) - (SizeOf(Stub^.JmpShort) + SizeOf(Stub^.Displacement)); // Return a pointer to the stub Result := Stub; end; { ----------------------------------------------------------------------------- } { ----------------------------------------------------------------------------- } procedure DisposeStub(Stub: Pointer); begin // 1/10/04 Support for 64 bit, executable code must be in virtual space VirtualFree(Stub, SizeOf(TStub),MEM_DECOMMIT); end; { ----------------------------------------------------------------------------- } {$IFNDEF COMPILER_5_UP} function Supports(const Instance: IUnknown; const IID: TGUID; out Intf): Boolean; begin Result := (Instance <> nil) and (Instance.QueryInterface(IID, Intf) = 0); end; function Supports(const Instance: TObject; const IID: TGUID; out Intf): Boolean; overload; begin Result := False; Pointer( Intf) := nil; if Assigned(Instance) then Result := Instance.GetInterface(IID, Intf) end; procedure FreeAndNil(var Obj); var Temp: TObject; begin Temp := TObject(Obj); Pointer(Obj) := nil; Temp.Free; end; {$ENDIF} {$IFNDEF COMPILER_6_UP} function Supports(const Instance: IUnknown; const IID: TGUID): Boolean; var Temp: IUnknown; begin {$IFNDEF COMPILER_5_UP} Result := Supports(Instance, IID, Temp); {$ELSE} Result := SysUtils.Supports(Instance, IID, Temp) {$ENDIF} end; procedure ClearMenuItems(Menu: TMenu); var I: Integer; begin for I := Menu.Items.Count - 1 downto 0 do Menu.Items[I].Free; end; {$ENDIF} procedure CreateProcessMP(ExeFile, Parameters, InitalDir: WideString); var pi: TProcessInformation; siW: TStartupInfoW; siA: _STARTUPINFOA; wA, wB, wC: PWideChar; aA, aB, aC: PChar; begin FillChar(pi, SizeOf(pi), #0); if Assigned(CreateProcessW_MP) then begin FillChar(siW, SizeOf(siW), #0); wA := nil; wB := nil; wC := nil; if ExeFile <> '' then wA := PWideChar(ExeFile); if Parameters <> '' then wB := PWideChar(Parameters); if InitalDir <> '' then wC := PWideChar(InitalDir); CreateProcessW_MP( wA, // path to the executable file: wB, nil, nil, False, NORMAL_PRIORITY_CLASS, nil, wC, siW, pi ); end else begin FillChar(siA, SizeOf(siA), #0); aA := nil; aB := nil; aC := nil; if ExeFile <> '' then aA := PChar( string(ExeFile)); if Parameters <> '' then aB := PChar( string(Parameters)); if InitalDir <> '' then aC := PChar( string(InitalDir)); CreateProcessA( aA, aB, // path to the executable file: nil, nil, False, NORMAL_PRIORITY_CLASS, nil, aC, siA, pi ); end; if pi.hProcess <> 0 then CloseHandle(pi.hProcess); if pi.hThread <> 0 then CloseHandle(pi.hThread) end; function DiffRectHorz(Rect1, Rect2: TRect): TRect; // Returns the "difference" rectangle of the passed rects in the Horz direction. // Assumes that one corner is common between the two rects begin Rect1 := ProperRect(Rect1); Rect2 := ProperRect(Rect2); // Make sure we contain every thing horizontally Result.Left := Min(Rect1.Left, Rect2.Left); Result.Right := Max(Rect1.Right, Rect1.Right); // Now find the difference rect height if Rect1.Top = Rect2.Top then begin // The tops are equal so it must be the bottom that contains the difference Result.Bottom := Max(Rect1.Bottom, Rect2.Bottom); Result.Top := Min(Rect1.Bottom, Rect2.Bottom); end else begin // The bottoms are equal so it must be the tops that contains the difference Result.Bottom := Max(Rect1.Top, Rect2.Top); Result.Top := Min(Rect1.Top, Rect2.Top); end end; function DiffRectVert(Rect1, Rect2: TRect): TRect; // Returns the "difference" rectangle of the passed rects in the Vert direction. // Assumes that one corner is common between the two rects begin Rect1 := ProperRect(Rect1); Rect2 := ProperRect(Rect2); // Make sure we contain every thing vertically Result.Top := Min(Rect1.Top, Rect2.Bottom); Result.Bottom := Max(Rect1.Top, Rect1.Bottom); // Now find the difference rect width if Rect1.Left = Rect2.Left then begin // The tops are equal so it must be the bottom that contains the difference Result.Right := Max(Rect1.Right, Rect2.Right); Result.Left := Min(Rect1.Right, Rect2.Right); end else begin // The bottoms are equal so it must be the tops that contains the difference Result.Right := Max(Rect1.Left, Rect2.Left); Result.Left := Min(Rect1.Left, Rect2.Left); end end; function AbsRect(ARect: TRect): TRect; // Makes all coodinates positive begin Result := ARect; if Result.Left < 0 then Result.Left := 0; if Result.Top < 0 then Result.Top := 0; if Result.Right < 0 then Result.Right := 0; if Result.Bottom < 0 then Result.Bottom := 0; end; function CenterRectInRect(OuterRect, InnerRect: TRect): TRect; begin if RectWidth(InnerRect) > RectWidth(OuterRect) then begin // If the inner rect is wider than the result is the outer rect x bounds Result.Left := OuterRect.Left; Result.Right := OuterRect.Right; end else begin // If not then center the inner rectangle in the outer in the x direction Result.Left := OuterRect.Left; Result.Right := Result.Left + RectWidth(InnerRect); OffsetRect(Result, (RectWidth(OuterRect) - RectWidth(InnerRect)) div 2, 0); end; if RectHeight(InnerRect) > RectHeight(OuterRect) then begin // If the inner rect is wider than the result is the outer rect y bounds Result.Top := OuterRect.Top; Result.Bottom := OuterRect.Bottom; end else begin // If not then center the inner rectangle in the outer in the y direction Result.Top := OuterRect.Top; Result.Bottom := Result.Top + RectHeight(InnerRect); OffsetRect(Result, 0, (RectHeight(OuterRect) - RectHeight(InnerRect)) div 2); end; end; function CenterRectHorz(OuterRect, InnerRect: TRect): TRect; begin if RectWidth(InnerRect) > RectWidth(OuterRect) then begin // If the inner rect is wider than the result is the outer rect x bounds Result.Left := OuterRect.Left; Result.Right := OuterRect.Right; end else begin // If not then center the inner rectangle in the outer in the x direction Result.Left := OuterRect.Left; Result.Right := Result.Left + RectWidth(InnerRect); OffsetRect(Result, (RectWidth(OuterRect) - RectWidth(InnerRect)) div 2, 0); end; end; function CenterRectVert(OuterRect, InnerRect: TRect): TRect; begin if RectHeight(InnerRect) > RectHeight(OuterRect) then begin // If the inner rect is wider than the result is the outer rect y bounds Result.Top := OuterRect.Top; Result.Bottom := OuterRect.Bottom; end else begin // If not then center the inner rectangle in the outer in the y direction Result.Top := OuterRect.Top; Result.Bottom := Result.Top + RectHeight(InnerRect); OffsetRect(Result, 0, (RectHeight(OuterRect) - RectHeight(InnerRect)) div 2); end; end; function CommonSupports(const Instance: IUnknown; const IID: TGUID; out Intf): Boolean; overload; begin Result := (Instance <> nil) and (Instance.QueryInterface(IID, Intf) = S_OK); end; function CommonSupports(const Instance: TObject; const IID: TGUID; out Intf): Boolean; overload; var LUnknown: IUnknown; begin Result := (Instance <> nil) and (Instance.GetInterface(IID, Intf) or (Instance.GetInterface(IUnknown, LUnknown) and Supports(LUnknown, IID, Intf))); end; function CommonSupports(const Instance: IUnknown; const IID: TGUID): Boolean; overload; var Temp: IUnknown; begin Result := Supports(Instance, IID, Temp); end; function CommonSupports(const Instance: TObject; const IID: TGUID): Boolean; overload; var Temp: IUnknown; begin Result := Supports(Instance, IID, Temp); end; procedure CopyToNullBufferA(S: WideString; Buffer: PChar; CharCount: Cardinal); var ANSI: string; begin ANSI := S; FillChar(Buffer^, CharCount, #0); if Length(ANSI) > 0 then begin if Cardinal(Length(ANSI)) + 1 > CharCount then CharCount := CharCount - 1 // Leave room for the null else CharCount := Length(ANSI); MoveMemory(Buffer, PChar(ANSI), CharCount); end end; procedure CopyToNullBufferW(S: WideString; Buffer: PWideChar; CharCount: Cardinal); begin FillChar(Buffer^, CharCount * 2, #0); if Length(S) > 0 then begin if Cardinal(Length(S)) + 2 > CharCount then CharCount := CharCount - 2 // Leave room for the null else CharCount := Length(S); MoveMemory(Buffer, PWideChar(S), CharCount * 2); end end; procedure MinMax(var A, B: Integer); // Makes sure that A < B var Temp: Integer; begin if A > B then begin Temp := A; A := B; B := Temp end end; function IsRectProper(Rect: TRect): Boolean; begin Result := (Rect.Right >= Rect.Left) and (Rect.Bottom >= Rect.Top) end; function AddCommas(NumberString: WideString): WideString; var // i: integer; BufferA: array[0..128] of Char; BufferW: array[0..128] of WideChar; begin // Make the number format based on the local not the US 3 digit comma format if Assigned(GetNumberFormatW_MP) then begin GetNumberFormatW_MP(LOCALE_USER_DEFAULT, 0, PWideChar(NumberString), nil, BufferW, SizeOf(BufferW)); Result := BufferW; end else begin GetNumberFormatA(LOCALE_USER_DEFAULT, 0, PChar(string(NumberString)), nil, BufferA, SizeOf(BufferA)); Result := BufferA end; { Trimming white space in Unicode is tough don't pass any } { i := Length(NumberString) mod 3; if i = 0 then i := 3; while i < Length(NumberString) do begin InsertW(ThousandSeparator, NumberString, i); Inc(i, 4); end; Result := NumberString } end; function CalcuateFolderSize(FolderPath: WideString; Recurse: Boolean): Int64; // Recursivly gets the size of the folder and subfolders var S: string; FreeSpaceAvailable, TotalSpace: Int64; SectorsPerCluster, BytesPerSector, FreeClusters, TotalClusters: DWORD; begin Result := 0; if Recurse and WideIsDrive(FolderPath) then begin if IsUnicode and Assigned(GetDiskFreeSpaceExW_MP) then begin if GetDiskFreeSpaceExW_MP(PWideChar(FolderPath), FreeSpaceAvailable, TotalSpace, nil) then Result := TotalSpace - FreeSpaceAvailable end else if not IsWin95_SR1 and Assigned(GetDiskFreeSpaceExA_MP) then begin S := FolderPath; if GetDiskFreeSpaceExA_MP(PChar(S), FreeSpaceAvailable, TotalSpace, nil) then Result := TotalSpace - FreeSpaceAvailable; end else begin GetDiskFreeSpaceA(PChar( S), SectorsPerCluster, BytesPerSector, FreeClusters, TotalClusters); Result := SectorsPerCluster * BytesPerSector * TotalClusters end; end else begin SumFolderAbort := False; SumFolder(FolderPath, Recurse, Result); end end; function GetMyDocumentsVirtualFolder: PItemIDList; const MYCOMPUTER_GUID = WideString('::{450d8fba-ad25-11d0-98a8-0800361b1103}'); var dwAttributes, pchEaten: ULONG; Desktop: IShellFolder; begin Result := nil; dwAttributes := 0; SHGetDesktopFolder(Desktop); pchEaten := Length(MYCOMPUTER_GUID); if not Succeeded(Desktop.ParseDisplayName(0, nil, PWideChar(MYCOMPUTER_GUID), pchEaten, Result, dwAttributes)) then Result := nil end; procedure WideInsert(Source: WideString; var S: WideString; Index: Integer); { It appears there is a WideString Insert in the VCL already but since mine } { looks better and is simpler and I spent my time I will use mine } { _WStrInsert in System through compiler magic. } var OriginalLen: integer; begin if (Index < Length(S) + 1) and (Index > - 1) then begin OriginalLen := Length(S); SetLength(S, Length(Source) + Length(S)); { We are correct up to Index } { Slide to end of new string leaving space for insert } Move(S[Index + 1], S[Index + 1 + Length(Source)], (OriginalLen - Index) * 2); Move(Source[1], S[Index + 1], Length(Source) * 2); end end; function WideGetCurrentDir: WideString; var BufferW: array[0..MAX_PATH] of Widechar; BufferA: array[0..MAX_PATH] of Char; begin if Assigned(GetCurrentDirectoryW_MP) then begin if GetCurrentDirectoryW_MP(MAX_PATH, BufferW) > 0 then Result := BufferW; end else begin if GetCurrentDirectoryA(MAX_PATH, BufferA) > 0 then Result := BufferA; end end; function WideGetTempDir: WideString; var BufferW: array[0..MAX_PATH] of Widechar; BufferA: array[0..MAX_PATH] of Char; begin if Assigned(GetTempPathW_MP) then begin if GetTempPathW_MP(MAX_PATH, BufferW) > 0 then Result := BufferW; end else begin if GetTempPathA(MAX_PATH, BufferA) > 0 then Result := BufferA; end end; function WideIncludeTrailingBackslash(Path: WideString): WideString; begin {$IFDEF TNTSUPPORT} Result := TntSysUtils.WideIncludeTrailingBackslash(Path); {$ELSE} {$IFDEF COMPILER_6_UP} {$WARN SYMBOL_PLATFORM OFF} {$ENDIF} Result := IncludeTrailingBackslash(Path); {$IFDEF COMPILER_6_UP} {$WARN SYMBOL_PLATFORM ON} {$ENDIF} {$ENDIF} end; function WideIncrementalSearch(CompareStr, Mask: WideString): Integer; begin SetLength(CompareStr, Length(Mask)); if IsUnicode then Result := lstrcmpiW(PWideChar(Mask), PWideChar(CompareStr)) else Result := lstrcmpi(PChar(string(Mask)), PChar(string(CompareStr))); end; function WideIntToStr(Value: integer): WideString; { Need to find a way to do this in Unicode. } begin Result := IntToStr(Value); end; function WideIsDrive(Drive: WideString): Boolean; begin if Length(Drive) = 3 then Result := (LowerCase(Drive[1]) >= 'a') and (LowerCase(Drive[1]) <= 'z') and (Drive[2] = ':') and (Drive[3] = '\') else if Length(Drive) = 2 then Result := (LowerCase(Drive[1]) >= 'a') and (LowerCase(Drive[1]) <= 'z') and (Drive[2] = ':') else Result := False end; function WideIsFloppy(FileFolder: WideString): boolean; begin if Length(FileFolder) > 0 then Result := WideIsDrive(FileFolder) and (Char(FileFolder[1]) in ['A', 'a', 'B', 'b']) else Result := False end; function IsAnyMouseButtonDown: Boolean; begin Result := not(((GetAsyncKeyState(VK_LBUTTON) and $8000) = 0) and ((GetAsyncKeyState(VK_RBUTTON) and $8000) = 0) and ((GetAsyncKeyState(VK_MBUTTON) and $8000) = 0)) end; function IsFTPPath(Path: WideString): Boolean; begin if Length(Path) > 3 then begin Path := UpperCase(Path); Result := (Path[1] = 'F') and (Path[2] = 'T') and (Path[3] = 'P') end else Result := False end; function WideNewFolderName(ParentFolder: WideString; SuggestedFolderName: WideString = ''): WideString; var i: integer; TempFoldername: String; begin ParentFolder := WideStripTrailingBackslash(ParentFolder, True); // Strip even if a root folder i := 1; if SuggestedFolderName = '' then Begin Result := ParentFolder + '\' + STR_NEWFOLDER; TempFoldername := STR_NEWFOLDER; end else Begin Result := ParentFolder + '\' + SuggestedFolderName; Tempfoldername := SuggestedFolderName; End; while DirExistsW(PWideChar(Result)) and (i <= High(WORD)) do begin Result := ParentFolder + '\' + Tempfoldername + ' (' + IntToStr(i) + ')'; Inc(i); end; if i > High(WORD) then Result := ''; end; function WidePathMatchSpec(Path, Mask: WideString): Boolean; begin if Assigned(PathMatchSpecW_MP) then Result := PathMatchSpecW_MP(PWideChar(Path), PWideChar( Mask)) else if Assigned(PathMatchSpecA_MP) then Result := PathMatchSpecA_MP(PChar( string(Path)), PChar( string(Mask))) else Result := False end; function WidePathMatchSpecExists: Boolean; begin Result := Assigned(PathMatchSpecW_MP) or Assigned(PathMatchSpecA_MP) end; function WideIsPathDelimiter(const S: WideString; Index: Integer): Boolean; begin Result := (Index > 0) and (Index <= Length(S)) and (S[Index] = '\'); end; function IsTextTrueType(DC: HDC): Boolean; var TextMetrics: TTextMetric; begin GetTextMetrics(DC, TextMetrics); Result := TextMetrics.tmPitchAndFamily and TMPF_TRUETYPE <> 0 end; function IsTextTrueType(Canvas: TCanvas): Boolean; begin Result := IsTextTrueType(Canvas.Handle); end; function IsUNCPath(const Path: WideString): Boolean; begin Result := ((Path[1] = '\') and (Path[2] = '\')) and (DirExistsW(PWideChar(Path)) or FileExistsW(Path)) end; function StrRetToStr(StrRet: TStrRet; APIDL: PItemIDList): WideString; { Extracts the string from the StrRet structure. } var P: PChar; // S: string; begin case StrRet.uType of STRRET_CSTR: begin SetString(Result, StrRet.cStr, lStrLen(StrRet.cStr)); // Result := S end; STRRET_OFFSET: begin if Assigned(APIDL) then begin {$R-} P := PChar(@(APIDL).mkid.abID[StrRet.uOffset - SizeOf(APIDL.mkid.cb)]); {$R+} SetString(Result, P, StrLen(P)); // Result := S; end else Result := ''; end; STRRET_WSTR: begin Result := StrRet.pOleStr; if Assigned(StrRet.pOleStr) then PIDLMgr.FreeOLEStr(StrRet.pOLEStr); end; end; end; function SystemDirectory: WideString; var Len: integer; S: string; begin Result := ''; if Win32Platform = VER_PLATFORM_WIN32_NT then Len := GetSystemDirectoryW_MP(PWideChar(Result), 0) else Len := GetSystemDirectoryA(PChar(S), 0); if Len > 0 then begin if Win32Platform = VER_PLATFORM_WIN32_NT then begin SetLength(Result, Len - 1); GetSystemDirectoryW_MP(PWideChar(Result), Len); end else begin SetLength(S, Len - 1); GetSystemDirectoryA(PChar(S), Len); Result := S end end end; function SysMenuFont: HFONT; var MetricsA: TNonClientMetricsA; MetricsW: TNonClientMetricsW; begin if IsUnicode then begin FillChar(MetricsW, SizeOf(MetricsW), #0); MetricsW.cbSize := SizeOf(MetricsW); SystemParametersInfoW_MP(SPI_GETNONCLIENTMETRICS, Sizeof(MetricsW), @MetricsW, 0); Result := CreateFontIndirectW_MP(MetricsW.lfMenuFont); end else begin FillChar(MetricsA, SizeOf(MetricsA), #0); MetricsA.cbSize := SizeOf(MetricsA); SystemParametersInfoA(SPI_GETNONCLIENTMETRICS, Sizeof(MetricsA), @MetricsA, 0); Result := CreateFontIndirectA(MetricsA.lfMenuFont); end end; function SysMenuHeight: Integer; var MetricsA: TNonClientMetricsA; MetricsW: TNonClientMetricsW; begin if IsUnicode then begin FillChar(MetricsW, SizeOf(MetricsW), #0); MetricsW.cbSize := SizeOf(MetricsW); SystemParametersInfoW_MP(SPI_GETNONCLIENTMETRICS, Sizeof(MetricsW), @MetricsW, 0); Result := MetricsW.iMenuHeight end else begin FillChar(MetricsA, SizeOf(MetricsA), #0); MetricsA.cbSize := SizeOf(MetricsA); SystemParametersInfoA(SPI_GETNONCLIENTMETRICS, Sizeof(MetricsA), @MetricsA, 0); Result := MetricsA.iMenuHeight end end; function TextExtentW(Text: WideString; Font: TFont): TSize; var Canvas: TCanvas; begin FillChar(Result, SizeOf(Result), #0); if Text <> '' then begin Canvas := TCanvas.Create; try Canvas.Handle := GetDC(0); Canvas.Lock; Canvas.Font.Assign(Font); Result := InternalTextExtentW(PWideChar(Text), Canvas.Handle); finally if Assigned(Canvas) and (Canvas.Handle <> 0) then ReleaseDC(0, Canvas.Handle); Canvas.Unlock; Canvas.Free end end end; function TextExtentW(Text: WideString; Canvas: TCanvas): TSize; begin FillChar(Result, SizeOf(Result), #0); if Assigned(Canvas) and (Text <> '') then begin Canvas.Lock; Result := InternalTextExtentW(PWideChar(Text), Canvas.Handle); Canvas.Unlock; end; end; function TextExtentW(Text: PWideChar; Canvas: TCanvas): TSize; begin FillChar(Result, SizeOf(Result), #0); if Assigned(Canvas) and (Assigned(Text)) then begin Canvas.Lock; Result := InternalTextExtentW(Text, Canvas.Handle); Canvas.Unlock; end; end; function TextExtentW(Text: PWideChar; DC: hDC): TSize; begin FillChar(Result, SizeOf(Result), #0); if (DC <> 0) and (Assigned(Text)) then Result := InternalTextExtentW(Text, DC); end; type TABCArray = array of TABC; function TextTrueExtentsW(Text: WideString; DC: HDC): TSize; var ABC: TABC; TextMetrics: TTextMetric; S: string; i: integer; begin // Get the Height at least GetTextExtentPoint32W(DC, PWideChar(Text), Length(Text), Result); GetTextMetrics(DC, TextMetrics); if TextMetrics.tmPitchAndFamily and TMPF_TRUETYPE <> 0 then begin Result.cx := 0; // Is TrueType if Win32Platform = VER_PLATFORM_WIN32_NT then begin for i := 1 to Length(Text) do begin GetCharABCWidthsW_MP(DC, Ord(Text[i]), Ord(Text[i]), ABC); Result.cx := Result.cx + ABC.abcA + integer(ABC.abcB) + ABC.abcC; end end else begin S := Text; for i := 1 to Length(S) do begin GetCharABCWidthsA(DC, Ord(S[i]), Ord(S[i]), ABC); Result.cx := Result.cx + ABC.abcA + integer(ABC.abcB) + ABC.abcC; end end; end end; function UniqueFileName(const AFilePath: WideString): WideString; { Creates a unique file name in based on other files in the passed path } var i: integer; WP: PWideChar; begin Result := AFilePath; i := 2; while FileExistsW(Result) and (i < 20) do begin Result := AFilePath; WP := WideStrRScan(PWideChar( Result), '.'); if Assigned(WP) then WideInsert( ' (' + WideIntToStr(i) + ')', Result, PWideChar(WP) - PWideChar(Result)) else begin Result := ''; Break; end; Inc(i) end; end; function TNTConditionallyDefined: Boolean; begin {$IFDEF TNTSUPPORT} Result := True; {$ELSE} Result := False; {$ENDIF} end; function UnicodeStringLists: Boolean; begin {$IFDEF TNTSUPPORT} Result := True; {$ELSE} Result := False; {$ENDIF} end; function UniqueDirName(const ADirPath: WideString): WideString; var i: integer; begin Result := ADirPath; i := 2; while DirExistsW(PWideChar(Result)) and (i < 20) do begin Result := ADirPath; WideInsert( ' (' + WideIntToStr(i) + ')', Result, Length(Result)); Inc(i) end; end; function WideStripExt(AFile: WideString): WideString; { Strips the extenstion off a file name } var i: integer; Done: Boolean; begin i := Length(AFile); Done := False; Result := AFile; while (i > 0) and not Done do begin if AFile[i] = '.' then begin Done := True; SetLength(Result, i - 1); end; Dec(i); end; end; function WideStripRemoteComputer(const UNCPath: WideString): WideString; // Strips the \\RemoteComputer\ part of an UNC path var Head: PWideChar; begin Result := ''; if IsUNCPath(UNCPath) then begin Result := ''; if IsUNCPath(UNCPath) then begin Result := UNCPath; Head := @Result[1]; Head := Head + 2; // Skip past the '\\' Head := WideStrScan(Head, WideChar('\')); if Assigned(Head) then begin Head := Head + 1; Move(Head[0], Result[1], (lstrlenW(Head) + 1) * 2); end; SetLength(Result, lstrlenW(PWideChar(Result))); end; end; end; function WideStripTrailingBackslash(const S: WideString; Force: Boolean = False): WideString; begin Result := S; if Result <> '' then begin // Works with FilePaths and FTP Paths if Result[ Length(Result)] in [WideChar('/'), WideChar('\')] then if not WideIsDrive(Result) or Force then // Don't strip off if is a root drive SetLength(Result, Length(Result) - 1); end; end; function WideStripLeadingBackslash(const S: WideString): WideString; begin Result := S; if Result <> '' then begin if (S[1] = '\') and (Length(S) > 1) then Result := PWideChar( @S[2]) else Result := '' end; end; function WideStringReplace(const S, OldPattern, NewPattern: WideString; Flags: TReplaceFlags; WholeWord: Boolean = False): WideString; begin {$IFDEF TNTSUPPORT} Result := Tnt_WideStringReplace(S, OldPattern, NewPattern, [rfReplaceAll, rfIgnoreCase], WholeWord); {$ELSE} StringReplace(S, OldPattern, NewPattern, [rfReplaceAll, rfIgnoreCase]) {$ENDIF}; end; function WideShellExecute(hWnd: HWND; Operation, FileName, Parameters, Directory: WideString; ShowCmd: Integer = SW_NORMAL): HINST; var OperationA, FileNameA, ParametersA, DirectoryA: string; PA, DA: PChar; PW, DW: PWideChar; begin if Assigned(ShellExecuteW_MP) then begin PW := nil; DW := nil; if Parameters = '' then PW := PWideChar(Parameters); if Directory = '' then DW := PWideChar(Directory); Result := ShellExecuteW_MP(hWnd, PWideChar(Operation), PWideChar(FileName), PW, DW, SW_NORMAL) end else begin OperationA := Operation; FileNameA := FileName; ParametersA := Parameters; DirectoryA := Directory; PA := nil; DA := nil; if ParametersA <> '' then PA := PChar( ParametersA); if DirectoryA <> '' then DA := PChar( DirectoryA); Result := ShellExecuteA(hWnd, PChar(OperationA), PChar(FileNameA), PA, DA, SW_NORMAL) end end; procedure WideShowMessage(Window: HWND; ACaption, AMessage: WideString); var TextA, CaptionA: string; begin if IsUnicode then MessageBoxW(Window, PWideChar( AMessage), PWideChar( ACaption), MB_ICONEXCLAMATION or MB_OK) else begin TextA := AMessage; CaptionA := ACaption; MessageBoxA(Window, PChar( TextA), PChar( CaptionA), MB_ICONEXCLAMATION or MB_OK) end end; function WideLowerCase(const Str: WideString): WideString; begin Result := Str; if IsUnicode then CharLowerBuffW_MP(PWideChar(Result), Length(Result)) else CharLowerBuffA(PChar(string(Result)), Length(Result)) end; function WideMessageBox(Window: HWND; const ACaption, AMessage: WideString; uType: integer): integer; var TextA, CaptionA: string; begin if IsUnicode then Result := MessageBoxW(Window, PWideChar( AMessage), PWideChar( ACaption), uType) else begin TextA := AMessage; CaptionA := ACaption; Result := MessageBoxA(Window, PChar( TextA), PChar( CaptionA), uType) end end; function IncludeTrailingBackslashW(const S: WideString): WideString; begin Result := S; if not WideIsPathDelimiter(Result, Length(Result)) then Result := Result + '\'; end; function DiskInDrive(C: Char): Boolean; var OldErrorMode: Integer; begin C := UpCase(C); if C in ['A'..'Z'] then begin OldErrorMode := SetErrorMode(SEM_FAILCRITICALERRORS); Result := DiskFree(Ord(C) - Ord('A') + 1) > -1; SetErrorMode(OldErrorMode); end else Result := False end; function WideStrIComp(Str1, Str2: PWideChar): Integer; // Insensitive case comparison var S1, S2: string; begin if Win32Platform = VER_PLATFORM_WIN32_NT then Result := lstrcmpiW_MP(Str1, Str2) else begin S1 := Str1; S2 := Str2; Result := lstrcmpi(PChar(S1), PChar(S2)) end end; function StrRScanW(Str: PWideChar; Chr: WideChar): PWideChar; // returns a pointer to the last occurance of Chr in Str asm PUSH EDI MOV EDI, Str MOV ECX, 0FFFFFFFFH XOR AX, AX REPNE SCASW NOT ECX STD SUB EDI, 2 MOV AX, Chr REPNE SCASW MOV EAX, 0 JNE @@1 MOV EAX, EDI ADD EAX, 2 @@1: CLD POP EDI end; function WideStrComp(Str1, Str2: PWideChar): Integer; // Sensitive case comparison var S1, S2: string; begin if Win32Platform = VER_PLATFORM_WIN32_NT then Result := lstrcmpW_MP(Str1, Str2) else begin S1 := Str1; S2 := Str2; Result := lstrcmp(PChar(S1), PChar(S2)) end end; function WideStrLower(Str: PWideChar): PWideChar; // Returns the string in Str converted to lower case var S: string; WS: WideString; begin Result := Str; if IsUnicode then CharLowerBuffW_MP(Str, lstrlenW(Str)) else begin S := Str; CharLowerBuffA(PChar(S), Length(S)); WS := S; { WS is a string index from 1, Result is PWideChar index from 0 } Move(WS[1], Result[0], Length(WS)); end; end; procedure WideStrLCopy(Str1, Str2: PWideChar; Count: Integer); // Count must include the terminating null begin if Win32Platform = VER_PLATFORM_WIN32_NT then lstrcpynW_MP(Str1, Str2, Count) else Move(Str2, Str1, Count * 2); end; function ShortenStringEx(DC: HDC; const S: WideString; Width: Integer; RTL: Boolean; EllipsisPlacement: TShortenStringEllipsis): WideString; // Shortens the passed string and inserts ellipsis "..." in the requested place. // This is not a fast function but it is clear how it works. Also the RTL implmentation // is still being understood. var Len: Integer; EllipsisWidth: Integer; TargetString: WideString; Tail, Head, Middle: PWideChar; L, ResultW: integer; begin Len := Length(S); if (Len = 0) or (Width <= 0) then Result := '' else begin // Determine width of triple point using the current DC settings TargetString := '...'; EllipsisWidth := TextExtentW(PWideChar(TargetString), DC).cx; if Width <= EllipsisWidth then Result := '' else begin TargetString := S; Head := PWideChar(TargetString); Tail := Head; Inc(Tail, lstrlenW(PWideChar(TargetString))); case EllipsisPlacement of sseEnd: begin L := EllipsisWidth + TextExtentW(PWideChar(TargetString), DC).cx; while (L > Width) do begin Dec(Tail); Tail^ := WideNull; L := EllipsisWidth + TextExtentW(PWideChar(TargetString), DC).cx; end; Result := PWideChar(TargetString) + '...'; end; sseFront: begin L := EllipsisWidth + TextExtentW(PWideChar(TargetString), DC).cx; while (L > Width) do begin Inc(Head); L := EllipsisWidth + TextExtentW(PWideChar(Head), DC).cx; end; Result := '...' + PWideChar(Head); end; sseMiddle: begin L := EllipsisWidth + TextExtentW(PWideChar(TargetString), DC).cx; while (L > Width div 2 - EllipsisWidth div 2) do begin Dec(Tail); Tail^ := WideNull; L := TextExtentW(PWideChar(TargetString), DC).cx; end; Result := PWideChar(TargetString) + '...'; ResultW := TextExtentW(PWideChar(Result), DC).cx; TargetString := S; Head := PWideChar(TargetString); Middle := Head; Inc(Middle, lstrlenW(PWideChar(Result)) - 3); // - 3 for ellipsis letters Tail := Head; Inc(Tail, lstrlenW(PWideChar(TargetString))); Head := Tail - 1; L := ResultW + TextExtentW(Head, DC).cx; while (L < Width) and (Head >= Middle) do begin Dec(Head); L := ResultW + TextExtentW(PWideChar(Head), DC).cx; end; Inc(Head); Result := Result + Head; end; sseFilePathMiddle: begin Head := Tail - 1; L := EllipsisWidth + TextExtentW(Head, DC).cx; while (Head^ <> '\') and (Head <> @TargetString[1]) and (L < Width) do begin Dec(Head); L := EllipsisWidth + TextExtentW(Head, DC).cx; end; if Head^ <> '\' then Inc(Head); Result := '...' + Head; ResultW := TextExtentW(PWideChar(Result), DC).cx; Head^ := WideNull; SetLength(TargetString, lstrlenW(PWideChar(TargetString))); Head := PWideChar(TargetString); Tail := Head; Inc(Tail, lstrlenW(Head)); L := ResultW + TextExtentW(PWideChar(TargetString), DC).cx; while (L > Width) and (Tail > @TargetString[1]) do begin Dec(Tail); Tail^ := WideNull; L := ResultW + TextExtentW(PWideChar(TargetString), DC).cx; end; Result := Head + Result; end; end; // Windows 2000 automatically switches the order in the string. For every other system we have to take care. { if IsWin2000 or not RTL then Result := Copy(S, 1, N - 1) + '...' else Result := '...' + Copy(S, 1, N - 1); } end; end; end; function WindowsDirectory: WideString; var Len: integer; S: string; begin Result := ''; if Win32Platform = VER_PLATFORM_WIN32_NT then Len := GetWindowsDirectoryW_MP(PWideChar(Result), 0) else Len := GetWindowsDirectoryA(PChar(S), 0); if Len > 0 then begin if IsUnicode then begin SetLength(Result, Len - 1); GetWindowsDirectoryW_MP(PWideChar(Result), Len); end else begin SetLength(S, Len - 1); GetWindowsDirectoryA(PChar(S), Len); Result := S end end end; function ModuleFileName(PathOnly: Boolean = True): Widestring; var BufferA: array[0..MAX_PATH] of Char; BufferW: array[0..MAX_PATH] of WideChar; begin if IsUnicode then begin FillChar(BufferW, SizeOf(BufferW), #0); if GetModuleFileNameW(0, BufferW, SizeOf(BufferW)) > 0 then begin if PathOnly then Result := ExtractFileDirW(BufferW) else Result := BufferW; end end else begin FillChar(BufferA, SizeOf(BufferA), #0); if GetModuleFileNameA(0 , BufferA, SizeOf(BufferA)) > 0 then begin if PathOnly then Result := ExtractFileDirW(BufferA) else Result := BufferA end end end; function PIDLToPath(PIDL: PItemIDList): WideString; var PIDLMgr: TCommonPIDLManager; LastID: PItemIDList; LastCB: Word; Desktop, Folder: IShellFolder; StrRet: TStrRet; begin Result := ''; if Assigned(PIDL) then begin FillChar(StrRet, SizeOf(StrRet), #0); PIDLMgr := TCommonPIDLManager.Create; try SHGetDesktopFolder(Desktop); if PIDLMgr.IsDesktopFolder(PIDL) then begin end else begin PIDLMgr.StripLastID(PIDL, LastCB, LastID); try if Succeeded(Desktop.BindToObject(PIDL, nil, IShellFolder, pointer(Folder))) then begin LastID.mkid.cb := LastCB; if Succeeded(Folder.GetDisplayNameOf(LastID, SHGDN_FORPARSING, StrRet)) then Result := StrRetToStr(StrRet, LastID); end finally LastID.mkid.cb := LastCB; end end finally PIDLMgr.Free end end end; function ShortFileName(const FileName: WideString): WideString; var S: string; BufferA: array[0..MAX_PATH] of char; BufferW: array[0..MAX_PATH] of WideChar; begin if IsUnicode then begin if GetShortPathNameW(PWideChar(FileName), BufferW, SizeOf(BufferW)) > 0 then Result := BufferW end else begin S := FileName; if GetShortPathNameA(PChar(S), BufferA, SizeOf(BufferA)) > 0 then Result := BufferA end end; function ShortPath(const Path: WideString): WideString; begin Result := ShortFileName(Path) end; procedure LoadWideString(S: TStream; var Str: WideString); var Count: Integer; begin S.Read(Count, SizeOf(Integer)); SetLength(Str, Count); S.Read(PWideChar(Str)^, Count * 2) end; procedure SaveWideString(S: TStream; Str: WideString); var Count: Integer; begin Count := Length(Str); S.Write(Count, SizeOf(Integer)); S.Write(PWideChar(Str)^, Count * 2) end; function WideStrPos(Str, SubStr: PWideChar): PWideChar; // returns a pointer to the first occurance of SubStr in Str asm PUSH EDI PUSH ESI PUSH EBX OR EAX, EAX JZ @@2 OR EDX, EDX JZ @@2 MOV EBX, EAX MOV EDI, EDX XOR AX, AX MOV ECX, 0FFFFFFFFH REPNE SCASW NOT ECX DEC ECX JZ @@2 MOV ESI, ECX MOV EDI, EBX MOV ECX, 0FFFFFFFFH REPNE SCASW NOT ECX SUB ECX, ESI JBE @@2 MOV EDI, EBX LEA EBX, [ESI - 1] @@1: MOV ESI, EDX LODSW REPNE SCASW JNE @@2 MOV EAX, ECX PUSH EDI MOV ECX, EBX REPE CMPSW POP EDI MOV ECX, EAX JNE @@1 LEA EAX, [EDI - 2] JMP @@3 @@2: XOR EAX, EAX @@3: POP EBX POP ESI POP EDI end; function ProperRect(Rect: TRect): TRect; // Makes sure a rectangle's left is less than its right and its top is less than its bottom var Temp: integer; begin Result := Rect; if Result.Right < Result.Left then begin Temp := Result.Right; Result.Right := Rect.Left; Result.Left := Temp; end; if Rect.Bottom < Rect.Top then begin Temp := Result.Top; Result.Top := Rect.Bottom; Result.Bottom := Temp; end end; function DragDetectPlus(Handle: HWND; Pt: TPoint): Boolean; // Replacement for DragDetect API which is buggy. // Pt is in Client Coords of the Handle window var DragRect: TRect; Msg: TMsg; TestPt: TPoint; HadCapture, Done: Boolean; begin Result := False; Done := False; HadCapture := GetCapture = Handle; if (not ClientToScreen(Handle, Pt)) then Exit; DragRect.TopLeft := Pt; DragRect.BottomRight := Pt; InflateRect(DragRect, GetSystemMetrics(SM_CXDRAG), GetSystemMetrics(SM_CYDRAG)); SetCapture(Handle); try while (not Result) and (not Done) do if (PeekMessage(Msg, Handle, 0,0, PM_REMOVE)) then begin case (Msg.message) of WM_MOUSEMOVE: begin TestPt := Msg.Pt; // Not sure why this works. The Message point "should" be in client // coordinates but seem to be screen // Windows.ClientToScreen(Msg.hWnd, TestPt); Result := not(PtInRect(DragRect, TestPt)); end; WM_RBUTTONUP, WM_LBUTTONUP, WM_CANCELMODE, WM_LBUTTONDBLCLK, WM_MBUTTONUP: begin // Let the window get these messages after we have ended our // local message loop PostMessage(Msg.hWnd, Msg.message, Msg.wParam, Msg.lParam); Done := True; end; WM_QUIT: begin PostQuitMessage(Msg.wParam); Done := True; end else TranslateMessage(Msg); DispatchMessage(Msg) end end else Sleep(0); finally ReleaseCapture; if HadCapture then Mouse.Capture := Handle; end; end; procedure FillWideChar(var Dest; count: Integer; Value: WideChar); var I: Integer; P: PWideChar; begin P := PWideChar(@Dest); for I := count-1 downto 0 do P[I] := Value; end; procedure FreeMemAndNil(var P: Pointer); { Frees the memeory allocated with GetMem and nils the pointer } var Temp: Pointer; begin Temp := P; P := nil; FreeMem(Temp); end; function IsRectNull(ARect: TRect): Boolean; begin Result := EqualRect(ARect, Rect(0, 0, 0, 0)) end; function IsUnicode: Boolean; begin Result := Win32Platform = VER_PLATFORM_WIN32_NT end; function IsWinNT: Boolean; begin Result := Win32Platform = VER_PLATFORM_WIN32_NT end; function IsWin2000: Boolean; begin Result := False; if Win32Platform = VER_PLATFORM_WIN32_NT then Result := LoWord(Win32MajorVersion) >= 5 end; function IsWin95_SR1: Boolean; begin Result := False; if Win32Platform = VER_PLATFORM_WIN32_WINDOWS then Result := ((Win32MajorVersion = 4) and (Win32MinorVersion = 0) and (LoWord(Win32BuildNumber) <= 1080)) end; function IsWinME: Boolean; begin Result := False; if Win32Platform = VER_PLATFORM_WIN32_WINDOWS then Result := Win32BuildNumber >= $045A0BB8 end; function IsWinNT4: Boolean; begin Result := False; if Win32Platform = VER_PLATFORM_WIN32_NT then Result := Win32MajorVersion < 5 end; function IsWinXP: Boolean; begin Result := (Win32Platform = VER_PLATFORM_WIN32_NT) and (Win32MajorVersion = 5) and (Win32MinorVersion > 0) end; function IsWinXPOrUp: Boolean; begin Result := (Win32Platform = VER_PLATFORM_WIN32_NT) and (Win32MajorVersion >= 5) and (Win32MinorVersion > 0) end; function IsWinVista: Boolean; begin Result := (Win32Platform = VER_PLATFORM_WIN32_NT) and (Win32MajorVersion = 6) and (Win32MinorVersion >= 0) end; function IsWinVistaOrUp: Boolean; begin Result := (Win32Platform = VER_PLATFORM_WIN32_NT) and (Win32MajorVersion >= 6) and (Win32MinorVersion >= 0) end; function RectHeight(R: TRect): Integer; begin Result := R.Bottom - R.Top end; function RectToStr(R: TRect): string; begin Result := 'Left = ' + IntToStr(R.Left) + ' Top = ' + IntToStr(R.Top) + ' Right = ' + IntToStr(R.Right) + ' Bottom = ' + IntToStr(R.Bottom) end; function RectToSquare(R: TRect): TRect; // Takes the passed rectangle and makes it square based on the longest dimension begin if RectWidth(R) > RectHeight(R) then R.Right := R.Left + RectHeight(R) else if RectHeight(R) > RectWidth(R) then begin R.Bottom := R.Top + RectWidth(R); end else Result := R end; function RectWidth(R: TRect): Integer; begin Result := R.Right - R.Left end; function ContainsRect(OuterRect, InnerRect: TRect): Boolean; // // Returns true if the InnerRect is completely contained within the // OuterRect // begin Result := (InnerRect.Left >= OuterRect.Left) and (InnerRect.Right <= OuterRect.Right) and (InnerRect.Top >= OuterRect.Top) and (InnerRect.Bottom <= OuterRect.Bottom) end; function KeyDataToShiftState(KeyData: Longint): TShiftState; begin Result := []; if GetKeyState(VK_SHIFT) < 0 then Include(Result, ssShift); if GetKeyState(VK_CONTROL) < 0 then Include(Result, ssCtrl); if KeyData and $20000000 <> 0 then Include(Result, ssAlt); end; function DropEffectToDropEffectState(Effect: Integer): TCommonDropEffect; begin Result := cdeNone; if Effect and DROPEFFECT_COPY <> 0 then Result := cdeCopy else if Effect and DROPEFFECT_MOVE <> 0 then Result := cdeMove else if Effect and DROPEFFECT_LINK <> 0 then Result := cdeLink end; function DropEffectStateToDropEffect(Effect: TCommonDropEffect): Integer; begin Result := 0; if cdeCopy = Effect then Result := Result or DROPEFFECT_COPY else if cdeMove = Effect then Result := Result or DROPEFFECT_MOVE else if cdeLink = Effect then Result := Result or DROPEFFECT_LINK end; function DropEffectToDropEffectStates(Effect: Integer): TCommonDropEffects; begin Result := []; if (Effect = 0) or (Longword(Effect) = DROPEFFECT_SCROLL) then Include(Result, cdeNone); if Effect and DROPEFFECT_COPY <> 0 then Include(Result, cdeCopy); if Effect and DROPEFFECT_MOVE <> 0 then Include(Result, cdeMove); if Effect and DROPEFFECT_LINK <> 0 then Include(Result, cdeLink); if Effect and DROPEFFECT_SCROLL <> 0 then Include(Result, cdeScroll) end; function DropEffectStatesToDropEffect(Effect: TCommonDropEffects): Integer; begin Result := 0; if cdeCopy in Effect then Result := Result or DROPEFFECT_COPY; if cdeMove in Effect then Result := Result or DROPEFFECT_MOVE; if cdeLink in Effect then Result := Result or DROPEFFECT_LINK; if cdeScroll in Effect then Result := Result or Integer(DROPEFFECT_SCROLL); end; function KeyToKeyStates(Keys: Word): TCommonKeyStates; begin Result := []; if Keys and MK_CONTROL <> 0 then Include(Result, cksControl); if Keys and MK_LBUTTON <> 0 then Include(Result, cksLButton); if Keys and MK_MBUTTON <> 0 then Include(Result, cksMButton); if Keys and MK_RBUTTON <> 0 then Include(Result, cksRButton); if Keys and MK_SHIFT <> 0 then Include(Result, cksShift); if Keys and MK_ALT <> 0 then Include(Result, cksAlt); if Keys and MK_BUTTON <> 0 then Include(Result, cksButton); end; function KeyStatesToMouseButton(Keys: Word): TCommonMouseButton; begin if Keys and MK_LBUTTON <> 0 then Result := cmbLeft else if Keys and MK_MBUTTON <> 0 then Result := cmbMiddle else if Keys and MK_RBUTTON <> 0 then Result := cmbRight else Result := cmbNone end; function KeyStatesToKey(Keys: TCommonKeyStates): Longword; begin Result := 0; if cksControl in Keys then Result := Result or MK_CONTROL; if cksLButton in Keys then Result := Result or MK_LBUTTON; if cksMButton in Keys then Result := Result or MK_MBUTTON; if cksRButton in Keys then Result := Result or MK_RBUTTON; if cksShift in Keys then Result := Result or MK_SHIFT; if cksAlt in Keys then Result := Result or MK_ALT; end; function KeyStateToDropEffect(Keys: TCommonKeyStates): TCommonDropEffect; begin Result := cdeMove; // The default if (cksControl in Keys) and not ((cksShift in Keys) or (cksAlt in Keys)) then Result := cdeCopy else if ((cksAlt in Keys) and not ((cksShift in Keys) or (cksControl in Keys))) or ((cksShift in Keys) and (cksControl in Keys)) then Result := cdeLink end; function KeyStateToMouseButton(KeyState: TCommonKeyStates): TCommonMouseButton; begin if KeyState * [cksLButton] <> [] then Result := cmbLeft else if KeyState * [cksRButton] <> [] then Result := cmbRight else if KeyState * [cksMButton] <> [] then Result := cmbMiddle else Result := cmbNone end; function FileIconInit(FullInit: BOOL): BOOL; stdcall; // Forces the system to load all Images into the ImageList. Normally with NT // system icons are only loaded as needed. type TFileIconInit = function(FullInit: BOOL): BOOL; stdcall; var ShellDLL: HMODULE; PFileIconInit: TFileIconInit; begin Result := False; if (Win32Platform = VER_PLATFORM_WIN32_NT) then begin ShellDLL := CommonLoadLibrary(Shell32); PFileIconInit := GetProcAddress(ShellDLL, PChar(660)); if (Assigned(PFileIconInit)) then Result := PFileIconInit(FullInit); end; end; function SHGetImageList(iImageList: Integer; const RefID: TGUID; out ppvOut): HRESULT; stdcall; // Retrieves the system ImageList interface var ShellDLL: HMODULE; ImageList: TSHGetImageList; begin Result := E_NOTIMPL; if (Win32Platform = VER_PLATFORM_WIN32_NT) then begin // if GetFileVersion(comctl32) >= $00060000 then begin ShellDLL := CommonLoadLibrary(Shell32); ImageList := GetProcAddress(ShellDLL, PChar(727)); if (Assigned(ImageList)) then Result := ImageList(iImageList, RefID, ppvOut); end end; end; function Size(cx, cy: Integer): TSize; begin Result.cy := cy; Result.cx := cx end; function ShortenTextW(DC: hDC; TextToShorten: WideString; MaxSize: Integer): WideString; // Shortens the passed string in such a way that if it does not fit in the MaxSize // (in Pixels) a "..." is inserted at the correct place where the new string fixs // in MaxSize var Size: TSize; EllipsisSize: TSize; // TailA: String; StrLen, Middle, Low, High{, LastHigh}: Cardinal; // WChar: WideChar; // Done: Boolean; begin if TextToShorten <> '' then begin GetTextExtentPoint32W(DC, PWideChar(TextToShorten), Length(TextToShorten), Size); GetTextExtentPoint32W(DC, '...', 3, EllipsisSize); StrLen := Length(TextToShorten); if Size.cx > MaxSize then begin (* Middle := StrLen div 2; Done := False; Low := 0; High := StrLen; LastHigh := High; // Do a psudo binary search while not Done do begin GetTextExtentPoint32W(DC, @TextToShorten[1], Middle, Size); Size.cx := Size.cx + EllipsisSize.cx; if Size.cx > MaxSize then begin // Still to long LastHigh := High; High := Middle; Middle := Low + (High - Low) div 2; end else begin // Too short High := LastHigh; Low := Middle; Middle := Low + (High - Low) div 2; end; Done := High - Low <= 2; end; *) // Milan's version Low:=0; High:=StrLen-1; while (Low 0 then Result := TextToShorten + '...' else Result := '...' end else Result := TextToShorten end else Result := ''; end; (* function SplitTextW(DC: hDC; TextToSplit: WideString; MaxWidth: Integer; var Buffer: TWideCharArray; MaxSplits: Integer): Integer; // Takes the passed string and breaks it up so each piece fits within the MaxWidth // The function detects any LF/CR pairs and treats them as one break if CR or LF // is defined as a break character. // The Buffer is a set of NULL terminated strings for each line, with the last // one being terminated with a double NULL. Much like the SHFileOperation API // This makes it ready to use to pass the strings to ExtTextOutW in a loop // If the buffer is too small the Result will be false // If MaxSplits = -1 then the function splits as many time as necessary // The Return is the total number of lines the passed text was split into var Head, Tail, LastBreakChar, BufferHead: PWideChar; Size: TSize; LineWidth, SplitCount, Len: Integer; TextMetrics: TTextMetric; { PeriodW1, PeriodW2, PeriodW3: Integer; } begin Result := 0; if MaxWidth = 0 then begin SetLength(Buffer, 2); Buffer[0] := WideNull; Buffer[1] := WideNull; end else begin GetTextMetrics(DC, TextMetrics); // Can get into deep trouble if a single letter won't fit in the MaxWidth if TextMetrics.tmMaxCharWidth > MaxWidth then begin Len := Length(TextToSplit); SetLength(Buffer, Len + 2); if Len > 0 then begin Head := @TextToSplit[1]; CopyMemory(Buffer, Head, Len*2); Result := 1; end; Buffer[Len] := #0; Buffer[Len + 1] := #0; end else begin FillChar(Size, SizeOf(Size), #0); // Arbitrary size that should be ok in most instances. This will be enough space // for 127 lines SetLength(Buffer, Length(TextToSplit) + 128); BufferHead := @Buffer[0]; Head := PWideChar(TextToSplit); Tail := Head; SplitCount := 0; while (Tail^ <> #0) and ((SplitCount < MaxSplits) or (MaxSplits = -1)) do begin LineWidth := 0; LastBreakChar := nil; while (Tail^ <> WideNull) and (LineWidth <= MaxWidth) and (Tail^ <> WideCR) and (Tail^ <> WideLF) do begin GetTextExtentPoint32W(DC, PWideChar(Tail), 1, Size); Inc(LineWidth, Size.cx); Inc(Tail); if (LineWidth <= MaxWidth) and (Tail^ = WideSpace) then LastBreakChar := Tail; end; if (LineWidth > MaxWidth) {and (Tail^ <> WideSpace)} then begin // Over ran the line unless it exactly fits if Assigned(LastBreakChar) and (LineWidth > MaxWidth) then begin // We have word break to go back to Tail := LastBreakChar; Inc(SplitCount); if SplitCount = MaxSplits then begin // If no more splits allows copy the entire rest of the string to the buffer Len := lStrLenW(Head); CopyMemory(BufferHead, Head, Len*2); Inc(BufferHead, Len); end else begin CopyMemory(BufferHead, Head, (Tail-Head)*2); Inc(BufferHead, Tail-Head); end; BufferHead^ := WideNull; Inc(BufferHead); end else begin // Special case, the Tail is the end of the Text to split if Tail^ <> WideNull then Dec(Tail); Inc(SplitCount); if SplitCount = MaxSplits then begin // If no more splits allows copy the entire rest of the string to the buffer Len := lStrLenW(Head); CopyMemory(BufferHead, Head, Len*2); Inc(BufferHead, Len); end else begin CopyMemory(BufferHead, Head, (Tail-Head)*2); Inc(BufferHead, Tail-Head); end; BufferHead^ := WideNull; Inc(BufferHead); end; Inc(Result); end else begin Inc(SplitCount); if SplitCount = MaxSplits then begin // If no more splits allows copy the entire rest of the string to the buffer Len := lStrLenW(Head); CopyMemory(BufferHead, Head, Len*2); Inc(BufferHead, Len); end else begin CopyMemory(BufferHead, Head, (Tail-Head)*2); Inc(BufferHead, Tail-Head); end; BufferHead^ := WideNull; Inc(BufferHead); Inc(Result) end; while (Tail^ <> #0) and ((Tail^ = WideCR) or (Tail^ = WideLF) or (Tail^ = WideSpace)) do Inc(Tail); Head := Tail end end end end; *) // Solerman's version function SplitTextW(DC: hDC; TextToSplit: WideString; MaxWidth: Integer; var Buffer: TCommonWideCharArray; MaxSplits: Integer): Integer; // Takes the passed string and breaks it up so each piece fits within the MaxWidth // The function detects any LF/CR pairs and treats them as one break if CR or LF // is defined as a break character. // The Buffer is a set of NULL terminated strings for each line, with the last // one being terminated with a double NULL. Much like the SHFileOperation API // This makes it ready to use to pass the strings to ExtTextOutW in a loop // If the buffer is too small the Result will be false // If MaxSplits = -1 then the function splits as many time as necessary // The Return is the total number of lines the passed text was split into var Head, Tail, LastBreakChar, BufferHead: PWideChar; // TailA: String; Size: TSize; LineWidth, SplitCount, Len: Integer; TextMetrics: TTextMetric; begin Result := 0; if MaxWidth = 0 then begin SetLength(Buffer, 2); Buffer[0] := WideNull; Buffer[1] := WideNull; end else begin GetTextMetrics(DC, TextMetrics); // Can get into deep trouble if a single letter won't fit in the MaxWidth if TextMetrics.tmMaxCharWidth > MaxWidth then begin Len := Length(TextToSplit); SetLength(Buffer, Len + 2); if Len > 0 then begin Head := @TextToSplit[1]; CopyMemory(Buffer, Head, Len*2); Result := 1; end; Buffer[Len] := #0; Buffer[Len + 1] := #0; end else begin FillChar(Size, SizeOf(Size), #0); // Arbitrary size that should be ok in most instances. This will be enough space // for 127 lines SetLength(Buffer, Length(TextToSplit) + 128); BufferHead := @Buffer[0]; Head := PWideChar(TextToSplit); Tail := Head; SplitCount := 0; while (Tail^ <> #0) and ((SplitCount < MaxSplits) or (MaxSplits = -1)) do begin LineWidth := 0; LastBreakChar := nil; while (Tail^ <> WideNull) and (LineWidth <= MaxWidth) and (Tail^ <> WideCR) and (Tail^ <> WideLF) do begin // if IsUnicode then GetTextExtentPoint32W(DC, PWideChar(Tail), 1, Size); // else begin // TailA := Tail^; // GetTextExtentPoint32(DC, @TailA[1], 1, Size); // end; Inc(LineWidth, Size.cx); Inc(Tail); if (LineWidth <= MaxWidth) and (Tail^ = WideSpace) then LastBreakChar := Tail; end; if (LineWidth > MaxWidth) then begin // We overran the MaxWidth if entering this block // Over ran the line unless it exactly fits if Assigned(LastBreakChar) and (LineWidth > MaxWidth) then // We have word break to go back to Tail := LastBreakChar else // Special case, the Tail is the end of the Text to split if {(Tail^ <> WideNull) and} ((SplitCount + 1 < MaxSplits) or (MaxSplits < 0)) then Dec(Tail); end; Inc(SplitCount); // If we reach the MaxSplits make the last line be the rest of the text. if (SplitCount > 0) and (SplitCount = MaxSplits) then Inc(Tail, lstrlenW(Tail)); CopyMemory(BufferHead, Head, (Tail-Head)*2); Inc(BufferHead, Tail-Head); BufferHead^ := WideNull; Inc(BufferHead); Inc(Result); while (Tail^ <> #0) and ((Tail^ = WideCR) or (Tail^ = WideLF) or (Tail^ = WideSpace)) do Inc(Tail); Head := Tail end end end end; {$IFNDEF COMPILER_6_UP} function VarToWideStrDef(const V: Variant; const ADefault: WideString): WideString; begin if not VarIsNull(V) then Result := V else Result := ADefault; end; function VarToWideStr(const V: Variant): WideString; begin Result := VarToWideStrDef(V, NullAsStringValue); end; function BoolToStr(B: Boolean; UseBoolStrs: Boolean = False): string; const cSimpleBoolStrs: array [boolean] of String = ('0', '-1'); begin if UseBoolStrs then begin if B then Result := 'True' else Result := 'False' end else Result := cSimpleBoolStrs[B]; end; function CompareTime(const A, B: TDateTime): TValueRelationship; begin if Abs(Frac(A) - Frac(B)) < (1 / MSecsPerDay) then Result := 0 else if Frac(A) < Frac(B) then Result := Low(TValueRelationship) else Result := High(TValueRelationship); end; function WideCompareText(S1, S2: WideString): Integer; begin Result := WideStrComp( PWideChar(S1), PWideChar(S2)) end; function ExcludeTrailingBackslash(Path: WideString): WideString; begin Result := Path; if Result <> '' then begin Result := Trim(Result); if Length(Result) > 0 then if Result[Length(Result)] = '\' then SetLength(Result, Length(Result) - 1) end end; function IncludeTrailingBackslash(Path: WideString): WideString; begin Result := Path; if Path <> '' then Result := ExcludeTrailingBackslash(Path) + '\' end; {$ENDIF} function VariantToCaption(const V: Variant): WideString; var DateTime: TDateTime; begin case VarType(V) of varString, varOleStr: Result := VarToWideStr(V); varInteger,{$IFDEF COMPILER_6_UP}varWord, varShortInt, varLongWord,{$ENDIF} varSmallint, varByte: Result := IntToStr(V); varDate: begin DateTime := V; if Trunc(DateTime) = DateTime then // no time portion Result := DateToStr(DateTime) else Result := DateTimeToStr(DateTime); end; varCurrency: Result := CurrToStr(V); varSingle, varDouble: Result := FloatToStrF(V, ffFixed, 15, 2); varBoolean: Result := BoolToStr(V, True); else Result := VarToWideStrDef(V, ''); end; end; function StrCopyW(Dest, Source: PWideChar): PWideChar; // copies Source to Dest and returns Dest asm PUSH EDI PUSH ESI MOV ESI, EAX MOV EDI, EDX MOV ECX, 0FFFFFFFFH XOR AX, AX REPNE SCASW NOT ECX MOV EDI, ESI MOV ESI, EDX MOV EDX, ECX MOV EAX, EDI SHR ECX, 1 REP MOVSD MOV ECX, EDX AND ECX, 1 REP MOVSW POP ESI POP EDI end; //---------------------------------------------------------------------------------------------------------------------- function BrightenColor(const RGB: TCommonRGB; Amount: Double): TCommonRGB; var HLS: TCommonHLS; begin HLS := RGBToHLS(RGB); HLS.L := (1 + Amount) * HLS.L; Result := HLSToRGB(HLS); end; function DarkenColor(const RGB: TCommonRGB; Amount: Double): TCommonRGB; var HLS: TCommonHLS; begin HLS := RGBToHLS(RGB); // Darken means to decrease luminance. HLS.L := (1 - Amount) * HLS.L; Result := HLSToRGB(HLS); end; function RGBToHLS(const RGB: TCommonRGB): TCommonHLS; // Converts from RGB to HLS. // Input parameters and result values are all in the range 0..1. // Note: Hue is normalized so 360° corresponds to 1. var Delta, Max, Min: Double; begin with RGB, Result do begin Max := MaxValue([R, G, B]); Min := MinValue([R, G, B]); L := (Max + Min) / 2; if Max = Min then begin // Achromatic case. S := 0; H := 0; // Undefined end else begin Delta := Max - Min; if L < 0.5 then S := Delta / (Max + Min) else S := Delta / (2 - (Max + Min)); if R = Max then H := (G - B) / Delta else if G = Max then H := 2 + (B - R) / Delta else if B = Max then H := 4 + (R - G) / Delta; H := H / 6; if H < 0 then H := H + 1; end end; end; function HLSToRGB(const HLS: TCommonHLS): TCommonRGB; // Converts from HLS (hue, luminance, saturation) to RGB. // Input parameters and result values are all in the range 0..1. // Note: Hue is normalized so 360° corresponds to 1. //--------------- local function -------------------------------------------- function HueToRGB(m1, m2, Hue: Double): Double; begin if Hue > 1 then Hue := Hue - 1 else if Hue < 0 then Hue := Hue + 1; if 6 * Hue < 1 then Result := m1 + (m2 - m1) * Hue * 6 else if 2 * Hue < 1 then Result := m2 else if 3 * Hue < 2 then Result := m1 + (m2 - m1) * (2 / 3 - Hue) * 6 else Result := m1; end; //--------------- end local function ---------------------------------------- var m1, m2: Double; begin with HLS, Result do begin if S = 0 then begin // Achromatic case (no hue). R := L; G := L; B := L; end else begin if L <= 0.5 then m2 := L * (S + 1) else m2 := L + S - L * S; m1 := 2 * L - m2; R := HueToRGB(m1, m2, H + 1 / 3); G := HueToRGB(m1, m2, H); B := HueToRGB(m1, m2, H - 1 / 3) end; end; end; function MakeTRBG(Color: TColor): TCommonRGB; var RGB: Longint; begin RGB := ColorToRGB(Color); Result.B := GetBValue(RGB) / 255; Result.G := GetGValue(RGB) / 255; Result.R := GetRValue(RGB) / 255; end; function MakeTColor(RGB: TCommonRGB): TColor; begin Result := TColor(MakeColorRef(RGB)); end; function MakeColorRef(RGB: TCommonRGB; Gamma: Double = 1): COLORREF; // Converts a floating point RGB color to an 8 bit color reference as used by Windows. // The function takes care not to produce out-of-gamut colors and allows to apply an optional gamma correction // (inverse gamma). begin GammaCorrection(RGB, Gamma); with RGB do Result := Windows.RGB(Round(R * 255), Round(G * 255), Round(B * 255)); end; procedure GammaCorrection(var RGB: TCommonRGB; Gamma: Double); // Computes the gamma corrected RGB color and ensures the result is in-gamut. begin if Gamma <> 1 then begin Gamma := 1 / Gamma; with RGB do begin if R > 0 then R := Power(R, Gamma) else R := 0; if G > 0 then G := Power(G, Gamma) else G := 0; if B > 0 then B := Power(B, Gamma) else B := 0; end; end; MakeSafeColor(RGB); end; function MakeSafeColor(var RGB: TCommonRGB): Boolean; // Ensures the given RGB color is in-gamut, that is, no component is < 0 or > 1. // Returns True if the color had to be adjusted to be in-gamut, otherwise False. begin Result := False; if RGB.R < 0 then begin Result := True; RGB.R := 0; end; if RGB.R > 1 then begin Result := True; RGB.R := 1; end; if RGB.G < 0 then begin Result := True; RGB.G := 0; end; if RGB.G > 1 then begin Result := True; RGB.G := 1; end; if RGB.B < 0 then begin Result := True; RGB.B := 0; end; if RGB.B > 1 then begin Result := True; RGB.B := 1; end; end; function UpsideDownDIB(Bits: TBitmap): Boolean; var OldRGB, TempRGB: LongInt; P: PLongInt; begin Result := False; Assert(Bits.PixelFormat = pf32Bit, 'UpsideDownDIB only works with 32 bit bitmaps'); if Bits.PixelFormat = pf32Bit then begin P := Bits.ScanLine[0]; OldRGB := P^; Result := True; // if Equal then we can can't be sure if upsidedown if (P^ and $00FFFFFF) = (ColorToRGB(Bits.Canvas.Pixels[0, 0]) and $00FFFFFF) then begin // Flip the pixel bits TempRGB := not OldRGB and $00FFFFFF; Bits.Canvas.Pixels[0, 0] := TempRGB; Result := (P^ and $00FFFFFF) <> (ColorToRGB(Bits.Canvas.Pixels[0, 0]) and $00FFFFFF); P^ := OldRGB end end end; procedure ActivateTopLevelWindow(Child: HWND); var Parent: HWND; Style: LongWord; begin Parent := GetParent(Child); while Parent <> 0 do begin Style := GetWindowLong(Parent, GWL_STYLE); if ((Style and WS_POPUP <> 0)) or (Style and WS_CHILD = 0) then begin BringWindowToTop(Parent); Exit end; Parent := GetParent(Parent); end end; initialization PIDLMgr := TCommonPIDLManager.Create; // We can be sure these are already loaded. This keeps us from having to // reference count when VSTools is being used in an OCX Shell32Handle := GetModuleHandle(Shell32); Kernel32Handle := GetModuleHandle(Kernel32); User32Handle := GetModuleHandle(User32); GDI32Handle := GetModuleHandle(GDI32); AdvAPI32Handle := GetModuleHandle(AdvAPI32); ShlwapiHandle := CommonLoadLibrary(Shlwapi); GetDiskFreeSpaceExA_MP := GetProcAddress(Kernel32Handle, 'GetDiskFreeSpaceA'); TrackMouseEvent_MP := GetProcAddress(User32Handle, 'TrackMouseEvent'); if Win32Platform = VER_PLATFORM_WIN32_NT then begin GetDriveTypeW_MP := GetProcAddress(Kernel32Handle, 'GetDriveTypeW'); DrawTextW_MP := GetProcAddress(User32Handle, 'DrawTextW'); SHGetFileInfoW_MP := GetProcAddress(Shell32Handle, 'SHGetFileInfoW'); CreateFileW_MP := GetProcAddress(Kernel32Handle, 'CreateFileW'); SHGetDataFromIDListW_MP := GetProcAddress(Shell32Handle, 'SHGetDataFromIDListW'); FindFirstFileW_MP := GetProcAddress(Kernel32Handle, 'FindFirstFileW'); FindNextFileW_MP := GetProcAddress(Kernel32Handle, 'FindNextFileW'); lstrcmpiW_MP := GetProcAddress(Kernel32Handle, 'lstrcmpiW'); lstrcmpW_MP := GetProcAddress(Kernel32Handle, 'lstrcmpW'); lstrcpynW_MP := GetProcAddress(Kernel32Handle, 'lstrcpynW'); lstrcpyW_MP := GetProcAddress(Kernel32Handle, 'lstrcpyW'); CharLowerBuffW_MP := GetProcAddress(User32Handle, 'CharLowerBuffW'); CharUpperBuffW_MP := GetProcAddress(User32Handle, 'CharUpperBuffW'); CreateDirectoryW_MP := GetProcAddress(Kernel32Handle, 'CreateDirectoryW'); GetFullPathNameW_MP := GetProcAddress(Kernel32Handle, 'GetFullPathNameW'); ShellExecuteExW_MP := GetProcAddress(Shell32Handle, 'ShellExecuteExW'); ShellExecuteW_MP := GetProcAddress(Shell32Handle, 'ShellExecuteW'); FindFirstChangeNotificationW_MP := GetProcAddress(Kernel32Handle, 'FindFirstChangeNotificationW'); GetCharABCWidthsW_MP := GetProcAddress(GDI32Handle, 'GetCharABCWidthsW'); GetFileAttributesW_MP := GetProcAddress(Kernel32Handle, 'GetFileAttributesW'); GetSystemDirectoryW_MP := GetProcAddress(Kernel32Handle, 'GetSystemDirectoryW'); GetWindowsDirectoryW_MP := GetProcAddress(Kernel32Handle, 'GetWindowsDirectoryW'); GetDiskFreeSpaceExW_MP := GetProcAddress(Kernel32Handle, 'GetDiskFreeSpaceExW'); SetWindowTextW_MP := GetProcAddress(User32Handle, 'SetWindowTextW'); GetNumberFormatW_MP := GetProcAddress(Kernel32Handle, 'GetNumberFormatW'); RegOpenKeyW_MP := GetProcAddress(AdvAPI32Handle, 'RegOpenKeyW'); RegOpenKeyExW_MP := GetProcAddress(AdvAPI32Handle, 'RegOpenKeyExW'); RegQueryValueW_MP := GetProcAddress(AdvAPI32Handle, 'RegQueryValueW'); WritePrivateProfileStringW_MP := GetProcAddress(Kernel32Handle, 'WritePrivateProfileStringW'); GetPrivateProfileStringW_MP := GetProcAddress(Kernel32Handle, 'GetPrivateProfileStringW'); TryEnterCriticalSection_MP := GetProcAddress(Kernel32Handle, 'TryEnterCriticalSection'); InsertMenuItemW_MP := GetProcAddress(User32Handle, 'InsertMenuItemW'); SendMessageW_MP := GetProcAddress(User32Handle, 'SendMessageW'); SetFileAttributesW_MP := GetProcAddress(Kernel32Handle, 'SetFileAttributesW'); CreateFontIndirectW_MP := GetProcAddress(GDI32Handle, 'CreateFontIndirectW'); SystemParametersInfoW_MP := GetProcAddress(User32Handle, 'SystemParametersInfoW'); SHGetPathFromIDListW_MP := GetProcAddress(Shell32Handle, 'SHGetPathFromIDListW'); SHFileOperationW_MP := GetProcAddress(Shell32Handle, 'SHFileOperationW'); SHBrowseForFolderW_MP := GetProcAddress(Shell32Handle, 'SHBrowseForFolderW'); GetDiskFreeSpaceW_MP := GetProcAddress(Kernel32Handle, 'GetDiskFreeSpaceW'); GetCurrentDirectoryW_MP := GetProcAddress(Kernel32Handle, 'GetCurrentDirW'); GetTempPathW_MP := GetProcAddress(Kernel32Handle, 'GetTempPathW'); if ShlwapiHandle <> 0 then begin PathMatchSpecA_MP := GetProcAddress(ShlwapiHandle, 'PathMatchSpecA'); PathMatchSpecW_MP := GetProcAddress(ShlwapiHandle, 'PathMatchSpecW'); end; CreateProcessW_MP := GetProcAddress(Kernel32Handle, 'CreateProcessW'); SHDoDragDrop_MP := GetProcAddress(Shell32Handle, 'SHDoDragDrop'); SHGetKnownFolderPath_MP := GetProcAddress(Shell32Handle, 'SHGetKnownFolderPath'); ExpandEnvironmentStringsW_MP := GetProcAddress(Kernel32Handle, 'ExpandEnvironmentStringsW'); // NTFS Volume (Junction) only functions DeleteVolumeMountPoint_MP := GetProcAddress(Kernel32Handle, 'DeleteVolumeMountPointA'); GetVolumeNameForVolumeMountPoint_MP := GetProcAddress(Kernel32Handle, 'GetVolumeNameForVolumeMountPointA'); GetVolumePathName_MP := GetProcAddress(Kernel32Handle, 'GetVolumePathNameA'); SetVolumeMountPoint_MP := GetProcAddress(Kernel32Handle, 'SetVolumeMountPointA'); FindFirstVolume_MP := GetProcAddress(Kernel32Handle, 'FindFirstVolumeA'); FindNextVolume_MP := GetProcAddress(Kernel32Handle, 'FindNextVolumeA'); FindVolumeClose_MP := GetProcAddress(Kernel32Handle, 'FindVolumeClose'); FindFirstVolumeMountPoint_MP := GetProcAddress(Kernel32Handle, 'FindFirstVolumeMountPointA'); FindNextVolumeMountPoint_MP := GetProcAddress(Kernel32Handle, 'FindNextVolumeMountPointA'); FindVolumeMountPointClose_MP := GetProcAddress(Kernel32Handle, 'FindVolumeMountPointClose'); FindFirstFileExW_MP := GetProcAddress(Kernel32Handle, 'FindFirstFileExW'); end; FindFirstFileExA_MP := GetProcAddress(Kernel32Handle, 'FindFirstFileExA'); // SHMultiFileProperties only supported on Win2k and WinXP // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/shell/reference/functions/shmultifileproperties.asp SHMultiFileProperties_MP := GetProcAddress(Shell32Handle, PChar(716)); CDefFolderMenu_Create2_MP := GetProcAddress(Shell32Handle, PChar(701)); CDefFolderMenu_Create_MP := GetProcAddress(Shell32Handle, PChar(700)); // If this unit is to be weak packages this must be removed finalization FreeAndNil(PIDLMgr); CommonUnloadAllLibraries; end.