doublecmd/plugins/dsx/everything/src/everything.pas
2017-08-14 18:03:45 +00:00

307 lines
8.9 KiB
ObjectPascal

{
Everything search engine interface via IPC
Copyright (C) 2017 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
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_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
TFoundCallback = procedure(FileName: PWideChar);
var
ChangeWindowMessageFilterEx: function(hWnd: HWND; message: UINT; action: DWORD; filter: PChangeFilterStruct): BOOL; stdcall;
procedure Start(FileMask: String; Flags: Integer; pr: TFoundCallback);
implementation
function SendQuery(hwnd: HWND; num: DWORD; search_string: PWideChar; search_flags: integer): Boolean;
var
query: ^TEVERYTHING_IPC_QUERYW;
len: Int32;
size: Int32;
everything_hwnd: HWND;
cds: COPYDATASTRUCT;
begin
everything_hwnd:= FindWindow(EVERYTHING_IPC_WNDCLASS, nil);
if (everything_hwnd <> 0) then
begin
len := StrLen(search_string);
size := SizeOf(TEVERYTHING_IPC_QUERYW) - SizeOf(WideChar) + len * SizeOf(WideChar) + SizeOf(WideChar);
query := GetMem(size);
if Assigned(query) then
begin
query^.offset := 0;
query^.max_results := num;
query^.reply_copydata_message := COPYDATA_IPCTEST_QUERYCOMPLETEW;
query^.search_flags := search_flags;
query^.reply_hwnd := hwnd;
StrLCopy(@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
//HeapFree(GetProcessHeap(),0,query);
//return 1;
REsult:= True;
end
else
begin
//write(L"Everything IPC service not running.\n");
end;
FreeMem(query);
end;
end;
Result:= False;
end;
function EVERYTHING_IPC_ITEMPATHW(list: PEVERYTHING_IPC_LISTW; item: PEVERYTHING_IPC_ITEMW): PWideChar;
begin
Result:= PWideChar(PByte(list) + item^.path_offset);
end;
function EVERYTHING_IPC_ITEMFILENAMEW(list: PEVERYTHING_IPC_LISTW; item: PEVERYTHING_IPC_ITEMW): PWideChar;
begin
Result:= PWideChar(PByte(list) + item^.filename_offset);
end;
procedure listresultsW(hwnd2: HWND; list: PEVERYTHING_IPC_LISTW);
var
I: Integer;
Item: PEVERYTHING_IPC_ITEMW;
CallB: TFoundCallback;
Result: PWideChar;
Res: UnicodeString;
begin
CallB:= TFoundCallback(GetWindowLongPtr(hwnd2, GWL_USERDATA));
for i:=0 to list^.numitems - 1 do
begin
Item:= PEVERYTHING_IPC_ITEMW(@list^.items) + i;
if (Item^.flags and EVERYTHING_IPC_DRIVE) <> 0 then
begin
//WriteLn(WideString(EVERYTHING_IPC_ITEMFILENAMEW(list, Item)));
Result:= EVERYTHING_IPC_ITEMFILENAMEW(list, Item);
end
else
begin
//WriteLn(WideString(EVERYTHING_IPC_ITEMPATHW(list, Item)));
//WriteLn(WideString(EVERYTHING_IPC_ITEMFILENAMEW(list, Item)));
Res:= UnicodeString(EVERYTHING_IPC_ITEMPATHW(list, Item)) + PathDelim + UnicodeString(EVERYTHING_IPC_ITEMFILENAMEW(list, Item));
Result:= PWideChar(Res);//EVERYTHING_IPC_ITEMFILENAMEW(list, Item);
end;
CallB(REsult);
end;
CallB(nil);
PostQuitMessage(0);
end;
function window_proc(hwnd: HWND; msg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
var
cds: PCOPYDATASTRUCT;
begin
case msg of
WM_COPYDATA:
begin
cds := PCOPYDATASTRUCT(lParam);
if cds^.dwData = COPYDATA_IPCTEST_QUERYCOMPLETEW then
begin
listresultsW(hwnd, PEVERYTHING_IPC_LISTW(cds^.lpData));
Exit(1);
end;
end;
end;
Result:= DefWindowProc(hwnd, msg, wParam, lParam);
end;
procedure Start(FileMask: String; Flags: Integer; pr: TFoundCallback);
var
wcex: WNDCLASSEX;
hwnd2: HWND;
HH: HMODULE;
lpMsg: TMsg;
begin
ZeroMemory(@wcex, SizeOf(wcex));
wcex.cbSize := sizeof(wcex);
wcex.hInstance := System.HINSTANCE;;
wcex.lpfnWndProc := @window_proc;
wcex.lpszClassName := 'IPCTEST';
if (RegisterClassEx(@wcex) = 0) then
begin
WriteLn('failed to register IPCTEST window class');
end;
hwnd2 := CreateWindow(
'IPCTEST',
'',
0,
0,0,0,0,
0,0,HINSTANCE,nil);
HH:= LoadLibrary('user32.dll');
Pointer(ChangeWindowMessageFilterEx) := GetProcAddress(HH, 'ChangeWindowMessageFilterEx');
ChangeWindowMessageFilterEx(hwnd2, WM_COPYDATA, MSGFLT_ALLOW, nil);
SetWindowLongPtr(hwnd2, GWL_USERDATA, LONG_PTR(pr));
sendquery(hwnd2,EVERYTHING_IPC_ALLRESULTS,PWideChar(WideString(FileMask)), Flags);
while (True) do
begin
if (PeekMessage(lpmsg, 0,0,0,0)) then
begin
if not GetMessage(lpmsg,0,0,0) then Exit;
// let windows handle it.
TranslateMessage(lpmsg);
DispatchMessage(lpmsg);
end
else
begin
WaitMessage();
end;
end;
end;
end.