Mooore aggressive GC; rewrite native dll

This commit is contained in:
Paddy Xu
2017-05-29 00:11:16 +03:00
parent 13dfa7a815
commit 5ad20b8a53
15 changed files with 252 additions and 352 deletions

View File

@@ -5,22 +5,12 @@
#define EXPORT extern "C" __declspec(dllexport) #define EXPORT extern "C" __declspec(dllexport)
EXPORT int GetFocusedWindowType() EXPORT Shell32::FocusedWindowType GetFocusedWindowType()
{ {
return Shell32::GetFocusedWindowType(); return Shell32::GetFocusedWindowType();
} }
EXPORT void SaveCurrentSelection() EXPORT void GetCurrentSelection(PWCHAR buffer)
{ {
Shell32::SaveCurrentSelection(); Shell32::GetCurrentSelection(buffer);
}
EXPORT int GetCurrentSelectionCount()
{
return Shell32::GetCurrentSelectionCount();
}
EXPORT void GetCurrentSelectionBuffer(PWCHAR buffer)
{
Shell32::GetCurrentSelectionBuffer(buffer);
} }

View File

@@ -1,201 +1,19 @@
#include "stdafx.h" #include "stdafx.h"
#include "Shell32.h" #include "Shell32.h"
#include <atlcomcli.h>
using namespace std; using namespace std;
vector<wstring> Shell32::vector_items;
void Shell32::SaveCurrentSelection()
{
vector_items.clear();
switch (GetFocusedWindowType())
{
case EXPLORER:
SaveSelectedFromExplorer();
break;
case DESKTOP:
SaveSelectedFromDesktop();
break;
default:
break;
}
}
UINT Shell32::GetCurrentSelectionCount()
{
return vector_items.size();
}
void Shell32::GetCurrentSelectionBuffer(PWCHAR buffer)
{
PWCHAR pos = buffer;
for (vector<wstring>::iterator it = vector_items.begin(); it < vector_items.end(); ++it)
{
int l = it->length();
wcscpy_s(pos, l + 1, it->c_str());
pos += l;
// overwrite NULL
wcscpy_s(pos++, 2, L"|");
}
// remove last "|"
wcscpy_s(pos - 1, 1, L"");
}
void Shell32::SaveSelectedFromExplorer()
{
CoInitialize(nullptr);
CComPtr<IShellWindows> psw;
HRESULT ret = psw.CoCreateInstance(CLSID_ShellWindows);
auto hwndFGW = GetForegroundWindow();
auto fFound = FALSE;
for (int i = 0; !fFound; i++)
{
VARIANT vi;
V_VT(&vi) = VT_I4;
V_I4(&vi) = i;
CComPtr<IDispatch> pdisp;
// ReSharper disable once CppSomeObjectMembersMightNotBeInitialized
if (SUCCEEDED(psw->Item(vi, &pdisp)))
{
CComPtr<IWebBrowserApp> pwba;
if (SUCCEEDED(pdisp->QueryInterface(IID_IWebBrowserApp, reinterpret_cast<void**>(&pwba))))
{
HWND hwndWBA;
if (SUCCEEDED(pwba->get_HWND(reinterpret_cast<LONG_PTR*>(&hwndWBA))) && hwndWBA == hwndFGW)
{
fFound = TRUE;
CComPtr<IDispatch> ppdisp;
if (SUCCEEDED(pwba->get_Document(&ppdisp)))
{
CComPtr<IShellFolderViewDual2> pshvd;
if (SUCCEEDED(ppdisp->QueryInterface(IID_IShellFolderViewDual2, reinterpret_cast<void**>(&pshvd))))
{
CComPtr<FolderItems> pfis;
if (SUCCEEDED(pshvd->SelectedItems(&pfis)))
{
LONG pCount = 0L;
pfis->get_Count(&pCount);
for (int ii = 0; ii < pCount; ii++)
{
VARIANT vii;
V_VT(&vii) = VT_I4;
V_I4(&vii) = ii;
CComPtr<FolderItem> pfi;
// ReSharper disable once CppSomeObjectMembersMightNotBeInitialized
if (SUCCEEDED(pfis->Item(vii, &pfi)))
{
CComBSTR pbs = SysAllocStringLen(L"", MAX_PATH);
pfi->get_Path(&pbs);
wstring ws = wstring(pbs);
ws.shrink_to_fit();
vector_items.push_back(ws);
}
}
}
}
}
}
}
}
}
}
CComQIPtr<IWebBrowser2> Shell32::AttachDesktopShellWindow()
{
CoInitialize(nullptr);
CComPtr<IShellWindows> psw;
CComQIPtr<IWebBrowser2> pdispOut;
if (SUCCEEDED(psw.CoCreateInstance(CLSID_ShellWindows)))
{
VARIANT pvarLoc = {VT_EMPTY};
long phwnd;
psw->FindWindowSW(&pvarLoc, &pvarLoc, SWC_DESKTOP, &phwnd, SWFO_NEEDDISPATCH, reinterpret_cast<IDispatch**>(&pdispOut));
}
return pdispOut;
}
void Shell32::SaveSelectedFromDesktop()
{
auto pWebBrowser2 = AttachDesktopShellWindow();
if (!pWebBrowser2)
return;
CComQIPtr<IServiceProvider> psp(pWebBrowser2);
if (!psp) return;
CComPtr<IShellBrowser> psb;
if (SUCCEEDED(psp->QueryService(SID_STopLevelBrowser, IID_IShellBrowser, reinterpret_cast<LPVOID*>(&psb))))
{
CComPtr<IShellView> psv;
if (SUCCEEDED(psb->QueryActiveShellView(&psv)))
{
CComPtr<IFolderView> pfv;
if (SUCCEEDED(psv->QueryInterface(IID_IFolderView, reinterpret_cast<void**>(&pfv))))
{
CComPtr<IDataObject> dao;
if (SUCCEEDED(psv->GetItemObject(SVGIO_SELECTION, IID_IDataObject, reinterpret_cast<void**>(&dao))))
vectorFromDataObject(dao);
}
}
}
}
void Shell32::vectorFromDataObject(CComPtr<IDataObject> dao)
{
FORMATETC formatetc;
STGMEDIUM medium;
formatetc.cfFormat = CF_HDROP;
formatetc.ptd = nullptr;
formatetc.dwAspect = DVASPECT_CONTENT;
formatetc.lindex = -1;
formatetc.tymed = TYMED_HGLOBAL;
medium.tymed = TYMED_HGLOBAL;
if (SUCCEEDED(dao->GetData(&formatetc, &medium)))
{
int n = DragQueryFile(HDROP(medium.hGlobal), 0xFFFFFFFF, nullptr, 0);
for (int i = 0; i < n; i++)
{
WCHAR buffer[MAX_PATH];
DragQueryFile(HDROP(medium.hGlobal), i, buffer, MAX_PATH - 1);
wstring ws = wstring(buffer);
ws.shrink_to_fit();
vector_items.push_back(ws);
}
}
}
Shell32::FocusedWindowType Shell32::GetFocusedWindowType() Shell32::FocusedWindowType Shell32::GetFocusedWindowType()
{ {
auto type = INVALID; auto type = INVALID;
auto hwndfg = GetForegroundWindow(); auto hwndfg = GetForegroundWindow();
if (isCursorActivated(hwndfg))
return INVALID;
auto classBuffer = new WCHAR[MAX_PATH]; auto classBuffer = new WCHAR[MAX_PATH];
if (SUCCEEDED(GetClassName(hwndfg, classBuffer, MAX_PATH))) if (SUCCEEDED(GetClassName(hwndfg, classBuffer, MAX_PATH)))
{ {
@@ -215,3 +33,132 @@ Shell32::FocusedWindowType Shell32::GetFocusedWindowType()
return type; return type;
} }
void Shell32::GetCurrentSelection(PWCHAR buffer)
{
switch (GetFocusedWindowType())
{
case EXPLORER:
getSelectedFromExplorer(buffer);
break;
case DESKTOP:
getSelectedFromDesktop(buffer);
break;
default:
break;
}
}
void Shell32::getSelectedFromExplorer(PWCHAR buffer)
{
CoInitialize(nullptr);
CComPtr<IShellWindows> psw;
if (FAILED(psw.CoCreateInstance(CLSID_ShellWindows)))
return;
auto hwndFGW = GetForegroundWindow();
auto count = 0L;
psw->get_Count(&count);
for (auto i = 0; i < count; i++)
{
VARIANT vi;
V_VT(&vi) = VT_I4;
V_I4(&vi) = i;
CComPtr<IDispatch> pdisp;
// ReSharper disable once CppSomeObjectMembersMightNotBeInitialized
if (FAILED(psw->Item(vi, &pdisp)))
continue;
CComQIPtr<IWebBrowserApp> pwba;
if (FAILED(pdisp->QueryInterface(IID_IWebBrowserApp, reinterpret_cast<void**>(&pwba))))
continue;
HWND hwndWBA;
if (FAILED(pwba->get_HWND(reinterpret_cast<LONG_PTR*>(&hwndWBA))))
continue;
if (hwndWBA != hwndFGW || isCursorActivated(hwndWBA))
continue;
getSelectedInternal(pwba, buffer);
}
}
void Shell32::getSelectedFromDesktop(PWCHAR buffer)
{
CoInitialize(nullptr);
CComPtr<IShellWindows> psw;
CComQIPtr<IWebBrowserApp> pwba;
if (FAILED(psw.CoCreateInstance(CLSID_ShellWindows)))
return;
VARIANT pvarLoc = {VT_EMPTY};
long phwnd;
if (FAILED(psw->FindWindowSW(&pvarLoc, &pvarLoc, SWC_DESKTOP, &phwnd, SWFO_NEEDDISPATCH, reinterpret_cast<IDispatch**>(&pwba))))
return;
if (isCursorActivated(reinterpret_cast<HWND>(phwnd)))
return;
getSelectedInternal(pwba, buffer);
}
void Shell32::getSelectedInternal(CComQIPtr<IWebBrowserApp> pwba, PWCHAR buffer)
{
CComQIPtr<IServiceProvider> psp;
if (FAILED(pwba->QueryInterface(IID_IServiceProvider, reinterpret_cast<void**>(&psp))))
return;
CComPtr<IShellBrowser> psb;
if (FAILED(psp->QueryService(SID_STopLevelBrowser, IID_IShellBrowser, reinterpret_cast<LPVOID*>(&psb))))
return;
CComPtr<IShellView> psv;
if (FAILED(psb->QueryActiveShellView(&psv)))
return;
CComPtr<IDataObject> dao;
if (FAILED(psv->GetItemObject(SVGIO_SELECTION, IID_IDataObject, reinterpret_cast<void**>(&dao))))
return;
return obtainFirstItem(dao, buffer);
}
void Shell32::obtainFirstItem(CComPtr<IDataObject> dao, PWCHAR buffer)
{
FORMATETC formatetc;
STGMEDIUM medium;
formatetc.cfFormat = CF_HDROP;
formatetc.ptd = nullptr;
formatetc.dwAspect = DVASPECT_CONTENT;
formatetc.lindex = -1;
formatetc.tymed = TYMED_HGLOBAL;
medium.tymed = TYMED_HGLOBAL;
if (FAILED(dao->GetData(&formatetc, &medium)))
return;
int n = DragQueryFile(HDROP(medium.hGlobal), 0xFFFFFFFF, nullptr, 0);
if (n < 1)
return;
DragQueryFile(HDROP(medium.hGlobal), 0, buffer, MAX_PATH - 1);
}
bool Shell32::isCursorActivated(HWND hwnd)
{
auto tId = GetWindowThreadProcessId(hwnd, nullptr);
GUITHREADINFO gui = {sizeof gui};
GetGUIThreadInfo(tId, &gui);
return gui.flags || gui.hwndCaret;
}

View File

@@ -13,16 +13,13 @@ public:
}; };
static FocusedWindowType GetFocusedWindowType(); static FocusedWindowType GetFocusedWindowType();
static void SaveCurrentSelection(); static void GetCurrentSelection(PWCHAR buffer);
static UINT GetCurrentSelectionCount();
static void GetCurrentSelectionBuffer(PWCHAR buffer);
private: private:
static std::vector<std::wstring> vector_items; static void getSelectedFromDesktop(PWCHAR buffer);
static void getSelectedFromExplorer(PWCHAR buffer);
static void SaveSelectedFromDesktop(); static void getSelectedInternal(CComQIPtr<IWebBrowserApp> pWebBrowserApp, PWCHAR buffer);
static void SaveSelectedFromExplorer(); static void obtainFirstItem(CComPtr<IDataObject> dao, PWCHAR buffer);
static bool isCursorActivated(HWND hwndfg);
static CComQIPtr<IWebBrowser2> AttachDesktopShellWindow();
static void vectorFromDataObject(CComPtr<IDataObject> dao);
}; };

View File

@@ -4,6 +4,7 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Windows; using System.Windows;
using QuickLook.Helpers;
namespace QuickLook namespace QuickLook
{ {

View File

@@ -17,6 +17,7 @@ namespace QuickLook
public void Dispose() public void Dispose()
{ {
_hook?.Dispose(); _hook?.Dispose();
_hook = null;
} }
private void HotkeyEventHandler(object sender, KeyEventArgs e) private void HotkeyEventHandler(object sender, KeyEventArgs e)

View File

@@ -1,19 +1,18 @@
using System; using System;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using QuickLook.Helpers;
namespace QuickLook namespace QuickLook
{ {
internal class FocusMonitor internal class FocusMonitor
{ {
public delegate void FocusedItemChangedEventHandler(FocusedItemChangedEventArgs e); public delegate void HeartbeatEventHandler(HeartbeatEventArgs e);
private static FocusMonitor _instance; private static FocusMonitor _instance;
public bool IsRunning { get; private set; } public bool IsRunning { get; private set; }
public event FocusedItemChangedEventHandler FocusedItemChanged; public event HeartbeatEventHandler Heartbeat;
public void Start() public void Start()
{ {
@@ -21,21 +20,16 @@ namespace QuickLook
new Task(() => new Task(() =>
{ {
var currentPath = NativeMethods.QuickLook.GetCurrentSelectionFirst();
while (IsRunning) while (IsRunning)
{ {
Thread.Sleep(500); Thread.Sleep(500);
if (WindowHelper.IsFocusedControlExplorerItem()) if (NativeMethods.QuickLook.GetFocusedWindowType() ==
{ NativeMethods.QuickLook.FocusedWindowType.Invalid)
var file = NativeMethods.QuickLook.GetCurrentSelectionFirst(); continue;
if (file != currentPath)
{ var file = NativeMethods.QuickLook.GetCurrentSelection();
FocusedItemChanged?.Invoke(new FocusedItemChangedEventArgs(file)); Heartbeat?.Invoke(new HeartbeatEventArgs(DateTime.Now.Ticks, file));
currentPath = file;
}
}
} }
}).Start(); }).Start();
} }
@@ -51,13 +45,15 @@ namespace QuickLook
} }
} }
internal class FocusedItemChangedEventArgs : EventArgs internal class HeartbeatEventArgs : EventArgs
{ {
public FocusedItemChangedEventArgs(string files) public HeartbeatEventArgs(long tick, string files)
{ {
InvokeTick = tick;
FocusedFile = files; FocusedFile = files;
} }
public long InvokeTick { get; }
public string FocusedFile { get; } public string FocusedFile { get; }
} }
} }

View File

@@ -1,7 +1,7 @@
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
namespace QuickLook namespace QuickLook.Helpers
{ {
internal static class PidHelper internal static class PidHelper
{ {

View File

@@ -1,6 +1,5 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Text;
using System.Windows; using System.Windows;
using System.Windows.Forms; using System.Windows.Forms;
using System.Windows.Interop; using System.Windows.Interop;
@@ -61,65 +60,21 @@ namespace QuickLook.Helpers
pixelY = (int) (matrix.M22 * unitY); pixelY = (int) (matrix.M22 * unitY);
} }
internal static bool IsForegroundWindowBelongToSelf()
{
var hwnd = User32.GetForegroundWindow();
if (hwnd == IntPtr.Zero)
return false;
User32.GetWindowThreadProcessId(hwnd, out uint procId);
return procId == Process.GetCurrentProcess().Id;
}
internal static void SetNoactivate(WindowInteropHelper window) internal static void SetNoactivate(WindowInteropHelper window)
{ {
User32.SetWindowLong(window.Handle, User32.GWL_EXSTYLE, User32.SetWindowLong(window.Handle, User32.GWL_EXSTYLE,
User32.GetWindowLong(window.Handle, User32.GWL_EXSTYLE) | User32.GetWindowLong(window.Handle, User32.GWL_EXSTYLE) |
User32.WS_EX_NOACTIVATE); User32.WS_EX_NOACTIVATE);
} }
internal static string GetWindowClassName(IntPtr window)
{
var pClassName = new StringBuilder(256);
User32.GetClassName(window, pClassName, pClassName.Capacity);
return pClassName.ToString();
}
internal static IntPtr GetParentWindow(IntPtr child)
{
return User32.GetParent(child);
}
internal static IntPtr GetFocusedWindow()
{
var activeWindowHandle = User32.GetForegroundWindow();
var activeWindowThread = User32.GetWindowThreadProcessId(activeWindowHandle, IntPtr.Zero);
var currentThread = Kernel32.GetCurrentThreadId();
User32.AttachThreadInput(activeWindowThread, currentThread, true);
var focusedControlHandle = User32.GetFocus();
User32.AttachThreadInput(activeWindowThread, currentThread, false);
return focusedControlHandle;
}
internal static bool IsFocusedWindowSelf()
{
var procId = Process.GetCurrentProcess().Id;
uint activeProcId;
User32.GetWindowThreadProcessId(GetFocusedWindow(), out activeProcId);
return activeProcId == procId;
}
internal static bool IsFocusedControlExplorerItem()
{
if (NativeMethods.QuickLook.GetFocusedWindowType() == 0)
return false;
var focusedWindowClass = GetWindowClassName(GetFocusedWindow());
var focusedWindowParentClass =
GetWindowClassName(GetParentWindow(GetFocusedWindow()));
if (focusedWindowClass != "SysListView32" && focusedWindowClass != "DirectUIHWND")
return false;
if (focusedWindowParentClass != "SHELLDLL_DefView")
return false;
return true;
}
} }
} }

View File

@@ -62,11 +62,17 @@
WindowChrome.IsHitTestVisibleInChrome="True" WindowChrome.IsHitTestVisibleInChrome="True"
Height="15" Margin="10,0" Foreground="Gray" Height="15" Margin="10,0" Foreground="Gray"
Cursor="Hand" /> Cursor="Hand" />
<Button x:Name="buttonOpenWith" DockPanel.Dock="Right" Content="Open with ..." Height="20" <Button x:Name="buttonOpenWith" DockPanel.Dock="Right" Content="Open with..." Height="20"
Margin="10,0,0,0" Padding="5,0" Margin="10,0,0,0" Padding="5,0"
Focusable="False" Cursor="Hand" Focusable="False" Cursor="Hand"
Background="#E5EEEEEE" BorderBrush="#E59A9A9A" Background="#E5EEEEEE" BorderBrush="#E59A9A9A"
WindowChrome.IsHitTestVisibleInChrome="True" Foreground="#FF404040" /> WindowChrome.IsHitTestVisibleInChrome="True" Foreground="#FF404040">
<Button.ContentTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding}" RecognizesAccessKey="False" />
</DataTemplate>
</Button.ContentTemplate>
</Button>
<!-- set grid.background colour makes it clickable --> <!-- set grid.background colour makes it clickable -->
<Grid x:Name="titleArea" Background="Transparent"> <Grid x:Name="titleArea" Background="Transparent">
<TextBlock Text="{Binding ContextObject.Title, ElementName=mainWindow}" FontSize="14" <TextBlock Text="{Binding ContextObject.Title, ElementName=mainWindow}" FontSize="14"

View File

@@ -3,13 +3,11 @@ using System.Diagnostics;
using System.IO; using System.IO;
using System.Runtime.ExceptionServices; using System.Runtime.ExceptionServices;
using System.Windows; using System.Windows;
using System.Windows.Forms;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Threading; using System.Windows.Threading;
using QuickLook.Helpers; using QuickLook.Helpers;
using QuickLook.Helpers.BlurLibrary; using QuickLook.Helpers.BlurLibrary;
using QuickLook.Plugin; using QuickLook.Plugin;
using KeyEventArgs = System.Windows.Forms.KeyEventArgs;
namespace QuickLook namespace QuickLook
{ {
@@ -38,16 +36,16 @@ namespace QuickLook
BlurWindow.EnableWindowBlur(this); BlurWindow.EnableWindowBlur(this);
}; };
buttonCloseWindow.MouseLeftButtonUp += (sender, e) => ViewWindowManager.GetInstance() buttonCloseWindow.MouseLeftButtonUp += (sender, e) =>
.InvokeRoutine(new KeyEventArgs(Keys.Escape)); ViewWindowManager.GetInstance().ClosePreview();
buttonOpenWith.Click += (sender, e) => ViewWindowManager.GetInstance() buttonOpenWith.Click += (sender, e) =>
.InvokeRoutine(new KeyEventArgs(Keys.Enter)); ViewWindowManager.GetInstance().RunAndClosePreview();
} }
public ContextObject ContextObject { get; private set; } public ContextObject ContextObject { get; private set; }
internal void RunAndClose() internal void RunAndHide()
{ {
if (string.IsNullOrEmpty(_path)) if (string.IsNullOrEmpty(_path))
return; return;
@@ -83,6 +81,12 @@ namespace QuickLook
internal void UnloadPlugin() internal void UnloadPlugin()
{ {
// the focused element will not processed by GC: https://stackoverflow.com/questions/30848939/memory-leak-due-to-window-efectivevalues-retention
FocusManager.SetFocusedElement(this, null);
Keyboard.DefaultRestoreFocusMode =
RestoreFocusMode.None; // WPF will put the focused item into a "_restoreFocus" list ... omg
Keyboard.ClearFocus();
ContextObject.Reset(); ContextObject.Reset();
_plugin?.Cleanup(); _plugin?.Cleanup();
@@ -151,10 +155,6 @@ namespace QuickLook
internal void BeginHide() internal void BeginHide()
{ {
// the focused element will not processed by GC: https://stackoverflow.com/questions/30848939/memory-leak-due-to-window-efectivevalues-retention
FocusManager.SetFocusedElement(this, null);
Keyboard.ClearFocus();
UnloadPlugin(); UnloadPlugin();
Hide(); Hide();

View File

@@ -1,5 +1,4 @@
using System.Linq; using System.Runtime.InteropServices;
using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -8,40 +7,30 @@ namespace QuickLook.NativeMethods
internal static class QuickLook internal static class QuickLook
{ {
[DllImport("QuickLook.Native.Shell32.dll", CallingConvention = CallingConvention.Cdecl)] [DllImport("QuickLook.Native.Shell32.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern int GetFocusedWindowType(); internal static extern FocusedWindowType GetFocusedWindowType();
[DllImport("QuickLook.Native.Shell32.dll", CallingConvention = CallingConvention.Cdecl)] [DllImport("QuickLook.Native.Shell32.dll", EntryPoint = "GetCurrentSelection",
internal static extern void SaveCurrentSelection(); CallingConvention = CallingConvention.Cdecl)]
internal static extern void GetCurrentSelectionNative([MarshalAs(UnmanagedType.LPWStr)] StringBuilder sb);
[DllImport("QuickLook.Native.Shell32.dll", CallingConvention = CallingConvention.Cdecl)] internal static string GetCurrentSelection()
internal static extern int GetCurrentSelectionCount();
[DllImport("QuickLook.Native.Shell32.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern void GetCurrentSelectionBuffer([MarshalAs(UnmanagedType.LPWStr)] StringBuilder buffer);
internal static string[] GetCurrentSelection()
{ {
StringBuilder sb = null; StringBuilder sb = null;
// communicate with COM in a separate thread // communicate with COM in a separate thread
Task.Run(() => Task.Run(() =>
{ {
SaveCurrentSelection(); sb = new StringBuilder(255 + 1);
GetCurrentSelectionNative(sb);
var n = GetCurrentSelectionCount();
if (n != 0)
{
sb = new StringBuilder(n * 261); // MAX_PATH + NULL = 261
GetCurrentSelectionBuffer(sb);
}
}).Wait(); }).Wait();
return sb == null || sb.Length == 0 ? new string[0] : sb.ToString().Split('|');
return sb?.ToString() ?? string.Empty;
} }
internal static string GetCurrentSelectionFirst() internal enum FocusedWindowType
{ {
var files = GetCurrentSelection(); Invalid,
Desktop,
return files.Any() ? files.First() : string.Empty; Explorer
} }
} }
} }

View File

@@ -31,8 +31,9 @@
</Image> </Image>
<Grid Grid.Column="1"> <Grid Grid.Column="1">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="20" /> <ColumnDefinition Width="5" />
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
<ColumnDefinition Width="10" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="10" /> <RowDefinition Height="10" />
@@ -42,7 +43,11 @@
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<TextBlock x:Name="filename" Grid.Row="1" Grid.Column="1" FontSize="19" FontWeight="SemiBold" Padding="3">Filename.ext</TextBlock> <TextBlock x:Name="filename" Grid.Row="1" Grid.Column="1" FontSize="19" FontWeight="SemiBold" Padding="3"
TextWrapping="Wrap"
LineHeight="25" MaxHeight="60" TextTrimming="CharacterEllipsis">
FilenameFilenameFilenameFilenameFilenameFilenameFilenameFilenameFilenameFilename.ext
</TextBlock>
<TextBlock x:Name="modDate" Grid.Row="3" Grid.Column="1" Padding="3">Last modified at 01/01/2017 00:00:00</TextBlock> <TextBlock x:Name="modDate" Grid.Row="3" Grid.Column="1" Padding="3">Last modified at 01/01/2017 00:00:00</TextBlock>
<TextBlock x:Name="totalSize" Grid.Row="4" Grid.Column="1" Padding="3">Calculating size...</TextBlock> <TextBlock x:Name="totalSize" Grid.Row="4" Grid.Column="1" Padding="3">Calculating size...</TextBlock>
</Grid> </Grid>

View File

@@ -116,7 +116,7 @@
<Compile Include="NativeMethods\Shell32\IWshShortcut.cs" /> <Compile Include="NativeMethods\Shell32\IWshShortcut.cs" />
<Compile Include="NativeMethods\Shell32\ShellLink.cs" /> <Compile Include="NativeMethods\Shell32\ShellLink.cs" />
<Compile Include="NativeMethods\Shell32\WshShell.cs" /> <Compile Include="NativeMethods\Shell32\WshShell.cs" />
<Compile Include="PidHelper.cs" /> <Compile Include="Helpers\PidHelper.cs" />
<Compile Include="PluginManager.cs" /> <Compile Include="PluginManager.cs" />
<Compile Include="Plugin\InfoPanel\DpiHelpers.cs" /> <Compile Include="Plugin\InfoPanel\DpiHelpers.cs" />
<Compile Include="Plugin\InfoPanel\Extensions.cs" /> <Compile Include="Plugin\InfoPanel\Extensions.cs" />

View File

@@ -3,7 +3,6 @@ using System.Diagnostics;
using System.Windows.Forms; using System.Windows.Forms;
using QuickLook.Helpers; using QuickLook.Helpers;
using QuickLook.Properties; using QuickLook.Properties;
using Application = System.Windows.Application;
namespace QuickLook namespace QuickLook
{ {
@@ -26,14 +25,17 @@ namespace QuickLook
{ {
_icon = new NotifyIcon _icon = new NotifyIcon
{ {
Text = $"QuickLook v{Application.ProductVersion}",
Icon = Resources.app, Icon = Resources.app,
Visible = true, Visible = true,
ContextMenu = new ContextMenu(new[] ContextMenu = new ContextMenu(new[]
{ {
new MenuItem($"Check for &Updates ({System.Windows.Forms.Application.ProductVersion})", new MenuItem($"v{Application.ProductVersion}") {Enabled = false},
new MenuItem("-"),
new MenuItem("Check for &Updates...",
(sender, e) => Process.Start(@"http://pooi.moe/QuickLook/")), (sender, e) => Process.Start(@"http://pooi.moe/QuickLook/")),
_itemAutorun, _itemAutorun,
new MenuItem("&Quit", (sender, e) => Application.Current.Shutdown()) new MenuItem("&Quit", (sender, e) => System.Windows.Application.Current.Shutdown())
}) })
}; };

View File

@@ -9,13 +9,14 @@ using QuickLook.Plugin;
namespace QuickLook namespace QuickLook
{ {
internal class ViewWindowManager internal class ViewWindowManager : IDisposable
{ {
private static ViewWindowManager _instance; private static ViewWindowManager _instance;
private readonly MainWindowNoTransparent _viewWindowNoTransparent; private readonly MainWindowNoTransparent _viewWindowNoTransparent;
private readonly MainWindowTransparent _viewWindowTransparentTransparent; private readonly MainWindowTransparent _viewWindowTransparentTransparent;
private MainWindowTransparent _currentMainWindow; private MainWindowTransparent _currentMainWindow;
private long _lastSwitchTick;
private string _path = string.Empty; private string _path = string.Empty;
@@ -27,10 +28,13 @@ namespace QuickLook
_currentMainWindow = _viewWindowTransparentTransparent; _currentMainWindow = _viewWindowTransparentTransparent;
} }
public void Dispose()
{
StopFocusMonitor();
}
internal void InvokeRoutine(KeyEventArgs kea) internal void InvokeRoutine(KeyEventArgs kea)
{ {
Debug.WriteLine(kea.KeyCode);
switch (kea.KeyCode) switch (kea.KeyCode)
{ {
case Keys.Up: case Keys.Up:
@@ -53,22 +57,29 @@ namespace QuickLook
} }
} }
private void RunAndClosePreview(KeyEventArgs kea = null) internal void RunAndClosePreview(KeyEventArgs kea = null)
{ {
if (!WindowHelper.IsFocusedControlExplorerItem() && !WindowHelper.IsFocusedWindowSelf()) if (NativeMethods.QuickLook.GetFocusedWindowType() ==
return; NativeMethods.QuickLook.FocusedWindowType.Invalid)
if (!WindowHelper.IsForegroundWindowBelongToSelf())
return;
if (_currentMainWindow.Visibility != Visibility.Visible) if (_currentMainWindow.Visibility != Visibility.Visible)
return; return;
StopFocusMonitor(); StopFocusMonitor();
_currentMainWindow.RunAndClose(); _currentMainWindow.RunAndHide();
if (kea != null) if (kea != null)
kea.Handled = true; kea.Handled = true;
} }
internal void ClosePreview(KeyEventArgs kea = null) internal void ClosePreview(KeyEventArgs kea = null)
{ {
if (NativeMethods.QuickLook.GetFocusedWindowType() ==
NativeMethods.QuickLook.FocusedWindowType.Invalid)
if (!WindowHelper.IsForegroundWindowBelongToSelf())
return;
StopFocusMonitor(); StopFocusMonitor();
_currentMainWindow.BeginHide(); _currentMainWindow.BeginHide();
@@ -78,8 +89,12 @@ namespace QuickLook
private void TogglePreview(KeyEventArgs kea = null) private void TogglePreview(KeyEventArgs kea = null)
{ {
if (!WindowHelper.IsFocusedControlExplorerItem() && !WindowHelper.IsFocusedWindowSelf()) _lastSwitchTick = DateTime.Now.Ticks;
return;
if (NativeMethods.QuickLook.GetFocusedWindowType() ==
NativeMethods.QuickLook.FocusedWindowType.Invalid)
if (!WindowHelper.IsForegroundWindowBelongToSelf())
return;
if (_currentMainWindow.Visibility == Visibility.Visible) if (_currentMainWindow.Visibility == Visibility.Visible)
{ {
@@ -87,7 +102,7 @@ namespace QuickLook
} }
else else
{ {
_path = NativeMethods.QuickLook.GetCurrentSelectionFirst(); _path = NativeMethods.QuickLook.GetCurrentSelection();
InvokeViewer(); InvokeViewer();
} }
if (kea != null) if (kea != null)
@@ -99,38 +114,34 @@ namespace QuickLook
if (_currentMainWindow.Visibility != Visibility.Visible) if (_currentMainWindow.Visibility != Visibility.Visible)
return; return;
if (!WindowHelper.IsFocusedControlExplorerItem()) _lastSwitchTick = DateTime.Now.Ticks;
if (NativeMethods.QuickLook.GetFocusedWindowType() ==
NativeMethods.QuickLook.FocusedWindowType.Invalid)
return; return;
_path = NativeMethods.QuickLook.GetCurrentSelectionFirst(); _path = NativeMethods.QuickLook.GetCurrentSelection();
InvokeViewer(); InvokeViewer();
if (kea != null) if (kea != null)
kea.Handled = false; kea.Handled = false;
} }
private void SwitchPreviewRemoteInvoke(FocusedItemChangedEventArgs e) private void SwitchPreviewRemoteInvoke(HeartbeatEventArgs e)
{ {
Debug.WriteLine("SwitchPreviewRemoteInvoke"); // sleep for 1.5s
if (e.InvokeTick - _lastSwitchTick < 1.5 * TimeSpan.TicksPerSecond)
return;
if (e.FocusedFile == _path) if (e.FocusedFile == _path)
return; return;
Debug.WriteLine("SwitchPreviewRemoteInvoke:" + (e.InvokeTick - _lastSwitchTick));
if (string.IsNullOrEmpty(e.FocusedFile)) if (string.IsNullOrEmpty(e.FocusedFile))
return; return;
_currentMainWindow?.Dispatcher.Invoke(() => _currentMainWindow?.Dispatcher.Invoke(() => SwitchPreview());
{
if (_currentMainWindow.Visibility != Visibility.Visible)
return;
if (!WindowHelper.IsFocusedControlExplorerItem())
return;
_path = NativeMethods.QuickLook.GetCurrentSelectionFirst();
InvokeViewer();
});
} }
private void RunFocusMonitor() private void RunFocusMonitor()
@@ -138,7 +149,7 @@ namespace QuickLook
if (!FocusMonitor.GetInstance().IsRunning) if (!FocusMonitor.GetInstance().IsRunning)
{ {
FocusMonitor.GetInstance().Start(); FocusMonitor.GetInstance().Start();
FocusMonitor.GetInstance().FocusedItemChanged += SwitchPreviewRemoteInvoke; FocusMonitor.GetInstance().Heartbeat += SwitchPreviewRemoteInvoke;
} }
} }
@@ -147,7 +158,7 @@ namespace QuickLook
if (FocusMonitor.GetInstance().IsRunning) if (FocusMonitor.GetInstance().IsRunning)
{ {
FocusMonitor.GetInstance().Stop(); FocusMonitor.GetInstance().Stop();
FocusMonitor.GetInstance().FocusedItemChanged -= SwitchPreviewRemoteInvoke; FocusMonitor.GetInstance().Heartbeat -= SwitchPreviewRemoteInvoke;
} }
} }