Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
143 views
in Technique[技术] by (71.8m points)

.net - Controlling Application's Volume: By Process-ID

I need to control the volume level of the audio produced by a specific application. I have found Simon's simple & neat answer for a similar question here, but it enumerates audio-generating applications by Display-Name, which for some applications - is empty, and hence cannot be used to detect the target application (whose volume is to be controlled).

Hence, I'm looking for a very similar solution, enumerating audio-playing applications' process IDs, through which I could detect applications' actual names (process names & main window titles).

I'm using VB.Net 2010, for Windows 7.

Question&Answers:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Here is a set of utility C# classes that allow you to get information on audio devices and audio session, including process information. They use the Windows Core Audio Library, and only work on Windows 7 or higher:

// sample program
class Program
{
    static void Main(string[] args)
    {
        // dump all audio devices
        foreach (AudioDevice device in AudioUtilities.GetAllDevices())
        {
            Console.WriteLine(device.FriendlyName);
        }

        // dump all audio sessions
        foreach (AudioSession session in AudioUtilities.GetAllSessions())
        {
            if (session.Process != null)
            {
                // only the one associated with a defined process
                Console.WriteLine(session.Process.ProcessName);
            }
        }
    }
}

// audio utilities
public static class AudioUtilities
{
    private static IAudioSessionManager2 GetAudioSessionManager()
    {
        IMMDevice speakers = GetSpeakers();
        if (speakers == null)
            return null;

        // win7+ only
        object o;
        if (speakers.Activate(typeof(IAudioSessionManager2).GUID, CLSCTX.CLSCTX_ALL, IntPtr.Zero, out o) != 0 || o == null)
            return null;

        return o as IAudioSessionManager2;
    }

    public static AudioDevice GetSpeakersDevice()
    {
        return CreateDevice(GetSpeakers());
    }

    private static AudioDevice CreateDevice(IMMDevice dev)
    {
        if (dev == null)
            return null;

        string id;
        dev.GetId(out id);
        DEVICE_STATE state;
        dev.GetState(out state);
        Dictionary<string, object> properties = new Dictionary<string, object>();
        IPropertyStore store;
        dev.OpenPropertyStore(STGM.STGM_READ, out store);
        if (store != null)
        {
            int propCount;
            store.GetCount(out propCount);
            for (int j = 0; j < propCount; j++)
            {
                PROPERTYKEY pk;
                if (store.GetAt(j, out pk) == 0)
                {
                    PROPVARIANT value = new PROPVARIANT();
                    int hr = store.GetValue(ref pk, ref value);
                    object v = value.GetValue();
                    try
                    {
                        if (value.vt != VARTYPE.VT_BLOB) // for some reason, this fails?
                        {
                            PropVariantClear(ref value);
                        }
                    }
                    catch
                    {
                    }
                    string name = pk.ToString();
                    properties[name] = v;
                }
            }
        }
        return new AudioDevice(id, (AudioDeviceState)state, properties);
    }

    public static IList<AudioDevice> GetAllDevices()
    {
        List<AudioDevice> list = new List<AudioDevice>();
        IMMDeviceEnumerator deviceEnumerator = null;
        try
        {
            deviceEnumerator = (IMMDeviceEnumerator)(new MMDeviceEnumerator());
        }
        catch
        {
        }
        if (deviceEnumerator == null)
            return list;

        IMMDeviceCollection collection;
        deviceEnumerator.EnumAudioEndpoints(EDataFlow.eAll, DEVICE_STATE.MASK_ALL, out collection);
        if (collection == null)
            return list;

        int count;
        collection.GetCount(out count);
        for (int i = 0; i < count; i++)
        {
            IMMDevice dev;
            collection.Item(i, out dev);
            if (dev != null)
            {
                list.Add(CreateDevice(dev));
            }
        }
        return list;
    }

    private static IMMDevice GetSpeakers()
    {
        // get the speakers (1st render + multimedia) device
        try
        {
            IMMDeviceEnumerator deviceEnumerator = (IMMDeviceEnumerator)(new MMDeviceEnumerator());
            IMMDevice speakers;
            deviceEnumerator.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eMultimedia, out speakers);
            return speakers;
        }
        catch
        {
            return null;
        }
    }

    public static IList<AudioSession> GetAllSessions()
    {
        List<AudioSession> list = new List<AudioSession>();
        IAudioSessionManager2 mgr = GetAudioSessionManager();
        if (mgr == null)
            return list;

        IAudioSessionEnumerator sessionEnumerator;
        mgr.GetSessionEnumerator(out sessionEnumerator);
        int count;
        sessionEnumerator.GetCount(out count);

        for (int i = 0; i < count; i++)
        {
            IAudioSessionControl ctl;
            sessionEnumerator.GetSession(i, out ctl);
            if (ctl == null)
                continue;

            IAudioSessionControl2 ctl2 = ctl as IAudioSessionControl2;
            if (ctl2 != null)
            {
                list.Add(new AudioSession(ctl2));
            }
        }
        Marshal.ReleaseComObject(sessionEnumerator);
        Marshal.ReleaseComObject(mgr);
        return list;
    }

    public static AudioSession GetProcessSession()
    {
        int id = Process.GetCurrentProcess().Id;
        foreach (AudioSession session in GetAllSessions())
        {
            if (session.ProcessId == id)
                return session;

            session.Dispose();
        }
        return null;
    }

    [DllImport("ole32.dll")]
    private static extern int PropVariantClear(ref PROPVARIANT pvar);

    [ComImport]
    [Guid("BCDE0395-E52F-467C-8E3D-C4579291692E")]
    private class MMDeviceEnumerator
    {
    }

    [Flags]
    private enum CLSCTX
    {
        CLSCTX_INPROC_SERVER = 0x1,
        CLSCTX_INPROC_HANDLER = 0x2,
        CLSCTX_LOCAL_SERVER = 0x4,
        CLSCTX_REMOTE_SERVER = 0x10,
        CLSCTX_ALL = CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER | CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER
    }

    private enum STGM
    {
        STGM_READ = 0x00000000,
    }

    private enum EDataFlow
    {
        eRender,
        eCapture,
        eAll,
    }

    private enum ERole
    {
        eConsole,
        eMultimedia,
        eCommunications,
    }

    private enum DEVICE_STATE
    {
        ACTIVE = 0x00000001,
        DISABLED = 0x00000002,
        NOTPRESENT = 0x00000004,
        UNPLUGGED = 0x00000008,
        MASK_ALL = 0x0000000F
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct PROPERTYKEY
    {
        public Guid fmtid;
        public int pid;

        public override string ToString()
        {
            return fmtid.ToString("B") + " " + pid;
        }
    }

    // NOTE: we only define what we handle
    [Flags]
    private enum VARTYPE : short
    {
        VT_I4 = 3,
        VT_BOOL = 11,
        VT_UI4 = 19,
        VT_LPWSTR = 31,
        VT_BLOB = 65,
        VT_CLSID = 72,
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct PROPVARIANT
    {
        public VARTYPE vt;
        public ushort wReserved1;
        public ushort wReserved2;
        public ushort wReserved3;
        public PROPVARIANTunion union;

        public object GetValue()
        {
            switch (vt)
            {
                case VARTYPE.VT_BOOL:
                    return union.boolVal != 0;

                case VARTYPE.VT_LPWSTR:
                    return Marshal.PtrToStringUni(union.pwszVal);

                case VARTYPE.VT_UI4:
                    return union.lVal;

                case VARTYPE.VT_CLSID:
                    return (Guid)Marshal.PtrToStructure(union.puuid, typeof(Guid));

                default:
                    return vt.ToString() + ":?";
            }
        }
    }

    [StructLayout(LayoutKind.Explicit)]
    private struct PROPVARIANTunion
    {
        [FieldOffset(0)]
        public int lVal;
        [FieldOffset(0)]
        public ulong uhVal;
        [FieldOffset(0)]
        public short boolVal;
        [FieldOffset(0)]
        public IntPtr pwszVal;
        [FieldOffset(0)]
        public IntPtr puuid;
    }

    [Guid("A95664D2-9614-4F35-A746-DE8DB63617E6"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    private interface IMMDeviceEnumerator
    {
        [PreserveSig]
        int EnumAudioEndpoints(EDataFlow dataFlow, DEVICE_STATE dwStateMask, out IMMDeviceCollection ppDevices);

        [PreserveSig]
        int GetDefaultAudioEndpoint(EDataFlow dataFlow, ERole role, out IMMDevice ppEndpoint);

        [PreserveSig]
        int GetDevice([MarshalAs(UnmanagedType.LPWStr)] string pwstrId, out IMMDevice ppDevice);

        [PreserveSig]
        int RegisterEndpointNotificationCallback(IMMNotificationClient pClient);

        [PreserveSig]
        int UnregisterEndpointNotificationCallback(IMMNotificationClient pClient);
    }

    [Guid("7991EEC9-7E89-4D85-8390-6C703CEC60C0"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    private interface IMMNotificationClient
    {
        void OnDeviceStateChanged([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId, DEVICE_STATE dwNewState);
        void OnDeviceAdded([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId);
        void OnDeviceRemoved([MarshalAs(UnmanagedType.LPWStr)] string deviceId);
        void OnDefaultDeviceChanged(EDataFlow flow, ERole role, string pwstrDefaultDeviceId);
        void OnPropertyValueChanged([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId, PROPERTYKEY key);
    }

    [Guid("0BD7A1BE-7A1A-44DB-8397-CC5392387B5E"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    private interface IMMDeviceCollection
    {
        [PreserveSig]
        int GetCount(out int pcDevices);

        [PreserveSig]
        int Item(int nDevice, out IMMDevice ppDevice);
    }

    [Guid("D666063F-1587-4E43-81F1-B948E807363F"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    private interface IMMDevice
    {
        [PreserveSig]
        int Activate([MarshalAs(UnmanagedType.LPStruct)] Guid riid, CLSCTX dwClsCtx, IntPtr pActivationParams, [MarshalAs(UnmanagedType.IUnknown)] out object ppInterface);

        [PreserveSig]
        int OpenPropertyStore(STGM stgmAccess, out IPropertyStore ppProperties);

        [PreserveSig]
        int GetId([MarshalAs(UnmanagedType.LPWStr)] out string ppstrId);

        [PreserveSig]
        int GetState(out DEVICE_STATE pdwState);
    }

    [Guid("6f79d558-3e96-4549-a1d1-7d75d2288814"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    private interface IPropertyDescription
    {
        [PreserveSig]
        int GetPropertyKey(out PROPERTYKEY pkey);

        [PreserveSig]
        int GetCanonicalName(out IntPtr ppszName);

        [PreserveSig]
        int GetPropertyType(out short pvartype);

        [PreserveSig]
        int GetDisplayName(out IntPtr ppszName);

        // WARNING: the rest is undefined. you *can't* implement it, only use it.
    }

    [Guid("886d8eeb-8cf2-4446-8d02-cdba1dbdcf99"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    private interface IPropertyStore
    {
        [PreserveSig]
        int GetCount(out int cProps);

        [PreserveSig]
        int GetAt(int iProp, out PROPERTYKEY pkey);

        [PreserveSig]
        int GetValue(ref PROPERTYKEY key, ref PROPVARIANT pv);

        [PreserveSig]
        int SetValue(ref PROPERTYKEY key, ref PROPVARIANT propvar);

        [PreserveSig]
        int Commit();
    }

    [Guid("BFA971F1-4D5E-40BB-935E-967039BFBEE4"), InterfaceTy

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...