異世界
2012年12月4日 星期二
2012年12月3日 星期一
Task Scheduler Managed Wrapper: 由程式來排程工作
資料來源:http://msdn.microsoft.com/zh-tw/ee922578.aspx
延伸閱讀 : http://taskscheduler.codeplex.com/wikipage?title=Examples&referringTitle=Home
Task Scheduler 基本概念
- Task Scheduler 是由一個或數個 Task(工作)組成,一個 Task 代表一個任務,它會有幾個參數:
- 觸發器(trigger),指示此工作會在何時觸發。在 Task Scheduler 2.0 時,一個工作可以有多個觸發器。1.0 時則每個工作只能有一個觸發器。
- 動作(action),指示此工作會觸發哪些程式或指令。在 Task Scheduler 2.0 時,一個工作可以有多個動作(最多 32 個動作)。1.0 時則每個工作只能有一個動作。
- 執行權限(Principals, Security Context),指示此工作要用哪個帳戶或安全權限執行,所有動作都會使用相同的權限來呼叫。
- 條件(Settings),可以決定工作在何種情況下才會啟動。
- 登錄資料(Registration Information),記錄系統管理的相關資訊,像是建立者(author)或工作說明(description)。
- 資料(Data),此工作所需要的額外資訊。
- Task 內部結構圖
- Task Scheduler 的動作支援四種類型,分別為啟動程式、自動發信、顯示訊息以及 COM 自訂處理器(handler),其中只有 COM 自訂處理器一項必須要由程式來做以外,其他三種均可以透過使用者介面設定。
-
Task Scheduler Managed Wrapper Library
Task Scheduler Managed Wrapper Library 最早是在 Code Project 網站中的一個專案,由 David Hall 於 2002 年所開發,當時還只是 .NET Framework 1.0 的時代,在 Code Project 的專案內容中,David 說明了如何由 Platform SDK 所附的 mstask.idl,使用 Tlbimp.exe 產生引用的 C# 程式碼再移植到他的專案中的方法。裡面使用了許多的 COM Interoperability 的技巧,值得學習 COM Interop 的開發人員作為參考。
下列範例程式為簡單操作 Task Scheduler API 的範例:
using System;
using Microsoft.Win32.TaskScheduler;
class Program
{
static void Main(string[] args)
{
// Get the service on the local machine
TaskService ts = new TaskService();
// Create a new task definition and assign properties
TaskDefinition td = ts.NewTask();
td.RegistrationInfo.Description = "Does something";
// Create a trigger that will fire the task at this time every other day
td.Triggers.Add(new DailyTrigger { DaysInterval = 2 });
// Create an action that will launch Notepad whenever the trigger fires
td.Actions.Add(new ExecAction("notepad.exe", "c:\\test.log", null));
// Register the task in the root folder
ts.RootFolder.RegisterTaskDefinition(@"Test", td);
// Remove the task we just created
ts.RootFolder.DeleteTask("Test");
}
}
Task Scheduler Wrapper 的起點是 TaskService 類別,它提供了管理與列舉資料夾的功能,以及取得工作以及連線狀態的功能,並且也可以在建構時設定連結到指定的伺服器。
TaskFolder 物件是處理資料夾的物件,它具有下列功能:
- 建立與刪除子資料夾(CreateFolder 與 DeleteFolder 方法)。
- 登錄工作(RegisterTaskDefinition 方法)以及刪除工作(DeleteTask 方法)。
- 取得子工作清單(Tasks 屬性)。
- 取得子資料夾清單(SubFolders 屬性)。
- 設定與取得 Security Descriptior 的 SDDL Form 資訊(GetSecurityDescriptorSddlForm 與 SetSecurityDescriptotSddlForm 方法)。
它的使用方法也很簡單,例如下列程式可以建立與刪除資料夾:
- create folder
// create folder.
TaskService service = new TaskService();
TaskFolder folder = service.GetFolder(@"\");
folder.CreateFolder(this.txtFolderName.Text, folder.GetSecurityDescriptorSddlForm(AccessControlSections.Access));
folder = null;
service = null;
// create folder.
TaskService service = new TaskService();
TaskFolder folder = service.GetFolder(@"\");
folder.CreateFolder(this.txtFolderName.Text, folder.GetSecurityDescriptorSddlForm(AccessControlSections.Access));
folder = null;
service = null;
- delete folder
// delete folder.
this._taskService = new TaskService();
TaskFolder folder = this._taskService.GetFolder(@"\MyFolder");
if (folder.Tasks.Count > 0)
{
throw new InvaildOperationException("TASKS_EXIST");
}
if (folder.SubFolders.Count > 0)
{
throw new InvaildOperationException("SUB_FOLDERS_EXIST");
}
TaskFolder folderParent = this._taskService.GetFolder(folder.Path.Replace(@"\" + folder.Name, ""));
folderParent.DeleteFolder(folder.Name);
folderParent = null;
folder = null;
- 例如建立一個 Email 的 Action 的程式如下:
EmailAction eAction = new EmailAction("Task fired", "sender@email.com", "recipient@email.com", "You just got a message", "smtp.company.com");
eAction.Cc = "alternate@email.com";
- 可用的 Trigger 均繼承自 Trigger 物件,支援的 Triggers 清單如下:
- 例如設定每月執行的觸發器的程式如下:
MonthlyTrigger mTrigger = new MonthlyTrigger();
mTrigger.StartBoundary = DateTime.Today + TimeSpan.FromHours(10);
mTrigger.DaysOfMonth = new int[] { 10, 20 };
mTrigger.MonthsOfYear = MonthsOfTheYear.July | MonthsOfTheYear.November;
進階功能:COM 處理器
進階功能:COM 處理器
這是 Task Scheduler 2.0 才有的新功能,允許開發人員自行建立一個支援 Task Scheduler COM 工作的元件,讓 Task Scheduler 幫你執行,雖然它名為 COM 處理器,但是 .NET 程式可以利用 COM Interop 方式將元件公開給 COM 介面,如此就可以當做一個 COM 元件供外部存取。另外,COM 處理器的工作只可以在程式碼中實作,無法透過 GUI 介面處理,因此這個能力算是保留給開發人員使用的,也就是需要額外撰寫登錄 COM 工作的程式碼並在安裝程式使用(或是撰寫成獨立的小程式亦可)。
要建立 COM 處理器,請執行下列步驟:
1. 建立一個類別庫專案。
2. 加入 Microsoft.Win32.TaskScheduler.dll 以及 TimeSpan2.dll 的參考。
3. 建立一個新類別,並設定與實作 Microsoft.Win32.TaskScheduler 命名空間的 ITaskHandler 介面。
4. 設定專案要產生 Type Library,即註冊 COM Interop:
5. 建置專案,並撰寫註冊COM Handler工作的程式碼,例如下列的程式碼:
ComHandlerAction comAction = new ComHandlerAction(new Guid("{CE7D4428-8A77-4c5d-8A13-5CAB5D1EC734}"));
comAction.Data = "Something specific the COM object needs to execute. This can be left unassigned as well.";
6. 登錄完成的工作,可以在動作中看到『自訂處理常式』,代表是COM Handler,而且這個動作是無法被編輯的:
ITaskHandler 介面具有四個方法,分別是 Start()、Pause()、Resume() 以及 Stop() 四個方法,代表啟動、暫停、繼續以及停止四個動作,由 Task Scheduler 視情況呼叫控制。其中 Start() 方法會傳入 Data 參數,這個參數是註冊 COM Handler 時所給定的 Data 屬性值,作為執行時的參數,而 Stop() 方法會需要傳回一個 RetCode,代表成功或失敗,若執行成功應傳回 0,若出現非零值時會被判斷是失敗。
下列程式碼是 COM Handler 的範例實作,功能為每五秒於事件記錄中寫入一筆資料,會寫入 12 次:
[ComVisible(true), Guid("CE7D4428-8A77-4c5d-8A13-5CAB5D1EC734"), ClassInterface(ClassInterfaceType.None)]
public sealed class MyCOMTask : ITaskHandler
{
private ITaskHandlerStatus handlerService;
private Timer timer;
private DateTime lastWriteTime = DateTime.MinValue;
private byte writeCount = 0;
private const string file = @"C:\TaskLog.txt";
void ITaskHandler.Start(object pHandlerServices, string Data)
{
handlerService = pHandlerServices as ITaskHandlerStatus;
lastWriteTime = DateTime.Now;
timer_Elapsed(null, null);
timer.Enabled = true;
}
void ITaskHandler.Stop(out int pRetCode)
{
timer.Enabled = false;
pRetCode = 0;
}
void ITaskHandler.Pause()
{
timer.Enabled = false;
}
void ITaskHandler.Resume()
{
timer.Enabled = true;
}
public MyCOMTask()
{
timer = new Timer(5000) { AutoReset = true };
timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
}
void timer_Elapsed(object sender, ElapsedEventArgs e)
{
if (writeCount < 12)
{
try
{
using (StreamWriter wri = File.AppendText(file))
wri.WriteLine("Log entry {0}", DateTime.Now);
handlerService.UpdateStatus((short)(++writeCount / 12), string.Format("Log file started at {0}", lastWriteTime));
}
catch { }
}
if (writeCount >= 12)
{
timer.Enabled = false;
writeCount = 0;
handlerService.TaskCompleted(0);
}
}
}
結語
Task Scheduler Wrapper Library 是相容性高(可用在 1.0 和 2.0)的一組工具函式庫,並直接控制 Task Scheduler 的功能,以實現能利用程式碼去存取與控制工作排程的能力,不必再額外去撰寫命令列指令或是使用 GUI 工具去設定排程了,讀者可以善用它來開發自己的工具,像是 Web 化的排程管理員或集中的管理工具等。
2012年12月1日 星期六
NAudio Audio Lib
CodePlex Project Hosting for Open Source Software
資料來源: http://naudio.codeplex.com/documentation
How do I...?
The best way to learn how to use NAudio is to download the source code and look at the two demo applications - NAudioDemo and NAudioWpfDemo. These demonstrate several of the key capabilities of the NAudio framework. They also have the advantage of being kept up to date, whilst some of the tutorials you will find on the internet refer to old versions of NAudio.
- Choose an audio output driver
- Play an MP3 File
- Convert an MP3 to WAV
- Play a WAV File
- Use the WavFileWriter class
- Play streaming MP3
- Implement Looped Playback
- Trim a WAV File
- Play a Sine Wave
- Merge MP3 Files
- Convert an AIFF file to WAV
- Work with Multi-Channel Audio
- Create an ID3v2 tag
Getting Started with NAudio – Downloading and Compiling
- Download a copy of the NAudio source code (or a pre-compiled version but the newest available code is available in source form first).
http://naudio.codeplex.com/SourceControl/list/changesets - The default project is set to the NAudio class library. Class Library’s don’t have anything to look at when you press F5. If this is your first time with NAudio, set the start-up project as NAudioDemo and then hit F5. The NAudioDemo project shows the base functionality you can utilise through your own project.
- In the BIN directory for the built solution, you can find a copy of the NAudio library for using and referencing in your own project. Make sure that you grab a copy of the NAudio.XML file as well if your copying it over to your own projects directory, that way you will have the intellisense documentation for use in Visual Studio when working with the NAudio API.
Additional Tutorials from OpenSebJ's blog (n.b. these are for NAudio 1.3):
- Tutorial 1: Introduction to Using NAudio
- Tutorial 2: Mixing Multiple Wave Files Together in Real Time
- Tutorial 3 - Sample Properties
- Tutorial 4 - Sample Reversing
- Tutorial 5 - Recording Audio
- Tutorial 6 - MIDI Interfacing
http://stackoverflow.com/
Last edited Nov 10 at 3:53 PM by markheath, version 28
2012年11月22日 星期四
C# Windows混音器控制
資料來源 : Audio Library Part I - (Windows Mixer Control)
Introduction
In this article, I'll show you how to use Windows Mixer
from C#.
For some time, I was trying to get information about how to program the mixer
from C#. I didn't have too much luck, and the few examples found were in C++, so… I took the hard and fun way... doing it myself.
This library is part of my Audio library to control Wave Audio (Playback
/Recording
), Mixer
,Playback
/Recording
compressed files using ACM, basic speech recognition and some other stuff that I'll be releasing in future articles.
AudioMixer Namespace
Hierarchical Objects View
The hierarchical view shows how the classes are organized.
The main object Mixers
contains two Mixer
objects, Playback
and Recording
; those will work with default devices, but can be changed by setting the property Mixers.Playback.DeviceId
orMixers.Recording.DeviceId
.
I made it as simple as possible by hiding all the flat Win32 API implementation from the developer and creating a hierarchical set of objects.
Every mixer
is autonomous, meaning you can have Playback mixer
set to one soundcard and Recordingmixer
to another one. Also each one contains two arrays or MixerLines
(Lines
, UserLines
).
Lines
object will contain all lines inside the mixer
, for example all the lines that don't have controls associated with it or lines that are not source lines.
UserLines
will contains all lines that the developer can interact with, having controls like Volume
, Mute
, Fadder
,Bass
, etc. (basically it is the same as Lines
object but a filter was applied to it).
Every Line
will contain a collection of controls, like Mute
, Volume
, Bass
, Fader
, etc.
If you are interested in knowing more about how Windows Mixer
works, here is an excellent link with all the information about it.
Let's See Some Code
-
To Get the Audio Devices in the Computer
foreach(MixerDetail mixerDetail in mMixers.Devices)
{
...
...
}
To Get the Input Devices
foreach(MixerDetail mixerDetail in mMixers.Recording.Devices)
{
...
...
}
More ………
2012年11月19日 星期一
Java Sound, Getting Started II
資料來源 : Java Sound, Getting Started, Part 2, Playback
== 前言 ==
This series of lessons is designed to teach you how to use the Java Sound API.
The first lesson in the series was entitled Java Sound, An Introduction.
The previous lesson was entitled Java Sound, Getting Started, Part 1, Playback.
This lesson, entitled Java Sound, Getting Started, Part 2, Capture using Specified Mixer, is a follow-on to the previous lesson.
Summary
In this lesson, I showed you how to use the Java Sound API to capture audio data from a microphone and how to save that data in a ByteArrayOutputStream object. I also showed you how to identify the mixers available on your system, and how to specify a particular mixer for use in the acquisition of audio data from the microphone.
Complete Program Listing
A complete listing of the program is shown in Listing 12.
/*File AudioCapture02.java
This program demonstrates the capture and
subsequent playback of audio data.
A GUI appears on the screen containing the
following buttons:
Capture
Stop
Playback
Input data from a microphone is captured and
saved in a ByteArrayOutputStream object when the
user clicks the Capture button.
Data capture stops when the user clicks the Stop
button.
Playback begins when the user clicks the Playback
button.
This version of the program gets and displays a
list of available mixers, producing the following
output:
Available mixers:
Java Sound Audio Engine
Microsoft Sound Mapper
Modem #0 Line Record
ESS Maestro
Thus, this machine had the four mixers listed
above available at the time the program was run.
Then the program gets and uses one of the
available mixers instead of simply asking for a
compatible mixer as was the case in a previous
version of the program.
Either of the following two mixers can be used in
this program:
Microsoft Sound Mapper
ESS Maestro
Neither of the following two mixers will work in
this program. The mixers fail at runtime for
different reasons:
Java Sound Audio Engine
Modem #0 Line Record
The Java Sound Audio Engine mixer fails due to a
data format compatibility problem.
The Modem #0 Line Record mixer fails due to an
"Unexpected Error"
Tested using SDK 1.4.0 under Win2000
************************************************/
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import javax.sound.sampled.*;
public class AudioCapture02 extends JFrame{
boolean stopCapture = false;
ByteArrayOutputStream byteArrayOutputStream;
AudioFormat audioFormat;
TargetDataLine targetDataLine;
AudioInputStream audioInputStream;
SourceDataLine sourceDataLine;
public static void main(String args[]){
new AudioCapture02();
}//end main
public AudioCapture02(){//constructor
final JButton captureBtn =
new JButton("Capture");
final JButton stopBtn = new JButton("Stop");
final JButton playBtn =
new JButton("Playback");
captureBtn.setEnabled(true);
stopBtn.setEnabled(false);
playBtn.setEnabled(false);
//Register anonymous listeners
captureBtn.addActionListener(
new ActionListener(){
public void actionPerformed(
ActionEvent e){
captureBtn.setEnabled(false);
stopBtn.setEnabled(true);
playBtn.setEnabled(false);
//Capture input data from the
// microphone until the Stop button is
// clicked.
captureAudio();
}//end actionPerformed
}//end ActionListener
);//end addActionListener()
getContentPane().add(captureBtn);
stopBtn.addActionListener(
new ActionListener(){
public void actionPerformed(
ActionEvent e){
captureBtn.setEnabled(true);
stopBtn.setEnabled(false);
playBtn.setEnabled(true);
//Terminate the capturing of input data
// from the microphone.
stopCapture = true;
}//end actionPerformed
}//end ActionListener
);//end addActionListener()
getContentPane().add(stopBtn);
playBtn.addActionListener(
new ActionListener(){
public void actionPerformed(
ActionEvent e){
//Play back all of the data that was
// saved during capture.
playAudio();
}//end actionPerformed
}//end ActionListener
);//end addActionListener()
getContentPane().add(playBtn);
getContentPane().setLayout(new FlowLayout());
setTitle("Capture/Playback Demo");
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(250,70);
setVisible(true);
}//end constructor
//This method captures audio input from a
// microphone and saves it in a
// ByteArrayOutputStream object.
private void captureAudio(){
try{
//Get and display a list of
// available mixers.
Mixer.Info[] mixerInfo =
AudioSystem.getMixerInfo();
System.out.println("Available mixers:");
for(int cnt = 0; cnt < mixerInfo.length;
cnt++){
System.out.println(mixerInfo[cnt].
getName());
}//end for loop
//Get everything set up for capture
audioFormat = getAudioFormat();
DataLine.Info dataLineInfo =
new DataLine.Info(
TargetDataLine.class,
audioFormat);
//Select one of the available
// mixers.
Mixer mixer = AudioSystem.
getMixer(mixerInfo[3]);
//Get a TargetDataLine on the selected
// mixer.
targetDataLine = (TargetDataLine)
mixer.getLine(dataLineInfo);
//Prepare the line for use.
targetDataLine.open(audioFormat);
targetDataLine.start();
//Create a thread to capture the microphone
// data and start it running. It will run
// until the Stop button is clicked.
Thread captureThread = new CaptureThread();
captureThread.start();
} catch (Exception e) {
System.out.println(e);
System.exit(0);
}//end catch
}//end captureAudio method
//This method plays back the audio data that
// has been saved in the ByteArrayOutputStream
private void playAudio() {
try{
//Get everything set up for playback.
//Get the previously-saved data into a byte
// array object.
byte audioData[] = byteArrayOutputStream.
toByteArray();
//Get an input stream on the byte array
// containing the data
InputStream byteArrayInputStream =
new ByteArrayInputStream(audioData);
AudioFormat audioFormat = getAudioFormat();
audioInputStream = new AudioInputStream(
byteArrayInputStream,
audioFormat,
audioData.length/audioFormat.
getFrameSize());
DataLine.Info dataLineInfo =
new DataLine.Info(
SourceDataLine.class,
audioFormat);
sourceDataLine = (SourceDataLine)
AudioSystem.getLine(dataLineInfo);
sourceDataLine.open(audioFormat);
sourceDataLine.start();
//Create a thread to play back the data and
// start it running. It will run until
// all the data has been played back.
Thread playThread = new PlayThread();
playThread.start();
} catch (Exception e) {
System.out.println(e);
System.exit(0);
}//end catch
}//end playAudio
//This method creates and returns an
// AudioFormat object for a given set of format
// parameters. If these parameters don't work
// well for you, try some of the other
// allowable parameter values, which are shown
// in comments following the declartions.
private AudioFormat getAudioFormat(){
float sampleRate = 8000.0F;
//8000,11025,16000,22050,44100
int sampleSizeInBits = 16;
//8,16
int channels = 1;
//1,2
boolean signed = true;
//true,false
boolean bigEndian = false;
//true,false
return new AudioFormat(
sampleRate,
sampleSizeInBits,
channels,
signed,
bigEndian);
}//end getAudioFormat
//=============================================//
//Inner class to capture data from microphone
class CaptureThread extends Thread{
//An arbitrary-size temporary holding buffer
byte tempBuffer[] = new byte[10000];
public void run(){
byteArrayOutputStream =
new ByteArrayOutputStream();
stopCapture = false;
try{//Loop until stopCapture is set by
// another thread that services the Stop
// button.
while(!stopCapture){
//Read data from the internal buffer of
// the data line.
int cnt = targetDataLine.read(tempBuffer,
0,
tempBuffer.length);
if(cnt > 0){
//Save data in output stream object.
byteArrayOutputStream.write(tempBuffer,
0,
cnt);
}//end if
}//end while
byteArrayOutputStream.close();
}catch (Exception e) {
System.out.println(e);
System.exit(0);
}//end catch
}//end run
}//end inner class CaptureThread
//===================================//
//Inner class to play back the data
// that was saved.
class PlayThread extends Thread{
byte tempBuffer[] = new byte[10000];
public void run(){
try{
int cnt;
//Keep looping until the input read method
// returns -1 for empty stream.
while((cnt = audioInputStream.read(
tempBuffer, 0,
tempBuffer.length)) != -1){
if(cnt > 0){
//Write data to the internal buffer of
// the data line where it will be
// delivered to the speaker.
sourceDataLine.write(tempBuffer,0,cnt);
}//end if
}//end while
//Block and wait for internal buffer of the
// data line to empty.
sourceDataLine.drain();
sourceDataLine.close();
}catch (Exception e) {
System.out.println(e);
System.exit(0);
}//end catch
}//end run
}//end inner class PlayThread
//=============================================//
}//end outer class AudioCapture02.java