doublecmd/plugins/dsx/everything/src/everything.pas
2025-06-25 21:16:52 +03:00

308 lines
9.1 KiB
ObjectPascal

{
Everything search engine interface via IPC
Copyright (C) 2017-2023 Alexander Koblov (alexx2000@mail.ru)
Based on Everything command line interface source
Copyright (C) 2016 David Carpenter
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
}
unit everything;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, Windows;
const
EVERYTHING_DSX_WNDCLASS = 'EVERYTHING_DSX';
const
COPYDATA_IPCTEST_QUERYCOMPLETEW = 0;
MSGFLT_RESET = 0;
MSGFLT_ALLOW = 1;
MSGFLT_DISALLOW = 2;
EVERYTHING_IPC_COPYDATAQUERYW = 2;
EVERYTHING_IPC_SEARCH_WNDCLASS = 'EVERYTHING';
EVERYTHING_IPC_WNDCLASS = 'EVERYTHING_TASKBAR_NOTIFICATION';
EVERYTHING_IPC_ALLRESULTS = $FFFFFFFF; // all results
EVERYTHING_IPC_FOLDER = $00000001; // The item is a folder. (its a file if not set)
EVERYTHING_IPC_DRIVE = $00000002; // The folder is a drive. Path will be an empty string.
// search flags for querys
EVERYTHING_IPC_MATCHCASE = $00000001; // match case
EVERYTHING_IPC_MATCHPATH = $00000004; // include paths in search
EVERYTHING_IPC_REGEX = $00000008; // enable regex
type
PChangeFilterStruct = ^TChangeFilterStruct;
TChangeFilterStruct = record
cbSize: DWORD;
ExtStatus: DWORD;
end;
{$push}{$packrecords 1}
TEVERYTHING_IPC_QUERYW = record
// the window that will receive the new results.
reply_hwnd: HWND;
// the value to set the dwData member in the COPYDATASTRUCT struct
// sent by Everything when the query is complete.
reply_copydata_message: ULONG_PTR;
// search flags (see EVERYTHING_MATCHCASE | EVERYTHING_MATCHWHOLEWORD | EVERYTHING_MATCHPATH)
search_flags: DWORD;
// only return results after 'offset' results (0 to return the first result)
// useful for scrollable lists
offset: DWORD;
// the number of results to return
// zero to return no results
// EVERYTHING_IPC_ALLRESULTS to return ALL results
max_results: DWORD;
// null terminated string. arbitrary sized search_string buffer.
search_string: WCHAR;
end;
PEVERYTHING_IPC_ITEMW = ^TEVERYTHING_IPC_ITEMW;
TEVERYTHING_IPC_ITEMW = record
// item flags
flags: DWORD;
// The offset of the filename from the beginning of the list structure.
// (wchar_t *)((char *)everything_list + everythinglist->name_offset)
filename_offset: DWORD;
// The offset of the filename from the beginning of the list structure.
// (wchar_t *)((char *)everything_list + everythinglist->path_offset)
path_offset: DWORD;
end;
PEVERYTHING_IPC_LISTW = ^TEVERYTHING_IPC_LISTW;
TEVERYTHING_IPC_LISTW = record
// the total number of folders found.
totfolders: DWORD;
// the total number of files found.
totfiles: DWORD;
// totfolders + totfiles
totitems: DWORD;
// the number of folders available.
numfolders: DWORD;
// the number of files available.
numfiles: DWORD;
// the number of items available.
numitems: DWORD;
// index offset of the first result in the item list.
offset: DWORD;
// arbitrary sized item list.
// use numitems to determine the actual number of items available.
items: TEVERYTHING_IPC_ITEMW;
end;
{$pop}
type
TFindData = class
Cancel: Boolean;
PluginNumber: Integer;
FoundCallback: procedure(FileName: PWideChar; UserData: Pointer);
end;
procedure Start(FileMask: String; Flags: Integer; FindData: TFindData);
implementation
var
ChangeWindowMessageFilterEx: function(hWnd: HWND; message: UINT; action: DWORD; filter: PChangeFilterStruct): BOOL; stdcall;
function SendQuery(hwnd: HWND; num: DWORD; const search_string: UnicodeString; search_flags: integer): Boolean;
var
len: Int32;
size: Int32;
cds: COPYDATASTRUCT;
everything_hwnd: HWND;
query: ^TEVERYTHING_IPC_QUERYW;
begin
everything_hwnd:= FindWindow(EVERYTHING_IPC_WNDCLASS, nil);
if (everything_hwnd = 0) then
MessageBoxW(0, 'Everything not found!', nil, MB_OK or MB_ICONERROR)
else begin
len:= Length(search_string);
size := SizeOf(TEVERYTHING_IPC_QUERYW) + len * SizeOf(WideChar);
query:= GetMem(size);
if (query = nil) then
begin
MessageBoxW(0, PWideChar(UnicodeString(SysErrorMessage(E_OUTOFMEMORY))), nil, MB_OK or MB_ICONERROR);
Exit(False);
end;
try
query^.offset := 0;
query^.max_results := num;
query^.reply_copydata_message := COPYDATA_IPCTEST_QUERYCOMPLETEW;
query^.search_flags := search_flags;
query^.reply_hwnd := hwnd;
StrPLCopy(@query^.search_string, search_string, len);
cds.cbData := size;
cds.dwData := EVERYTHING_IPC_COPYDATAQUERYW;
cds.lpData := query;
if (SendMessage(everything_hwnd, WM_COPYDATA, WPARAM(hwnd), LPARAM(@cds)) <> 0) then
begin
Exit(True);
end;
finally
FreeMem(query);
end;
end;
Result:= False;
end;
function EVERYTHING_IPC_ITEMPATHW(AList: PEVERYTHING_IPC_LISTW; Item: PEVERYTHING_IPC_ITEMW): PWideChar; inline;
begin
Result:= PWideChar(PByte(AList) + Item^.path_offset);
end;
function EVERYTHING_IPC_ITEMFILENAMEW(AList: PEVERYTHING_IPC_LISTW; Item: PEVERYTHING_IPC_ITEMW): PWideChar; inline;
begin
Result:= PWideChar(PByte(AList) + Item^.filename_offset);
end;
procedure ListResults(hWnd: HWND; AList: PEVERYTHING_IPC_LISTW);
var
I: DWORD = 0;
Temp: UnicodeString;
FileName: PWideChar;
FindData: TFindData;
Item: PEVERYTHING_IPC_ITEMW;
dwOldLong: LONG_PTR absolute FindData;
begin
dwOldLong:= GetWindowLongPtr(hWnd, GWL_USERDATA);
while I < AList^.numitems do
begin
Item:= PEVERYTHING_IPC_ITEMW(@AList^.items) + I;
if (Item^.flags and EVERYTHING_IPC_DRIVE) <> 0 then
begin
FileName:= EVERYTHING_IPC_ITEMFILENAMEW(AList, Item);
end
else
begin
Temp:= UnicodeString(EVERYTHING_IPC_ITEMPATHW(AList, Item)) + PathDelim + UnicodeString(EVERYTHING_IPC_ITEMFILENAMEW(AList, Item));
FileName:= PWideChar(Temp);
end;
if (FindData.Cancel) then Break;
FindData.FoundCallback(FileName, FindData);
Inc(I);
end;
FindData.FoundCallback(nil, FindData);
PostMessage(hWnd, WM_CLOSE, 0, 0);
end;
function WindowProc(hwnd: HWND; msg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
var
cds: PCopyDataStruct absolute lParam;
begin
Result:= 0;
case msg of
WM_COPYDATA:
begin
if cds^.dwData = COPYDATA_IPCTEST_QUERYCOMPLETEW then
begin
ListResults(hwnd, PEVERYTHING_IPC_LISTW(cds^.lpData));
Exit(1);
end;
end;
WM_CLOSE:
DestroyWindow(hwnd);
else
Result:= DefWindowProc(hwnd, msg, wParam, lParam);
end;
end;
procedure Start(FileMask: String; Flags: Integer; FindData: TFindData);
var
hWnd: Windows.HWND;
dwNewLong: LONG_PTR absolute FindData;
begin
hWnd := CreateWindow(EVERYTHING_DSX_WNDCLASS,
'', 0, 0, 0, 0, 0, 0, 0, HINSTANCE,nil);
if Assigned(ChangeWindowMessageFilterEx) then
begin
ChangeWindowMessageFilterEx(hWnd, WM_COPYDATA, MSGFLT_ALLOW, nil);
end;
SetWindowLongPtr(hWnd, GWL_USERDATA, dwNewLong);
if not SendQuery(hWnd, EVERYTHING_IPC_ALLRESULTS, UnicodeString(FileMask), Flags) then
begin
FindData.FoundCallback(nil, FindData);
PostMessage(hWnd, WM_CLOSE, 0, 0);
end;
end;
procedure Initialize;
var
hUser32: HMODULE;
wcex: TWndClassEx;
begin
wcex:= Default(TWndClassEx);
wcex.cbSize:= SizeOf(TWndClassEx);
wcex.hInstance:= System.HINSTANCE;
wcex.lpfnWndProc:= @WindowProc;
wcex.lpszClassName:= EVERYTHING_DSX_WNDCLASS;
if (RegisterClassEx(@wcex) = 0) then
begin
// WriteLn('failed to register IPCTEST window class');
end;
hUser32:= GetModuleHandle(User32);
Pointer(ChangeWindowMessageFilterEx):= GetProcAddress(hUser32, 'ChangeWindowMessageFilterEx');
end;
initialization
Initialize;
end.