C# Hooking 오픈 소스입니다.

예제 프로젝트도 함께 있습니다.

 

globalhook2_src.zip

 

 

참고:

http://www.codeproject.com/Articles/7294/Processing-Global-Mouse-and-Keyboard-Hooks-in-C

 

지정한 폴더 감시하기

 

1. 개요

[그림 1. 프로그램 동작화면]

파일을 공유하는 프로그램을 작성하다 보면, 특정 폴더에 있는 파일 및 폴더의 정보를 실시간으로 리스트에 띄워주어야 하는 경우가 발생할 수 있습니다.

여기에서는 이러한 경우에 필요한 기술 노하우를 전달하려고 합니다.

 

이를 위해 [그림1. 프로그램 동작화면]과 같이 특정 폴더 안에 파일 및 폴더가 생성, 이름변경, 삭제가 발생 하였을 때,

파일 목록 리스트를 갱신하고 Log를 남기는 예를 보여주고자 합니다.

 

2. 얻을 수 있는 기술

-지정한 폴더 감시

-특정 폴더의 파일 정보 얻기

 

3. 사용할 Class 소개하기

FileSystemWatcher : 파일 시스템 변경 알림을 수신하면서 디렉토리 또는 디렉토리의 파일이 변경되면 이벤트를 발생시킵니다.

-네임스페이스 : System.IO

-어셈블리 : System(System.dll)

 

 

    

속성

설명

Path

조사할 폴더의 경로를 가져오거나 설정

NotifyFilter

조사할 변경 내용 형식을 가져오거나 설정

Filter

폴더에서 모니터닝할 파일을 결정하는데 사용되는 필터 문자열을 가져오거나 설정,

특정 파일 감시 ex)*.exe,(모두 감시"", *.*) 

EnableRaisingEvents

구성 요소가 활성화 되는지 여부를 나타내는 값을 가져오거나 설정

 

 

const string FolderName = "Test";

FileSystemWatcher fs = new FileSystemWatcher();//개체 생성 
										

fs.Path = FolderName; //Test 폴더 감시 
										

fs.NotifyFilter = NotifyFilters.FileName | NotifyFilters.DirectoryName; 

fs.Filter = ""// *.*
										

              

fs.Created += new FileSystemEventHandler(fs_Created); 

fs.Deleted += new FileSystemEventHandler(fs_Deleted); 

fs.Renamed += new RenamedEventHandler(fs_Renamed);               

fs.EnableRaisingEvents = true//이벤트 활성화 
										

 

 

 

DirectoryInfo : 디렉토리 및 하위 디렉토리를 만들고, 이동하고, 열거하는 인스턴스 메서드를 노출합니다.

-네임스페이스 : System.IO

-어셈블리 : mscorlib(mscorlib.dll)

const string FolderName = "Test";

DirectoryInfo dti = new DirectoryInfo(FolderName); //개체 생성
										

FileInfo[] files = dti.GetFiles();   //해당 폴더의 파일정보 얻기

 

 

4. 프로그래밍 UI 디자인

단계1. 프로젝트 생성

[그림 2. 프로젝트 생성]

    

    단계2. Form UI 디자인하기

    

[ 그림 3. Form UI 디자인하기]

5. 프로그래밍하기

5.1 프로그래밍에 앞서 고민하기

이 프로그램에서 처리할 이벤트를 생각해 봅시다.

먼저, Form이 Load되는 시점에 이벤트를 등록하여야만 프로그램 동작 중에 감시하는 폴더의 이벤트를 감지 할 수 있겠습니다.

또한 감시하는 폴더에 원래 존재 하였던 파일들의 정보도 얻어와서 리스트 박스에 띄워줘야 하기 때문에 DictionaryInfo 클래스를 이용해

감시하는 폴더의 파일들의 정보를 얻어와서 listBox_filelist에 아이템을 추가해줍니다.

프로그램 초기화는 끝났고, 이제 프로그램 동작 시점에 대해 설명해드리겠습니다.

FileSystemWatcher 개체에서 파일이 생성 되었다면, listBox_filelist에 아이템을 추가하고,

수정되었다면 아이템을 찾아서 아이템을 수정하고, 삭제되었다면 아이템을 찾아 삭제해주면 되겠습니다.

이 모든 이벤트들은 listBox_log에 아이템을 추가하게 됩니다. 주의 할 점은 이벤트 발생시 ListBox 컨트롤에 접근하므로 크로스쓰레드 문제를 처리해야 합니다.

 

5.2 본격적으로 프로그래밍하기

노하우를 효과적으로 전달하기 위하여 설계와 OOP 철학에 대한 부분은 생략하도록 하겠습니다.

컴파일을 하여 Debug 폴더 생성 후 테스트를 위한 Test폴더를 생성 합니다.

Form이 Load 되었을 때, Test폴더의 파일 정보를 얻는 InitFilelist 메소드와 폴더를 감시할 개체를 생성하는 InitFileSystemWatch 메소드를 호출합니다.

InitFileSystemWatch 메서드에서는 FileSystemWatch 개체를 생성 후, 속성들의 값을 넣어주고 이벤트 핸들러를 등록 합니다.

마지막으로는 이벤트 발생 여부 속성인 EnableRaisingEvents의 값을 true로 지정해줍니다.

 

 


										

public partial class Form_filesystemwatcher : Form
										

{

    const string FolderName = "Test";

    public Form_filesystemwatcher()

    {

        InitializeComponent();

    }

 

    private void Form_filesystemwatcher_Load(object sender, EventArgs e)

    {

        InitFilelist();

        InitFileSystemWatcher();

    }

 

    private void InitFilelist()

    {

        DirectoryInfo dti = new DirectoryInfo(FolderName);

        FileInfo[] files = dti.GetFiles();

        foreach (FileInfo file in files)

        {

            listBox_filelist.Items.Add(file.Name);

        }

    }

 

    private void InitFileSystemWatcher()

    {

        FileSystemWatcher fs = new FileSystemWatcher();

        fs.Path = FolderName; 

        fs.NotifyFilter = NotifyFilters.FileName | NotifyFilters.DirectoryName; 

        fs.Filter = ""

        fs.Created += new FileSystemEventHandler(fs_Created); 

        fs.Deleted += new FileSystemEventHandler(fs_Deleted); 

        fs.Renamed += new RenamedEventHandler(fs_Renamed);    

        fs.EnableRaisingEvents = true;     }

 

 

FileSystemWatcher 개체에서 이벤트가 발생되면 AddLog 메소드를 이용해 Log를 남기고,

추가, 수정, 삭제 되었을 때의 처리를 해줍니다. 크로스쓰레드 문제를 해결 하기 위하여 delegate를 이용하였습니다.

delegate void FileListboxDele(string text, WatcherChangeTypes watcherChangeTypes);

delegate void LogListboxDele(string text);

void fs_Created(object sender, FileSystemEventArgs e)

{

    AddLog(e.FullPath, e.ChangeType.ToString());

    UpdateFileList(e.Name, e.ChangeType);

}

void fs_Renamed(object sender, RenamedEventArgs e)

{

    AddLog(e.FullPath, e.ChangeType.ToString());

    ModFileList(e.OldName, e.Name);

}

void fs_Deleted(object sender, FileSystemEventArgs e)

{

    AddLog(e.FullPath, e.ChangeType.ToString());

    UpdateFileList(e.Name, WatcherChangeTypes.Deleted);

}

 

private void AddLog(string fullPath, string eventName)

{

    string log = string.Format("Path :{0} , Event :{1}", fullPath, eventName);

    listBox_log.Invoke(new LogListboxDele(AddLog2), new object[] { log });

}

private void AddLog2(string log)

{

    listBox_log.Items.Add(log);

}

 

private void UpdateFileList(string filename, WatcherChangeTypes watcherChangeTypes)

{

    listBox_log.Invoke(new FileListboxDele(UpdateFileList2), new object[] { filename, watcherChangeTypes });

}

 

private void UpdateFileList2(string filename, WatcherChangeTypes watcherChangeTypes)

{

    switch(watcherChangeTypes)

    {

        case WatcherChangeTypes.Created: AddFileList(filename); break;

        case WatcherChangeTypes.Deleted: DelFileList(filename); break;

    }

}

 

private void AddFileList(string filename)

{

    listBox_filelist.Items.Add(filename);

}

private void ModFileList(string oldName, string name)

{

    UpdateFileList(oldName, WatcherChangeTypes.Deleted);

    UpdateFileList(name, WatcherChangeTypes.Created);

}

private void DelFileList(string filename)

{

    listBox_filelist.Items.Remove(filename);

}

 

 

 

How_to_-_지정한_폴더_감시하기.docx

 

How_to_-_지정한_폴더_감시하기.zip

How to C# Component - tip

FpSpreadSheet4.0 Sheet의 데이터 및 스키마를 Save 및 Open 하기

 

1. 개요

[그림 1.Sheet의 데이터]

프로젝트를 진행 하다보면 Sheet의 데이터를 저장하였다가 프로그램 재 구동시 불러와야 할 경우가 있습니다.

제가 생각하는 방법은 2가지 입니다.

1. Sheet에 바인딩 되어있는 DataTable을 Serialize하여 txt나 xml로 저장 하였다가 Deserialize하여 가져온다.

2. FpSpreadSheet의 Save 메소드와 Open 메소드를 이용하여 xml로 관리한다.

두 번째 방법을 이용하면 Sheet의 스키마도 Save 및 Open 할 수 있으니 두 번째 방법으로 설명하겠습니다.

 

 

2. 얻을 수 있는 기술

    - FpSpreadSheet의 Save, Open

 

3. 사용할 Method 소개하기

    Save : Sheet의 데이터를 인코딩하여 파일로 저장합니다. (Sheet의 스키마도 저장가능)

    Open : Sheet의 데이터를 디코딩하여 Sheet에 불러옵니다. (Sheet의 스키마도 로드가능)

 

 

4. 프로그래밍 UI 디자인

단계1. 프로젝트 생성

[그림 2. 프로젝트 생성]

    

    단계2. Form UI 디자인하기

 

 

            [그림 3. Form UI 디자인하기-FormMain.cs]

 

 

 

 

 

        [그림 4. Form UI 디자인하기-FormTest1.cs]

 

 

 

 

5. 프로그래밍하기

     5.1 프로그래밍에 앞서 고민하기

         프로그램 규모가 작으면 당장엔 상관 없겠지만 점점 커지고 방대해 지다 보면 Sheet를 보여주는 Form이 1000개가 될 수 있습니다.

         메인 폼에서 다른 자식들의 폼을 1000개를 가지고 있는 List<Form> foms 필드가 있습니다.

        1000개의 폼에 존재하는 Sheet를 저장 및 로드를 해야 합니다. 1000개의 메소드를 일일이 호출 할 수는 없으니 Interface를 사용하였습니다.

 

 

     5.2 본격적으로 프로그래밍하기

          노하우를 효과적으로 전달하기 위하여 설계와 OOP 철학에 대한 부분은 생략하도록 하겠습니다.

         IExportAndImport Interface에 대해 먼저 설명하겠습니다. controlFileName은 Export, Import할 파일 이름입니다.

        Directory는 사용자에게 입력 받을 수 있기 때문에 파일 이름만 필드에 넣었습니다. Export는 Save, Import는 Open기능 입니다.

 

 

namespace AboutFarPointExportAndImport

{

/// <summary>

/// Export와 Import Interface

/// </summary>

interface IExportAndImport

{

/// <summary>

/// Export, Import 할 파일 이름

/// </summary>

string controlFileName { get; set; }

 

/// <summary>

/// Save

/// </summary>

/// <param name="filePath"></param>

void Export(string folderPath);

 

/// <summary>

/// Open

/// </summary>

/// <param name="filePath"></param>

void Import(string folderPath);

}

}

 

 

 

 

     각 폼은 IExportAndImport Interface를 상속 받아 구현합니다.

    Save 메소드의 2번째 인자는 데이터만 저장 할 것인지 Sheet의 스키마도 xml형식으로 저장 할 것인지 지정합니다.

    true일 경우 데이터만 false일경우 데이터+스키마 모두 저장합니다.

public partial class FormTest1 : Form, IExportAndImport

{

public FormTest1()

{

InitializeComponent();

InitTable();

}

 

#region IExportAndImport 멤버

 

public string controlFileName { get; set; }

 

public void Export(string filePath)

{

System.IO.FileStream fs = new System.IO.FileStream(filePath, System.IO.FileMode.Create, System.IO.FileAccess.Write);

this.sheet.Save(fs, true);

fs.Close();

}

 

public void Import(string filePath)

{

System.IO.FileStream fs = System.IO.File.Open(filePath, System.IO.FileMode.Open, System.IO.FileAccess.Read);

this.sheet.Open(fs);

fs.Close();

}

 

 

#endregion

 

}

 

.........

 

     FormMain의 동작을 설명하겠습니다.

     메인 폼은 List<Form> forms 라는 필드 하나를 가지고 있습니다. Load 할 때, Form 개체를 생성하고

        IExportAndImport의 필드인 controlFileName에 Save 및 Open할 파일 이름을 지정해줍니다.

 

public partial class FormMain : Form

{

/// <summary>

/// 생성한 Form

/// </summary>

List<Form> forms = null;

 

public FormMain()

{

InitializeComponent();

}

 

/// <summary>

/// Form Load할때

/// </summary>

/// <param name="sender"></param>

/// <param name="e"></param>

private void FormMain_Load(object sender, EventArgs e)

{

FormTest1 form1 = new FormTest1();

FormTest1 form2 = new FormTest1();

form1.controlFileName = "form1.xml";

form2.controlFileName = "form2.xml";

 

forms = new List<Form>();

forms.Add(form1);

forms.Add(form2);

}

......

 

      

 

 

     프로그램 구동 중에 Save를 하려고 Save 버튼을 눌렀을 시와 Load를 하려고 Load 버튼을 눌렀을 시입니다.

    자식 폼들이 또 자식 폼을 가지고 있다면, 해당 자식 Form의 Export 메소드를 구현 할 때,

    아래와 같은 구조로 작성하신다면 문제가 없을 것입니다.

 

/// <summary>

/// Export

/// </summary>

/// <param name="sender"></param>

/// <param name="e"></param>

private void btn_save_Click(object sender, EventArgs e)

{

FolderBrowserDialog fb = new FolderBrowserDialog();

if (fb.ShowDialog() == DialogResult.OK)

{

for (int i = 0; i < this.forms.Count; i++)

{

IExportAndImport exportAndImportMember = this.forms[i] as IExportAndImport;

if (exportAndImportMember != null)

{

exportAndImportMember.Export(string.Format("{0}\\{1}", fb.SelectedPath, exportAndImportMember.controlFileName));

}

}

}

}

 

/// <summary>

/// Import

/// </summary>

/// <param name="sender"></param>

/// <param name="e"></param>

private void btn_load_Click(object sender, EventArgs e)

{

FolderBrowserDialog fb = new FolderBrowserDialog();

if (fb.ShowDialog() == DialogResult.OK)

{

for (int i = 0; i < this.forms.Count; i++)

{

IExportAndImport exportAndImportMember = this.forms[i] as IExportAndImport;

if (exportAndImportMember != null)

{

exportAndImportMember.Export(string.Format("{0}\\{1}", fb.SelectedPath, exportAndImportMember.controlFileName));

}

}

}

}

 

 

 

 

How_to_-_FarPoint Sheet 저장 및 불러오기.docx

 

AboutFarPointExportAndImport.zip

 

디자인 창에서 FpSpread 작업 할 때, SheetView 추가하여 Column Header 조작하기를 설명하겠습니다.

FpSpread를 하나 생성 하신 후, 속성 중 Sheets를 클릭하여 SheetView를 하나 생성합니다.

이 때, Column의 개수 및 Row 개수를 조절 하실 수 있습니다. (물론 Spread Designer에서도 변경 가능합니다.)

 

 

 

 

 

 

 

 

 

 

 

 

 

오른쪽 마우스 버튼을 눌러 Spread Designer를 클릭합니다.

 

 

 

 

 

 1번 지점을 클릭하시면 전체 선택이됩니다. 2번의 ColumnHeaderRowCount 속성을 조절하시면 3번과 같이
Header의 Row 개수가 3개로 증가한걸 확인 하실 수 있습니다.

 

 

 

 

 

 

 

 

 

 

 

 

1번 처럼 드래그 하여 선택을 한 후 오른쪽 마우스 버튼을 눌러 맨아래의 "Headers.."를 클릭하면 Header Editor창이
뜨게 됩니다. 2번은 1번의 상태와 동일합니다.

 

 

 

 

 

 

 

 

 

 

 

 

 

1번의 위치의 Cell을 클릭한 후, ColumnSpan을 2로 변경하면 Column 방향으로 2개가 합쳐지게 됩니다.

 

 

 

 

 

 

 

 

 

 

 

 

 

그 상태에서 RowSpan을 3으로 주면 Column 2개와 Row 3개가 합쳐진 모습을 미리 보여줍니다. Ok를 눌러줍니다.

 

 

 

 

 

 

 

 

 

 

 

 

 

1번과 같은 상태가 됩니다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

이처럼 Column 헤더를 조작하면 위와 같은 복잡한 헤더도 간단히 구현 가능합니다.

 

 #region 필드
        /// <summary>
        /// 다음 단어를 찾을 때 Index 위치
        /// </summary>
        int seekIndex = 0;
        #endregion

#region public method
        /// <summary>
        /// 해당 키워드들을 원하는 Color로 지정해줍니다.
        /// </summary>
        /// <param name="words"></param>
        /// <param name="color"></param>
        public void SetColorWords(string []words, System.Drawing.Color color)
        {
            for (int i = 0; i < words.Length; i++)
            {
                string word = words[i];
                while (true)
                {
                    int findIndex = 0;

                    findIndex = NextFind(word);

                    //검색된 것이 없다면
                    if (findIndex == -1)
                        break;

                    int result = this.Find(word, findIndex, findIndex + word.Length, RichTextBoxFinds.MatchCase);
                    if (result == -1)
                        break;

                    this.SelectionColor = color;
                    this.DeselectAll();
                    this.SelectionColor = System.Drawing.Color.Black;
                }
                this.seekIndex = 0;
            }
            this.SelectionLength = 0;
        }
        #endregion
        /// <summary>
        /// seekIndex부터 word에 해당하는 시작되는 Index 반환
        /// </summary>
        /// <param name="word"></param>
        /// <returns></returns>
        private int NextFind(string word)
        {
            int i = 0;
            for (i = seekIndex; i < this.Text.Length; i++)
            {
                int wordIndex = 0;
                while (true)
                {
                    if (this.Text[i] == word[wordIndex])
                    {
                        i++;
                        wordIndex++;
                        if (wordIndex == word.Length)
                        {
                            this.seekIndex = i;
                            return i - word.Length;
                        }
                    }
                    else
                    {
                        break;
                    }
                }
            }
            this.seekIndex = i;
            return -1;
        }

 

 

결과 화면

 

 

 

 

 

TestRichTextBox.zip

 

 

'.Net > Winform' 카테고리의 다른 글

C# Keyboard, Mouse Hooking  (0) 2012.06.28
C# 지정한 폴더 감시하기  (0) 2012.06.27
RichTextBox Keyword 비쥬얼 스튜디오 효과내기  (0) 2012.05.10
C# WndProc이용, USB인식 및 해제 감시  (0) 2011.12.07
C# FileSystemWatcher  (3) 2011.12.07

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;

namespace WebSolus.Windows.Controls
{
    class KeyWordRichTextBox:RichTextBox
    {
        #region 속성
        /// <summary>
        /// 검색할 KeyWord를 가져오거나 설정합니다.
        /// </summary>
        public List<string> KeyWords {get;set; }
        #endregion

        #region 생성자
        /// <summary>
        /// 생성자
        /// </summary>
        public KeyWordRichTextBox()
        {
            InitializeComponent();
        }
        #endregion

        #region InitializeComponent
        /// <summary>
        /// InitializeComponent
        /// </summary>
        private void InitializeComponent()
        {
            this.SuspendLayout();
            this.ResumeLayout(false);
            this.KeyWords = new List<string>();
            this.KeyUp += new KeyEventHandler(KeyWordRichTextBox_KeyUp);
        }
        #endregion

        #region Api Function
        /// <summary>
        /// Caret의 위치를 가져옵니다.
        /// </summary>
        /// <param name="pt">Point 개체</param>
        /// <returns>true 성공,false 실패</returns>
        [System.Runtime.InteropServices.DllImport("user32.dll")]
        private static extern bool GetCaretPos(out System.Drawing.Point pt);
        #endregion

        #region Event
        /// <summary>
        /// 키를 놓을 때 발생합니다.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void KeyWordRichTextBox_KeyUp(object sender, KeyEventArgs e)
        {
            for (int i = 0; i < this.KeyWords.Count; i++)
            {
                string word = this.KeyWords[i];
                if (this.SelectionStart >= word.Length)
                {
                    int result = this.Find(word, this.SelectionStart - word.Length, this.SelectionStart, RichTextBoxFinds.MatchCase);
                    if (result != -1)
                    {
                        System.Drawing.Point nearPoint = new System.Drawing.Point();
                        GetCaretPos(out nearPoint);
                        int index = this.GetCharIndexFromPosition(nearPoint);
                        this.SelectionColor = System.Drawing.Color.Blue;
                        if (index + 1 == this.TextLength)
                        {
                            this.SelectionStart = index + 1;
                        }
                        else
                        {
                            this.SelectionStart = index;
                        }
                        this.SelectionColor = System.Drawing.Color.Black;
                        this.DeselectAll();

                    }

                }
            }
        }
        #endregion
    }
}

 

 

 

결과 이미지

 

 

 

 

 

프레임워크 버젼 : 닷넷 프레임워크 2.0

 

 

TestRichTextBox.zip

'.Net > Winform' 카테고리의 다른 글

C# Keyboard, Mouse Hooking  (0) 2012.06.28
C# 지정한 폴더 감시하기  (0) 2012.06.27
RichTextBox 원하는 단어들 원하는 색으로 변경  (0) 2012.05.11
C# WndProc이용, USB인식 및 해제 감시  (0) 2011.12.07
C# FileSystemWatcher  (3) 2011.12.07

 

protected override void WndProc(ref Message m)

{

      UInt32 WM_DEVICECHANGE = 0x0219;

      UInt32 DBT_DEVTUP_VOLUME = 0x02;

      UInt32 DBT_DEVICEARRIVAL = 0x8000;

      UInt32 DBT_DEVICEREMOVECOMPLETE = 0x8004;

if ((m.Msg == WM_DEVICECHANGE) && (m.WParam.ToInt32() == DBT_DEVICEARRIVAL)) //디바이스 연결

{

      int devType = Marshal.ReadInt32(m.LParam, 4);

      if (devType == DBT_DEVTUP_VOLUME)

      {

            RefreshDevice();

      }

}

if ((m.Msg == WM_DEVICECHANGE) && (m.WParam.ToInt32() == DBT_DEVICEREMOVECOMPLETE)) //디바이스 연결 해제

{

      int devType = Marshal.ReadInt32(m.LParam, 4);

      if (devType == DBT_DEVTUP_VOLUME)

      {

            RefreshDevice();

      }

}

 

base.WndProc(ref m);

}

 

 

 

public void RefreshDevice()

{

            listBox1.Items.Clear();

            string[] ls_drivers = System.IO.Directory.GetLogicalDrives(); //연결 되어있는 디바이스 얻어오기

            foreach (string device in ls_drivers)

            {

            System.IO.DriveInfo dr = new System.IO.DriveInfo(device);

            if (dr.DriveType == System.IO.DriveType.Removable) //제거 가능한 타입이라면

            {

                  listBox1.Items.Add(device);

            }

      }

}

 

FileSystemWatcher

작성자 : 김동영

작성일 : 2011. 12. 7

제목 : C# 폴더 감시

 

FileSystemWatcher 클래스는 파일 시스템 변경 알림을 수신하면서 디렉토리 또는

디렉토리의 파일이 변경되면 이벤트를 발생시킵니다.

 

FileSystemWatcher fs = new FileSystemWatcher();//개체 생성

fs.Path = "Test"; //Test 폴더 감시

fs.NotifyFilter = NotifyFilters.FileName|NotifyFilters.DirectoryName; //파일 이름과 폴더 이름 감시

fs.Filter = ""; //특정 파일 감시 ex)*.exe,(모두 감시"", *.*)

fs.Created += new FileSystemEventHandler(fs_Created); //조건에 해당하는 파일 및 폴더의 생성 이벤트 등록

fs.Deleted+=new FileSystemEventHandler(fs_Deleted); //조건에 해당하는 파일 및 폴더의 삭제 이벤트 등록

fs.EnableRaisingEvents = true; //이벤트 활성화

 

testeventhandler += new mydele(Form1_testeventhandler);

 

 

속성

설명

Path

조사할폴더의경로를가져오거나설정

NotifyFilter

조사할변경내용형식을가져오거나설정

Filter

폴더에서 모니터닝할 파일을 결정하는데 사용되는 필터 문자열을 가져오거나 설정

EnableRaisingEvents

구성 요소가 활성화 되는지 여부를 나타내는 값을 가져오거나 설정

 

 

public partial class Form1 : Form

{

delegate void mydele(string path);

event mydele testeventhandler;

 

public Form1()

{

InitializeComponent();

InitWatcher();

}

 

private void InitWatcher()

{

FileSystemWatcher fs = new FileSystemWatcher();

fs.Path = "Test";//

fs.NotifyFilter = NotifyFilters.FileName | NotifyFilters.DirectoryName;

fs.Filter = "";

fs.Created += new FileSystemEventHandler(fs_Created);

fs.Deleted += new FileSystemEventHandler(fs_Deleted);

fs.EnableRaisingEvents = true;

 

testeventhandler += new mydele(Form1_testeventhandler);

}

 

void fs_Deleted(object sender, FileSystemEventArgs e)

{

MakeMessage(e.FullPath, "삭íe제|");

}

 

void Form1_testeventhandler(string path)

{

listBox1.Items.Add(path);

}

 

 

 

void fs_Created(object sender, FileSystemEventArgs e)

{

MakeMessage(e.FullPath,"생성");

}

 

private void MakeMessage(string FullPath, string msg)

{

string path = string.Format("{0}\\{1}", Application.StartupPath, FullPath);

string extension = Path.GetExtension(path); //확장자 검사 폴더면 Null 반환

if (extension == string.Empty)

{

path = string.Format("{0} 폴더가 {1}되었습니다", path, msg);

}

else

{

path = string.Format("{0} 파일이 {1}되었습니다", path, msg);

}

listBox1.Invoke(testeventhandler, new object[] { path });

}

}

 

    

결과:

 

 

 

 

자세한 내용: MSDN 링크

 

UI자동화

&스피치

 

 

 

 

 

 

 

선문비트 21

김동영

 

 

 

 

 

 

 

===========================================================================

 

 

 

 

목 차

 

 

u UI Automation

 

u Text To Speech

 

u Speech To Text

 

 

 

 

 

 

 

 

 

 

 

===========================================================================

 

 


 

u UI Automation

 

Microsoft UI 자동화는 WPF를 지원하는 모든 운영 체제에서 사용할 수 있는 새로운 내게 필요한 옵션 프레임워크입니다.

UI 자동화를 통해 데스크톱에 있는 대부분의 UI(사용자 인터페이스) 요소에 프로그래밍 방식으로 엑세스할 수 있으므로 화면 판독기 등의 보조 기술 제품에서 최종 사용자에게 UI에 대한 정보를 제공할 수 있으며 표준 입력 이외의 방법으로 조작할 수 있습니다. 또한 자동화된 테스트 스크립트에서 UI 자동화를 통해 UI와 상호 작용할 수 있습니다. 

 

 

 

 

 

 

u UI 자동화 트리

 

1) 개요

현재 데스크톱을 나타내며 해당 자식 요소가 응용 프로그램 창을 나타내는 루트 요소(RootElement)가 있습니다. 이러한 각 자식 요소에는 메뉴, 단추, 도구 모음, 목록 상자 등의 UI 항목을 나타내는 요소가 포함될 수 있습니다. 포함된 각 요소도 목록 항목과 같은 요소를 포함할 수 있습니다.

UI 자동화 공급자는 대개 창에서 호스팅되는 루트와 하위 트리로 구성되는 조각의 항목 간에 탐색을 구현하여 UI 자동화 트리를 지원합니다. 그러나 공급자는 컨트롤 간의 탐색을 고려하지 않습니다. 이러한 탐색은 기본 창 공급자의 정보를 사용하여 UI 자동화 핵심에서 관리합니다.

 

2) 자동화 트리의 뷰

UI 자동화 트리를 필터링 하여 특정 클라이언트에 관련된 AutomationElement 개체만 포함하는 뷰를 만들 수 있습니다.

UI 자동화 공급자는 IsControlElementProperty IsContentElementProperty 속성을 비롯하여 요소에 대한 속성을 정의하여 필터링을 지원합니다.

UI 자동화에서는 세 가지 기본 뷰를 제공합니다. 이들 뷰는 수행되는 필터링 유형에 의해 정의되며 그 범위는 응용 프로그램에 의해 정의됩니다. 또한 응용 프로그램에서는 속성에 다른 필터를 적용할 수도 있는데, 예를 들어 컨트롤 뷰에 활성화된 컨트롤만 포함할 수도 있습니다.

 

 

 

 

u UI 자동화 컨트롤 패턴

 

1) 개요

UI 자동화는 컨트롤 패턴을 사용하여 많이 사용되는 컨트롤 동작을 나타냅니다. 예를 들어 단추와 같이 호출 가능한 컨트롤에는 Invoke 컨트롤 패턴을 사용하고, 목록상자, 목록 뷰 또는 콤보 상자와 같이 스크롤 막대가 있는 컨트롤에는 Scroll 컨트롤 패턴을 사용합니다.

 

2) UI 자동화 공급자 및 클라이언트

UI 자동화 공급자는 컨트롤 패턴을 구현하여 컨트롤이 지원하는 특정 기능에 대한 적절한 동작을 노출합니다.

UI 자동화 클라이언트는 UI 자동화 컨트롤 패턴 클래스의 메서드와 속성에 엑세스하고 이를 사용하여 UI에 대한 정보를 얻거나 UI를 조작합니다

 

InvokePattern invoke = ae.GetCurrentPattern(InvokePattern.Pattern) as InvokePattern;

//현재 AutomationElement 객체의 패턴이 InvokePattern인지 체크함

 

3) 컨트롤 패턴 클래스 및 인터페이스

 

컨트롤 패턴 클래스

공급자 인터페이스

설명

DockPattern

IDockProvider

도킹할 수 있는 컨트롤에 사용됨 예) 도구 모음

ExpandCollapsePattern

IExpandCollapseProvider

확장하거나 축소할 수 있는 컨트롤에 사용됨 예) 파일 메뉴

GridPattern

IGridProvider

특정 셀로 이동 및 크기 조정 같은 표 기능을 지원하는 컨트롤

GridItemPattern

IGridItemProvider

표 안에 셀이 있는 컨트롤에 사용됨

InvokePattern

IInvokeProvider

호출할 수 있는 컨트롤에 사용됨 예) 단추

MultipleViewPattern

IMultipleViewProvider

같은 정보, 데이터 또는 자식 집합을 여러 표현 간에 전환할 수 있는 컨트롤에 사용됨

RangeVal!uePattern

IRangeVal!ueProvider

컨트롤에 적용할 수 있는 값의 범위가 있는 컨트롤에 사용됨

ScrollPattern

IScrollProvider

스크롤할 수 있는 컨트롤에 사용됨

ScrollItemPattern

IScrollItemProvider

스크롤되는 목록에 개별 항목이 있는 컨트롤에 사용됨 예) 콤보 상자

SelectionPattern

ISelectionProvider

선택 컨테이너 컨트롤에 사용됨 예) 목록 상자와 콤보 상자

SelectionItemPattern

ISelectionItemProvider

목록 상자 및 콤보 상자와 같은 선택 컨테이너 컨트롤에 있는 개별 항목에 사용됨

TablePattern

ITableProvider

표와 머리말 정보가 있는 컨트롤에 사용됨

TableItemPattern

ITableItemProvider

테이블의 항목에 사용됨

TextPattern

ITextProvider

텍스트 정보를 노출하는 문서 및 편집 컨트롤에 사용됨

TogglePattern

IToggleProvider

상태를 전환할 수 있는 컨트롤에 사용됨 예) 확인란

TransformPattern

ITransformProvider

크기, 이동, 회전할 수 있는 컨트롤에 사용됨

ValuePattern

IValueProvider

값 범위를 지원하지 않는 컨트롤에서 클라이언트가 값을 가져오거나 설정할 수 있음 예) 날짜 시간 선택 컨트롤

WindowPattern

IWindowProvider

MS O/S에 대한 기본 개념인 창에 관련된 정보를 노출

 

 

u 예제

 

private void ProcessStart()

        {           

            OpenFileDialog of = new OpenFileDialog();

            bool? check = of.ShowDialog();

            if (check == true)

            {

string filename = of.FileName;

                Process pro = Process.Start(filename);

                Thread.Sleep(2000);

 

SetEditElements(pro.MainWindowHandle);

                SetButtonElements(pro.MainWindowHandle);

}

        }

파일 대화상자를 이용해, 제어할 프로세스를 하나 선택, Process 클래스를 이용해 프로세스를 실행하고, 프로세스가 로딩되는 시간이 있으므로 2초 정도 대기합니다.   

EditBox Button 컨트롤을 제어 할 것 이므로 SetEditElements메소드와 SetButtonElements메소드에 시작한 프로세스의 메인 윈도우의 핸들값을 넘겨줍니다.

 

 

 

 

private void SetEditElements(IntPtr hWnd)

        {

            AutomationElement ae = AutomationElement.FromHandle(hWnd);

            System.Windows.Automation.Condition condition = new AndCondition(

                new PropertyCondition(AutomationElement.IsEnabledProperty, true),

                new PropertyCondition(AutomationElement.ControlTypeProperty,

ControlType.Edit));

 

            AutomationElementCollection aec = ae.FindAll(TreeScope.Children, condition);

 

            foreach (AutomationElement child_ae in aec)

            {

                ar.Add(new MyAE(child_ae));

            }           

        }

핸들 값으로 AutomationElement 개체를 얻어옵니다.

Condition 클래스는 UI 자동화 요소 검색에 사용할 수 있는 대부분의 방법에서 검색하려는 요소를 정의하는 조건의 집합입니다. IsEnabledProperty true이며 ControlTypeProperty Edit인 요소를 얻어 올 것이므로 AndCondition을 사용 하였습니다. 한 개는 Condition, 한 개만 만족해도 얻어 오려면 OrCondition을 사용 하시면 됩니다.

 FindFirst 또는 FindAll을 사용하여 수행하는 검색에는 시작 지점과 범위가 있어야 합니다. 시작 지점에는 요소 자체와 형제 요소, 부모 요소, 상위 요소, 바로 아래 자식 요소 및 하위 요소가 포함 될 수 있습니다. 시작 지점을 자식 요소, 바로 위에서 생성한 condition 개체를 인자로 넘겨 줍니다.

 얻어온 요소들을 ArrayList에 보관 하였습니다.

 

 

 

 

private void SetButtonElements(IntPtr hWnd)

        {

            AutomationElement ae = AutomationElement.FromHandle(hWnd);

            System.Windows.Automation.Condition condition = new AndCondition(

                new PropertyCondition(AutomationElement.IsEnabledProperty, true),

                new PropertyCondition(AutomationElement.ControlTypeProperty,

 ControlType.Button));

            AutomationElementCollection aec = ae.FindAll(TreeScope.Children, condition);

 

                          

            foreach (AutomationElement child_ae in aec)

            {  

                ar.Add(new MyAE(child_ae));         

            }           

        }

Condition ControlType.Button을 제외하곤 동일합니다.

 

 

 

 

 

 

 

void it_InputTextEventHandler(string text)

        {           

            MyAE mae = listView1.SelectedItem as MyAE;

            AutomationElement ae = mae.AE;

            object obj;

            try

            {

                if (ae.TryGetCurrentPattern(ValuePattern.Pattern, out obj))

                {

                    ValuePattern valp = obj as ValuePattern;

                    valp.SetValue(text);

                }

 

            }

            catch

            {

 

            }

        }

선택한 요소의 타입이 Edit 일 경우의 처리입니다. 요소의 TryGetCurrentPattern 메소드는 컨트롤 패턴을 구현하는 개체를 검색합니다.

컨트롤 별 패턴은 http://msdn.microsoft.com/ko-kr/library/ms750574 을 참조하시길 바랍니다. 요소가 Value 패턴을 지원하는지 검색 하고, Value 패턴을 지원 한다면 True return 하고 ValuePattern 개체를 얻습니다. 이 개체를 통해 공급자의 EditBox에 접근 할 수 있습니다.

 

 

 

 

 

 

 

MyAE ate = ar[index] as MyAE;

 

                    object obj;

                    try

                    {

                        if (ate.AE.TryGetCurrentPattern(InvokePattern.Pattern, out obj))

                        {

                            InvokePattern invp = obj as InvokePattern;

                            invp.Invoke();

                        }

                    }

                    catch

                    {

 

                    }

버튼도 비슷합니다. InvokePattern을 지원 하는지 검색하여 InvokePattern 패턴이라면 Invoke해주고 있습니다.

 

 

 

 

 

 

 

===========================================================================

 

 

u Text To Speech

WPF에서는 시각적으로 불편한 사람들을 위하여 텍스트를 읽어주는 기능, Speech 기능을 지원하고 있습니다. 우선 Speech 기능을 사용하기 위해선 참조추가를 해줘야 하는데, System.Speech 항목을 추가하여야만 사용이 가능합니다. SpeechSDK51를 설치해야 System.Speech가 설치 됩니다. 아래의 경로로 이동하여 SpeechSDK51.exe 를 다운 받으시면 됩니다.

http://www.microsoft.com/downloads/en/details.aspx?FamilyID=5e86ec97-40a7-453f-b0ee-6583171b4530

 

 

 

SpeechSynthesizer synthesizer = new SpeechSynthesizer();

synthesizer.Speak("I love you");

 

SpeechSynthesizer 클래스를 이용해 간단하게 원하는 영어 단어를 읽어줍니다. 아직 한글 지원은 하지 않습니다. 비동기적으로 이용하시려면 SpeckAsync메소드를 이용하시면 됩니다. 

 

읽어주는 속도를 변경하고자 하면 Rate 속성을 -10~10 사이의 값을 이용해 변경 해주시고,  볼륨을 조절 할 때는 Volume 속성의 값을 0~100 사이의 값을 이용해 변경해 주시면 됩니다.

 

다양한 음성이 설치 되어 있다면 SelectVoice메소드를 이용하시면 되고, SelectVoiceByHints 메소드를 이용해 원하는 목소리와 연령대도 선택 할 수 있습니다.

 

 

 

 

 

 

 

 

 

 

 ===========================================================================

 

 

u Speech To Text

Speech To Text는 곧 음성 인식입니다. SpeechSDK51를 설치 하면 Speech.dll을 이용하여

음성인식을 간단하게 사용 하실 수 있습니다. 저는 SpeechToText Class를 정의 하였습니다.

 

SpeechToText Class

namespace SupportSpeech

{

    class SpeechToText

    {

        SpeechRecognizer sr = null;

        public SpeechToText()

        {

           

        }

        public SpeechRecognizer ListenStart(string[] key)

        {

            sr = new SpeechRecognizer();

            SrgsDocument doc = new SrgsDocument();

 

            CultureInfo info = CultureInfo.GetCultureInfo("en-US");

            doc.Culture = info;

 

            SrgsRule command = new SrgsRule("command");

                       

            command.Add(new SrgsOneOf(key));

            doc.Rules.Add(command);

            doc.Root = command;

            Grammar grammer = new Grammar(doc);

            sr.LoadGrammar(grammer);

            return sr;

        }

       

        public void DisposeSpeechRecognizer()

        {

            sr.Dispose();

        }       

    }

}

SpeechRecognizer는 음성 인식에 관련된 클래스 입니다.  ListenStart 메소드는 인자로 인식할 keyword들을 받아 등록 시키는 메소드 입니다.

SpeechRecognizer 개체를 하나 생성 합니다. SrgsDocument 클래스를 사용하면 xml을 이용하여 입력 규칙을 등록 할 수 있습니다. 위의 예제에서는 xml을 이용하지 않고 바로 등록 하고 있습니다.

 

 

CultureInfo 클래스를 사용한 이유는 컴퓨터의 언어가 기본적으로 한국어로 되어 있는 컴퓨터의 경우 아래의 같은 에러가 발생합니다.

 

'SupportSpeech, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' 어셈블리에 정의된 'Window1'의 인스턴스를 만들 수 없습니다.

 

문화권을 en-US로 변경하여 줍니다. SrgsRule 개체를 하나 생성 하였는데 인자로는 id 값입니다. 이 개체를 이용해 입력 규칙을 저장 한 후, LoadGrammer 메소드를 이용해 SpeechRecognizer 개체에 입력 규칙을 추가합니다.

 

 

 

 

SpeechRecognizer sr = st.ListenStart(temp);

sr.SpeechRecognized += new EventHandler<SpeechRecognizedEventArgs>(sr_SpeechRecognized);

이제 추가한 입력 규칙에 해당하는 음성이 발생을 시켜야 하는데, 그러려면 SpeechRecognizer 개체의 SpeechRecognized 이벤트 핸들러를 등록 합니다.

 

 

 

 

[DllImport!("user32.dll")]

public static extern void keybd_event(byte vk, byte scan, int flags, ref int extrainfo);

 

        void sr_SpeechRecognized(object sender, SpeechRecognizedEventArgs e)

        {

            if (e.Result.Text == "tab")

            {

Activate();

                byte TabKey = 9;

                int info = 0;

                keybd_event(TabKey, 0, 0, ref info);

            }           

            else if (e.Result.Text == "enter")

            {

                byte enter = 0x0D;

                int result = 0;

                keybd_event(enter, 0, 0, ref result);

            }           

           

        }

입력 규칙에 해당하는 단어나 문자가 사용자의 음성에 의해 입력되게 되면 두번째 인자인 SpeechRecognizedEventArgs 개체가 넘어옵니다. Result.Text 속성이 입력된 단어이므로 조건문을 이용하여 처리합니다. 키보드 이벤트를 발생 시키게 하기 위하여 마이그레이션 하였습니다.

 

 

 

 

 

참고자료

1. http://msdn.microsoft.com/ko-kr/library/ms753388.aspx

2. 선문비트 19 4 RUSIA 천정민씨 ,김동현씨 기술문서

3. 애덤 네이선의 WPF 언리쉬드

+ Recent posts