Доброго времени суток.
Пытаюсь сделать программу, которая автоматически могла бы менять источник звука по умолчанию. Реализацию в Delphi я к сожалению не нашел, но нашел реализацию на VC++, ссылка на ресурс:
Код HTML: http://eretik.omegahg.com/art/07.html
Пытаюсь перенести код из С++ на Delphi, но на последней функции застрял
Код моего проекта:
Код:
IMMDeviceEnumerator = interface(IUnknown)
[IID_IMMDeviceEnumerator]
function EnumAudioEndpoints(dataFlow: EDataFlow; deviceState: SYSUINT; out DevCollection: IMMDeviceCollection): HRESULT; stdcall;
end;
IPolicyConfig = interface(IUnknown)
['{f8679f50-850a-41cf-9c72-430f290290c8}']
function SetDefaultEndpoint (wszDeviceID:PWideChar; ERole:ERole):Hresult; stdcall;
end;
type
TForm2 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form2: TForm2;
implementation
{$R *.dfm}
procedure TForm2.Button1Click(Sender: TObject);
var
AudioEndpoints:IMMDeviceEnumerator;
Collection:IMMDeviceCollection;
Device:IMMDevice;
Proper:IPropertyStore;
Id_Dev:PWideChar;
Def_Dev_Ch:IPolicyConfig;
begin
CoCreateInstance (CLSID_MMDeviceEnumerator,nil,CLSCTX_INPROC_SERVER,IID_IMMDeviceEnumerator,AudioEndpoints);
AudioEndpoints.EnumAudioEndpoints(eAll, DEVICE_STATE_ACTIVE, &Collection);
Collection.Item(0,Device);
Device.GetId(Id_Dev);
if Succeeded(CoCreateInstance (Class_PolicyConfigClient,nil,CLSCTX_INPROC_SERVER,IPolicyConfig,Def_Dev_Ch)) then
begin
if Succeeded(Def_Dev_Ch.SetDefaultEndpoint(Id_Dev,eMultimedia)) then
begin
showmessage ('eMultimedia');
end;
end;
end;
end.
Дело в том, чот инициализация всех указателей происходит нормально, ID устройства (для примера выбираю первое) получаю верное, но при попытке выполнить Def_Dev_Ch.SetDefaultEndpoint(Id_De v,eMultimedia) появляется ошибка: raise exception class eaccessviolation with message 'Access violation at address 73a44fbb in module 'audioses.dll' Write of address 00000001', причем ошибка записи в память меняется в зависимости от роли, которую я указываю (при консольной роли ошибки не появляется, но я думаю что это из-за того, что eConsole = $00000000;, при роли eMultimedia запись 00000001, при eCommunications запись в адрес 00000002).
Прошу помочь мне с этой проблемой, объяснить где я накасячил.
Заранее огромное спасибо!
Уважаемые авторы вопросов! Большая просьба сообщить о результатах решения проблемы на этой странице. Иначе, следящие за обсуждением, возможно имеющие аналогичные проблемы, не получают ясного представления об их решении. А авторы ответов не получают обратной связи. Что можно расценивать, как проявление неуважения к отвечающим от автора вопроса.
05-09-2012 11:08 | Сообщение от автора вопроса
Код моей тестовой программки для отображения и выбора устройства по умолчанию:
IMMDeviceEnumerator = interface(IUnknown)
[IID_IMMDeviceEnumerator]
function EnumAudioEndpoints(dataFlow: EDataFlow; deviceState: SYSUINT; out DevCollection: IMMDeviceCollection): HRESULT; stdcall;
end;
IPolicyConfig = interface(IUnknown)
['{f8679f50-850a-41cf-9c72-430f290290c8}']
// не уверен в правильности параметров, но как заглушки работает
function GetMixFormat(a: PWideChar; var b: TWAVEFORMATEX): HRESULT; // stdcall?
function GetDeviceFormat(a: PWideChar; b: integer; var c: TWAVEFORMATEX): HRESULT; stdcall;
function ResetDeviceFormat(a: PWideChar): HRESULT; stdcall;
function SetDeviceFormat(a: PWideChar; var b: TWAVEFORMATEX; var c: TWAVEFORMATEX): HRESULT; stdcall;
function GetProcessingPeriod(a: PWideChar; b: integer; c: PINT64; d: PINT64): HRESULT; stdcall;
function SetProcessingPeriod(a: PWideChar; b: PINT64): HRESULT; stdcall;
function GetShareMode(a: PWideChar; b: pointer{struct DeviceShareMode *}): HRESULT; stdcall;
function SetShareMode(a: PWideChar; b: pointer{struct DeviceShareMode *}): HRESULT; stdcall;
function GetPropertyValue(devID: PWideChar; const Key: _tagpropertykey; V: TPROPVARIANT): HRESULT; stdcall;
function SetPropertyValue(devID: PWideChar; const Key: _tagpropertykey; V: TPROPVARIANT): HRESULT; stdcall;
// а вот и нужный метод
function SetDefaultEndpoint(wszDeviceID: PWideChar; ARole: ERole):Hresult; stdcall;
end;
type
TForm1 = class(TForm)
Button_Find_Sound: TButton;
Button_Ch_Sound: TButton;
ListBox_Sound: TListBox;
Button1: TButton;
procedure Button_Find_SoundClick(Sender: TObject);
procedure Button_Ch_SoundClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button_Ch_SoundClick(Sender: TObject);
var
AudioEndpoints:IMMDeviceEnumerator;
Collection:IMMDeviceCollection;
Device:IMMDevice;
Def_Dev_Ch:IPolicyConfig;
Id_Dev:PWideChar;
ID_Dev_Def:WideString;
begin
CoCreateInstance (CLSID_MMDeviceEnumerator,nil,CLSCTX_INPROC_SERVER,IID_IMMDeviceEnumerator,AudioEndpoints);
AudioEndpoints.EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, Collection);
if Succeeded(CoCreateInstance (Class_PolicyConfigClient,nil,CLSCTX_INPROC_SERVER,IPolicyConfig,&Def_Dev_Ch)) then
begin
if Succeeded(Def_Dev_Ch.SetDefaultEndpoint(Id_Dev,eConsole)) then
begin
//showmessage ('eConsole')
end
else begin
showmessage (IntToStr(HResult (Def_Dev_Ch.SetDefaultEndpoint(Id_Dev,eConsole))));
end;
if Succeeded(Def_Dev_Ch.SetDefaultEndpoint(Id_Dev,eMultimedia)) then
begin
//showmessage ('eMultimedia')
end
else begin
showmessage (IntToStr(HResult (Def_Dev_Ch.SetDefaultEndpoint(Id_Dev,eMultimedia))));
end;
if Succeeded(Def_Dev_Ch.SetDefaultEndpoint(Id_Dev,eCommunications)) then
begin
//showmessage ('eCommunications')
end
else begin
showmessage (IntToStr(HResult (Def_Dev_Ch.SetDefaultEndpoint(Id_Dev,eCommunications))));
end;
end;
end;
procedure TForm1.Button_Find_SoundClick(Sender: TObject);
var
AudioEndpoints:IMMDeviceEnumerator;
Collection:IMMDeviceCollection;
Device:IMMDevice;
Да, и еще по поводу IMMDevice.GetID()
получать его корректно так
var lpwStr: PWideChar;
FID: wideString;
ppDevice: IMMDevice;
...
FID := '';
if Succeeded(ppDevice.GetId(lpwStr)) and (lpwStr<>nil) then begin
FID := wideString(lpwStr);
// надо освободить память
CoTaskMemFree(lpwStr);
end;
В описании интерфейса IPolicyConfig перед методом SetDefaultEndpoint есть еще методы, надо их указать.
У меня заработало с таким определением IPolicyConfig:
type
PCWSTR = PWideChar;
IPolicyConfig = interface(IUnknown)
['{f8679f50-850a-41cf-9c72-430f290290c8}']
// не уверен в правильности параметров, но как заглушки работает
function GetMixFormat(a: PCWSTR; var b: TWAVEFORMATEX): HRESULT; // stdcall?
function GetDeviceFormat(a: PCWSTR; b: integer; var c: TWAVEFORMATEX): HRESULT; stdcall;
function ResetDeviceFormat(a: PCWSTR): HRESULT; stdcall;
function SetDeviceFormat(a: PCWSTR; var b: TWAVEFORMATEX; var c: TWAVEFORMATEX): HRESULT; stdcall;
function GetProcessingPeriod(a: PCWSTR; b: integer; c: PINT64; d: PINT64): HRESULT; stdcall;
function SetProcessingPeriod(a: PCWSTR; b: PINT64): HRESULT; stdcall;
function GetShareMode(a: PCWSTR; b: pointer{struct DeviceShareMode *}): HRESULT; stdcall;
function SetShareMode(a: PCWSTR; b: pointer{struct DeviceShareMode *}): HRESULT; stdcall;
function GetPropertyValue(devID: PCWSTR; const Key: _tagpropertykey; V: tag_inner_PROPVARIANT): HRESULT; stdcall;
function SetPropertyValue(devID: PCWSTR; const Key: _tagpropertykey; V: tag_inner_PROPVARIANT): HRESULT; stdcall;
// а вот и нужный метод
function SetDefaultEndpoint(wszDeviceID: PCWSTR; ARole: ERole):Hresult; stdcall;
end;
ибо, насколько я помню, интерфейс - это VMT и порядок методов имеет значение.
При попытке получить Id устройста через строчку Device.GetId(Id_Dev), если в функции поменять тип возвращаемого параметра с PWideChar на WideString с изменением типа переменной Id_Dev, переменная становится Inaccessible value :(
Поэтому функцию GetId оставил в PWideChar, а потом значение передавал в переменную с типом WideString, но так все равно не работает :(
IMMDeviceEnumerator = interface(IUnknown)
[IID_IMMDeviceEnumerator]
function EnumAudioEndpoints(dataFlow: EDataFlow; deviceState: SYSUINT; out DevCollection: IMMDeviceCollection): HRESULT; stdcall;
end;
IPolicyConfig = interface(IUnknown)
[IID_IPolicyConfig]
function SetDefaultEndpoint (wszDeviceID:WideString; ERole:ERole):Hresult; stdcall;
end;
type
TForm2 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form2: TForm2;
implementation
{$R *.dfm}
procedure TForm2.Button1Click(Sender: TObject);
var
AudioEndpoints:IMMDeviceEnumerator;
Collection:IMMDeviceCollection;
Device:IMMDevice;
Def_Dev_Ch:IPolicyConfig;
Id_Dev:PWideChar;
ID_Dev_Def:WideString;
begin
CoCreateInstance (CLSID_MMDeviceEnumerator,nil,CLSCTX_INPROC_SERVER,IID_IMMDeviceEnumerator,AudioEndpoints);
AudioEndpoints.EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, Collection);
Collection.Item(1,Device);
Device.GetId(Id_Dev);
ID_Dev_Def:=Id_Dev;
if Succeeded(CoCreateInstance (CLSID_PolicyConfigClient,nil,CLSCTX_INPROC_SERVER,IID_IPolicyConfig,&Def_Dev_Ch)) then
begin
if Succeeded(Def_Dev_Ch.SetDefaultEndpoint(ID_Dev_Def,eConsole)) then
begin
showmessage ('eConsole')
end
else begin
showmessage (IntToStr(HResult (Def_Dev_Ch.SetDefaultEndpoint(ID_Dev_Def,eConsole))));
end;
if Succeeded(Def_Dev_Ch.SetDefaultEndpoint(ID_Dev_Def,eMultimedia)) then
begin
showmessage ('eMultimedia')
end
else begin
showmessage (IntToStr(HResult (Def_Dev_Ch.SetDefaultEndpoint(ID_Dev_Def,eMultimedia))));
end;
if Succeeded(Def_Dev_Ch.SetDefaultEndpoint(ID_Dev_Def,eCommunications)) then
begin
showmessage ('eCommunications')
end
else begin
showmessage (IntToStr(HResult (Def_Dev_Ch.SetDefaultEndpoint(ID_Dev_Def,eCommunications)));
end;
end;
end;
Добрый день еще раз. Спасибо за ответ. Попробовал сделать как Вы посоветовали, ошибка та же :(
Может быть я опять что-нибудь неверно сделал, я товичек в использовании недокментированных функций, да и в использовании API, если не сказать больше.
Немного подправил код:
IMMDeviceEnumerator = interface(IUnknown)
[IID_IMMDeviceEnumerator]
function EnumAudioEndpoints(dataFlow: EDataFlow; deviceState: SYSUINT; out DevCollection: IMMDeviceCollection): HRESULT; stdcall;
end;
IPolicyConfig = interface(IUnknown)
[IID_IPolicyConfig]
function SetDefaultEndpoint (wszDeviceID:WideString; ERole:ERole):Hresult; stdcall;
end;
type
TForm2 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form2: TForm2;
implementation
{$R *.dfm}
procedure TForm2.Button1Click(Sender: TObject);
var
AudioEndpoints:IMMDeviceEnumerator;
Collection:IMMDeviceCollection;
Device:IMMDevice;
Def_Dev_Ch:IPolicyConfig;
Id_Dev:PWideChar;
ID_Dev_Def:WideString;
begin
CoCreateInstance (CLSID_MMDeviceEnumerator,nil,CLSCTX_INPROC_SERVER,IID_IMMDeviceEnumerator,AudioEndpoints);
AudioEndpoints.EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, Collection);
Collection.Item(1,Device);
Device.GetId(Id_Dev);
ID_Dev_Def:=Id_Dev;
if Succeeded(CoCreateInstance (CLSID_PolicyConfigClient,nil,CLSCTX_INPROC_SERVER,IID_IPolicyConfig,&Def_Dev_Ch)) then
begin
if Succeeded(Def_Dev_Ch.SetDefaultEndpoint(Id_Dev,eConsole)) then
begin
showmessage ('eConsole')
end
else begin
showmessage (IntToStr(HResult (Def_Dev_Ch.SetDefaultEndpoint(Id_Dev,eConsole))));
end;
if Succeeded(Def_Dev_Ch.SetDefaultEndpoint(Id_Dev,eMultimedia)) then
begin
showmessage ('eMultimedia')
end
else begin
showmessage (IntToStr(HResult (Def_Dev_Ch.SetDefaultEndpoint(Id_Dev,eConsole))));
end;
if Succeeded(Def_Dev_Ch.SetDefaultEndpoint(Id_Dev,eCommunications)) then
begin
showmessage ('eCommunications')
end
else begin
showmessage (IntToStr(HResult (Def_Dev_Ch.SetDefaultEndpoint(Id_Dev,eConsole))));
end;
end;
end;
end.
Если выбираю другой класс и другой интерфейс (CLSID_PolicyConfigClient:TGUID ='{294935CE-F637-4E7C-A41B-AB255460B862}'; IID_IPolicyConfig : TGUID ='{568b9108-44bf-40b4-9006-86afe5b5a620}';),который описан на сайте и в VC++ программе, то программа отрабатывает без ошибок, но не меняет звуковое устройство "по умолчанию". Может я еще что то пропустил?
Заранее спасибо :)
Если вы заметили орфографическую ошибку на этой странице, просто выделите ошибку мышью и нажмите Ctrl+Enter. Функция может не работать в некоторых версиях броузеров.