C++ & C#(Wrapper): Definir/Pegar o Master Volume do Windows – Windows Vista+

Estava precisando desenvolver alguma forma para definir e pegar o volume da saída de som do Windows no C#/WPF. Obviamente, eu teria que lidar com a API do Windows.

Só que dessa vez, eu resolvi o problema de uma forma diferente. Pensei, a API do Windows foi escrita em C++, eu teria que importar um monte de interfaces, métodos, e estruturas para o C#. Então eu tive uma ideia melhor, escrever os métodos em uma DLL C++ e importa-los no C#. Foi exatamente o que eu fiz, ou quase. Na verdade o código em C++ eu encontrei na web, necessitando apenas uma pequena adaptação, no que acabou sendo meu primeiro projeto no C++. Depois criei o Wrapper (Api que faz a interoperabilidade entre a DLL C++, código nativo, e o .Net Framework, código gerenciado.

No fim das contas, ao invés de ter que importar um monte de interfaces, métodos, e estruturas para o C#, eu precisei importar apenas dois métodos. Aí eu vi vantagem em estudar C++, na verdade eu já vinha pensando em fazer esse tipo de coisa antes, mas nunca tive a real necessidade

Segue o código C++:

#include <mmdeviceapi.h>
#include <endpointvolume.h>
/*
The below code was based on: http://www.codeproject.com/Tips/233484/Change-Master-Volume-in-Visual-Cplusplus
Thanks to the author, Mr. Sayyed Mostafa Hashemi.
*/
extern "C" 
{
	_declspec(dllexport) bool WinVistaSetVolume(_In_ double nVolume, _In_ bool bScalar)
	{
		HRESULT hr = NULL;
		bool decibels = false;
		bool scalar = false;
		double newVolume = nVolume;

		CoInitialize(NULL);
		IMMDeviceEnumerator *deviceEnumerator = NULL;
		hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER,
			__uuidof(IMMDeviceEnumerator), (LPVOID *)&deviceEnumerator);
		IMMDevice *defaultDevice = NULL;

		hr = deviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &defaultDevice);
		deviceEnumerator->Release();
		deviceEnumerator = NULL;

		IAudioEndpointVolume *endpointVolume = NULL;
		hr = defaultDevice->Activate(__uuidof(IAudioEndpointVolume),
			CLSCTX_INPROC_SERVER, NULL, (LPVOID *)&endpointVolume);
		defaultDevice->Release();
		defaultDevice = NULL;

		float currentVolume = 0;
		endpointVolume->GetMasterVolumeLevel(&currentVolume);


		hr = endpointVolume->GetMasterVolumeLevelScalar(&currentVolume);

		if (bScalar == false)
		{
			hr = endpointVolume->SetMasterVolumeLevel((float)newVolume, NULL);
		}
		else if (bScalar == true)
		{
			hr = endpointVolume->SetMasterVolumeLevelScalar((float)newVolume, NULL);
		}
		endpointVolume->Release();

		CoUninitialize();
		if (hr == 0x00000000)
			return true;
		else
			return false;
	}
	_declspec(dllexport) bool WinVistaGetVolume(_Out_ double& nVolume, _In_ bool bScalar)
	{
		HRESULT hr = NULL;
		CoInitialize(NULL);
		IMMDeviceEnumerator *deviceEnumerator = NULL;
		hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER,
			__uuidof(IMMDeviceEnumerator), (LPVOID *)&deviceEnumerator);
		IMMDevice *defaultDevice = NULL;

		hr = deviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &defaultDevice);
		deviceEnumerator->Release();
		deviceEnumerator = NULL;

		IAudioEndpointVolume *endpointVolume = NULL;
		hr = defaultDevice->Activate(__uuidof(IAudioEndpointVolume),
			CLSCTX_INPROC_SERVER, NULL, (LPVOID *)&endpointVolume);
		defaultDevice->Release();
		defaultDevice = NULL;
		float currentVolume = 0;

		if (bScalar)
			hr = endpointVolume->GetMasterVolumeLevelScalar(&currentVolume);
		else
			hr = endpointVolume->GetMasterVolumeLevel(&currentVolume);
		endpointVolume->Release();

		CoUninitialize();

		nVolume = (double)currentVolume;
		
		if (hr == 0x00000000)
			return true;
		else
			return false;
	}
}

E o Wrapper em C#:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;

namespace HL.CSharp.SystemVolume
{
    public static class WinVista
    {
        public static double GetVolume()
        {
            double volume = 0;
            WinVistaGetVolume(ref volume, true);
            return volume;
        }
        public static bool SetVolume(double Volume)
        {
            return WinVistaSetVolume(Volume, true);
        }
        [DllImport("HL.CPP.SystemVolume.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
        private static extern bool WinVistaSetVolume(double nVolume, bool bScalar);

        [DllImport("HL.CPP.SystemVolume.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
        private static extern bool WinVistaGetVolume(ref double nVolume, bool bScalar);
    }
}

Vou disponibilizar também os arquivos do projeto e as DLLs já compiladas:

HL.CPP.SystemVolume

Espero que possa ser útil.

Anúncios

Deixe um comentário :)

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s