Сайт разработчика Александр Климова

/* Моя кошка замечательно разбирается в программировании. Стоит мне объяснить проблему ей - и все становится ясно. */
John Robbins, Debugging Applications, Microsoft Press, 2000

Системные звуки

В этой статье мы узнаем, как изменять системные звуки Windows. На всякий случай рекомендуется сделать резервную копию реестра, прежде чем вы будете запускать нашу программу, которая вносит изменения в реестре.

Возможно, вы знаете, что в Windows можно установить свои звуки сопровождения для различных событий, таких как Вход в Windows, Установка соединения, Уведомление о получении почты и так далее. Мы можем настроить на компьютере жертвы свои звуки, чтобы подшутить над коллегой. Есть много ресурсов, где можно найти хорошую коллекцию звуков, например, http://www.reelwavs.com/.

Настройка системных звуков

Если у вас есть доступ к компьютеру жертвы, то вы можете изменить системные звуки в Панели управления, открыв категорию Звук (Панель управления | Оборудование и звук | Звук | Изменение системных звуков). Вы можете пройтись по всем событиям и назначить свои звуки, указав пути к файлам.

Программная настройка системных звуков

Мы можем программно изменить системные звуки при помощи своей утилиты. Кроме того, утилита будет сохранять и восстанавливать звуковые настройки и воспроизводить звуки.

Но для начала нам нужно узнать, где хранится информация о файлах системных звуков. Как и многое другое, подобная информация хранится в реестре. Вы можете найти эту информацию в определенном месте:

Каждая папка в разделе Schemes/Apps/.Default соответствует определенному событию. Например, если вы отключали USB-устройство, то должны были слышать системный звук, связанный с событием DeviceDisconnect. У заданного события, подобного DeviceDisconnect, имеется несколько папок: .current, .Default, и папка для дополнительных звуковых схем.

Системное событие имеет следующую структуру:

  • .current — содержит пустой ключ со значением, содержащим путь к звуковому файлу, который используется в данной конфигурации. Для DeviceDisconnect в Windows XP текущим файлом является "C:\WINDOWS\media\Windows XP Hardware Remove.wav".
  • .Default — Содержит пустое значение, содержащее звуковой файл по умолчанию. Если вы не меняли звуковой файл, то это значение совпадает с ключом .current.
  • Другие папки — У вас могут быть другие папки, в которых хранятся звуковые схемы (пользовательские настройки).

Чтение и запись звуковых файлов для событий

Зная, где хранятся нужные настройки, можно создать DataSet, в котором будут содержаться системные события и путь к файлам для этих событий. Запустим новый проект Windows Forms и выберем "Add New Item..." из окна Solution Explorer, затем выберем шаблон DataSet. Добавим элемента DataColumn SoundName and SoundFile, как показано ниже:

Далее добавляем новый файл для класса RegistryWrapper.cs, в котором будут находиться все функции для работы с реестром: чтение, запись, сохранение, восстановление данных.

Чтение данных о системных событиях из реестра

Объявим две переменные в классе RegistryWrapper для хранения путей.


//these represent the location in the registry with the user sounds
string hivePrefix = @"AppEvents\Schemes\Apps\.Default\";
string hiveSuffix = @"\.current";

Далее, добавляем метод GetSystemSound(), который возвращает RegSoundDataTable, содержащий значения SoundName и SoundFile. Первое, что мы делаем – это получаем список всех подразделов для пути, который мы задаем при вызове метода GetSubKeyNames. Метод возвратит нам список всех системных звуков для событий. Потом, мы проходим через каждое событие, создавая новый ряд для DataTable до тех пор, пока настройки для SoundName к текущему событию и SoundFile в ключе реестра содержат путь к файлу. Обратите внимание, что когда мы вызываем метод GetValue для получения звукового файла, мы должны передать в имени ключа пустую строку "". Также мы добавим вспомогательную функцию для соединения двух переменных, объявленных ранее.


public RegSound.RegSoundDataTable GetSystemSound()
{
    //Get the subkey key
    string[] values = Registry.CurrentUser.OpenSubKey(hivePrefix).GetSubKeyNames();

    RegSound.RegSoundDataTable tb = new RegSound.RegSoundDataTable();

    foreach (string s in values)
    {
        //Loop through rows
        RegSound.RegSoundRow newRow = tb.NewRegSoundRow();
        newRow.SoundName = s;
        newRow.SoundFile = 
            (string)Registry.CurrentUser.OpenSubKey(getRegKeyPath(s)).GetValue("") ;
        tb.Rows.Add(newRow);
    }
    return tb;
}

//adds the full registry key including prefix and suffix
private string getRegKeyPath(string s)
{
    return hivePrefix + s + hiveSuffix;
}

Запись в реестр

Для установки всех звуковых событий мы создадим другой метод, который берет RegSound DataTable и звуковые файлы, которые мы меняем. Проходим в цикле через каждый ряд в DataTable и устанавливаем значение ключа в реестре для звука при помощи метода SetValue. Вызывая метод SetValue, нам нужно знать имя ключа (в нашем случае это пустая строка ""), значение ключа (путь к звуковому файлу), и RegistryKind, который описывает тип значения (мы используем тип строка).


public void SetSystemSound(RegSound.RegSoundDataTable sounds, 
        string soundPath)
{
    //loop through all sounds
    foreach (RegSound.RegSoundRow row in sounds)
    {
        //Set key and value
        RegistryKey key = 
           Registry.CurrentUser.OpenSubKey(getRegKeyPath(row.SoundName), true);
        key.SetValue("", soundPath, RegistryValueKind.String);    
    }
}

Резервное копирование текущих звуковых настроек

Меняя звуковые схемы у жертвы, мы должны предусмотреть возможность восстановить прежние настройки. Для этого, добавим метод SaveSystemSound, который использует DataTable для сохранения и пути файлов. Мы можем использовать метод WriteXml в объекте DataTable для сохранения DataTable как XML-файла.


public void SaveSystemSound(
     RegSound.RegSoundDataTable sounds, string savePath)
{
    //Save Sound DataSet
    sounds.WriteXml(savePath);
}

Восстановление сохраненных настроек

Теперь давайте добавим метод для восстановления настроек из предыдущего шага. Нам нужно знать, где был сохранен DataTable, и вызвать метод ReadXml для чтения данных. Теперь у нас есть возможность пройти в цикле через каждое звуковое событие и вызвать метод setValue для установки нового значения.


public void RestoreSystemSound(string savePath)
{
    //Restore Sound DataSet
    RegSound.RegSoundDataTable sounds = 
        new RegSound.RegSoundDataTable();
    sounds.ReadXml(savePath);

    foreach (RegSound.RegSoundRow row in sounds)
    {
        //Set Key
        RegistryKey key = 
               Registry.CurrentUser.OpenSubKey(
               getRegKeyPath(row.SoundName), true);
        key.SetValue("", row.SoundFile, RegistryValueKind.String);
    }
}

Воспроизведение звукового события

Наконец, мы добавим возможность воспроизведения звуков. Звуковые файлы находятся в папке media системной папки Windows, нам нужно быстро проверить, есть ли в пути к файлу обратный слэш ("\"), чтобы узнать, содержит ли файл путь и само имя файла. Если нет, то мы присоединяем путь к имени файла и воспроизводим его.


public void PlayRegistrySound(string soundFile)
{
    //play sound if there is an associated file
    if (soundFile != "")
    {
        SoundPlayer sp = new SoundPlayer();

        //add default path if there isn't one
        int a = soundFile.IndexOf('\\');
        if (a != 0)
        {
            soundFile = "%SystemRoot%\\media\\" + soundFile;
        }
        sp.SoundLocation = soundFile;
        sp.Play(); 
    }       
}

Создание пользовательского интерфейса

Создание пользовательского интерфейса мы начнем с добавления элементов управления на форму:

  • Элемент ToolStrip для кнопок Бэкап, Восстановить, Выбрать, и Применить изменения.
  • DataGridView, который мы можем перетаскивать при помощи щелчка "Data > Show Data Sources," и перетащив элемент RegSound DataGridView.
  • Два элемента OpenFileDialog, один для выбора, откуда восстановить настройки, а второй – для выбора звуковых файлов для замены.
  • Элемент SaveFileDialog для выбора, где сохранять резервную копию текущих системных звуков.

Загрузка данных

Итак, у нас все почти все готово для приложения. Добавим еще две переменные. Одна для представления RegistryWrapper, котору мы описали раньше и другая для хранения данных RegSoundDataTable. Для заполнения DataTable, мы вызовем метод GetRegistrySounds, который в свою очереднь вызовет метод GetSystemSound, созданный нами ранее. Мы вызываем метод GetRegistrySounds во время загрузки формы и во время восстановления звуков или когда мы применяем изменения, заполняя текущими звуковыми настройками DataGridView.


private void frmMainMenu_Load(object sender, EventArgs e)
{
    GetRegistrySounds();            
}
private void GetRegistrySounds()
{
    //Call the RegistryWrapper Class
    sounds = myReg.GetSystemSound();
    regSoundDataGridView.DataSource = sounds;
}

Настройка DataGridView

Займемся представлением данных в элементе DataGridView, изменяя некоторые свойства, например, установкой свойства AlternatingRowsDefaultCellStyle в различные цвета, изменяя шрифт DefaultCellStyle в Arial 10, и выключив возможность добавления, редактирования и удаления данных. Мы также добавим изображение "play" , чтобы прослушать текущий ассоциированный звук. Для этого щелкните правой кнопкой мыши на DataGridView и выберите "Edit Columns" для вызова диалогового окна Edit Column. Здесь мы добавим новую колонку "Play," установим тип DataGridViewImageColumn, присвоим свойству Image наше изображение музыки и установим свойство ImageLayout в "Zoom", чтобы изображения заполнило всю клетку колонки.

Добавим код для воспроизведения звука, когда будем щелкать на картинке. Для этого нужно использовать событие DataGridView CellContentClick. Звук будет играть, если мы щелкнем на третьей колонке (индекс отчитываестя с 0, поэтому для третьей колонки используемs #2). Для воспроизведения нам нужно знать путь к файлу, который мы получим, создавая DataGridViewTextBoxCell для колонки SoundFile и считывая ее значение.


private void regSoundDataGridView_CellContentClick(
       object sender, DataGridViewCellEventArgs e)
{
    //Represents col #3 the "Play" column
    if (e.ColumnIndex == 2)
    {
        DataGridViewTextBoxCell cell = (DataGridViewTextBoxCell)
          regSoundDataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex - 1]; 
        //Play Sound
        myReg.PlayRegistrySound(cell.Value.ToString());
    }
}

Заключение

Читать и записывать данные реестра очень просто, используя упорядоченные данные DataSet. Также легко найти новые звуки для приложения в интернете. Пробуйте. Счастливого вам программирования!

Скачать пример

Перевод: Василий Котов