Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions src/Jarvis.Addin.Windows/ImageUtils.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media.Imaging;

namespace Jarvis.Addin.Windows
{
public class ImageUtils
{
// reference https://stackoverflow.com/a/10077805
[DllImport("gdi32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool DeleteObject(IntPtr value);

public static BitmapSource GetImageStream(Image myImage)
{
if (myImage == null)
return null;

var bitmap = new Bitmap(myImage);
IntPtr bmpPt = bitmap.GetHbitmap();
BitmapSource bitmapSource =
System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
bmpPt,
IntPtr.Zero,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());

//freeze bitmapSource and clear memory to avoid memory leaks
bitmapSource.Freeze();
DeleteObject(bmpPt);

return bitmapSource;
}
}
}
75 changes: 75 additions & 0 deletions src/Jarvis.Addin.Windows/Jarvis.Addin.Windows.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{5E693056-A1ED-4434-9181-20632F0A300A}</ProjectGuid>
<OutputType>Library</OutputType>
<RootNamespace>Jarvis.Addin.Windows</RootNamespace>
<AssemblyName>Jarvis.Addin.Windows</AssemblyName>
<TargetFrameworkVersion>v4.7</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<StartupObject />
</PropertyGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Compile Include="ImageUtils.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="WindowsAddin.cs" />
<Compile Include="WindowsImports.cs" />
<Compile Include="WindowsProvider.cs" />
<Compile Include="WindowsResult.cs" />
</ItemGroup>
<ItemGroup>
<Reference Include="Autofac, Version=4.6.2.0, Culture=neutral, PublicKeyToken=17863af14b0044da, processorArchitecture=MSIL">
<HintPath>..\packages\Autofac.4.6.2\lib\net45\Autofac.dll</HintPath>
</Reference>
<Reference Include="JetBrains.Annotations, Version=11.1.0.0, Culture=neutral, PublicKeyToken=1010a0d8d6380325, processorArchitecture=MSIL">
<HintPath>..\packages\JetBrains.Annotations.11.1.0\lib\net20\JetBrains.Annotations.dll</HintPath>
</Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="PresentationCore" />
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Drawing" />
<Reference Include="System.IO.Compression.FileSystem" />
<Reference Include="System.Numerics" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq" />
<Reference Include="WindowsBase" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Jarvis.Core\Jarvis.Core.csproj">
<Project>{5826d320-9026-4be9-8c82-893c55b80871}</Project>
<Name>Jarvis.Core</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
23 changes: 23 additions & 0 deletions src/Jarvis.Addin.Windows/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Jarvis.Addin.Windows;
using Jarvis.Core;

// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Jarvis.Addin.Windows")]
[assembly: AssemblyDescription("")]

// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]

// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("5e357761-af5a-4a88-b997-dd2e0d405866")]


// The addin definition.
[assembly: Addin(typeof(WindowsAddin))]
14 changes: 14 additions & 0 deletions src/Jarvis.Addin.Windows/WindowsAddin.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@

using Autofac;
using Jarvis.Core;

namespace Jarvis.Addin.Windows
{
public class WindowsAddin : IAddin
{
public void Configure(ContainerBuilder builder)
{
builder.RegisterType<WindowsProvider>().As<IQueryProvider>().SingleInstance();
}
}
}
110 changes: 110 additions & 0 deletions src/Jarvis.Addin.Windows/WindowsImports.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security;
using System.Text;
using System.Threading.Tasks;

namespace Jarvis.Addin.Windows
{
public class WindowsImports
{
private static uint WM_GETICON = 0x007f;
private static IntPtr ICON_BIG = new IntPtr(0);
private static int GCL_HICON = -14;
private static int SW_RESTORE = 9;

[DllImport("psapi.dll")]
public static extern uint GetProcessImageFileName(
IntPtr hProcess,
[Out] StringBuilder lpImageFileName,
[In] [MarshalAs(UnmanagedType.U4)] int nSize
);

// reference https://stackoverflow.com/a/43640787
public delegate bool EnumWindowsProc(IntPtr hWnd, int lParam);

[DllImport("USER32.DLL"), SuppressUnmanagedCodeSecurity]
public static extern bool EnumWindows(EnumWindowsProc enumFunc, int lParam);

[DllImport("USER32.DLL")]
public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);

[DllImport("USER32.DLL")]
public static extern int GetWindowTextLength(IntPtr hWnd);

[DllImport("USER32.DLL")]
public static extern bool IsWindowVisible(IntPtr hWnd);

[DllImport("Oleacc.dll", SetLastError = true)]
public static extern IntPtr GetProcessHandleFromHwnd(IntPtr hWnd);

[DllImport("user32.dll")]
static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

[DllImport("user32.dll")]
static extern IntPtr LoadIcon(IntPtr hInstance, IntPtr lpIconName);

[DllImport("user32.dll", EntryPoint = "GetClassLong")]
static extern uint GetClassLong32(IntPtr hWnd, int nIndex);

[DllImport("user32.dll", EntryPoint = "GetClassLongPtr")]
static extern IntPtr GetClassLong64(IntPtr hWnd, int nIndex);

[DllImport("user32.dll")]
public static extern bool SetForegroundWindow(IntPtr hWnd);

static IntPtr GetClassLongPtr(IntPtr hWnd, int nIndex)
{
if (IntPtr.Size == 4)
return new IntPtr((long)GetClassLong32(hWnd, nIndex));
else
return GetClassLong64(hWnd, nIndex);
}

public static Image GetWindowIcon(IntPtr hWnd)
{
try
{
IntPtr hIcon = default(IntPtr);

hIcon = SendMessage(hWnd, WM_GETICON, ICON_BIG, IntPtr.Zero);

if (hIcon == IntPtr.Zero)
hIcon = GetClassLongPtr(hWnd, GCL_HICON);

if (hIcon == IntPtr.Zero)
hIcon = LoadIcon(IntPtr.Zero, (IntPtr)0x7F00 /*IDI_APPLICATION*/);

if (hIcon != IntPtr.Zero)
{
return new Bitmap(Icon.FromHandle(hIcon).ToBitmap(), 64, 64);
}

return null;
}
catch (Exception)
{
return null;
}
}

[System.Runtime.InteropServices.DllImport("User32.dll")]
private static extern bool ShowWindow(IntPtr handle, int nCmdShow);
[System.Runtime.InteropServices.DllImport("User32.dll")]
private static extern bool IsIconic(IntPtr handle);

public static void BringWindowToFront(IntPtr handle)
{
if (IsIconic(handle))
{
ShowWindow(handle, SW_RESTORE);
}

SetForegroundWindow(handle);
}

}
}
92 changes: 92 additions & 0 deletions src/Jarvis.Addin.Windows/WindowsProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
using Jarvis.Core;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Jarvis.Core.Diagnostics;
using Jarvis.Core.Scoring;

namespace Jarvis.Addin.Windows
{
public class WindowsProvider : QueryProvider<WindowsResult>
{
private readonly IJarvisLog _log;

public override string Command => "w";

public WindowsProvider(IJarvisLog log)
{
_log = log;
}

public override Task<IEnumerable<IQueryResult>> QueryAsync(Query query)
{
return Task.Run<IEnumerable<IQueryResult>>(() =>
{
Ensure.NotNull(query, nameof(query));
List<WindowsResult> results = new List<WindowsResult>();

bool enumWindows = WindowsImports.EnumWindows(delegate (IntPtr hWnd, int lParam)
{
if (!WindowsImports.IsWindowVisible(hWnd)) return true;

int length = WindowsImports.GetWindowTextLength(hWnd);
if (length == 0) return true;
IntPtr processHandle = WindowsImports.GetProcessHandleFromHwnd(hWnd);

StringBuilder builder = new StringBuilder(length);
WindowsImports.GetWindowText(hWnd, builder, length + 1);
var title = builder.ToString();
var processName = "";

try
{
StringBuilder filePath = new StringBuilder(2000);
WindowsImports.GetProcessImageFileName(processHandle, filePath, 2000);
processName = Path.GetFileName(filePath.ToString());
}
catch (Exception ex)
{
// ignore
}

// TODO not best string compare algorithm for this case
results.Add(new WindowsResult(hWnd, null, title, processName,
Math.Min(LevenshteinScorer.Score(processName, query.Argument, false),
LevenshteinScorer.Score(title, query.Argument, false)),
Math.Min(LevenshteinScorer.Score(processName, query.Argument),
LevenshteinScorer.Score(title, query.Argument))));

return true;
}, 0);

if (!enumWindows)
{
_log.Error(new Exception("Windows enum failed"), "Windows enumeration failed");
return Enumerable.Empty<IQueryResult>();
}

return results.OrderBy(result => result.Score).Take(5);

});
}

protected override Task ExecuteAsync(WindowsResult result)
{
WindowsImports.BringWindowToFront(result.HWnd);
return Task.CompletedTask;
}

protected override async Task<ImageSource> GetIconAsync(WindowsResult result) => ImageUtils.GetImageStream(WindowsImports.GetWindowIcon(result.HWnd));

}
}
Loading