異世界


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 代表一個任務,它會有幾個參數:
  1. 觸發器(trigger),指示此工作會在何時觸發。在 Task Scheduler 2.0 時,一個工作可以有多個觸發器。1.0 時則每個工作只能有一個觸發器。
  2. 動作(action),指示此工作會觸發哪些程式或指令。在 Task Scheduler 2.0 時,一個工作可以有多個動作(最多 32 個動作)。1.0 時則每個工作只能有一個動作。
  3. 執行權限(Principals, Security Context),指示此工作要用哪個帳戶或安全權限執行,所有動作都會使用相同的權限來呼叫。
  4. 條件(Settings),可以決定工作在何種情況下才會啟動。
  5. 登錄資料(Registration Information),記錄系統管理的相關資訊,像是建立者(author)或工作說明(description)。
  6. 資料(Data),此工作所需要的額外資訊。

 

  • Task 內部結構圖

image

 

  • 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;

image



  • 例如建立一個 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 清單如下:

image


 




  • 例如設定每月執行的觸發器的程式如下:

    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,若出現非零值時會被判斷是失敗。


    image




    下列程式碼是 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

    NAudio .NET Audio Library


    資料來源: 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.

    Getting Started with NAudio – Downloading and Compiling

    1. 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
    2. 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.
    3. 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):

    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
    Sample screenshot

    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