Compare commits
3 Commits
68d5154756
...
0f7f1fffcd
Author | SHA1 | Date | |
---|---|---|---|
0f7f1fffcd | |||
aa905fe443 | |||
160bedd461 |
@ -109,6 +109,13 @@ public sealed class AppxPackage
|
||||
var properties = reader.GetProperties();
|
||||
|
||||
properties.GetStringValue("DisplayName", out var displayName);
|
||||
|
||||
if(displayName.StartsWith("ms-resource:"))
|
||||
{
|
||||
var packageFullName = Marshal.PtrToStringUni(info.packageFullName);
|
||||
displayName = LoadResourceString(fullName, displayName);
|
||||
|
||||
}
|
||||
package.DisplayName = displayName;
|
||||
|
||||
properties.GetStringValue("Logo", out var logo);
|
||||
@ -158,6 +165,40 @@ public sealed class AppxPackage
|
||||
ClosePackageInfo(infoRef);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static string LoadResourceString(string packageFullName, string resource)
|
||||
{
|
||||
if (packageFullName == null)
|
||||
throw new ArgumentNullException("packageFullName");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(resource))
|
||||
return null;
|
||||
|
||||
const string resourceScheme = "ms-resource:";
|
||||
if (!resource.StartsWith(resourceScheme))
|
||||
return null;
|
||||
|
||||
string part = resource.Substring(resourceScheme.Length);
|
||||
string url;
|
||||
|
||||
if (part.StartsWith("/"))
|
||||
{
|
||||
url = resourceScheme + "//" + part;
|
||||
}
|
||||
else
|
||||
{
|
||||
url = resourceScheme + "///resources/" + part;
|
||||
}
|
||||
|
||||
string source = string.Format("@{{{0}? {1}}}", packageFullName, url);
|
||||
var sb = new StringBuilder(1024);
|
||||
int i = SHLoadIndirectString(source, sb, sb.Capacity, IntPtr.Zero);
|
||||
if (i != 0)
|
||||
return null;
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
|
||||
|
@ -12,6 +12,7 @@ public class AudioHelper
|
||||
|
||||
static object _lock = new object();
|
||||
int[] _currentProcesses;
|
||||
int _retryFallbackCount = 0;
|
||||
|
||||
public IAudioSession Current { get; private set; }
|
||||
|
||||
@ -49,9 +50,10 @@ public class AudioHelper
|
||||
deviceCollection.Item(d, out var device);
|
||||
|
||||
Guid iid = typeof(IAudioSessionManager2).GUID;
|
||||
device.Activate(ref iid, 0, IntPtr.Zero, out var m);
|
||||
device.Activate(ref iid, CLSCTX.ALL, IntPtr.Zero, out var m);
|
||||
var manager = (IAudioSessionManager2)m;
|
||||
|
||||
device.GetId(out var currentDeviceId);
|
||||
|
||||
manager.GetSessionEnumerator(out var sessionEnumerator);
|
||||
|
||||
@ -114,10 +116,18 @@ public class AudioHelper
|
||||
var processes = GetPossibleProcesses();
|
||||
var processIds = processes?.Select(x => x.Id).ToArray();
|
||||
|
||||
if (_currentProcesses == null || !_currentProcesses.SequenceEqual(processIds))
|
||||
//_currentProcesses null - first time getting sessions
|
||||
//_currentProcesses not equal to processIds - changed the active process
|
||||
//_retryFallbackCount - some processes like chrome or minecraft will start their audio process when they first try to do some sound stuff
|
||||
if (_currentProcesses == null || !_currentProcesses.SequenceEqual(processIds) || _retryFallbackCount == 5)
|
||||
{
|
||||
_retryFallbackCount = 0;
|
||||
Current = FindSession(processes);
|
||||
}
|
||||
else if(Current is SystemSoundsAudioSession || Current is SystemVolumeAudioSession)
|
||||
{
|
||||
_retryFallbackCount++;
|
||||
}
|
||||
|
||||
if (Current == null)
|
||||
{
|
||||
@ -214,7 +224,7 @@ public class AudioHelper
|
||||
deviceCollection.Item(d, out var device);
|
||||
|
||||
Guid iid = typeof(IAudioSessionManager2).GUID;
|
||||
device.Activate(ref iid, 0, IntPtr.Zero, out var m);
|
||||
device.Activate(ref iid, CLSCTX.ALL, IntPtr.Zero, out var m);
|
||||
var manager = (IAudioSessionManager2)m;
|
||||
|
||||
|
||||
@ -245,7 +255,7 @@ public class AudioHelper
|
||||
|
||||
|
||||
Guid iid = typeof(IAudioSessionManager2).GUID;
|
||||
device.Activate(ref iid, 0, IntPtr.Zero, out var m);
|
||||
device.Activate(ref iid, CLSCTX.ALL, IntPtr.Zero, out var m);
|
||||
var manager = (IAudioSessionManager2)m;
|
||||
|
||||
|
||||
@ -271,7 +281,7 @@ public class AudioHelper
|
||||
deviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia, out var device);
|
||||
|
||||
Guid iid = typeof(IAudioEndpointVolume).GUID;
|
||||
device.Activate(ref iid, 0, IntPtr.Zero, out var o);
|
||||
device.Activate(ref iid, CLSCTX.ALL, IntPtr.Zero, out var o);
|
||||
var endpointVolume = (IAudioEndpointVolume)o;
|
||||
|
||||
return new SystemVolumeAudioSession(endpointVolume);
|
||||
|
@ -53,10 +53,10 @@ public class NameAndIconHelper
|
||||
//so you have to send some messages to the apps to get the icons.
|
||||
//but they will only be 32x32 (or smaller) so we only want to use this logic for java
|
||||
//because these will be lower resolution than the normal way of getting icons
|
||||
if (process.ProcessName == "javaw" || process.ProcessName == "java")
|
||||
if (process.ProcessName == "javaw" || process.ProcessName == "java" || process.ProcessName == "dotnet")
|
||||
{
|
||||
var windowHandle = process.MainWindowHandle;
|
||||
var lazyIcon = new Lazy<Bitmap?>(() => JavaIconExtractor.GetWindowBigIcon(windowHandle));
|
||||
var lazyIcon = () => JavaIconExtractor.GetWindowBigIconWithRetry(windowHandle);
|
||||
results.IconWrapper = new RawIcon(windowHandle.ToString(), lazyIcon);
|
||||
|
||||
}
|
||||
|
@ -11,6 +11,32 @@ public class MMDeviceEnumerator
|
||||
{
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum CLSCTX : uint
|
||||
{
|
||||
INPROC_SERVER = 0x1,
|
||||
INPROC_HANDLER = 0x2,
|
||||
LOCAL_SERVER = 0x4,
|
||||
INPROC_SERVER16 = 0x8,
|
||||
REMOTE_SERVER = 0x10,
|
||||
INPROC_HANDLER16 = 0x20,
|
||||
RESERVED1 = 0x40,
|
||||
RESERVED2 = 0x80,
|
||||
RESERVED3 = 0x100,
|
||||
RESERVED4 = 0x200,
|
||||
NO_CODE_DOWNLOAD = 0x400,
|
||||
RESERVED5 = 0x800,
|
||||
NO_CUSTOM_MARSHAL = 0x1000,
|
||||
ENABLE_CODE_DOWNLOAD = 0x2000,
|
||||
NO_FAILURE_LOG = 0x4000,
|
||||
DISABLE_AAA = 0x8000,
|
||||
ENABLE_AAA = 0x10000,
|
||||
FROM_DEFAULT_CONTEXT = 0x20000,
|
||||
INPROC = INPROC_SERVER | INPROC_HANDLER,
|
||||
SERVER = INPROC_SERVER | LOCAL_SERVER | REMOTE_SERVER,
|
||||
ALL = SERVER | INPROC_HANDLER
|
||||
}
|
||||
|
||||
public enum DataFlow
|
||||
{
|
||||
Render,
|
||||
@ -35,6 +61,13 @@ public enum DeviceState : uint
|
||||
MaskAll = 0xFu
|
||||
}
|
||||
|
||||
public enum AudioSessionState
|
||||
{
|
||||
AudioSessionStateInactive = 0,
|
||||
AudioSessionStateActive = 1,
|
||||
AudioSessionStateExpired = 2
|
||||
}
|
||||
|
||||
|
||||
[Guid("0BD7A1BE-7A1A-44DB-8397-CC5392387B5E")]
|
||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
@ -62,7 +95,13 @@ public interface IMMDeviceEnumerator
|
||||
public interface IMMDevice
|
||||
{
|
||||
[PreserveSig]
|
||||
int Activate(ref Guid iid, int dwClsCtx, IntPtr pActivationParams, [MarshalAs(UnmanagedType.IUnknown)] out object ppInterface);
|
||||
int Activate(ref Guid iid, CLSCTX dwClsCtx, IntPtr pActivationParams, [MarshalAs(UnmanagedType.IUnknown)] out object ppInterface);
|
||||
|
||||
[PreserveSig]
|
||||
int NotImpl1();
|
||||
[PreserveSig]
|
||||
int GetId([Out, MarshalAs(UnmanagedType.LPWStr)] out string ppstrId);
|
||||
|
||||
}
|
||||
|
||||
[Guid("77AA99A0-1BD6-484F-8BC7-2C654C9A9B6F"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
@ -104,9 +143,9 @@ public interface ISimpleAudioVolume
|
||||
[Guid("bfb7ff88-7239-4fc9-8fa2-07c950be9c6d"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
public interface IAudioSessionControl2
|
||||
{
|
||||
// IAudioSessionControl
|
||||
//elgato seems to use this to determine whether the icon should be black and white
|
||||
[PreserveSig]
|
||||
int NotImpl0();
|
||||
int GetState(out AudioSessionState audioSessionState);
|
||||
|
||||
[PreserveSig]
|
||||
int GetDisplayName([MarshalAs(UnmanagedType.LPWStr)] out string pRetVal);
|
||||
@ -134,7 +173,7 @@ public interface IAudioSessionControl2
|
||||
|
||||
// IAudioSessionControl2
|
||||
[PreserveSig]
|
||||
int GetSessionIdentifier([MarshalAs(UnmanagedType.LPWStr)] out string pRetVal);
|
||||
uint GetSessionIdentifier([MarshalAs(UnmanagedType.LPWStr)] out string pRetVal);
|
||||
|
||||
[PreserveSig]
|
||||
int GetSessionInstanceIdentifier([MarshalAs(UnmanagedType.LPWStr)] out string pRetVal);
|
||||
|
@ -2,12 +2,7 @@
|
||||
using BitFaster.Caching.Lru;
|
||||
using FocusVolumeControl.UI;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FocusVolumeControl.AudioSession
|
||||
{
|
||||
@ -74,15 +69,16 @@ namespace FocusVolumeControl.AudioSession
|
||||
{
|
||||
private readonly string _data;
|
||||
|
||||
public RawIcon(string name, Lazy<Bitmap?> lazyIcon)
|
||||
public RawIcon(string name, Func<Bitmap?> getIcon)
|
||||
{
|
||||
_data = _iconCache.GetOrAdd(name, (key) =>
|
||||
{
|
||||
var icon = lazyIcon.Value;
|
||||
var icon = getIcon();
|
||||
if (icon == null)
|
||||
{
|
||||
return FallbackIconData;
|
||||
}
|
||||
|
||||
if (icon.Height < 48 && icon.Width < 48)
|
||||
{
|
||||
using var newImage = new Bitmap(48, 48);
|
||||
|
@ -6,8 +6,10 @@ internal class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
#if DEBUG
|
||||
// Uncomment this line of code to allow for debugging
|
||||
//while (!System.Diagnostics.Debugger.IsAttached) { System.Threading.Thread.Sleep(100); }
|
||||
#endif
|
||||
|
||||
SDWrapper.Run(args);
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FocusVolumeControl.UI
|
||||
@ -25,6 +26,22 @@ namespace FocusVolumeControl.UI
|
||||
[DllImport("user32.dll", EntryPoint = "DestroyIcon", SetLastError = true)]
|
||||
public static extern int DestroyIcon(IntPtr hIcon);
|
||||
|
||||
public static Bitmap? GetWindowBigIconWithRetry(IntPtr hWnd)
|
||||
{
|
||||
var retry = 5;
|
||||
var icon = GetWindowBigIcon(hWnd);
|
||||
|
||||
while(icon == null || retry > 0)
|
||||
{
|
||||
Thread.Sleep(100);
|
||||
icon = GetWindowBigIcon(hWnd);
|
||||
retry--;
|
||||
}
|
||||
|
||||
return icon;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a big icon (32*32) of a window
|
||||
/// </summary>
|
||||
|
@ -92,14 +92,16 @@ public partial class MainWindow : Window
|
||||
deviceEnumerator.EnumAudioEndpoints(DataFlow.Render, DeviceState.Active, out var deviceCollection);
|
||||
deviceCollection.GetCount(out var num);
|
||||
|
||||
|
||||
for (int i = 0; i < num; i++)
|
||||
{
|
||||
deviceCollection.Item(i, out var device);
|
||||
//todo: put the device name in the output
|
||||
sb.AppendLine("----");
|
||||
sb.AppendLine($"----");
|
||||
|
||||
|
||||
Guid iid = typeof(IAudioSessionManager2).GUID;
|
||||
device.Activate(ref iid, 0, IntPtr.Zero, out var m);
|
||||
device.Activate(ref iid, CLSCTX.ALL, IntPtr.Zero, out var m);
|
||||
var manager = (IAudioSessionManager2)m;
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user