2012-07-24

우클릭 막힘 풀기, Personalized Web

우클릭 막힘을 해제하는 방법으로 자바스크립트를 인젝션 하는 방법과 크롬 확장프로그램을 사용하는 방법, 자바스크립트를 실행하지 않는 방법 등에 대해 글을 작성했었다.
그 중에 Allow-RightClick은 글을 작성한 때에는 괜찮은 확장프로그램이었는데, 이후 시도때도 안가리고 꽤 큼직한 이미지 광고를 인젝션을 해대는 패치가 있어 아쉽게 만들었었다.

크롬 확장프로그램 중 인젝션 프로그램을 한가지 더 소개한다.
전에 소개했던 Javascript Injector라는 확장프로그램은 단순히 자바스크립트를 인젝션 하는 기능만 되었고,
지금 소개하는  Personalized Web 은 태그의 속성별로 태그 삭제, 자바스크립트 인젝션, HTML 인젝션이 가능하다.
기본 설정되어 있는 값으로 속성별로 태그를 삭제해 광고를 차단하는 기능이 저장되어 있다.

전에 포스팅한 방법으로 자바스크립트를 인젝션 해도 우클릭이 풀리지 않는 페이지를 보다보니
JEagleEyeClient.setEnable()을 의심 했으나 아니었고


LayoutTopCommon이라는 자바스크립트가 막고 있었다.
이걸 차단하면, 우클릭은 되나 댓글도 펼쳐지지 않았다.

Personalized Web Option에서 "Add new rule"을 클릭하고,
아래 룰을 입력한다.
---
Rule Name: 네이버, 우클릭 해제
Match URLs: [blog,cafe].naver.com
Removers:
Tag(s): script  Attribute: src   Value: /nversioning/LayoutTop (대소문자 구분 없음)
---


Gmail을 넓게 보고 싶어 우측 광고영역을 태그 삭제로 없애봤다.
--
Rule name: Gmail, 광고 공간 제거
Match URLs: ^https://mail.google.com
Removers:
Tag(s): div   & Attribute: class    & Value: nH adC
Tag(s): div   & Attribute: class    & Value: nH PS


--
Dump rule로 생성한 텍스트 데이터를 아래 첨부한다.
사용은 해당 텍스트를 복사하고, Load rule을 클릭하고 붙여넣기 하면 된다.

네이버, 우클릭 해제
{
    "name":"네이버, 우클릭 해제",
    "urlRegex":"[blog,cafe].naver.com",
    "urlExcludeRegex":"",
    "enabled":true,
    "preserveDocWrite":false,"css":"","html":"","js":"",

    "filters":
    [
        {"tags":"script","attribute":"src","value":"","valueRegex":"/nversioning/LayoutTop"}
    ]
}

Gmail, 광고 공간 제거
{
    "name":"Gmail 광고공간 제거",
    "urlRegex":"^https://mail.google.com",
    "urlExcludeRegex":"",
    "enabled":true,
    "preserveDocWrite":false,"css":"","html":"","js":"",

    "filters":
    [
        {"tags":"div","attribute":"class","value":"nH adC","valueRegex":""},
        {"tags":"div","attribute":"class","value":"nH PS" ,"valueRegex":""}
    ]
}

우클릭을 막는 사람은 그 나름의 이유와 정당함을 가지고 있을 것이고.. 우클릭 방지를 반대하는 사람도 추구하는 방향이 있을 것이다.. 나는 우클릭 방지를 반대하는 입장이지만, 어느 한쪽이 옳다고 하기는 힘들다. 내 것이라는 지식, 내 것이라 올린 사진은 진정 내 것인가 진정 보호해야할 지적재산과 공동의 발전을 방해하는 무분별한 행위를 구별해야 한다.

2012-07-11

C#, 크로스 스레드 작업이 잘못되었습니다.

방법: 스레드로부터 안전한 방식으로 Windows Forms 컨트롤 호출(참조: http://msdn.microsoft.com/ko-kr/library/ms171728.aspx)

InvalidOperationException
크로스 스레드 작업이 잘못되었습니다. 'textBox1' 컨트롤이 자신이 만들어진 스레드가 아닌 스레드에서 액세스되었습니다.

다른 스레드에서 컨트롤에 접근하려 할 때, 예외가 발생한다.
이 예외는 릴리즈 모드에서는 나타나지 않고, 디버그 모드에서만 나타난다고 한다.
이 예외를 무시하거나, Invoke하는 방법으로 크로스 스레드 작업을 가능하게 할 수 있다.

무시하는 방법은 아래 코드를 삽입하면 된다.

CheckForIllegalCrossThreadCalls = false;

2012-07-06

C# DBAdapter.Update(), "INSERT INTO 오류"


DB에 액세스 하는 방법에 대해 공부를 하던 중, MS Access로 DB를 만들고 DBAdapter와 DataSet을 이용해 간단하게 데이터를 보여주고 업데이트하고 수정하는 것까지는 할 수 있다고 생각했는데,
어떤 DB에서는 DBAdapter.Update() 메쏘드로 Insert 때에 "INSERT INTO 오류"가 났다.
직접 필드를 추가한 DB에서는 정상 작동하고, 꼭 엑셀파일로 필드와 데이터를 추출한 DB만 오류가 있었다.

키값, Null허용, 필드의 디자인이 어떻게 되있는지 모두 확인 했지만 답을 못찼고 있었는데
답은 필드의 이름에 공백이었다.
"날 짜"를 "날짜"로 변경하니 정상적으로 INSERT 되었다.

2012-06-28

C# 텍스트파일을 읽어서 2차원 배열로 만들기

성능 데이터가 기록된 텍스트 파일을 열어서 엑셀에 붙여넣기 하기 위해 만듬.

using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.IO;
namespace automating_system_report
{
    #region RAWDATASTRUCT
    public class RAWDATASTRUCT
    {
        public int col, row;      // FieldValue가 있던, raw 파일 내에서의 위치
        public string FieldValue; // rawData 하나의 값
    }
    #endregion /RAWDATASTRUCT

    #region RawDataLOADER
    /// 
    /// XML에 추가할 RAWDATA를 로드하고, list에 등록하는 클래스
    /// fieldDelimiter의 값에 따라 필드구분을 하려고 했으나, 기본값 " "으로만 동작.
    /// 
    class RawDATALoader:FileManager
    {
        #region public Member
        public string filedDelimiter = " ";//텍스트파일의 필드구분자, 기본값 spacebar 를 필드 구분으로 함.
        public List rawdataList = new List();

        #endregion /public Member

        #region public Method
        public bool Load(string filename) //
        {
            FullPath = filename; 
            if (ExistFile)
            {
                FileStream fs = File.OpenRead(filename);
                StreamReader sr = new StreamReader(fs);
                int row = 0;// 줄 수
                while (sr.Peek() > -1) //한줄 한줄 배열에 저장
                {
                    SaveToList(sr.ReadLine(), row); 
                    row ++;
                }
                sr.Close();
                fs.Close();
                return true;
            }
            return false;
        }
        public string[,] To2DArray()
        {
            int SizeCol = rawdataList[rawdataList.Count - 1].col;
            int SizeRow = rawdataList[rawdataList.Count - 1].row;
            string[,] rtnArray = new string[SizeRow + 1, SizeCol + 1];
            int i = 0;
            foreach (RAWDATASTRUCT r in rawdataList)
            {
                rtnArray[r.row, r.col] = r.FieldValue;
            }
            return rtnArray;
        }
        #endregion /public Method

        #region private Method
        private bool SaveToList(string buf,int row)
        {
            int whereIsDelimiter = 0; //필드구분자의 문자열 내에서 위치
            int col = 0; // 필드 순번..
            string cropElement;
            buf=buf.Trim(); //앞이나 뒤에서 공백을 필드로 오인하는 것을 방지하기 위해 앞뒤 공백 삭제

            try
            {
                do
                {
                    whereIsDelimiter = buf.IndexOf(filedDelimiter);  //맨 왼쪽 필드 한 개의 길이를 찾기 위해,공백을 찾음.
                    if (whereIsDelimiter > -1) //공백을 찾은 경우
                    {
                        cropElement = buf.Substring(0, whereIsDelimiter); // 찾은 단일 필드를 저장하기 위해 임시로 변수에 저장
                        buf = buf.Remove(0, whereIsDelimiter); //다음 반복을 위해 buf에서 크롭한 부분 제거
                    }
                    else//공백을 못찾은 마지막 필드인 경우
                    {
                        cropElement = buf;
                    }

                    RAWDATASTRUCT fieldAdd = new RAWDATASTRUCT(); //crop한 필드를 저장하기 위해 임시 RAWDATASTRUCT를 만듬.
                    fieldAdd.FieldValue = cropElement;
                    fieldAdd.row = row; //텍스트 문서상의 줄 위치, 2차원 배열 생성시 사용됨
                    fieldAdd.col = col; //컬럼값, 2차원 배열 생성시 사용됨
                    rawdataList.Add(fieldAdd);//crop한 필드를 리스트에 저장
                    buf = buf.TrimStart();              //문자열 앞의 의미없는 공백제거
                    col += 1;

                } while (buf != null & whereIsDelimiter > -1); //buf가 null일 때까지, 더 이상 필드가 없을 때까지 반복.
                return true;
            }
            catch (Exception e) 
            {
                MessageBox.Show(e.Message);
                return false;
            }
        }
        #endregion /private Method
    }
    #endregion /RawDataLOADER
}
//

2012-06-27

C# 문자열의 yy, mm, dd를 지정한 날짜데이터의 숫자로 변환하기

파일을 날짜별로 생성해야 해서 만들었다.

public string ConvertDateInString(string filenameFormat, DateTime targetdate)

string filenameFormat에 "yymmdd_파일이름_yymmdd-1.xlsx"를  넘겨주면,
targetdate의 년월일의 숫자데이터로 변경한다.
return string은 변경된 이름이다.
(소문자 yy, mm, dd 또는 yyyy만 인식한다.)

 
/// 지정한 날짜 데이터의 숫자로 yy, mm ,dd 또는 yyyy를 replace 함.
/// 
using System;

namespace automating_system_report
{
    class DateStringConverter
    {
        #region global var
        FindNumberInString fn = new FindNumberInString(); // source에서 숫자 부분을 검출,  +나 - 기호의 뒤에 숫자가 위치하면, 날짜 데이터에 가감하기 위해 선언
        #endregion global var

        /// 
        /// filenameFormat을 yy, dd, mm의 해당 문자열을 targetdate의 날짜 데이터로 바꿈
        /// 파라미터 설명은 2012년 6월 26일을 예로 함.
        /// 
        /// 
ex) yyyymmdd점검일지yy-mm-dd-1.xlsx
        /// 
ex)날짜 데이터(2012-06-26)
        /// ex) 20120626점검일지12-06-25.xlsx
        public string ConvertDateInString(string filenameFormat, DateTime targetdate)
        {
            fn.FindNumber(filenameFormat);
            DateTime convertedDate=targetdate;

            filenameFormat = RecursiveReplaceDate(filenameFormat, "dd", ref targetdate); //filenameFormat의 dd부분을 days로 변경
            filenameFormat = RecursiveReplaceDate(filenameFormat, "mm", ref targetdate); //filenameFormat의 mm부분을 months로 변경
            if(filenameFormat.LastIndexOf("yyyy")>-1)
                filenameFormat = RecursiveReplaceDate(filenameFormat, "yyyy", ref targetdate); //filenameFormat의 yyyy부분을 years로 변경
            else if(filenameFormat.LastIndexOf("yy")>-1)
                filenameFormat = RecursiveReplaceDate(filenameFormat, "yy", ref targetdate); //filenameFormat의 yy부분을 years로 변경
            
            if ((filenameFormat.IndexOf("yy")>-1) ||(filenameFormat.IndexOf("mm")>-1) ||(filenameFormat.IndexOf("dd")>-1) )
            {
                filenameFormat=ConvertDateInString(filenameFormat,targetdate);
            }
            return filenameFormat;
        }
        public string RecursiveReplaceDate(string source, string repStr, ref DateTime convertedDate)
        {
            int whereIsrepStr=-1;  // repStr이 있는 위치를 저장할 변수
            int adjustLocPredication=-1; // repStr뒤에 +나 -로 조정할 기호가 있을 위치를 비교하기 위한 변수
            int adjustVal=0;  // +나 -로 조정할 기호가 있다면 조정할 수치
            int adjustLength=0; // +나 - 기호부터 ~ 수치까지 길이
            string dating=""; //년월일에 변경할 숫자 문자.
            string prestr; //숫자로 변경할 날짜부분의 앞
            string poststr;//숫자로 변경할 날짜부분의 뒤
            DateTime convertedDate;// targetdate에서 +,-로 변경한 날짜데이터.
            try
            {
                whereIsrepStr = source.LastIndexOf(repStr); //뒤에서부터 repStr을 검색, 위치를 기억
            }
            catch { whereIsrepStr = -1; } //repStr을 source문자열에서 못찾을 경우

            if (whereIsrepStr > -1)// 바꿀 대상인 repStr이 문자열에 있으면, 숫자로 변환하기 위해 아래를 수행
            {
                adjustLocPredication = whereIsrepStr + repStr.Length; //adjustLocPredication는 문자열에서 repStr위치한 다음 칸

                // 먼저 repStr뒤에 +나 - 기호의 조정자가 있는지 검색
                foreach (FindNumberInString.NumberInStringSTRUCT n in fn.ListNumber) //filenameFormat의 숫자의 값과 위치를 담고 있는 ListNumber를 foreach
                {
                    //Trace.WriteLine(n.StartIndex);
                    if (n.StartIndex == adjustLocPredication + 1) //foreach수행한 요소마다 위치가 +, - 기호 뒤에 위치하는지 비교
                    {                                             //(+,-기호 위치 바로 뒤에 오는 숫자는 날짜데이터에서 변경할 값으로 인식)
                        adjustVal = Int32.Parse(n.NumberString); //adjustVal은 날짜에서 변경할 값
                        // +기호인지 -기호인지 확인
                        char[] sign = source.ToCharArray();
                        if (sign[adjustLocPredication] == '-')
                        {
                            adjustVal *= (-1); //음수
                            adjustLength=n.Length+1;
                        }
                        else if (sign[adjustLocPredication ] == '+')
                        {
                            adjustLength=n.Length+1;
                        }
                        else { adjustVal = 0; } // + , - 기호가 없으면 날짜에서 변경할 값이 아닌 일반 숫자로 받아들임
                    }
                }
                switch (repStr) //repStr의 종류에 따라 위에서 찾은 adjustVal만큼 조정후, 년월일에 해당하는 문자열을 substring
                {
                            case "dd":
                                convertedDate = targetdate.AddDays(adjustVal);
                                dating = convertedDate.Date.ToString().Substring(8, 2);

                                break;
                            case "mm":
                                convertedDate = targetdate.AddMonths(adjustVal);
                                dating = convertedDate.Date.ToString().Substring(5, 2);

                                break;
                            case "yyyy":
                                convertedDate = targetdate.AddYears(adjustVal);
                                dating=convertedDate.Date.ToString().Substring(0,4);

                                break;
                            case "yy":
                                convertedDate= targetdate.AddYears(adjustVal);
                                dating=convertedDate.Date.ToString().Substring(2,2);
                                break;
                }
                if (dating!="")
                {
                     prestr = source.Substring(0, whereIsrepStr); //변경할 문자열을 제외한 앞부분
                     poststr = source.Substring(whereIsrepStr + repStr.Length + adjustLength);//변경할 문자열을 제외한 뒷부분
                     source = prestr + dating + poststr;//앞부분 + 날짜문자열 + 뒷부분을 다시 source에 저장
                }
                
            }
            return source;
        }
    }
}
//

2012-06-25

C# 배열의 내용을 Excel sheet에 채우기


 
/// 
/// Excel
/// .Application 액셀 어플리케이션 자체
/// ._Workbook 워크북 = 엑셀 파일
/// .Sheets 엑셀 시트의 배열
/// ._Worksheet 액티브된 단일 시트
/// .Range 편집범위
/// 
using System;
using System.Text;
using Excel=Microsoft.Office.Interop.Excel;
using System.Windows.Forms;
using System.Diagnostics;
namespace automating_system_report
{
    class ExcelUpdater:FileManager
    {


        #region const var
        #endregion const var

        #region public global var
        public Excel.Application objApp = new Excel.Application();
        public Excel._Workbook objBook;
        #endregion public global var
        
        #region 생성자&소멸자
        public ExcelUpdater() //기본 생성자
        { }
        public ExcelUpdater(string sourcefile, string targetfile)//템플릿 파일과 생성할 파일을 파라미터로 주고 open
        {
            LoadFile(sourcefile,targetfile);
        }
        ~ExcelUpdater()
        {
            //PRB: Visual Studio .NET 클라이언트에서 자동화 후에 Office 응용 프로그램이 종료되지 않는다(http://support.microsoft.com/kb/317109)
            UnLoadFile();
        }
        #endregion 생성자&소멸자

        #region public Method
        /// 
        /// updateSheet, 배열의 내용으로 sheet를 채운다.
        /// 줄과 칸을 각각 받았으나 "A1" 형식의 위치를 받기로 변경함.
        /// 
        /// 데이터를 붙여넣을 sheet번호. 1부터 시작
        /// 붙여넣기 시작할 위치, (예: "A1")
        /// 붙여넣을 내용, 문자열 2차원 배열
        public bool updateSheet(int sheetNumber, string writeStartLoc, ref string[,] rawdata)
        {
            try
            {
                Excel.Sheets objSheets = objBook.Sheets;
                Excel._Worksheet objSheet = objSheets[sheetNumber];
                Excel.Range range;
                range = objSheet.get_Range(writeStartLoc);
                range = range.get_Resize(rawdata.GetLength(0), rawdata.GetLength(1));
                range.set_Value(null, rawdata);
                return true;
            }
            catch(Exception e)
            {
                MessageBox.Show(e.Message);
                return false;
            }
        }
        /// col과 row로 호출하면, "A1"형식으로 변환.
        /// 데이터를 붙여넣을 sheet번호. 1부터 시작
        /// 붙여넣기 시작할 줄, 1부터 시작
        /// 붙여넣기 시작할 칸, 0부터 시작(0~16383)
        /// 붙여넣을 내용, 문자열 2차원 배열
        public void updateSheet(int sheetNumber,int writeStartRow,int writeStartCol,ref string[,] rawdata)
        {
            // Row와 Col을 저장할 변수들
            string writeStartLoc;
            writeStartLoc = DecToAlphabet(writeStartCol) + writeStartRow.ToString(); // row 1, col 0일 경우 loc은 "A1"
            updateSheet(sheetNumber, writeStartLoc, ref rawdata);
        }
        public bool LoadFile(string TemplateXlsFile, string CopiedXlsFile) // source파일의 사본을 만들고 로드
        {
            FullPath = CopiedXlsFile;

            try // 엑셀파일이 열려있을 경우 오류
            {
                //System.IO.File.Delete(FullPath);
                System.IO.File.Copy(TemplateXlsFile, CopiedXlsFile, true); //템플릿을 복사
                objBook = objApp.Workbooks.Open(CopiedXlsFile);
                //  objApp.Visible = true; // Excel app의 visible=false;

                return true;
            }
            catch(Exception e)
            {
                MessageBox.Show(e.Message);
                return false;
            }
        }
        public void UnLoadFile() // 엑셀 프로세스를 저장없이 그냥 닫을 때 사용.
        {
            objBook = null;
            objApp = null;
        }
        public bool CloseAndSave() // 엑셀 파일 저장.
        {
            try
            {
                objBook.Save();
                objBook.Close();
                objApp.Quit();  //프로세스에서 삭제
                return true;
            }
            catch (Exception e)
            {
                MessageBox.Show(e.Message);
                return false;
            }
        }

     
        /// 
        /// 10진수 컬럼 값을 엑셀에서 사용하는 컬럼 값 처럼, 알파벳으로 변환
        /// 
        /// 0은 A, Z는 25
        /// 
        public string DecToAlphabet(int num)
        {
            int rest; //나눗셈 계산에 사용될 나머지 값
            string alphabet; //10진수에서 알파벳으로 변환될 값

            byte[] asciiA = Encoding.ASCII.GetBytes("A"); // 0=>A
            rest = num % 26; // A~Z 26자
            asciiA[0] += (byte)rest; // num 0일 때 A, num 4일 때 A+4 => E

            alphabet = Encoding.ASCII.GetString(asciiA); //변환된 알파벳 저장
            
            num = num / 26 -1; // 그 다음 자리의 알파벳 계산을 재귀하기 위해, 받은 수/알파벳수 -1 (0은 A라는 문자값이 있으므로 -1을 기준으로 계산함)
            if (num > -1) 
            {
                alphabet = alphabet.Insert(0, DecToAlphabet(num)); //재귀 호출하며 결과를 앞자리에 insert
            }
            return alphabet; // 최종값 return
        }
        /// int input=Int32.Parse(textBox1.Text.ToString());
        /// Trace.WriteLine(input+"=> "+ xlsmaker.DecToAlphabet(input));
        /// 결과
        /// 0=> A
        /// 27=> AB
        /// 16383=> XFD
        #endregion public Method
    }
}
//


2012-06-22

C# Excel worksheet.Cells의 성능 느림.


출처: http://support.microsoft.com/kb/306023/ko

전에 올린 Cell에 직접 데이터를 입력하는 방법은 쉬운 방법이지만,
대량의 데이터를 옴기기엔 속도가 매우 느리다.(문서에서는 300줄을 기준으로 삼음)
8600줄의 데이터를 엑셀파일로 옴겼더니, 20분째 어플리케이션은 행이 걸려있고 엑셀문서는 한줄 한줄 타이핑 잘하는 사람이 입력하는 걸 빨리감기 하는 정도 속도로 쓰고 있다.

Cell에 직접 데이터를 입력하는 방식의 성능과 관련해 검색을 해보니, 이 방법은 대량데이터를 옴기기엔 적합하지 않다고 한다.

이렇게 입력할 데이터가 많을 경우 다른 방법을 제시했는데,
1. 배열을 워크시트로 전송
2. ADO 레코드를 워크시트로 전송
3. Windows 클립보드를 이용한 전송
이렇게 3가지 방법이다.

예제코드는 출처로 가보길 바람.

C# 숫자 컬럼값을 액셀 알파벳컬럼값으로 변환

숫자 컬럼 값을 엑셀에서 사용하는 알파벳으로 변환해 봄.
 
        /// 
        /// 10진수 컬럼 값을 엑셀에서 사용하는 컬럼 값 처럼, 알파벳으로 변환
        /// 
        /// 

        /// 
        public string DecToAlphabet(int num)
        {
            int rest; //나눗셈 계산에 사용될 나머지 값
            string alphabet; //10진수에서 알파벳으로 변환될 값

            byte[] asciiA = Encoding.ASCII.GetBytes("A"); // 0=>A
            rest = num % 26; // A~Z 26자
            asciiA[0] += (byte)rest; // num 0일 때 A, num 4일 때 A+4 => E

            alphabet = Encoding.ASCII.GetString(asciiA); //변환된 알파벳 저장
            
            num = num / 26 -1; // 그 다음 자리의 알파벳 계산을 재귀하기 위해, 받은 수/알파벳수 -1 (0은 A라는 문자값이 있으므로 -1을 기준으로 계산함)
            if (num > -1) 
            {
                alphabet=alphabet.Insert(0,DecToAlphabet(num)); //재귀 호출하며 결과를 앞자리에 insert
            }
            return alphabet; // 최종값 return
        }
        ///
        /// int input=Int32.Parse(textBox1.Text.ToString());
        /// Trace.WriteLine(input+"=> "+ xlsmaker.DecToAlphabet(input));
        /// 결과
        /// 0=> A
        /// 27=> AB
        /// 16383=> XFD
//

2012-06-19

C# Find number in String

문자열에서 숫자열을 찾아, 순서대로 List에 저장하는 클래스.

using System.Collections.Generic;

namespace automating_system_report
{
    class FindNumberInString
    {
        public List ListNumber=new List();
        public void FindNumber(string numStr)
        {
            int startIndex = 0;       // substring할 시작지점
            int lengthOfNumber = 0;   // substring할 길이
            int count=0;              // foreach에서 사용할 카운터
            bool bThisTurnIsNumeric;  // foreach에서 문자가 숫자인지 확인할 bool
            
            if (ListNumber != null)   // 함수가 이미 호출되어 리스트에 값이 있는 경우 clear
            { 
                ListNumber.Clear(); 
            }

            /// numStr += "/";  
            /// numStr에 "/"를 문자열 끝에 붙였던 이유는, 
            /// 문자열에서 숫자가 나오다 숫자가 아닌 문자가 나오면 그 순간 숫자열을 판단 하는데,
            /// 문자열 마지막이 숫자로 끝날 경우, List에 저장하지 못하고 foreach문을 끝내게된다..
            /// 
            /// 마지막에 if (bThisTurnIsNumeric == true && numStr.Length == (count + 1)) 로 대체함.
            foreach (char c in numStr)
            {
                bThisTurnIsNumeric = (c == '0' || c == '1' || c == '2' || c == '3' || c == '4' || c == '5' || c == '6' || c == '7' || c == '8' || c == '9');
                if (lengthOfNumber == 0)   //숫자가 아닌 경우, lengthOfNumber가 0이면 아직 문자열에서 숫자가 발견되지 않은 것, startIndex를 +1
                {
                    startIndex = count;
                }
                if (bThisTurnIsNumeric == true) //숫자를 찾은 경우, LengthOfNumber를 +1
                {
                    lengthOfNumber++;
                }

                if ((bThisTurnIsNumeric == false&&lengthOfNumber>0)                   //숫자열이 끝나고 문자가 시작되면 숫자열을 Substring할 준비가 된 것.
                    || (bThisTurnIsNumeric == true && numStr.Length == (count + 1)))  //문자열 마지막에 숫자열이 있을 경우, 다시 foreach를 실행하지 않을 것이므로 지금 저장
                {
                    ListNumber.Add(numStr.Substring(startIndex, lengthOfNumber));
                    lengthOfNumber = 0; // 문자열의 숫자부분 길이를 리셋
                }
                count++;
            }
        }
    }
}
/*          FindNumberInString fnfs = new FindNumberInString();
            fnfs.FindNumber("오늘 날짜는 2012년 06월 19일 17:29:00 ");
            int i = 0;
            foreach (string s in fnfs.ListNumber)
            {
                Trace.WriteLine(i.ToString() + ":" + s);
                i++;
            }

결과:
0:2012
1:06
2:19
3:17
4:29
5:00
*/
//

2012-06-15

C# load INI file


사용자의 설정을 기억할 목적으로 ini파일을 사용해 보고자 만들었다.
INI파일을 로드하고, 원하는 항목을 return 하는 기능을 구현했다.
일부러 이번엔 다른 사람의 코드를 먼저 보지않고, 내 나름대로 작성한 후에 봤다.

작성 후, 다른 사람의 소스를 보니 굉장히 간결했다. 아직 가야할 길이 먼 것 같다.
(비교: http://www.codeproject.com/Articles/1966/An-INI-file-handling-class-using-C)
검색된 소스는 GetPrivateProfileString, WritePrivateProfileString라는 API를 사용했다.


 
using System.Collections.Generic;
using System.Collections;
using System.IO;
using System.Diagnostics;
namespace automating_system_report
{

        #region INISTRUCT
        public class INISTRUCT  //INI파일의 한 줄의 내용을 저장할 class. List로 사용함.
        {
            public string lvar; // 왼쪽 문자열(상수이름) 
            public string rvar; // 오른쪽 문자열(상수값)
            public INISTRUCT(string inputLvar, string inputRvar)
            {
                lvar = inputLvar;
                rvar = inputRvar;
            }
        }
        #endregion

    #region INILoader
    /// 
    /// INI 파일을 읽어 , 데이터 이름과 데이터 값을 listINI 리스트에 저장하는 클래스.
    /// INILoader를 선언하고, LoadINIFile("filename") 메쏘드를 실행하면,
    /// INI파일의 값들을 클래스 내의 listINI 리스트에 저장한다.
    /// 
    public class INILoader
    {        
        #region globar var
        private  string matchString; //Predicate함수로 전달할 목적으로 전역 변수를 사용함.
        #endregion

        #region public member
        //INISTRUCT의 List
        public List listINI = new List();
        
        #endregion
        
        #region public method
        // INI파일을 로딩, 
        public bool LoadINIFile(string filename)
        {
            try
            {
                if (File.Exists(filename))
                {
                    FileStream fs = File.OpenRead(filename);
                    StreamReader sr = new StreamReader(fs);
                    while (sr.Peek() > -1) // Peek은  문자가 더 이상 없으면 반환 값은 -1 이다.
                    {
                        FillINISTRUCT(sr.ReadLine());  // FillINIStruct로 전역 리스트 listINI에 값을 저장함.
                    }
                    sr.Close();
                    fs.Close();
                    return true;
                }
                return false;
            }
            catch { return false; }

        }
        public void SaveINIFile(string filename)
        {
            try
            {
                /* FileStream fs=new FileStream(filename);
                 * StreamWriter sw=new StreamWriter(fs);
                 * 이렇게 해서 쓰면, 생성된 파일 끝부분에 이전의 쓰레기 값이 남았다...왜 그런지는 알아봐야함.
                 * 이미 있는 파일에 덮어쓰기 할 때, 이전 값이 더 클 경우 쓰레기 값이 남는 것으로 보임.
                 */
                StreamWriter sw = new StreamWriter(filename,false,  System.Text.Encoding.Unicode);
                foreach (INISTRUCT i in listINI)
                {
                    sw.WriteLine(@i.lvar + " = " + @i.rvar);
                    //Trace.WriteLine("sw:"+i.lvar + "," + i.rvar);
                }
                sw.Close();

            }
            catch { }
        }
        public bool AddINI(string inistr)
        {
            INISTRUCT inputINI = StringToINISTRUCT(inistr);
            for (int i = 0; i < listINI.Count; i++)
            {
                if (listINI[i].lvar == inputINI.lvar) //이미 해당 lvar가 있으면 rvar를 수정
                {
                    Trace.WriteLine(listINI[i].rvar+"->"+inputINI.rvar);
                    listINI[i].rvar = inputINI.rvar;
                    
                    return true;
                }
            }
            listINI.Add(inputINI); // 기존에 없던 rvar값이면 추가.
            return true;
        }
        public string FindValue(string valueName)
        {
            //valueName의 lvar를 찾아 rvar를 return
            matchString = valueName;
            try
            {
                INISTRUCT pickINI = listINI.Find(MatchingPredicate); // MatchingPredicate의 주석 참조.
                return pickINI.rvar; //"valueName = matchString = 찾고자 하는 lvar의 이름"으로 찾은 rvar의 값
            }
            catch 
            {
                return null; // lvar에 valueName없음.
            }
        }

        #endregion

        #region private method
        private bool MatchingPredicate(INISTRUCT p)
        {
            /// http://msdn.microsoft.com/ko-kr/library/x0b5b5bc.aspx
            /// MSDN에서는 Predicate 함수를 static으로 선언하고 있어, matchString도 static으로 선언해야 한다...
            /// Predicate함수와 matchString을 둘 다 static에서 해제 했음.
            /// 람다식을 이용하면 더 깔끔한(전역변수 남용 없이, predicate 선언 없이..) 구현할 수 있을 것 같다.
            return (matchString == p.lvar);
        }

        /// 
        /// INI파일에서 읽어들인 레코드를 Substring, Trim 하는 함수
        /// 
        /// "상수이름 = 값" 형식의 문자열
        private void FillINISTRUCT(string tmpiniString)
        {
                //Trim된 Rvar와 Lvar를 리스트에 Add()
                INISTRUCT tmpinistruct = StringToINISTRUCT(tmpiniString);  
                listINI.Add(tmpinistruct);
        }
        public INISTRUCT StringToINISTRUCT(string inistr)
        {
            int locateEqualMark; // "=" 마크 위치
            int locateRvarStart, locateRvarEnd; // rvar substring 위치
            string tmpLvar; // lvar를 저장할 임시 변수
            string tmpRvar; // rvar를 저장할 임시 변수 
            //substring 할 위치
            locateEqualMark = inistr.IndexOf('=');
            locateRvarStart = locateEqualMark + 1;
            locateRvarEnd = inistr.Length - locateEqualMark - 1;
            //substring 
            if (locateEqualMark == -1)
            {return null;}
            // Trim하기 전의 임시변수
            tmpLvar = inistr.Substring(0, locateEqualMark);
            tmpRvar = inistr.Substring(locateRvarStart, locateRvarEnd);
            //앞뒤 공백제거 "    some string" or "some string        " => "some string"
            tmpLvar = tmpLvar.Trim();
            tmpRvar = tmpRvar.Trim();
            return  new INISTRUCT(tmpLvar, tmpRvar);
        }
        #endregion
    }
    #endregion
}
//

테스트 코드

 
        #region global var
        INILoader ini = new INILoader();
        #endregion

        #region const var
        public const string inifilename = @"C:\windows\win.ini";
        #endregion

        public DlgOption()
        {
            InitializeComponent();
            ini.LoadINIFile(inifilename);
            //테스트 foreach
            foreach (INISTRUCT s in ini.listINI)
            {
                string traceString;
                traceString=string.Format("Trace: \"{0}\", \"{1}\"",s.lvar,s.rvar);
                Trace.WriteLine(traceString);
            }
            Trace.WriteLine("find="+ini.FindValue("mp3"));
            Trace.WriteLine("find=" + ini.FindValue("MP3"));
        }
/* 테스트
Trace: "CMC", "1"
Trace: "MAPI", "1"
Trace: "MAPIX", "1"
Trace: "MAPIXVER", "1.0.0.1"
Trace: "OLEMessaging", "1"
find=MPEGVideo <- "MP3" 검색결과
'System.NullReferenceException' 형식의 첫째 예외가 automating_system_report.exe에서 발생했습니다.
find= <- 찾는 항목 없음 "mp3" 검색결과...
*/
//

2012-06-14

C# Get hWnd by mouse position

참고: http://www.codeproject.com/Articles/16481/NET-Object-Spy-and-InvokeRemote

지난 번, WH_MOUSE에 대해 Global hook을 해서 hwnd를 구하려고 했으나, WH_MOUSE는 Global hook이 지원되지 않는다는 글을 올렸다.
위 참고의 소스에서 마우스 좌표만으로 hwnd를 구하는 방법이 있어, 글을 남긴다.

WindowFromPoint로 hwnd를 구할 수 있다.
 // 마우스 좌표를 주면 hwnd를 return한다.
[DllImport("user32.dll")]
protected static extern IntPtr WindowFromPoint(int x , int y);



        #region 프로세스 정보 클래스
        class processInfobyMousePosition
        {
            #region public 멤버
            public int _mousex, _mousey;
            public IntPtr _hwnd;
            public string _processname;
            public string _windowtext;
            public string _classname;
            #endregion

            #region public method
            public void SetPositon(int x, int y)
            {
                if (x >= 0 & y >= 0)
                {
                    _mousex = x; _mousey = y;
                    _hwnd = GetHandle();
                    _processname = GetProcessName(_hwnd);
                    _windowtext = GetWindowText(_hwnd);
                    _classname = GetClassName(_hwnd);
                }
            }
            #endregion

            #region private method
            private string GetProcessName(IntPtr hWnd)
            {
                int pid;
                GetWindowThreadProcessId(hWnd, out pid);
                Process proc = Process.GetProcessById(pid);
                return proc.MainModule.ModuleName;
            }
            private IntPtr GetHandle()
            {
                return WindowFromPoint(_mousex, _mousey);
            }
            private string GetWindowText(IntPtr hWnd)
            {
                StringBuilder text = new StringBuilder(256);
                if (GetWindowText(hWnd, text, text.Capacity) > 0)
                {
                    return text.ToString();
                }
                return String.Empty;
            }
            private string GetClassName(IntPtr hWnd)
            {
                StringBuilder className = new StringBuilder(100);
                if (GetClassName(hWnd, className, className.Capacity) > 0)
                {
                    return className.ToString();
                }
                return String.Empty;
            }
            #endregion

            #region dll import
            [DllImport("user32.dll")]
            protected static extern int GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);
            [DllImport("user32.dll")]
            protected static extern IntPtr WindowFromPoint(int x , int y);
            [DllImport("user32.dll")]
            protected static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
            [DllImport("user32.dll")]
            protected static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
            #endregion
        }
        #endregion
// 

2012-06-13

C# Excel

액셀 cell에 직접 입력 하는 방법의 예.

 
/// 방법: Visual C# 2010 기능을 사용하여 Office Interop 개체에 액세스(C# 프로그래밍 가이드)
/// http://msdn.microsoft.com/ko-kr/library/dd264733
/// 솔루션-> 참조-> "COM" 탭 -> "Microsoft Excel 14.0 Object Library" 추가
/// 
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Excel = Microsoft.Office.Interop.Excel;

namespace sample_excel
{
    public class Account
    {
        public int ID { get; set; }
        public double Balance { get; set; }
    }

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            // Create a list of accounts.
            var bankAccounts = new List {
                                                    new Account { 
                                                                  ID = 345678,
                                                                  Balance = 541.27
                                                                },
                                                    new Account {
                                                                  ID = 1230221,
                                                                  Balance = -127.44
                                                                }
                                                };
            // Display the list in an Excel spreadsheet.
            DisplayInExcel(bankAccounts);
        }

        static void DisplayInExcel(IEnumerable accounts)
        {
            var excelApp = new Excel.Application();
            
            // Make the object visible.
            excelApp.Visible = true;

            // Create a new, empty workbook and add it to the collection returned 
            // by property Workbooks. The new workbook becomes the active workbook.
            // Add has an optional parameter for specifying a praticular template. 
            // Because no argument is sent in this example, Add creates a new workbook. 
            excelApp.Workbooks.Add();

            // This example uses a single workSheet. The explicit type casting is
            // removed in a later procedure.
            Excel._Worksheet workSheet = (Excel.Worksheet)excelApp.ActiveSheet;

            // Establish column headings in cells A1 and B1.
            workSheet.Cells[1, "A"] = "ID Number";
            workSheet.Cells[1, "B"] = "Current Balance";
            workSheet.Cells[1, "C"] = "=SUM(A2:B2)";
            var row = 1;
            foreach (var acct in accounts)
            {
                row++;
                workSheet.Cells[row, "A"] = acct.ID;
                workSheet.Cells[row, "B"] = acct.Balance;
            }

            workSheet.Columns[1].AutoFit();
            workSheet.Columns[2].AutoFit();
        }

    }

}
// 
...

WH_MOUSE는 글로벌 훅이 불가

마우스 커서가 위치한 창의 Process ID나 Thread ID를 가져오려면 WH_MOUSE를 사용해서 후킹을 해야하는데 잘 동작하지 않아, 검색해봤으나 아무리 찾아도 관련 정보를 찾을 수 없었다.
마우스 후킹된 데이터를 저장하는 스트럭쳐만 MSLLHOOKSTRUCT대신 MOUSEHOOKSTRUCT를 사용하는 소스는 봤는데, 그렇게 하면 그냥 마우스의 x,y 좌표만 얻을 수 있다(POINT pt값).

MOUSEHOOKSTRUCT의 구조( 이걸 사용해봤자.. 메모리에 있는 내용은 MSLLHOOKSTRUCT)
typedef struct tagMOUSEHOOKSTRUCT {
  POINT     pt;
  HWND      hwnd;
  UINT      wHitTestCode;
  ULONG_PTR dwExtraInfo;
} MOUSEHOOKSTRUCT, *PMOUSEHOOKSTRUCT, *LPMOUSEHOOKSTRUCT;

MSLLHOOKSTRUCT의 구조
typedef struct tagMSLLHOOKSTRUCT {
  POINT     pt;
  DWORD     mouseData;
  DWORD     flags;
  DWORD     time;
  ULONG_PTR dwExtraInfo;
} MSLLHOOKSTRUCT, *PMSLLHOOKSTRUCT, *LPMSLLHOOKSTRUCT;



그나마 microsoft의 페이지에서 WH_MOUSE로 후킹하는 자료를 찾았으나, 내용은 아래처럼 글로벌 훅이 지원되지 않는다는 말만 있고, 훅이 글로벌 훅이 아닌 Form1에서만 동작했다.
WH_MOUSE만 글로벌 훅이 안된다고 하면 그러려니 할탠데 글로벌 훅 자체가 안된다고 하니, 다른 방법이 있지 않을까 한다.
WH_MOUSE_LL, WH_KEYBOARD_LL은 글로벌 훅이 동작하니까..



이하 MS의 글로벌 후크가 지원되지 않는 다는 내용.

http://support.microsoft.com/kb/318804/ko

글로벌 후크가 .NET Framework에서 지원되지 않음

Microsoft .NET Framework에서는 글로벌 후크를 구현할 수 없습니다. 글로벌 후크를 설치하려면 일관성 있고 유효한 함수를 호출해야 하는 다른 프로세스에 DLL 자체를 삽입하는 기본 동적 연결 라이브러리(DLL) 내보내기가 후크에 있어야 합니다. 이러한 DLL 내보내기는 .NET Framework에서 지원되지 않습니다. 이러한 함수 포인터는 동적으로 만들어지는 프록시이기 때문에 관리되는 코드에는 함수 포인터에 대해 일관성 있는 값이라는 개념이 없습니다.

blogspot, blogger.com에 SyntaxHighLighter 적용

SyntaxHighLighter 페이지:http://code.google.com/p/syntaxhighlighter/
Blogger 적용방법 참조 : http://majumawm.blogspot.kr/2010/08/syntaxhighlighter-3083.html

SyntaxHighLighter를 사용하면 소스코드의 가독성을 높힐 수 있어 적용해 보았다.
Blogger에 적용하는 방법에 대해서는 위의 출처를 참조했다.

적용방법
1. Blogger 대쉬보드에서 "템플릿" -> "HTML 편집"

2. 자바스크립트를 붙여넣기 할, 태그 찾기

3. 태그 위에 SyntaxHighLighter 자바스크립트 붙여넣기




이것으로 사용할 준비가 끝났다.
사용할 때는, 소스코드 부분을 HTML편집 해야한다.

 
Console.WriteLine("pre 태그 안에 소스코드 작성...");

C# xml 데이터로 treeview 형태로 표현하기


출처: http://support.microsoft.com/kb/317597/ko



xmlDoc.Load("sample.xml");
XmlDocument xmlDoc = new XmlDocument();
treeView1.Nodes.Add(new TreeNode(xmlDoc.DocumentElement.Name)); //root node(family)를 treeview1에 업데이트
TreeNode tnode = new TreeNode(); //root node에 붙일 node 초기화
tnode=treeView1.Nodes[0]; // 0번 node에 tnode를 붙임

AddNode(xmlDoc.DocumentElement, tnode); //재귀호출
---------------------------------

        ///

        /// XmlNode로 부터 TreeNode를 기록하는 Recursive call.
        /// AddNode가 받을 TreeNode와 XmlNode parameter를 A와 B로 변경
        /// 재귀호출에 쓰일 TreeNode와 XmlNode를 AA와 BB로 변경함.
        ///

        /// 재귀탐색을 할 XmlNode
        /// 재귀기록을 할 TreeNode
        private void AddNode(XmlNode A,TreeNode B)
        {

             XmlNode AA;   //AddNode를 재귀호출 할 때 쓰일 XmlNode
             TreeNode BB;  //AddNode를 재귀호출 할 때 쓰일 TreeNode
             XmlNodeList xmlNodes; //A 밑의 ChildNodes를 저장할 컬렉션
             int i; // Child의 수만큼 재귀반복 할 count

             // Loop through the XML nodes until the leaf is reached.
             // Add the nodes to the TreeView during the looping process.
             if (A.HasChildNodes) //A가 child를 가지고 있으면 재귀호출하며 마지막 노드를 찾음.
             {
                xmlNodes = A.ChildNodes; // A child노드들을 노드컬렉션(xmlNodes)에 등록
                for(i = 0; i<=xmlNodes.Count - 1; i++) //xmlNodes의 수 많큼 반복
                {
                   AA = A.ChildNodes[i]; // A의 child노드를 하나씩 재귀호출에 쓰일 AA에 저장
                   B.Nodes.Add(new TreeNode(AA.Name)); //TreeNode B에 AA항목(node) 추가
                   BB = B.Nodes[i];//AA.Name으로 추가된 TreeNode를 다음 재귀호출에 쓰일 BB로 저장
                   AddNode(AA, BB); // AA와 BB로 재귀호출
                   B.Text = "!";
                }
             }
             else // child가 없는 leaf node(Xml의 tag가 아닌 Text에 해당)이면 B.Text에 XML태그 값을 저장
             {
                // Here you need to pull the data from the XmlNode based on the
                // type of node, whether attribute values are required, and so forth.
                B.Text = (A.OuterXml).Trim(); // A.OuterXml의 의미 없는 공백문자를 제거
             }
        }

-----------------------------------

2012-06-05

Mouse hook

(마우스 커서는 폼 밖에 있으나 x,y 좌표를 계속 받는다.)

풀소스 및 출처 : CodeSummoner, http://www.codeproject.com/Articles/28064/Global-Mouse-and-Keyboard-Library

keyboard와 mouse를 후킹 하는 라이브러리.

지난 번, keyboard hook과의 차이점은

keyboardHook.cs와 mouseHook.cs에서 globalHook.cs을 상속 받아 구현했다.
globalHook.cs에서 필요한 DllImport,
키보드 데이터 스트럭쳐와 마우스 데이터 스트럭쳐 정의,
후킹에 필요한 상수 정의,
후킹이 실행됬는지에 대한 Property정의,
Application.ApplicationExit+= new EventHandler(Application_ApplicationExit)으로 unhook() 함수를 대신하고
후킹의 콜백 프로시져는 HookCallbackProcedure로 가상 함수로 구현되었다.
SetWindowHookEx는 Hook()에서 Start()로 변경되었다.

테스트 실행에서 이미 디버깅되 실행파일로 나와 있는 샘플을 실행할 땐 정상 작동했으나, 직접 F5로 실행해보면 제대로 동작하지 못한다. 중간에 소스코드에 무슨 변화가 있던 것 같다.
임시로 hInstance가 Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]로 되어 있던 것을 LoadLibrary("User32")로 변경하면.. 일단 실행이 된다.

globalHook.cs에 마우스훅스트럭쳐를 보면 마우스 좌표를 기억하기 위한 point형을 사용하고 있다.

        protected class MouseLLHookStruct // MSDN MSLLHOOKSTRUCT와 동일
        {
            public POINT pt;  //마우스 좌표
            public int mouseData; // 메세지가 WM_MOUSEWHEEL일 경우, 휠을 움직인 델타값. X-button일 경우 어떤 X-button인지 저장.
            public int flags; // event injected 플래그
            public int time; // 메시지에 대한 time stamp, 키보드훅과 동일
            public int dwExtraInfo; // additional inform(?), MSDN에는 UintPtr type을 쓴다.
        }


        protected class POINT
        {
            public int x;
            public int y;
        }


mouseHook.cs에 overriding된 훅 프로시져(HookCallbackProcedure)의 내용은 크게 MouseButtons 객체에 후킹된 버튼 기억, 유저 어플리케이션에 전달될 MouseEventArgs 정의, 후킹된 이벤트에 따라 유저 어플리케이션으로 이밴트 발생이 있다.



protected override int HookCallbackProcedure(int nCode, int wParam, IntPtr lParam)
// nCode는 후킹할 대상, WH_MOUSE_LL (=14)

// wParam에는 마우스 메시지가..
// lParam에는 마우스의 훅 정보가 담긴 스트럭쳐의 포인터 (여기서는 MouseLLHookStruct)

MouseButtons button = GetButton(wParam); // 마우스 버튼 객체 정의

// MouseEventArgs 정의
MouseEventArgs e = new MouseEventArgs(
                    button,
                    (eventType == MouseEventType.DoubleClick ? 2 : 1),
                    mouseHookStruct.pt.x,
                    mouseHookStruct.pt.y,
                    (eventType == MouseEventType.MouseWheel ? (short)((mouseHookStruct.mouseData >> 16) & 0xffff) : 0));



        private MouseButtons GetButton(Int32 wParam) // case에 따라  "Left", "Right", "Middle", "None"을 return, MouseEventArgs.Button property에 기록된다.
        {

            switch (wParam)
            {

                case WM_LBUTTONDOWN:
                case WM_LBUTTONUP:
                case WM_LBUTTONDBLCLK:
                    return MouseButtons.Left;
                case WM_RBUTTONDOWN:
                case WM_RBUTTONUP:
                case WM_RBUTTONDBLCLK:
                    return MouseButtons.Right;
                case WM_MBUTTONDOWN:
                case WM_MBUTTONUP:
                case WM_MBUTTONDBLCLK:
                    return MouseButtons.Middle;
                default:
                    return MouseButtons.None;

            }

        }




유저 어플리케이션에서 이 라이브러리를 사용하는 방법은 지난 번 키보드 훅을 사용할 때와 같다.
MouseHook 객체를 생성하고, 생성한 객체의 이밴트에 이밴트핸들러를 추가하고, 어떤 일을 할지 이밴트핸들러를 정의해주면 된다.

using MouseKeyboardLibrary; //네임스페이스 사용
MouseHook mh = new MouseHook(); 마우스 훅 인스턴스 생성


mh.MouseMove += new MouseEventHandler(mh_MouseMove); //MouseHook에 정의된 이벤트에 유저 이벤트핸들러 추가
mh.MouseDown += new MouseEventHandler(mh_mousedown); //마우스 버튼 관련 이밴트 핸들러 추가
mh.Start(); // 마우스 훅 시작.



        private void mh_MouseMove(object sender,MouseEventArgs e) // 이벤트핸들러 구성
        {
            xyCoordinator(e.X, e.Y); //좌표 표시용 함수 호출
            textBox3.Text = mh.IsStarted.ToString(); //test, 후킹 중인지 상태 표시
        }

        private void xyCoordinator(int x, int y) //좌표 표시용 함수
        {
            textBox1.Text = String.Format("x= {0}", x);
            textBox2.Text = String.Format("y= {0}", y);
        }

        private void mh_mousedown(object sender, MouseEventArgs e)
        {
            textBox3.Text = e.Button.ToString(); // "Left" or "Right" 누른 마우스 버튼 표시
        }






2012-05-31

Keyboard hook


글로벌 후킹에 관심이 생겨 자료를 검색하다 보니 좋은 내용을 발견했다.
테스트 해보니 nprotect나 공인인증서에서는 키보드 보안이 user32.dll을 후킹하는 것을 차단하는 것 같다.
그런 보안이 없는 환경에서.. 믿을 만한 PC가 아니라면 중요한 정보는 입력하지 않는게 좋겠다.


후킹 과정 요약.
user32.dll의 SetWindowsHookEx로 후킹
SetWindowsHookEx로 후킹할 때 필요한 콜백함수에 대한 대리자 선언(keyboardHookProc)
후킹한 키 값을 저장할 구조체 선언(keyboardHookStruct)
후킹할 키 값을 List에 등록(HookedKeys)
키보드에 대한 이벤트 추가(KeyDown)
훅 프로시져 구성(hookProc)



출처 및 참고자료:
StormySpike http://www.codeproject.com/Articles/19004/A-Simple-C-Global-Low-Level-Keyboard-Hook
http://www.jiniya.net/wp/microsoftware


// 주석추가만 있고, 원본 수정 및 변경 없음

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices; // dllimport를 하기 위해 InteropServices네임스페이스 추가
using System.Windows.Forms;

namespace Utilities {
///








/// A class that manages a global low level keyboard hook
///

class globalKeyboardHook {
#region Constant, Structure and Delegate Definitions
///








/// defines the callback type for the hook
///

        // 콜백함수에 사용될 대리자 정의
public delegate int keyboardHookProc(int code, int wParam, ref keyboardHookStruct lParam);

        // 후킹한 키보드의 데이터를 저장할 구조체
public struct keyboardHookStruct {
public int vkCode;  //  virtual-key Code. (range 1~254) dword(4바이트) 이므로 int를 대신 씀
public int scanCode;
public int flags; // bit별로 숫자키패드에서 입력된 값인지, ALT키가 눌린 상태인지, keyboard의 press , release상태 등의 정보가 담긴 플래그
public int time; //
public int dwExtraInfo; // MSDN에서는 UintPtr 형을 쓰라고 함..
}

        ///


        ///  키보드 스트럭쳐에 대한 설명.
        ///  vkCode와 scanCode의 차이점
        ///  vkCode는 code값이 사람이 인식하기 쉬운 알파벳 순서
        ///  scanCode는 code값이 키보드에 위치적인 순서
        ///
        ///     vkCode | scanCode
        ///  a    65   |    30
        ///  b    66   |    48
        ///  c    67   |    46
        ///  d    68   |    32
        ///  --------------------------
        ///  q    81   |    16
        ///  w    87   |    17
        ///  e    69   |    18
        ///  r    82   |    19
        ///
        /// flags- 8bit의 추가 정보 flag
        /// 예) 일반키에 대해서는 down이벤트와 press이벤트일 때 0, up이벤트 일때 128
        /// test에 대한 플래그 코드, 시스템키라던지 확장키인지 플래그에 나타남
        /// 0 bit, 확장키(숫자패드, 펑션키일 때 1)
        /// 1~3 bit, reserved
        /// 4 bit, 이벤트에 inject되었으면 1
        /// 5 bit, context code, Alt키가 눌렸으면 1
        /// 6 bit, reserved
        /// 7 bit, 0= press, 1=release
        ///
        /// time은 타임스탬프, GetMessageTime API에 의해 키가 down, press, up상태마다 타임스탬프가 기록된다.
        /// 예) 163072875, 163077453
        ///
        /// dwExtraInfo
        /// Additional information associated with the message
        ///



const int WH_KEYBOARD_LL = 13; // 후킹할 Handle 상수
const int WM_KEYDOWN = 0x100;  // 후킹할 메시지 상수
const int WM_KEYUP = 0x101;
const int WM_SYSKEYDOWN = 0x104;
const int WM_SYSKEYUP = 0x105;
#endregion

#region Instance Variables
///








/// The collections of keys to watch for
///

        // 모니터링할 키의 List
public List HookedKeys = new List(); //유저 어플리케이션에서 A키와 B키를 후킹할 대상으로 List.Add했다.(ghk.HookedKeys.Add("Keys.A");)
///








/// Handle to the hook, need this to unhook and call the next hook
///

        // hook handle 정의
IntPtr hhook = IntPtr.Zero;
#endregion

#region Events
///








/// Occurs when one of the hooked keys is pressed
///

        public event KeyEventHandler KeyDown;  // KeyDown 이벤트, 사용자 어플리케이션에서 이벤트핸들러를 추가해서 사용. (예: gkh.KeyUp += new KeyEventHandler(gkh_KeyUp);)
///








/// Occurs when one of the hooked keys is released
///

public event KeyEventHandler KeyUp;   // KeyUp 이벤트,
#endregion

#region Constructors and Destructors
///








/// Initializes a new instance of the class and installs the keyboard hook.
///

public globalKeyboardHook() {
hook(); // 후킹을 시작할 함수
}

///








/// Releases unmanaged resources and performs other cleanup operations before the
/// is reclaimed by garbage collection and uninstalls the keyboard hook.
///

~globalKeyboardHook() {
unhook(); // 후킹을 일시정지할 함수
}
#endregion

#region Public Methods
///








/// Installs the global hook
///

public void hook() {
IntPtr hInstance = LoadLibrary("User32"); //User32.dll이 hInstance
            /// HHOOK SetWindowsHookEx(int idHook, HOOKPROC lpfn, HINSTANCE hMod, DWORD dwThreadId);
            /// idHook, WH_KEYBOARD_LL이 후킹할 대상
            /// lpfn, hookProc은 콜백함수
            /// hMod, hInstance("user32.dll") 후킹 프로시져가 존재하는 모듈의 핸들
            /// dwThreadId, 후킹할 쓰레드의 ID, 0은 시스템 전역을 뜻함.
hhook = SetWindowsHookEx(WH_KEYBOARD_LL, hookProc, hInstance, 0);
}

///








/// Uninstalls the global hook
///

        // 후킹 일시정지
public void unhook() {
UnhookWindowsHookEx(hhook);
}

///







        /// wParam        /// 키보드 입력 이벤트를 일으킨 가상 키 코드라고 한다...시스템키, 확장키, Down , up 에 따라 값이 변함.        /// lParam        /// 키보드 입력 이벤트에 대한 추가 정보라고 한다...        /// 75819508이 출력되는데 이 정보에 대한 주소        /// 비교) keyboardHookStruct = (KeyBoardHookStruct) Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct))        /// -> lparam의 주소를 참조해서 keyboardHookStructure 객체를 생성한 소스도 있다.        /// lparam의 비트별 정보는 아래를 참조        /// 0~15 bit, 반복횟수        /// 16~23 bit, scanCode        /// 24 bit, is Extended key        /// 25~28 bit, reserved        /// 29 bit, Alt key        /// 30 bit, 이전 키이벤트의 상태가 눌린 상태면 1        /// 31 bit, 지금 상태가 눌린 상태면 1 /// The callback for the keyboard hook
///

///
The hook code, if it isn't >= 0, the function shouldn't do anyting
///
The event type
///
The keyhook event information
///
public int hookProc(int code, int wParam, ref keyboardHookStruct lParam) {
if (code >= 0) {
Keys key = (Keys)lParam.vkCode; // LParam에 후킹한 vkCode. key는 후킹된 키보드 값이다.
if (HookedKeys.Contains(key)) { // HookedKeys는 후킹할 키의 리스트였음. 따라서 이 리스트에 지금 눌린 key값이 있으면 아래 KeyDown, KeyUp 이벤트가 실행된다.
KeyEventArgs kea = new KeyEventArgs(key);
if ((wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) && (KeyDown != null)) {
KeyDown(this, kea) ; // KeyDown(object sender, KeyEventArgs e)
} else if ((wParam == WM_KEYUP || wParam == WM_SYSKEYUP) && (KeyUp != null)) {
KeyUp(this, kea);
}
if (kea.Handled) //이벤트에 대한 Handled가 true인 경우, 후킹된 메시지는 유저 어플리케이션에서 처리하고 폐기됨. false일 경우 처리하고 그 후에 user32.dll에서 처리하도록 함.
return 1;    //Handled 값이 true여서 해당 키에 대해 유저 어플리케이션에서만 처리되고, 1을 return, user32.dll은 해당 KeyEvent를 못받음.
}
}

            return CallNextHookEx(hhook, code, wParam, ref lParam);  // 다른 후킹 프로시져에게 제어권을 넘겨주는 역할, return 0로 해도 실행은 됨.
}
#endregion

#region DLL imports
        // SetWindowsHookEx, UnhookWindowsHookEx, CallNextHookEx에 대한 DllImport.
///








/// Sets the windows hook, do the desired event, one of hInstance or threadId must be non-null
///

///
The id of the event you want to hook
///
The callback.
///
The handle you want to attach the event to, can be null
///
The thread you want to attach the event to, can be null
/// a handle to the desired hook
[DllImport("user32.dll")]
static extern IntPtr SetWindowsHookEx(int idHook, keyboardHookProc callback, IntPtr hInstance, uint threadId);

///








/// Unhooks the windows hook.
///

///
The hook handle that was returned from SetWindowsHookEx
/// True if successful, false otherwise
[DllImport("user32.dll")]
static extern bool UnhookWindowsHookEx(IntPtr hInstance);

///








/// Calls the next hook.
///

///
The hook id
///
The hook code
///
The wparam.
///
The lparam.
///
[DllImport("user32.dll")]
static extern int CallNextHookEx(IntPtr idHook, int nCode, int wParam, ref keyboardHookStruct lParam);

///








/// Loads the library.
///

///
Name of the library
/// A handle to the library
[DllImport("kernel32.dll")]
static extern IntPtr LoadLibrary(string lpFileName);
#endregion
}
}



사용 땐,
// 위 소스의 네임스페이스 사용
using utility;

// globalKeyboardHook 생성
globalKeyboardHook gkh = new globalKeyboardHook();


// HookedKeys 리스트에 모니터링할 키 추가
gkh.HookedKeys.Add(Keys.A);
gkh.HookedKeys.Add(Keys.B);
// 이벤트 추가

gkh.KeyDown += new KeyEventHandler(gkh_KeyDown);
gkh.KeyUp += new KeyEventHandler(gkh_KeyUp);
// 이벤트 처리



void gkh_KeyUp(object sender, KeyEventArgs e) {
lstLog.Items.Add("Up\t" + e.KeyCode.ToString());
e.Handled = true;
}

void gkh_KeyDown(object sender, KeyEventArgs e) {
lstLog.Items.Add("Down\t" + e.KeyCode.ToString());
e.Handled = true;
}


2012-03-16

Chrome About pages

1. plugin & 구글 실험실 기능
chrome://plugins/ plugin enable/disable
chrome://flags/ Google Chrome의 실험실 flags.

2. 설정 아이콘에 이미 있는 기능

chrome://downloads/ 다운로드 목록 열기
chrome://bookmarks/ 북마크 관리자 열기
chrome://settings/ Chrome 옵션 열기

기타
chrome://crash/ 크래쉬 화면 표시
chrome://version/ 버전 정보 표시 
chrome://cache/ cached resource의 list
chrome://memory-redirect/ memory 정보
chrome://stats/ 내부카운터(secret page)
chrome://histograms/ 내부미터의 히스토그램
chrome://conflicts/ 크롬에 로드된 모듈
chrome://crashes/ crash reports
chrome://sync-internals/ Chrome sync engine information
chrome://net-internals/ 네트워크 상태 정보
chrome://flash/ 플래시 정보
chrome://hang/ 특정탭만 hang(프로세스는 실행되고 있으나, 어떤 시그널도 받지 않는 상태)로 만든다.
chrome://dns/ dns prefetch 옵션의 상태
chrome://internets/ 이스터애그, 파이프라인 화면보호기 실행
...

출처 : http://googlesystem.blogspot.com/2008/09/google-chromes-about-pages.html

2012-03-15

크롬 확장프로그램 추천

전엔 아래처럼 썼는데, 막상 1GB에 육박하는 메모리 사용률 압박으로 ie tab Multi와 click clean, Lastpass, Personalized Web만 쓴다.
자신의 메모리 사용량을 고려해 설치하자.
확장프로그램당 메모리 사용량은 "Chrome설정 및 관리" -> "도구" -> "작업관리자"에서 확인할 수 있다.

아래는 본인 위주로 작성한 것이니 참고만 하시길 바랍니다.
--

1. 필수 확장프로그램
인터넷익스플로러와 호환을 위해 ..

Click&Clean
브라우징 히스토리 삭제
(12.06.27 브라우징 히스토리 중, 방문한 페이지는 지워지지 않고 있다.)


아이디/패스워드 일괄관리, 보안에 위험이 있다는 것을 알면서도 한번쓰면 계속 쓰게된다는 확장프로그램...



2. 선택사항

Personalized Web
웹페이지 커스터마이징 확장프로그램.
태그 삭제, 태그 인젝션, 자바스크립트 인젝션, CSS 인젝션 지원

Search by Image(by Google)
context menu에 구글 이미지서치를 등록
빠른 속도와 단순하지만 꼭 필요한 편집 기능이 있어 좋다.
Pixlr grabber는 속도가 느린편이고 내게 필요 없는 옵션 선택이 있기 때문에 사용하지 않지만, Pixlr editor를 좋아한다면, 스크린샷을 찍어서 Pixlr editor로 불러와 전문적인 편집이 가능하므로 Pixlr grabber를 한번쯤 사용해보고 선택하길 권함

Youtube 페이지에서 동영상을 mp3나 동영상 파일로 다운받는 메뉴 추가


웹페이지의 광고(특정태그)를 필터링

플래시에 download링크가 추가됨. 하지만, 동영상 플레이어 형태의 플래시에는 동영상이 저장되지 않음

그림 편집툴(포토샵과 유사), 플래시로 포토샵에 있는 레이어, 필터 등을 구현. 사용도 편리하고 빠름, 하지만 메모리 먹는 괴물

box
free 5GB 웹하드, 확장프로그램 자체는 필수라 생각하지 않지만.. 이곳의 웹하드 서비스는 정말 추천한다.
무료라면 이것 저것 제한이 붙게 마련인데 상당히 깔끔하고 사용하기도 편리했다.


3. 이런것도 있다.


Theme Creator
크롬 테마 커스터마이징


chrome://plugins, settings, experiments 등등을 바로 엑세스할 수 있는 메뉴가 생김.
서비스 페이지 url을 외우신 분은 필요 없음.

이미지의 좌상단에 EXIF 정보 표시, 사진, EXIF 정보에 관심 있으신 분 아니면 단순히 걸리적 거림

웹페이지를 PDF로 PC에 저장, 
PrimoPDF가 더 편리하다.
PC에 무엇인가 설치하기 곤란할 때 아니라면 궂이 사용할 필요는 없음

웹페이지를 PDF로 Google docs에 저장
내 사용 패턴엔 궂이 웹페이지를 PDF로 구글 Docs에서 볼 이유를 찾지 못했다.

이미지 후보정 템플릿, 사진 후보정

이미지 후보정 템플릿


Web page의 분석 및 테스트에 사용하는 툴


animated GIF 를 만들 때 사용

TS 이미지를 만들 때 사용


Chrome 원격 데스크톱 Beta
인터넷을 통해 원격 데스크톱

Chrome to Phone
링크, 지도, 현재 선택한 텍스트와 전화번호 등을 크롬에서 Android로 보내는 버튼 추가

Chrome Notepad
크롬으로 동기화되는 memo, 깔끔하기는 Evernote가 낫지만 편리성이 좋다.

Neo Vision
syntax 단어에 컬러를 입혀 가독성을 높힘

iMacros for Chrome
매크로

Switch Proxy , Hide my ass, web proxy
proxy



삭제한 확장프로그램

1번 필수 확장프로그램에 넣었었으나 무분별한 광고로 삭제.. 
요즘 웹서핑할 때면 광고가 자주떠서 아 그런가보다 했는데..
업무용 사이트에도 딱 광고가 떠서 자세히 보니, 
이 확장프로그램이 하라는 우클릭 방지에 대한 인젝션은 안하고 광고 인젝션을 하고 있었음.

javascript injector. Personalized Web을 사용하면서 삭제


크롬의 URL 표시줄에 현재 페이지의 RSS 목록 추가가 생긴다. 
Google Reader사용자에게는 편리.
(2012-09-25, 모든 웹페이지 하단에 광고 인젝션을 하고 있다.)

2012-03-13

인터넷 우클릭 막힘 풀기












(우클릭 및 드래그 선택을 방해하는 자바스크립트) document.oncontextmenu = new Function ('return false'); document.ondragstart = new Function ('return false'); document.onselectstart = new Function ('return false');

까페 및 블로그에서 글을 보다, 개인적인 목적으로 저장하려 하다보면 우클릭이 막혀 있거나, 심지어 글을 선택하는 것 마저도 막아 놓은 곳이 있다.
(자신의 자료도 아니고 어디선가 복사에 복사에 복사이고 출처도 어디인지 적지도 않아서 원본 자료를 구하기 힘든 경우가 대부분.)
pig toolbox에도 우클릭 잠금 해제 기능이 있지만, 사용하지 않는 이런 저런 기능을 가진 컨버전스 제품을 싫어하는 사람은 아래 방법1이나 방법2를 추천.



방법1. 크롬 확장프로그램 중 , Allow Right-Click이 아래의 방법으로 context menu를 다시 활성화 시키는 것 같다.
광고를 인젝션 하는 업데이트가 있었다. 이제는 사용하지 않는다.


방법2. Javascript injection. JavaScript Injector: Nicholas Workshop 를 추가하면 javascript를 인젝션할 수 있다.



script(빨간색 박스)에 아래 코드를 붙여넣기 하고, injection Now!를 클릭하면 스크립트가 인젝션 된다.
Url부분에 특정 Url을 지정하면, 인젝션할 스크립트를  사이트별로 구분이 가능하다.
(그냥 안적어도 된다.)





// -- javascript --
function r(d)
{
    d.oncontextmenu=null;
    d.onselectstart=null;
    d.ondragstart=null;
    d.onkeydown=null;
    d.onmousedown=null;
    d.body.oncontextmenu=null;
    d.body.onselectstart=null;
    d.body.ondragstart=null;
    d.body.onkeydown=null;
    d.body.onmousedown=null;
}

function s(f)
{
    if(f.frames.length!=0)
    {
    for(var i=0; i&lt; f.frames.length; i++)
         {
         s(f.frames[i]);
         }
    }
    try
    {
        r(f.document);
    }
    catch(error)
    {
    }
}

s(window.top);

//


javascript 소스는 출처 불분명으로 최초 공개하신 분이 누군지는 모르지만, 감사합니다.
Javascript injector의 두가지 버전이 있는데 둘다 비슷하고 제작자도 같다.
하나는 버전이 2.x.x이고 하나는 3.x.x인데 ..
(이 글을 작성한 후에도 자바스크립트 인젝션 하는 확장프로그램이 많이 나왔다.
개인적으로 "Personalized Web Options"가 깔끔하고 마음에 든다.
사용 방법은 비슷하므로 별도의 설명을 하지 않음)














방법3. 해당 사이트의 자바스크립트를 차단하면, 우클릭 막힘이 풀린다.
embed 태그 동영상이나 음악은 정상적으로 플레이되지만,
자바스크립트 소스 안의 동영상, 음악도 빈칸으로 보인다.

옵션 - 고급설정 - 콘텐츠 설정 - 자바스크립트 차단

또는 모두 허용하고, 자바스크립트 예외에서 차단



Google Calander 각종기념일



기타 각종기념일과 상업적인 14일 기념일은 구글에서 제공하는 대한민국 기념일(재미있는 캘린더)에 없기 때문에, 제 Calander를 url로 공개합니다.
필요하신 분은 추가해서 사용하세요~.