비쥬얼스튜디오는 추가기능 프로젝트를 통해 VS,NET Addin 을 개발할 수 있고, 디버깅도 가능하다.
하지만 비쥬얼스튜디오 2005 버전에서 초기 설정대로 Addin 프로젝트를 디버깅 하려면 다음과 같은 에러 메시지가 뜬다.
 
[그림1. 디버깅시 에러]
 
하지만 간단하게 해결해 보자.
 
[그림2. LoaderLock 해지 방법]
 
비쥬얼스튜디오의 “디버그 -> 예외” 를 클릭하여, Managed Debugging Assistants를 확장시켜 Loaderlock 항목의 체크를 해지한다.
 
이제, 즐겁게 Addin 프로젝트를 디버깅 하자 ^^

요즘은 피곤하니까 글은 짧게 ^_^;;  텨텨텨 =3=3=3

Posted by 땡초 POWERUMC

댓글을 달아 주세요



CommentHelper 0.9 Beta
버전을 공개합니다.
이전버전 기능에 대한 자잔한 버그를 잡고 리플랙션 기능을 추가로 도입하였습니다.
 
 
리플랙션 기능을 이용하여 어셈블리의 모든 메서드 및 프로퍼티 등을 확인하며, 메서드의 실행 및 프로퍼티, 필드 값을 확인 할 수 있습니다.
 
설치방법
 
다음과 같이 압축파일의 CommentHelper.DLL 과 CommentHelper.addin 파일을 내문서의 Visual Studio 2005 -> Addins 폴더에 복사하여 넣습니다.
 
 
그다음 CommentHelperRef.exe 파일과 CommentHelperRef.exe.config 파일을 Visual Stduio 2005 설치경로의 Common7\IDE 폴더에 복사하여 넣습니다.

※ 특정 어셈블리의 리플랙션을 위하여 환경정보는 CommentHelperRef.exe.Config 파일로 복사하시면 됩니다.
 
 
예) C:\Program Files\Microsoft Visual Studio 8\Common7\IDE 에 복사
 
 
사용방법)
 
이번에 추가된 리플랙션 기능을 사용하기 위해선 .NET 으로 만들어진 어셈블리가 필요합니다.
 
리플랙션을 수행하기 위한 어셈블리(*.DLL, *.EXE) 를 선택합니다.
 
 
파일을 선택하면 어셈블리의 모듈을 로드하며 다음과 같이 자신이 작성한 메서드 및 프로퍼티, 필드 등 정보를 볼 수 있습니다.
 
 
마우스 오른쪽 버튼을 눌러 “실행”을 클릭하면 메서드를 실행하거나 필드의 값을 확인할 수 있습니다.
 
메서드의 경우 필요한 인자값을 입력하여야 결과를 확인할 수 있습니다.
 
다음과 같이 인자값이 배열인 경우는 콤마(,)로 배열의 값으로 대체하여 넣을 수 있습니다.
 
 
인자값이 없는경우 바로 결과를 확인할 수 있습니다.
인자값이 필요한 경우 인자값을 넣고 “실행”을 클릭하면 실행결과를 바로 확인할 수 있습니다.(단, 메서드인 경우 리턴 타입이 있어야 합니다.)
 
 
리턴 타입이 DataSet, DataTable 인 경우는 Xml 과 스키마를 저장 할 수 도 있습니다.
 
위와같이 리턴 데이터가 그리드에 바인딩 가능한 리턴타입인 경우 그리드에 데이터가 바인딩 됩니다. ( DataSet, DataTable 또는 Entity Class 인 경우 IListSource 가 구현된 경우)
 
데이터가 그리드에 바인딩 가능한 타입이 아닌경우 아래 그림 처럼 데이터를 확인 할 수 있습니다.
 
 
 
본 프로그램은 상업적인 용도로 사용할 수 없으며 재배포를 할 수 없습니다.
Posted by 땡초 POWERUMC

댓글을 달아 주세요




주석을 달아주는 .NET Addin 을 제작해 보았습니다..
 
현재 진행중인 프로젝트에서 쓰기위해 만든 프로그램인데, 이렇게 공개해 봅니다.
 
 
주요기능)
1)    에디터 창에서 ESC 키를 두번 연속 누르면 기본설정된 주석이 에디터 창에 바로 표시됩니다.
2)    복사 History 기능으로 에디터에서 복사된 내용의 개수를 설정하여 History 를 볼 수 있습니다.
3)    어플케이션에서 사용될 XML 형태의 파일을 읽을 수 있습니다.
 
설치방법)
CommentHelper.zip 파일을 다음의 내문서\Visual Studio 2005\Addins 폴더에 압축을 풉니다
 
다음 Visual Studio 2005 를 실행시켜 도구의 추가기능 관리자를 클릭한다.
 
CommentHelper 항목과 시작 항목을 체크하고 확인을 누릅니다.
 
그럼 다음과 같이 도구 메뉴에 CommentHelper 항목이 추가되었습니다.
메뉴의 CommentHelper 를 클릭하세요.
 
그럼 다음과 같이 사용자를 등록하라는 메시지박스가 뜹니다. 확인을 클릭합니다.
 
아래와 같이 주석에 표시될 작성자를 적고, 확인을 클릭합니다.
 
그럼 기본적인 설치작업은 모두 끝났습니다.
 
 
주요 기능 사용방법입니다.
 
1) 기본설정된 주석을 자동으로 Visual Studio 의 에디터창에 붙여 넣을 수 있습니다.
기본으로 설정할 주석에 마우스 오른쪽 버튼을 눌러 “기본 설정”을 클릭합니다.

커서를 <summary>와 </summary> 사이에 두고 빠르게 ESC 키를 두번 누릅니다.
 
환경설정에서 에디터에 붙이기 및 클립보드에 복사할 수 있습니다.
 
2) 복사 History 기능을 이용하여 얼마전에 복사했던 내용을 다시 사용할 수 있습니다.
 
3) 어플케이션에서 사용될 XML 파일을 열어 검색할 수 있습니다.
 
환경설정에서 사용자의 편의에 맞게 설정을 바꿀 수 있습니다.
기타 궁금한 사항이나 버그 및 개선 사항은 댓글을 남겨주시면 수정 및 개선하도록 하겠습니다.
 
본 프로그램은 현업에 종사하시는 개발자 및 개인적인 용도로 사용할 수 있으며, 본 블로그외의 다른곳에서의 재배포를 금지합니다.
Posted by 땡초 POWERUMC

댓글을 달아 주세요


동적 이벤트 처리.. 어디에 써먹으면 좋은까..
 
우선 이에 앞서 리플랙션 이야기를 잠시만 언급하겠습니다.
System.Reflection 네임스페이스와 System.Type을 사용하면 우리가 원하는 어셈블리의 클래스, 인터페이스, 프로퍼티 와 맴버에 대한 정보를 얻을 수 있습니다.
간단히 말하면, 런타임으로 동적으로 다양한 작업을 하고자 할 때 사용됩니다.
 
대부분 다음과 같은 경우에 자주 사용됩니다.
 
1) 실제 코드가 아닌 정보들, 그 파일에 따라다니는 정보들 등 어플케이션의 메타 정보를 얻어서 유지보수에 도움을 받기도 합니다.
 
2) 어셈블리의 내용을 알고자 할 때 사용할 수 있습니다.
Assembly asm = Assembly.Load("Mscorlib.dll");
Type[] types = asm.GetTypes();
foreach(Type t in types)
{
Console.WriteLine("Type : {0}", t)
}
 
3) Design Pattern 의 Abstract Factory Pattern 의 단점을 보완할 수 있습니다. Factory Class가 추가될 때 마다 본문의 내용이 수정이 불가피 하지만, 리플랙션을 이용하면 Class명만으로도 이것을 쉽게 해결 할 수 있습니다.
 
4) Com 개체를 Late-Binding 으로 불러 사용할 수 있습니다.
private void OpenExcel()
{
Type excel = Type.GetTypeFromProgID("Excel.Microsoft");
 
object objExcel = Activator.CreateInstance(excel)
object[] param = object[1];
param[0]=true;
excel.InvokeMember("Visible", BindingFlags.SerProperty, null, objExcel, param);
}
 
대부분 리플랙션은 접근제한 맴버 또는 프로퍼티, 메서드 호출 등에 사용되거나, 참조되지 않은 어셈블리를 제어할 때 자주 사용되곤 합니다.
 
하지만, 이번에는 자주 사용되지 않는 Event 제어를 해 보도록 하겠습니다.
 
소개될 예제는 WinForm 을 기준으로 작성되었지만, WebForm 에서도 약간의 소스를 변경하면 정상적으로 작동합니다.
 
우리가 즐겨 사용하는 컨트롤중 Button 컨트롤이 있습니다. 모든 닷넷 컨트롤은 Control 을 상속하므로 기본적인 많은 Event 가 존재하고, 컨트롤의 기능에 따라 CustomEvent 또한 존재할 것입니다.
 
우선 컨트롤의 이벤트 목록을 얻어보도록 하겠습니다.
string msg = string.Empty;
EventDescriptorCollection events = TypeDescriptor.GetEvents(button1);
foreach (EventDescriptor ed in events)
{
             msg += ed.Name + "\r\n";
}
MessageBox.Show( msg );
 
실행결과)
 
EventDescriptor 클래스는 이벤트 이름, 특성, delegate 등으로 구성되어 있습니다.
이 클래스의 주요 메서드 중 AddEventHandler 와 RemoveEventHandler 가 존재합니다.
이것을 통해 이벤트를 제어할 수 있습니다.
 
그럼, 이제 우리가 이벤트를 제어해야할 가상의 상황을 만들도록 하겠습니다.
public Form1()
{
             InitializeComponent();
             button1.Click += new EventHandler(button1_Click);
}
 
private void button1_Click(object sender, EventArgs e)
{
             System.Threading.Thread.Sleep(1000);
             MessageBox.Show("처리되었습니다");
}
 
위와같이 샘플로 만든 폼은 button1 을 클릭하면 가상의 1초의 작업 후 MessageBox 를 띄우는 경우입니다.
 
하지만 갑자기 변해버린 요구사항엔 ‘주요 몇몇 공통버튼에 대해 “Loading…” 텍스트나 “작업중입니다” 라는 메시지를 보여달라’는 변경사항이 있을 수 있습니다.
단지 몇 개의 폼이라면 수작업으로 간단히 해결할 수 있지만, 몇백개에 해당하는 폼이라면 사정은 달라지게 됩니다. Otl
그렇다면 당신은 어떻게 하겠습니까?
 
우선 현재 Form 의 Type 과 Loading… 텍스트를 뿌려줘야 하는 Button Type 을 알아와야 합니다
Type thisType                   = this.GetType();
Type btn1                          = button1.GetType();
주의할 점은, button1 이 실행중인 어셈블리가 아니라면 다른 어셈블리에서 PropertyInfo 를 통해 Type을 찾아와야 합니다
 
그리고 Loading.. 이벤트와 Button_Click 이벤트의 메서드가 다음과 같이 구현되어 있습니다.
private void OnLoadingHandler(object sender, EventArgs e)
{
        lblLoading.Visible = true;
        this.Refresh();
}
 
private void OffLoadingHandler(object sender, EventArgs e)
{
        lblLoading.Visible = false;
}
 
private void button1_Click(object sender, EventArgs e)
{
        System.Threading.Thread.Sleep(1000);
        MessageBox.Show("처리되었습니다");
}

위  이벤트의 메서드를 MethodInfo 를 통해 메서드의 정보를 찾습니다.
MethodInfo thisMi = thisType.GetMethod("button1_Click", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance );
MethodInfo onLoadMi         = thisType.GetMethod("OnLoadingHandler", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance );
MethodInfo offLoadmi= thisType.GetMethod("OffLoadingHandler", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance );
여기에서도 Button1_Click 이 실행중인 어셈블리가 아니라면 thisType 은 button1_Click 이 구현되어 있는 Class의 Type 이 되어야 합니다.
 
이제 Button 의 Click 이벤트를 가져와야 합니다.
EventInfo ei          = btn1.GetEvent("Click" , BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance );
으읔…. 정말 간단하네요 -_-;
 
위의 처음 샘플의 EventDescriptor.AddEventHandler 의 메서드는 EventInfo 에도 존재합니다.
 
아시다시피, 모든 이벤트는 delegate 의 대리자를 통해 추가 또는 삭제할 수 있습니다.
그럼 우리는 이벤트의 delegate 를 만들어야 합니다.
 
Delegate d                         = Delegate.CreateDelegate( ei.EventHandlerType, this, thisMi );
Delegate onLoadD = Delegate.CreateDelegate( ei.EventHandlerType, this, onLoadMi );
Delegate offLoadD = Delegate.CreateDelegate( ei.EventHandlerType, this, offLoadmi);
 
각각 d 는 기존의 Click Event 이고, onLoadD, offLoadD 는 Loading.. 메시지를 보여주고, 없애는 delegate 가 됩니다.
 
이벤트는 += 와 -= 연산을 통해 추가, 또는 삭제할 수 있습니다. 이것을 리플랙션에서 RemoveEventHandler 메서드를 통해 구현할 수 있습니다.
ei.RemoveEventHandler( button1, d );
여기까지 샘플을 실행하게 되면 이미 Event 가 연결되어 있음에도 불구하고, 더 이상 Click 이벤트가 발생하지 않게 됩니다.
 
그럼 다음과 같이 AddEventHandler 메서드를 사용하여 delegate 를 추가합니다.
ei.AddEventHandler( button1, onLoadD );
ei.AddEventHandler( button1, d );
ei.AddEventHandler( button1, offLoadD );
우리는 Button_Click Event 를 제거한 상태입니다. 즉, 아무 반응이 없어 지지요.
Event 는 += 연산을 통해 여러 개의 delegate 를 추가할 수 있습니다.
바로 그것입니다.
Click 이벤트를 제거한 후 On Loding Event -> Click Event -> Off Loading Event 가 발생하도록 Event 를 조작하게 되었습니다.
 
그렇게 많은 라인을 추가하지 않았음에도, 리플랙션을 통해 이벤트를 조작함으로써 해결하였네요^^
 
대부분의 사람들은 “리플랙션” 이라고 하면 느리다고 합니다. 몇몇 사람들은 리플랙션 코드를 보는 순간, “느려집니다” 라고 단언합니다.
리플랙션을 사용하면 최대 1000배의 성능저하가 있다고 들은바 있습니다. 하지만 그것은 Invoke 를 하였을 때의 이야기 입니다.

하지만, Late-Binding 이므로 분명 눈에보이지는 않지만 성능저하는 있을 수 있습니다.
이것을 어떻게 적절히 사용하느냐가 문제인 것 같습니다. 분명 리플랙션은 OOP 의 상속성과 객체지향성을 거스르고, 퍼포먼스를 떨어 뜰일 수 있을테니까요.
 
오늘도, 내일도.. 바쁘시더라도 모두 좋은 하루 되시길 바랍니다. ^^
Posted by 땡초 POWERUMC

댓글을 달아 주세요

오늘은 비동기 ADO.NET 을 알아볼까 합니다.
아티클을 이해하기 위해서는 비동기 호출에 대한 지식이 약간 필요합니다.
 
우리는 다음과 같은 시간이 오래 걸리는 쿼리를 날릴것입니다.
WAITFOR DELAY '00:00:03'; SELECT * FROM Titles
WAITFOR DELAY '00:00:05'; SELECT * FROM Titles
WAITFOR DELAY '00:00:10'; SELECT * FROM Titles
 
위의 WAITFOR DELAY 는 명령문이 암시하듯 이유없는 대기를 하라는 명령입니다.
3초후에 SELECT, 5초후에 SELECT, 10초후에 SELECT 쿼리를 날려 시간이 오래걸리는 작업을 대체하기 위해서 이지요~
 
다음의 쿼리를 쿼리분석기를 통해 실행시켜 보겠습니다.
 
예상대로 18초가 걸리는군요~
하나하나의 쿼리가 종료해야만이 다음 쿼리를 실행할 수 있죠~
이런방식을 동기 방식이라고 합니다.
C# lock 과 같은 키워드가 여러 개의 쓰레드의 요청이 있더라도, 마치 일렬로 줄을 세워 한번에 하나씩 처리하는 것과 비슷하다고 생각하시면 됩니다.
 
그렇다면, 위의 쿼리를 비동기 방식으로 호출하였을 때 어떤 결과가 나올까요?
그럼, 그 의문을 하나하나씩 풀어보기로 합니다.
 
우선 전체 소스를 보면서 비밀을 하나하나씩 파헤쳐 보겠습니다.
using System;
using System.Data;
using System.Data.SqlClient;
using System.Threading;
 
namespace ConsoleTest
{
         class Program
         {
                  // 비동기로연결하기위해Asynchronous Processing 를true 로설정해놓은커넥션Connection String
                  public static string GetConnection
                  {
                           get
                           {
                                   return "Asynchronous Processing=true;server=localhost;database=pubs;uid=sa;password=xxxx";
                           }
                  }
 
                  static void Main(string[] args)
                  {
                           SqlConnection cn1 = new SqlConnection( GetConnection );
                           SqlConnection cn2 = new SqlConnection( GetConnection );
                           SqlConnection cn3 = new SqlConnection( GetConnection );
 
                           SqlCommand cmd1            = new SqlCommand("WAITFOR DELAY '00:00:03'; SELECT * FROM Titles", cn1);
                           SqlCommand cmd2            = new SqlCommand("WAITFOR DELAY '00:00:05'; SELECT * FROM Titles", cn2);
                           SqlCommand cmd3            = new SqlCommand("WAITFOR DELAY '00:00:10'; SELECT * FROM Titles", cn3);
 
                           cn1.Open();
                           cn2.Open();
                           cn3.Open();
 
                           // DataAccess 시작시간을기록한다.
                           DateTime startDate = DateTime.Now;
 
                           // 비동기작업을시작한다.
                           IAsyncResult result1       = cmd1.BeginExecuteReader();
                           IAsyncResult result2       = cmd2.BeginExecuteReader();
                           IAsyncResult result3       = cmd3.BeginExecuteReader();
 
                           WaitHandle[] waitHandle    = new WaitHandle[3];
                           string[] resultStr                 = new string[3];
 
                           waitHandle[0]                      = result1.AsyncWaitHandle;
                           waitHandle[1]                      = result2.AsyncWaitHandle;
                           waitHandle[2]                      = result3.AsyncWaitHandle;
 
                           for (int i = 0; i < waitHandle.Length; i++)
                           {
                               // 배열의핸들이모두신호를받을때까지기다립니다.
                               int endIndex           = WaitHandle.WaitAny( waitHandle, 20000, false );
 
                               switch (endIndex)
                               {
                                   case 0:
                                       cmd1.EndExecuteReader(result1);
                                       resultStr[0]   = DateTime.Now.ToString();
                                       break;
                                   case 1:
                                       cmd2.EndExecuteReader(result2);
                                       resultStr[1]   = DateTime.Now.ToString();
                                       break;
                                   case 2:
                                       cmd3.EndExecuteReader(result3);
                                       resultStr[2]   = DateTime.Now.ToString();
                                       break;
                               }
                           }
 
 
                           cn1.Close();
                           cn2.Close();
                           cn3.Close();
 
                           // DataAccess 작업완료시간을기록한다.
                           DateTime endDate = DateTime.Now;
 
                           for( int i=0; i<resultStr.Length;i++)
                                   Console.WriteLine( " Result {0} : {1}", i, resultStr[i] );
 
 
                           // 두시간의시간차를구하여출력한다.
                           TimeSpan timeSpan = endDate - startDate;
 
                           Console.WriteLine("총작업시간은: {0}", timeSpan.ToString() );
                  }
         }
}
 
 
우선 가장 눈에 띄게 차이 나는 것이 ConnectionString 입니다.
Asynchronous Processing=true;server=localhost;database=pubs;uid=sa;password=xxxx
Asynchronous Processing=true는 비동기로 DataBase 에 연결하기 위해 반드시 추가해 주어야 합니다.
 
또한 비동기 데이터베이스 작업을 하기 위해 SqlCommand 클래스는 세가지의 비동기 메서드가 존재합니다.
대부분의 비동기 메서드들이 Begin 으로 시작하는 네이밍을 갖는 것과 마찬가지로, BeginExecuteNonQuery(), BeginExecuteReader(), BeginExecuteXmlReader()가 바로 그것입니다.
이 메서드는 각각 EndExecuteNonQuery(), EndExecuteReader(), EndExecuteXmlReader() 가 호출됨으로써 비로서 비동기 작업의 콜백을 받을 수 있습니다.
위의 경우는 구현되지 않았지만, DataReader 의 경우 콜백이 완료된 후가 되어야지만 비로서 Read 작업을 수행할 수 있는것입니다. 물론 다른 비동기 메서드로 마찬가지입니다.
 
이 구문은 비동기 ADO.NET 작업의 가장 중요한 부분입니다.
waitHandle[0]                      = result1.AsyncWaitHandle;
waitHandle[1]                      = result2.AsyncWaitHandle;
waitHandle[2]                      = result3.AsyncWaitHandle;
WaitHandle.WaitAny( waitHandle, 20000, false );
 
우리는 waitHandler 배열에 비동기 작업이 끝나는데 사용되는 AsyncWaitHandle 를 넣었습니다.
AsyncWaitHandler 는 비동기 작업이 끝나기를 기다리는 핸들러 입니다.
물론 WaitAny 메서드의 3가지 Overload 는 모두 첫번째 인자를 배열로만 받습니다.
WaitWay 메서드는 우리가 넣은 배열의 요소중에 먼저 끝낸 배열의 Index 를 return 합니다.
waitHandler[2] 의 작업이 제일 먼저 끝나게 되면 배열의 인덱스인 2 를 리턴하게 됩니다.
만약 짧은 시간동안 어느 작업도 끝나지 않게 되면, WaitAny 는 두번째 인자에 적은 시간(밀리초)만큼 무한 대기 하게 됩니다.
 
배열에 개수만큼 루프를 돌면서, 비동기 작업이 끝나는 순서대로 SqlCommand 의 작업을 하게 됩니다.
때문에 결과 화면의 DateTime.Now 의 시간은 각각 달라질 수 있는 것입니다.
 
그럼 실행화면을 보겠습니다.
 
보는대로 총 소요시간은 10초가 되었습니다.
위의 동기작업의 쿼리 수행 결과보다 훨씬~ 빨라졌지요?
각가 쿼리 수행이 완료된 시간도 한번 눈여겨 볼만 하네요~
 
긴 시간이 소요되는 작업이나 여러 번 반복적으로 실행되야 하는 작업등에 적용해 보면 상당히 만족할만할 결과가 아닐 수 없네요~
물론 ASP.NET 의 웹 환경에서도 적용이 가능합니다.
 
그럼 이것으로 오늘 강좌 마칩니다. 뱌뱌 ^^//
Posted by 땡초 POWERUMC

댓글을 달아 주세요

기본적으로 웹폼을 생성하면 확장자는 ASPX 가 됩니다.
이와 달리 JSP 나 ASP 로 개발된 웹페이지는 여러확장자를 쓸 수 있지요.
가령 .html, .do, 등등.. 하지만 닷넷의 웹폼에선 확장자를 바꾸면 페이지가 동작하지 않습니다.
악의적인 목적으로 게시판이나 자료실 등 ASP 등으로 작성된 페이지를 업로드 하여, 다운로드 경로로 접근하여 페이지를 실행시킬 경우, 보안에 상당히 취약한 부분도 있었습니다.
하지만 언제나 .ASPX 확장자는 이제 식상할 때가 되지 않았습니까!
그럼 어디한번 바꾸어 봅시다.
 
우선 원하는 확장자를 IIS 에서 확장자 매핑 시키고 web.config 의 httpHandlers 에 등록해 보겠습니다.
여기에선 .umcx 라는 확장자를 사용하기로 합니다.
 
 
그다음은 web.config 의 httpHandlers 섹션에 다음과 같이 작성합니다.
<httpHandlers>
         <add path="*.umcx" verb="*" type="System.Web.UI.PageHandlerFactory" validate="true" />
</httpHandlers>
 
여기에서 위의 Type 에 등록된 클래스를 조금 짚고 넘어 가겠습니다.
PageHandlerFactory 는 IHttpHandlerFactory 를 구현한 클래스 입니다.
이 인터페이스는 IHttpHandler 를 리턴하는 GetHandler 메서드를 구현하기만 하면 됩니다.
public IHttpHandler GetHandler(HttpContext context,
        string requestType, String url, String pathTranslated)
 
이 인터페이스를 구현한 클래스를 web.config 의 httpHandlers 에 등록하게 되면 좀더 유연하게 가령, 요청이 get , post 방식에 따라 서로 다른 HttpHandler 를 반환 할 수 있습니다.
IIS 에서 확장자 매핑하면서 get,post 등 요청 방식을 지정할 수 있게 되어있습니다.
하지만 위 인터페이스를 활용하여 요청방식에 따른 각기 기능을 하나의 클래스에 넣을 수 도 있구요, 페이지 요청 방식에 따라 서로 상이한 페이지를 보여 줄 수 도 있습니다.
다음 기회 언젠가 좀 더 자세히 파 보도록 하겠습니다.
 
다시 원점으로 돌아와서,
하지만 이것만으로 끝이 아니랍니다.
우라가 작성한 페이지를 컴파일하고 컴파일 하는 동안 코드를 생성하는데 사용되는 빌드 공급자를 정의해 주어야 합니다.
 
이것또한 최상위 web.config 에 다음과 같이 정의되어 있지요
<buildProviders>
        <add extension=".aspx" type="System.Web.Compilation.PageBuildProvider" />
        <add extension=".ascx" type="System.Web.Compilation.UserControlBuildProvider" />
        <add extension=".master" type="System.Web.Compilation.MasterPageBuildProvider" />
        <add extension=".asmx" type="System.Web.Compilation.WebServiceBuildProvider" />
        <add extension=".ashx" type="System.Web.Compilation.WebHandlerBuildProvider" />
        <add extension=".soap" type="System.Web.Compilation.WebServiceBuildProvider" />
        <add extension=".resx" type="System.Web.Compilation.ResXBuildProvider" />
        <add extension=".resources" type="System.Web.Compilation.ResourcesBuildProvider" />
        <add extension=".wsdl" type="System.Web.Compilation.WsdlBuildProvider" />
        <add extension=".xsd" type="System.Web.Compilation.XsdBuildProvider" />
        <add extension=".js" type="System.Web.Compilation.ForceCopyBuildProvider" />
        <add extension=".lic" type="System.Web.Compilation.IgnoreFileBuildProvider" />
        <add extension=".licx" type="System.Web.Compilation.IgnoreFileBuildProvider" />
        <add extension=".exclude" type="System.Web.Compilation.IgnoreFileBuildProvider" />
        <add extension=".refresh" type="System.Web.Compilation.IgnoreFileBuildProvider" />
</buildProviders>
자주 보던 확장자들이 많이 있지요?
 
<compilation>
</compilation>
Web.config 의 위 섹션이 응용 프로그램의 컴파일을 담당하는 섹션입니다.
우리가 웹사이트를 작성하고 이후 수정된 사항을 복사만 하더라도 컴파일 되어 자동으로 적용되어 지는 미리 컴파일 기능 또한 위 섹션에서 담당하게 되지요~
 
이제 감이 오셨나요?
그럼 우리가 만든 페이지가 컴파일 되도록 하기 위해선 다음과 같이 섹션을 추가해 줍니다
<compilation debug="false">
         <buildProviders>
                  <add extension=".umcx" type="System.Web.Compilation.PageBuildProvider" />
         </buildProviders>
</compilation>
 
이제 모든 설정이 끝났습니다.
 
// 2009-06-20 아래의 주소는 더이상 접속할 수 없습니다.
자신이 만든 웹페이지의 확장자를 .umcx 로 바꾸어 보십시오.
위 예제 페이지는 제 블로그의 접속 차단 IP 를 위해 작성한 페이지 입니다.
 
실행결과
 
 
이번 한 주도 행운만이 가득한 한 주가 되세요~ ^^//

Posted by 땡초 POWERUMC

댓글을 달아 주세요

요즘은 시간이 많아서 그런지 자주 강좌를 올리네요 ㅋㅋ



 

이번에는 웹사이트의 핵심인 Core(코어) 모듈을 구성해 보는 시간을 가져봅니다.





잘 구성된 모듈은 안정된 시스템 그리고 코딩의 작업 속도와 바로 직결됩니다. 





모듈의 구성 방법은 개발자마다, 그리고 시스템 마다 각이 각색이겠지만, 






오늘 구성해 볼 모듈은 대부분의 웹사이트에서 적용할 수 있는 코어 구성 첫 번째




시간을 갖도록 합니다.
 




본 강의는 객체지향적인 문법을 공부하거나 소화한 분을 대상으로 합니다.
 





우리가 구성할 코어는 XML 로 구성된 사이트 맵이 필요합니다.



 
<?xmlversion="1.0"encoding="utf-8" ?>
<AdminID="ADMIN1" Path="WebAdmin">
    <Title>블로그 관리자</Title>
    <ModuleGroupID="MG1" Path="01Blog">
        <Title>기본 설정 관리</Title>
        <ModuleID="UBI1"ViewControl="/WebAdmin/01Blog/BlogBaseInfo.ascx">
            <Title>블로그 기본 정보</Title>
            <InitParams>
                <ParamName="umc">Umc</Param>
                <ParamName="umc1">Umc1</Param>
            </InitParams>
        </Module>
        <ModuleID="UBI2"ViewControl="/WebAdmin/01Blog/ProfileInfo.ascx">
            <Title>프로필 기본정보 관리</Title>
        </Module>
    </ModuleGroup>
    <ModuleGroupID="MG2"Path="02Privacy">
        <Title>프라이버시 관리</Title>
        <ModuleID="UP1"ViewControl="/WebAdmin/02Privacy/Privacy.ascx">
            <Title>프라이버시 관리</Title>
        </Module>
    </ModuleGroup>
    <ModuleGroupID="MG3"Path="02Bbs">
        <Title>게시판 관리자</Title>
        <ModuleID="Bbs1"ViewControl="/WebAdmin/02Privacy/Privacy.ascx">
            <Title>자유게시판</Title>
            <InitParams>
                <ParamName="BoardNo">1</Param>
                <ParamName="Template.Bbs.List">/WebAdmin/02Bbs/ListTemplate.ascx</Param>
                <ParamName="Template.Bbs.Write">/WebAdmin/02Bbs/WriteTemplate.ascx</Param>
                <ParamName="Template.Bbs.View">/WebAdmin/02Bbs/ViewTemplate.ascx</Param>
            </InitParams>
        </Module>
    </ModuleGroup>
</Admin>




ModuleGruop
섹션은 여러 개의 Module 로 구성되어 있습니다. 이 사이트맵을 기준으로






메뉴 컨트롤을 만들때나 이 Module의 부모가 누구인지가 필요할 때 유용합니다.




Module
섹션은 ViewControl 이라는 속성이 있습니다. 사용자 정의 콘트롤로 만들어진





이 컨트롤은 PageTemplate.ascx 이라는 마스터페이지와 같은 역할을 하는 PageTemplate의





PlaceHolder
컨트롤에 들어가게 될것입니다.




Module
섹션안에는 InitParams 라는 섹션이 또 존재하게 되는데,예를 들어 QueryString으로




넘겨야할 BoardNo 등의 파라메터들을 여기에 모두다 집어 넣을 수 있습니다.
 



이제 이 XML 을 사이트에 이용할 수 있도록 Parsing 해야 합니다.




로직은 “C# 디자인 패턴” 책의 Composite 패턴을 확장해 보았습니다. Composite 패턴에




대한 선수 학습이 필요합니다.





 
먼저 ISitemap 이라는 Interface를 만들어 보겠습니다.
namespace Umc.Core.WebAdmin.Sitemap
{
         public interface ISitemap
         {
                  string ID { get; set; }
                  string Path { get; set; }
                  string Title { get; set; }
                  string ViewControl { get; set; }
                  int Count { get; }
                  IEnumerator GetEnumerator();
                  ISitemap Parent { get; }
                  void Add(ISitemap sitemap);
                  bool HasChild { get; }
                  void ReadXml(XmlReader reader);
         }
}
 



Sitemap
에 필요한 모든 클래스는 위 Interface를 상속 받게 되고 가장 핵심이 되는




ReadXml
메서드가 바로 XML 을 해독하게 될 것입니다.




 
이 Interface는 SitemapModule가 상속받습니다.


namespace Umc.Core.WebAdmin.Sitemap
{
         public class SItemapModule : ISitemap
         {
                  protected ArrayList sitemap;
                  protected ISitemap parent;
                  protected string _id;
                  protected string _path;
                  protected string _title;
 
                  // InitParams 섹션
                  protected StringDictionary _initParam = new StringDictionary();
 
                  public SItemapModule(ISitemap _parent)
                  {
                           this.parent       = _parent;
                           this.sitemap      = new ArrayList();
                  }
                  #region ISitemap 멤버
 
                  public string ID
                  {
                           get { return _id; }
                           set { _id = value; }
                  }
 
                  public string Path
                  {
                           get { return _path; }
                           set { _path = value; }
                  }
 
                  public string Title
                  {
                           get { return _title; }
                           set { _title = value; }
                  }
 
                  public virtual string ViewControl
                  {
                           get { return null; }
                           set { throw new Exception("The method or operation is not implemented."); }
                  }
 
                  public int Count
                  {
                           get { return sitemap.Count; }
                  }
 
                  public System.Collections.IEnumerator GetEnumerator()
                  {
                           return sitemap.GetEnumerator();
                  }
 
                  public ISitemap Parent
                  {
                           get { return this.parent; }
                  }
 
                  public virtual void Add(ISitemap sitemap)
                  {
                           this.sitemap.Add( sitemap );
                  }
 
                  public bool HasChild
                  {
                           get { return sitemap.Count>0; }
                  }
 
                  public virtual void ReadXml(XmlReader reader)
                  {
                           throw new Exception("The method or operation is not implemented.");
                  }
                  #endregion
 
                  public void ReadInitParams(XmlReader reader)
                  {
                           while (reader.Read())
                           {
                                   if(reader.IsEmptyElement) return;
 
                                   if (reader.NodeType == XmlNodeType.Element)
                                   {
                                            if (reader.Name == Sitemap.NODE_PARAM)
                                            {
                                                     string _key       = reader["Name"];
                                                     string _value     = reader.ReadElementString();
                                                     _initParam.Add( _key, _value );
                                            }
                                   }
                                   if(reader.Name==Sitemap.NODE_INIT_PARAMS &&
                                            reader.NodeType==XmlNodeType.EndElement)
                                            return;
                           }
                  }
 
                  public StringDictionary GetInitParam
                  {
                           get { return _initParam; }
                  }
         }
}




기본이 되는 각각 메서드와 프로퍼티의 역할을 정의해 놓았습니다. 각각 쓰임새에 따라





특히 ReadXml 은 virtual 로 선언된 것을 볼 수 있습니다.





이 클래스는 각각 섹션을 처리할 클래스들의 뼈다가 되는 클래스로, 다음과 같은




다이어그램을 볼 수 있습니다.
 





 



차례대로 Sitemap 클래스부터 살펴보겠습니다.



public class Sitemap : SItemapModule
         {
                  public const string NODE_ADMIN              = "Admin";
                  public const string NODE_MODULE_GROUP      = "ModuleGroup";
                  public const string NODE_MODULE             = "Module";
                  public const string NODE_INIT_PARAMS       = "InitParams";
                  public const string NODE_PARAM              = "Param";
                  public const string NODE_TITLE              = "Title";
 
                  public Sitemap(ISitemap _parent)
                           : base(_parent)
                  {
                  }
                  public Sitemap() : base(null)
                  {
                  }
 
                  public override void Add(ISitemap _sitemap)
                  {
                           sitemap.Add( _sitemap );
                  }
 
                  public override void ReadXml(XmlReader reader)
                  {
                           while (reader.Read())
                           {
                                   if (reader.NodeType == XmlNodeType.Element)
                                   {
                                            if (reader.Name == NODE_ADMIN)
                                            {
                                                     this.ID = reader["ID"];
                                                     this.Path         = reader["Path"];
                                            }
                                            if (reader.Name == NODE_TITLE)
                                            {
                                                     this.Title        = reader.ReadElementString();
                                            }
                                            if (reader.Name == NODE_MODULE_GROUP)
                                            {
                                                     ModuleGroup moduleGroup = new ModuleGroup(this);
                                                     moduleGroup.ID = reader["ID"];
                                                     moduleGroup.Path = reader["Path"];
                                                     moduleGroup.ReadXml(reader);
 
                                                     this.Add(moduleGroup);
                                            }
                                   }
                                   if(reader.Name==NODE_ADMIN &&
                                            reader.NodeType==XmlNodeType.EndElement)
                                            break;
                           }
                  }
 
                  public static ISitemap ReadSitemap(string path)
                  {
                           XmlTextReader reader       = null;
                           try
                           {
                                   reader            = new XmlTextReader(path);
                                   ISitemap root     = new Sitemap();
 
                                   root.ReadXml( reader );
 
                                   return root;
                           }
                           catch(Exception ex)
                           {
                                   throw ex;
                           }
                           finally
                           {
                                   reader.Close();
                           }
                  }
         }




ReadSitemap
이라는 static 클래스가 시발점으로 각각의 노드를 탐색 할 수 있습니다.




클래스의 상단에는 XML 의 섹션들을 상수로 정의 해 놓았습니다.





ISitemap root = new Sitemap()
과 같이 최상단의 부모는 Null 이라는걸 알 수 있습니다.




ModuleGroup moduleGroup = new ModuleGroup(this);
구문이 의아해 하실겁니다.




XML
의 ModuleGroup 섹션을 만나면 ModuleGroup 내의 요소들을 처리할 또 다른 처리기가





필요합니다. 여기의 this 인자는 ModuleGroup 내에서는 이 this 가 부모 클래스가 되는




것입니다.
 
public class ModuleGroup : SItemapModule
         {
                  public ModuleGroup(ISitemap _parent)
                           : base(_parent)
                  {
                  }
 
                  public override void ReadXml(System.Xml.XmlReader reader)
                  {
                           int depth         = reader.Depth;
 
                           while (reader.Read())
                           {
                                   if (reader.NodeType == XmlNodeType.Element)
                                   {
                                            if (reader.Name == Sitemap.NODE_TITLE)
                                            {
                                                     this.Title        = reader.ReadElementString();
                                            }
                                            if (reader.Name == Sitemap.NODE_MODULE)
                                            {
                                                     ModuleInfo moduleInfo      = new ModuleInfo(this);
                                                     moduleInfo.ID              = reader["ID"];
                                                     moduleInfo.ViewControl     = reader["ViewControl"];
                                                     moduleInfo.ReadXml( reader );
 
                                                     this.Add(moduleInfo);
                                            }
                                   }
                                   if(reader.Depth <= depth ) return;
                           }
                  }
         }




여기도 별반 다를게 없네요~






ModuleGroup
을 탐색하다 Module 섹션을 만나면 this 클래스를 인자로 ModuleInfo




클래스에게 탐색을 넘깁니다.



 
public class ModuleInfo : SItemapModule
         {
                  private string _viewControl                 = null;
                  public ModuleInfo(ISitemap _parent)
                           : base(_parent)
                  {
                  }
 
                  public override string ViewControl
                  {
                           get { return _viewControl; }
                           set { _viewControl = value; }
                  }
 
                  public override void ReadXml(XmlReader reader)
                  {
                           int depth         = reader.Depth;
 
                           while (reader.Read())
                           {
                                   if (reader.NodeType == XmlNodeType.Element)
                                   {
                                            if (reader.Name == Sitemap.NODE_TITLE)
                                            {
                                                     this.Title        = reader.ReadElementString();
                                            }
                                            if (reader.Name == Sitemap.NODE_INIT_PARAMS)
                                            {
                                                     ReadInitParams( reader );
                                            }
                                   }
 
                                   if(reader.Name==Sitemap.NODE_MODULE &&
                                            reader.NodeType==XmlNodeType.EndElement)
                                            return;
                           }
                  }
         }




ViewControl
은 Module 섹션이 갖는 Attribute 이므로 여기에서 override 되었습니다.




그리고 ReadInitParams() 는 이미 부모 클래스에서 정의 해 놓았습니다.
 




이제 이 클래스를 통제시키는 SitemapManager 를 살펴봅니다.



public class SitemapManager
         {
                  private static SitemapManager _instance              = null;
                  private ISitemap _sitemap;
 
                  public ISitemap SiteInfo
                  {
                           get { return _sitemap; }
                  }
 
                  private SitemapManager()
                  {
                  }
 
                  public static SitemapManager GetInstance()
                  {
                           lock (typeof(SitemapManager))
                           {
                                   if (_instance == null)
                                   {
                                            _instance = new SitemapManager();
                                            _instance.Init();
                                   }
                           }
 
                           return _instance;
                  }
 
                  public void Init()
                  {
                           ReConfig();
                           string path       = Utility.GetAbsolutePath( Umc.Core.UmcConfiguration.Core["AdminXmlPath"]);
                           _sitemap          = Sitemap.ReadSitemap(path);
                  }
 
                  public void ReConfig()
                  {
                           _sitemap          = null;
                  }
 
                  public string GetNavigateString(ISitemap _map)
                  {
                           ISitemap _tempSitemap      = _map;
                           StringBuilder naviString   = new StringBuilder();
 
                           naviString.Insert(0, _tempSitemap.Title);
 
                           while (_tempSitemap.Parent != null)
                           {
                                   naviString.Insert(0, _tempSitemap.Parent.Title + " > " );
                                   _tempSitemap      = _tempSitemap.Parent;
                           }
 
                           return naviString.ToString();
                  }




여기에서 Singleton 패턴이 사용되었습니다.




생성자가 private 로 선언되어있습니다. 이 객체를 접근 하기 위해서 GetInstance() 라는




단 한 개의 통로만 열려있습니다.





이로 인한 이점은 객체의 정보를 소멸 시키지 않고 언제든 참조할 수 있습니다.




때문에 언제 어디서든 SiteInfo 프로퍼티를 호출하면 사이트맵의 정보를 다시 읽지 않고




고스란이 참조 할 수 있게 되는것입니다.




또한, GetNavigateString(ISitemap _map) 으로 부모의 Sitemap 까지 찾아 올라가




네비게이트 문자열을 뽑아 낼 수 있습니다.

 



여기의 Init는 언제 호출 될 것이냐가 의문이네요… 간단합니다..




Global.asax
에 첫 어플케이션이 시작될 때 초기화 되면 됩니다.



private SitemapManager _sitemapManager   = null;
        
    void Application_Start(object sender, EventArgs e)
    {
         _sitemapManager            = SitemapManager.GetInstance();
    }
 




이제 이 Sitemap 이 TreeView 컨트롤에 바인딩 될 수 있도록 Sitemap_Node 클래스를






만들어봅니다.



public class Sitemap_Node : System.Web.UI.WebControls.TreeNode
         {
                  ISitemap sitemap;
 
                  public Sitemap_Node(ISitemap _sitemap)
                           : base(_sitemap.Title)
                  {
                           this.sitemap               = _sitemap;
                           base.Value                 = _sitemap.ID;
                  }
 
                  public ISitemap Sitemap
                  {
                           get { return sitemap; }
                  }
         }




이와 같이 TreeView 에 Sitemap의 Title 프로퍼티가 바인딩 되고, TreeView의 Value 에는




Sitemap.ID
가 바인딩 됩니다.
 




그럼 우리가 구성한 Sitemap 은 TreeView 컨트롤과 거의 흡사한 형식으로 메모리에




저장됩니다. Sitemap 은 지금 내가 누구인지, 내 부모가 누구인지, 그리고 Root 가 



누구인지 까지 알아낼 수 있습니다.




 
마지막으로 재귀호출을 이용하여 TreeView 컨트롤에 Sitemap 노드들을 바인딩 해봅니다.



public partial class WebAdmin_Default : Umc.Core.WebAdmin.WebAdminTemplate
{
         private ISitemap sitemap;
         protected void Page_Load(object sender, EventArgs e)
         {
                  // Postback 또는 UpdatePanel 안의 클릭후 Sitemap_Node 로 캐스팅이 안되므로
                  // 매번 다시 바인딩
                  BuildTree();
         }
 
         private void BuildTree()
         {
                  treeMenu.Nodes.Clear();
                  sitemap = SitemapManager.GetInstance().SiteInfo;
                  Sitemap_Node node = new Sitemap_Node(sitemap);
 
                  treeMenu.Nodes.Add(node);
 
                  AddNodes(sitemap, node);
 
                  treeMenu.ExpandAll();
         }
 
         private void AddNodes(ISitemap sitemap, Sitemap_Node node)
         {
                  IEnumerator currentSitemap = sitemap.GetEnumerator();
 
                  while (currentSitemap.MoveNext())
                  {
                           ISitemap newSitemap                = (ISitemap)currentSitemap.Current;
                           Sitemap_Node newNode       = new Sitemap_Node(newSitemap);
                           node.ChildNodes.Add( newNode );
                           AddNodes( newSitemap, newNode );
                  }
         }
         protected void treeMenu_SelectedNodeChanged(object sender, EventArgs e)
         {
                  ISitemap _sitemap = (ISitemap)((Sitemap_Node)treeMenu.SelectedNode).Sitemap;
 
                  lblNavigate.Text = SitemapManager.GetInstance().GetNavigateString(_sitemap);
 
                  CurrentSitemapInfo         = _sitemap;
 
                  if (_sitemap.ViewControl != null)
                  {
                           Control template = LoadControl(_sitemap.ViewControl);
                           MainPlace.Controls.Clear();
                           MainPlace.Controls.Add(template);
                  }
         }
}
 









실행결과 화면은 다음과 같습니다.







이런 방식의 Sitemap 을 구성함으로써 다음과 같은 장점이 있습니다.






1.    
MasterPage 가 필요없음
2.     SiteMap 컨트롤 / SiteMapPath 컨트롤이 필요없음.

3.     불필요한 QueryString 이 필요없음

4.     Menu 컨트롤 등이 필요없음

5.     Sitemap 을 Dom 과 같은 형태로 탐색이 가능

6.     Atlas/Ajax 등을 활용하여 한 페이지로 동적인 사이트가 완성 가능


(사실 뭐좀 준비하고 있어요^^ㅋ)

 


이외에도 많은 장점을 가지고 있지만, 결정적인 단점은 직접 만들어야 한다는거~~ oTL
 




하지만 오늘 구성한 사이트맵이 가장 핵심이 되는 부분이므로, 핵심이 완성된다면 다른




부분은 쉽게 구현 가능 할 것입니다.



 
훔… 내가 쓰고 봐도 재미가 없네요 ㅋ




그럼 다가오는 크리스마스는 행복하게 보내시기 바랍니다^^//







※ UmcBlog 소스 ( http://umc.pe.kr/article_107.aspx ) 관리자 부분을 보시면 위의 패턴으로 구현이 되어있으니, 참고 하시기 바랍니다.


Posted by 땡초 POWERUMC

댓글을 달아 주세요

이번 시간에는 이미지에 워터마크를 찍어보는 시간을 갖겠습니다.
우선 이 내용이 이해가 가지 않는 분은
제네릭처리기를이용하여자동가입방지구현아티클을 선수학습 하세요~
 
오늘 알아볼 내용은 무척이나 간단하답니다.
 
우선 IIS 에서 자신의 웹사이트의 구성으로 가셔서 다음의 “.mark” 확장자를 매핑하세요
 
이제 web.config 의 httpHandlers 섹션에 다음의 구문을 추가해 줍니다
<addverb="*"path="*.mark"type="UmcMarkImageHandler, App_Code"></add>
Type 속성은 클래스이름(네임스페이스부터) 과 DLL 이름으로 구성되어 있습니다.
 
위의 UmcMarkImageHandler 는 IHttpHandler 만 상속받아 작성하시면 됩니다.
 
UmcMarkImageHandler.cs
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
 
///<summary>
/// UmcMarkImageHandler의 요약 설명입니다.
///</summary>
public class UmcMarkImageHandler : IHttpHandler
{
       public UmcMarkImageHandler()
       {
       }
 
       #region IHttpHandler 멤버
       public bool IsReusable
       {
             get
             {
                    return false;
             }
       }
 
       public void ProcessRequest(HttpContext context)
       {
             string filename = context.Request.Path;
             string fn = Path.GetFileName(filename).ToLower();
             string ext = Path.GetExtension(fn);
             // 확장자가 .mark 가 아니라면 걍 이미지를 뿌려준다.
             if (!(ext == ".mark"))
             {
                    System.Drawing.Image nonImg = System.Drawing.Image.FromFile(context.Server.MapPath(filename));
 
                    nonImg.Save(context.Response.OutputStream, ImageFormat.Jpeg);
 
                    nonImg.Dispose();
 
                    return;
             }
 
             // 이미지를 가져와서...
             System.Drawing.Image img = System.Drawing.Image.FromFile(context.Server.MapPath(filename));
             Bitmap bitmap = new Bitmap(img.Width, img.Height);
             Graphics g = Graphics.FromImage(bitmap);
 
             g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
             g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
 
             // 이 이미지는 합성될 이미지 입니다.
             System.Drawing.Image imgText = System.Drawing.Image.FromFile(context.Server.MapPath("mark.gif"));
 
             // 여기서부터 mark.gif 의 마크업 이미지에 투명도를 줘서 합성시킵니다.
             float[][] matrixItems ={
                                    new float[] {1, 0, 0, 0, 0},
                                    new float[] {0, 1, 0, 0, 0},                                      
                                    new float[] {0, 0, 1, 0, 0},
                                    new float[] {0, 0, 0, 0.8f, 0},                                          
                                    new float[] {0, 0, 0, 0, 1}};
             ColorMatrix colorMatrix = new ColorMatrix(matrixItems);
             ImageAttributes imageAtt = new ImageAttributes();
             imageAtt.SetColorMatrix(
                    colorMatrix,
                    ColorMatrixFlag.Default,
                    ColorAdjustType.Bitmap);
             g.DrawImage(imgText,
                    new Rectangle(0, 0, img.Width, img.Height),
                    0,
                    0,
                    imgText.Width,
                    imgText.Height,
                    GraphicsUnit.Pixel,
                    imageAtt);
             g.DrawImage(img,
                    new Rectangle(0, 0, img.Width, img.Height),
                    0,
                    0,
                    img.Width,
                    img.Height,
                    GraphicsUnit.Pixel,
                    imageAtt);
 
             bitmap.Save(context.Response.OutputStream, ImageFormat.Jpeg);
 
             g.Dispose();
             img.Dispose();
             imgText.Dispose();
             bitmap.Dispose();
       }
       #endregion
}
 
약간 소스가 쬐~~까~~ 긴감이 있네요~
 
여기서 mark.gif 이미지 만드는 법을 알려드리겠습니다.
포토샵에서 원하는 글자를 적으신 후 글자 이외의 배경은 투명하게 만드세요
 
Mark.gif
 
보이는 대로 위 이미지와 합성될 것입니다.
 
MarkImage.aspx
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>제목 없음</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
             두 이미지는 같은 이미지 입니다. 확장자만 바꾼결과 마크 이미지가 생겼습니다.<br>
             <br>
             image.jpg<br>
             <img src="image.jpg" />
             <p />
             image.mark<br>
             <img src="image.mark" />
    </div>
    </form>
</body>
</html>
 
이제 완성이 되었네요~
 
이 예제의 샘플을 보시려면

http://umc.pe.kr/Sample/MarkImage/marksample.umcx


근데 정확히 시간은 재보지 않았지만, 눈에 띄게 합성 속도가 느리더군요~
샘플로 사용된 이미지 자체가 뷁~ 스럽게 큰 이유도 있고,
이미지 출력을 JPG 압축하는 이유도 있겠습니다만..^^; 
한가지 팁… 확장자를 JPEG 또는 JPG 등으로 사용하시면 안됩니다.
브라우져가 먼저 이미지를 뿌려주지 않고, 닷넷이 먼저 확장자에 접근합니다.
 
이부분을 좀더 활용해 보시면 의외로 응용부분이 많을 겁니다^^
 
이런;;
 
다음번에는 이것보다 더 유익한거 하나 알려드릴께요^^
기대하세용~

Posted by 땡초 POWERUMC

댓글을 달아 주세요

ConfirmBitmapHandler.zip



정말 간만에 다시 글을 쓰게 되었네여~
세월아~ 네월아~~
오늘은 자동가입방지, 또는 자동댓글/글쓰기 방지를 위해 뭐좀 하나 만들어 보았습니다.
이번 방법은 제네릭 처리기를 이용하려고 합니다.
우선 다음과 같이 제네릭 처리기를 새항목 추가 합니다.
 
 

제네릭 처리기가 뭘까 해서 MSDN 을 보니…. 안나왔더군요~ ㅋ
우선 제네릭 처리기를 생성해 보기로 하겠습니다.
 
.. 단순히 IHttpHandler 를 상속하였네요~
친절하게도 Hello World 와 함께 말이져~
 
요놈은 IIS 에 C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll 과 같이 매핑이 되어있으며, C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\CONFIG\Web.Config 에
<add path="*.ashx" verb="*" type="System.Web.UI.SimpleHandlerFactory"
        validate="true" /> 과 같이 핸들러가 등록 되어 있군요~,
 
결론은 IHttpHandler 를 구현하라는 것이네요.
 
그럼 ConfirmBitmapHandler.ashx 의 소스를 보여드립니다.
<%@ WebHandler Language="C#" Class="ConfirmBitmapHandler" %>
 
using System;
using System.Web;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
using System.Web.SessionState;
 
public class ConfirmBitmapHandler : IHttpHandler, IRequiresSessionState {
   
    public void ProcessRequest (HttpContext context) {
        context.Response.ContentType = "image/jpeg";
              
               // 이미지사이즈
               int lenX = 80, lenY = 30;
              
               Bitmap bm              = new Bitmap( lenX, lenY );
              
               Graphics g             = Graphics.FromImage( bm );
              
               // 배경으로그라데이션처리
               LinearGradientBrush bgGr = new LinearGradientBrush(
                       new Point(0,0),
                       new Point(lenX, lenY),
                       Color.Blue,
                       Color.Black);
                             
               g.FillRectangle( bgGr, 0, 0, lenX, lenY );
              
               // 5자리숫자의난수를발생하여텍스트를만든다           
               string text = string.Empty;
               Random rnd = new Random();
               for(int i=0; i<5; i++)
               {
                       text += rnd.Next(9).ToString();
               }
              
               // 텍스트를그린다.
               Brush textBrush        = new SolidBrush( Color.White );
               g.DrawString( text, new Font("굴림", 15), textBrush, 10, 5, StringFormat.GenericDefault );
              
               // 스트림에비트맵을쓴다.
               bm.Save( context.Response.OutputStream, ImageFormat.Jpeg );
              
               // 5자리숫자를세션에담는다.
               context.Session["ValidateString"] = text;
    }
 
    public bool IsReusable {
        get {
            return false;
        }
    }
 
}
 
그럼 이제 우리가 만든 ConfirmBitmapHandler.ashx 를 브라우져 보기를 통해 실행해 보십시오
 
실행결과>>>
 
오오~~~~~ 짝짝짝~~ 잘 실행이 되었네여~
 
그럼 다음으로 방금 만든 전처리기를 이용하여 웹어플케이션을 만들어 봅시다.
 
소스 ( Default.aspx )
<%@ Page Language="C#" %>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 
<script runat="server">
 
        private void Page_Load(object sender, EventArgs e)
        {
        }
       
        protected void lnkValidate_Click(object sender, EventArgs e)
        {
               string validateString = (string)Session["ValidateString"] ?? string.Empty;
 
               if (txtValidateString.Text == validateString )
                       lblResult.Text = "오케이~~ 통과~!";
               else
                       lblResult.Text = "틀렷삼~~~~~ ";
        }
</script>
<script type="text/javascript">
        function checkEnter()
        {
               if( event.keyCode == 13 )
               {
                       __doPostBack('lnkValidate','');
               }
               else
                       return true;
        }
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>제목없음</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
               <img src="ConfirmBitmapHandler.ashx" />
               <p></p>
               <asp:TextBox ID="txtValidateString" runat="server" onkeypress="return checkEnter()"></asp:TextBox>&nbsp;&nbsp;&nbsp;
               <asp:LinkButton ID="lnkValidate" runat="server" OnClick="lnkValidate_Click">확인</asp:LinkButton>
               <p></p>
               <asp:Label ID="lblResult" runat="server"></asp:Label>
    </div>
    </form>
</body>
</html>
 
 
이 소스는 대부분 어렵지 않게 보실 수 있을 것 같아서 특별히 주석은 안달았습니다.
 
 
이것을 이용하여 다음엔 시간나는대로 이미지 도용방지 어플케이션을 작성해 보도록 할게여~
Posted by 땡초 POWERUMC

댓글을 달아 주세요

이 아티클은 Devpia.co.kr 에 제가 답변 등록한 내용입니다...
 


  2006-12-18 오후 9:38:43   /  번호: 4233  / 평점:  (-) category: General Discussions (일반 사항)  /  조회: 35 
 긴급) response.write의 문자열을 압축해서 찍기..  이종민 / mildblue  
이종민님께 메시지 보내기   
이종민님의 블로그 가기   
 response.write("aaaaaaaaaaaa")
했을대.. 실제로 "문자열"이 엄청 많다고 가정하고.
이걸 gzip로 압축된 문자열로 내보내는 명령이 무엇인가요?
 
response.write("aaa")이라고 해서 콘솔에서 웹서버에 연결해 호출해보면 전혀 압축되어 지지  않고 리턴이 됩니다.
 
jsp를 보면
void sendHTML( HttpServletResponse response , String zipHTML ) {        
 
    ServletOutputStream  svrOut = null ;
 
    BufferedOutputStream outStream = null ;
 
    GZIPOutputStream zipStream = null ;            
 
       try {
 
         response.setHeader("Content-Encoding","gzip");  
 
         svrOut = response.getOutputStream();  
 
         outStream =  new BufferedOutputStream( svrOut );           
 
         zipStream = new GZIPOutputStream(  outStream ) ;
 
         byte[] zipHTMLArray = zipHTML.getBytes();
 
         zipStream.write(  zipHTMLArray, 0, zipHTMLArray.length );      
 
         zipStream.flush();   
 
       } catch( Exception writeException ) {
 
         writeException.printStackTrace();
 
       } finally {
 
           try {
 
             if ( zipStream != null ) zipStream.close();         
 
} catch( Exception closeException ) {
 
            closeException.printStackTrace();
 
           }    
 
       }
 
  }
 
해서 압축하여 출력할수가 있는데.. asp.net에는 이렇게 하는 방법이 없는지요?
 
아무리 찾아도... 찾지를 못했습니다...
 
asp.net 2.0에 Imports System.IO.Compression 을 사용하면 되다는데. 이거는 도무지 파일을 압축하는 소스 밖에는 못 찾겠내요..
 
response.write 할때 값을 압출하는 방법을 즉..(text)를 바로 압축하여 string변수에 담을수 있는 방법을 가르쳐 주세요^^
 
이 글에 평점 주기:  
  2006-12-19 오후 5:51:06   /  번호: 4246  / 평점:  (-)  
 [답변]  엄준일 / umjunil  
엄준일님께 메시지 보내기   
엄준일님의 블로그 가기   
저도 첨 보는 클래스라서 연습삼아 해봤습니다. ^^;
 
private void Page_Load(object sender, EventArgs e)
        {
               Response.ContentType   = "gzip";
               Response.AddHeader("Content-Disposition", "attachment;filename=a.zip");
              
               FileStream fs          = new FileStream(@"C:\Documents and Settings\Administrator\My Documents\Visual Studio 2005\WebSites\MediaPlayer\intro.jpg", FileMode.Open);
              
               byte[] buffer          = new byte[fs.Length];
               fs.Read( buffer, 0, (int)fs.Length );
              
               fs.Close();
 
               MemoryStream ms               = new MemoryStream();
               GZipStream gs          = new GZipStream(ms, CompressionMode.Compress, true);
               gs.Write( buffer, 0, (int)buffer.Length );
 
 
               Response.Clear();
               Response.BinaryWrite( ms.GetBuffer() );
               Response.End();
              
               gs.Close();
               ms.Close();
        }
 
이런식으로 이미지 파일을 압축하여 a.zip 이라는 파일로 다운 받을 수 있습니다.
 
근데 압축 파일 열어보면 확장자도 없는 a 라는 파일이 달랑 하나 들어가 있는데..
 
이거 확장자를 다시 .jpg 로 바꾸어 주시면 됩니다.
 
위에 AddHeader를 빼시면 압축 내용을 그냥 화면에다가 뿌려줄 수도 있구요....
 
텍스트 압축도 위와 별단 다를거 없다고 생각합니다. 텍스트르 byte 배열로 넣어주시면 되구요
 
( JPG 압축하니 용량이 더 커지네요 ㅋ^^; )
이 글에 평점 주기:  




Posted by 땡초 POWERUMC

댓글을 달아 주세요

 
이번에는 Ajax.Net ( 이하 Ajax ) 이 제시한 두가지 콜백 방법의 차이에 대해서 알아봅시다.
예제에 필요한 DLL 을 다운 받기 위해서 다음의 사이트에서 다운 받으실수 있습니다.
 
다운받은 DLL 을 프로젝트에 참조시킵니다.
 
다음은 Web.Config 파일에 다음의 구문을 추가 합니다.
<system.web>
<httpHandlers>
<addverb="*"path="*.ashx"type="AjaxPro.AjaxHandlerFactory,AjaxPro.2"/>
</httpHandlers>
</system.web>
 
이제 기본 설정은 모두 마쳤습니다.
 
이번에도 여과없이 예제 샘플부터 보도록 하겠습니다.
publicpartialclassAjaxNet : System.Web.UI.Page
{
        protectedvoid Page_Load(object sender, EventArgs e)
        {
               AjaxPro.Utility.RegisterTypeForAjax( typeof(AjaxNet) );
 
               TextBox1.Attributes["OnKeyPress"]    = txtChange1(this)";
               TextBox2.Attributes["OnKeyPress"]    = txtChange2(this)";
        }
 
        [AjaxPro.AjaxMethod]
        publicstring GetMessage()
        {
               System.Threading.Thread.Sleep(1000);
               return"메서드가 호출되었습니다";
        }
} 
 
AjaxPro.Utility.RegisterTypeForAjax( typeof(AjaxNet) ); 의 구문으로 비동기 호출 대상 클래스를 등록할 수 있습니다. This.GetType() 과 같이 지정하신다면 정상적으로 작동되지 않습니다.
 
TextBox1.Attributes["OnKeyPress"]    = "txtChange1(this)";
TextBox2.Attributes["OnKeyPress"]    = "txtChange2(this)";
 
TextBox 에 밑의 html 소스에 포함되는 자바스크립트를 이벤트에 연결합니다.
 
GetMessage() 메서드는 콜백 받기 위한 메서드로 호출되면 1초의 쓰레드 Sleep을 주고 string을 리턴합니다
 
HTML 소스는 다음의 같습니다.
 
<headrunat="server">
    <title>제목 없음</title>
    <scripttype="text/javascript">
               function txtChange1( obj )
               {
                       if( obj.value.length == 9 )
                       {
                              alert( AjaxNet.GetMessage().value );
                       }
               }             
               function txtChange2( obj )
               {
                       if( obj.value.length == 9 || obj.value.length == 10)
                       {
                              AjaxNet.GetMessage(txtChange2_Callback);
                       }
               }             
               function txtChange2_Callback( response )
               {
                       var obj = response.value;
                       if( obj != null && typeof(obj) == "string" )
                       {
                              alert( obj );
                       }
               }
    </script>
</head>
<body>
    <formid="form1"runat="server">
    <div>
               <asp:TextBoxID="TextBox1"runat="server"MaxLength="100"></asp:TextBox>
               <p></p>
               <asp:TextBoxID="TextBox2"runat="server"MaxLength="100"></asp:TextBox>
    </div>
    </form>
</body>
 
 
(OnKeyPress 는 키보드가 눌려지는순간 발생하므로 10번째 텍스트부터 콜백이 일어납니다)
소스가 완성되었다면 각각 텍스트 박스에서 아무 알파벳을 꾸욱 누르고 계셔 보세요.
 
1. AjaxNet.GetMessage().value
 이 메서드가 호출되는 순간 스레가 1초 쉬고, 리턴받은string 을 보여줍니다.
 보시는 바와 같이 콜백이 모두 완료될때까지 클라이언트는 아무 것도 못합니다.
 마치, 컴퓨터가 잠깐 먹통이 된 듯 콜백이 완전히 종료되야 다음 텍스트가 눌러지는
 것을 확인할 수 있습니다.
 
2. AjaxNet.GetMessage(txtChange2_Callback);
 두번째 텍스트 박스의 함수는 Callback 과 매핑되어 있습니다.
 이상하게 1번 방법의 호출과는 상당히 다른 결과를 보여줍니다.
 눈치빠른 분이라면 잠깐 멈찟 하는듯 하나 키보드로 누른 텍스트는 이내 쭈욱 써지는
 것을 확인할 수 있습니다.
 
 
 이렇게 상이한 결과에 대해 많이 궁금하여 여러 문서를 검색해 보았으나, 그 이상의 해답을 찾지 못하였습니다.^^;
추측건데, 2번 경우의 호출은 단지 호출만 할뿐 Callback 에 대해선 txtChange2_Callback에게 위임하게 되고, 1번의 경우 호출과의 매핑된 콜백이 없기 때문에, 콜백이 완료되기 전까지 무한 대기 상태에 빠지는 듯 합니다.
 
다음에 또 기회가 된다면 보안적인 요소가 강화된 AjaxPro 에 추가된 AjaxEnum,AjaxProperty, 캐시,및 세션 다루는 방법에 대해 알아보도록 하겠습니다.
(기회가 된다면… ^^;; )
소스코드 첨부 하였습니다~
 
재미없는 글 읽어주셔서 감사합니다^^//

'.NET > AJAX' 카테고리의 다른 글

Ajax.Net 으로 DataSet 컬렉션 제어하기  (0) 2007.07.08
AJAX 로 구현한 윈도우  (0) 2007.04.06
Ajax.Net 의 두가지 콜백 방법의 차이  (0) 2007.04.05
채팅방 베타  (0) 2007.04.05
Ajax 를 활용한 간단한 채팅 로직  (0) 2007.04.05
Posted by 땡초 POWERUMC

댓글을 달아 주세요



너무 허접하게 만들어서 공개를 할까 말까 많이 망설여 지네여

AJAX이 뭔지 막 접한 분들을 위한 소스이니,

참고만 하세여.. 별 도움 될랑가 몰겟네~~


'.NET > AJAX' 카테고리의 다른 글

Ajax.Net 으로 DataSet 컬렉션 제어하기  (0) 2007.07.08
AJAX 로 구현한 윈도우  (0) 2007.04.06
Ajax.Net 의 두가지 콜백 방법의 차이  (0) 2007.04.05
채팅방 베타  (0) 2007.04.05
Ajax 를 활용한 간단한 채팅 로직  (0) 2007.04.05
Posted by 땡초 POWERUMC

댓글을 달아 주세요