using System;
using System.Diagnostics;
using System.Threading;
using System.Windows.Forms;
namespace TestLibrary
{
/// <summary>
/// 후킹 베이스
/// </summary>
/// <typeparam name="TMessage">메시지 타입</typeparam>
public abstract class HookBase<TMessage>
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Protected
#region Field
/// <summary>
/// 후킹 핸들
/// </summary>
protected int hookHandle = 0;
#endregion
////////////////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// 메시지 차단 큐
/// </summary>
private BlockingQueue<TMessage> messageBlockingQueue = new BlockingQueue<TMessage>();
/// <summary>
/// 메시지 쓰레드
/// </summary>
private Thread messageThread = null;
/// <summary>
/// 잠금 객체
/// </summary>
private Object lockObject = new Object();
/// <summary>
/// 후킹 대리자
/// </summary>
private WIN32Helper.HookDelegate hookDelegate = null;
/// <summary>
/// 유효 여부
/// </summary>
private bool isValid = true;
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Property
////////////////////////////////////////////////////////////////////////////////////////// Protected
#region 후킹 ID - HookID
/// <summary>
/// 후킹 ID
/// </summary>
protected abstract int HookID { get; }
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - HookBase()
/// <summary>
/// 생성자
/// </summary>
public HookBase()
{
this.hookDelegate = new WIN32Helper.HookDelegate(ProcessHook);
Application.ApplicationExit += Application_ApplicationExit;
this.messageThread = new Thread(ProcessMessageThread)
{
Name = "CommonHook",
IsBackground = true
};
this.messageThread.Start();
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 후킹 시작하기 - StartHook()
/// <summary>
/// 후킹 시작하기
/// </summary>
/// <returns>처리 결과</returns>
public bool StartHook()
{
lock(this.lockObject)
{
if(hookHandle != 0)
{
return false;
}
this.hookHandle = WIN32Helper.SetWindowsHookEx
(
HookID,
this.hookDelegate,
HookBase<TMessage>.GetCurrentModuleHandle(),
0
);
return (hookHandle != 0);
}
}
#endregion
#region 후킹 중단하기 - StopHook()
/// <summary>
/// 후킹 중단하기
/// </summary>
public void StopHook()
{
lock(this.lockObject)
{
if(this.hookHandle == 0)
{
return;
}
WIN32Helper.UnhookWindowsHookEx(hookHandle);
this.hookHandle = 0;
}
}
#endregion
#region 리소스 해제하기 - Dispose()
/// <summary>
/// 리소스 해제하기
/// </summary>
public void Dispose()
{
this.isValid = false;
StopHook();
Application.ApplicationExit -= Application_ApplicationExit;
}
#endregion
////////////////////////////////////////////////////////////////////////////////////////// Protected
#region 후킹 처리하기 - ProcessHook(code, wordParameter, longParameter)
/// <summary>
/// 후킹 처리하기
/// </summary>
/// <param name="code">코드</param>
/// <param name="wordParameter">WORD 매개 변수</param>
/// <param name="longParameter">LONG 매개 변수</param>
/// <returns>처리 결과</returns>
protected virtual int ProcessHook(int code, IntPtr wordParameter, IntPtr longParameter)
{
if(code >= 0)
{
TMessage message = ParseMessage(wordParameter, longParameter);
this.messageBlockingQueue.Enqueue(message);
}
return WIN32Helper.CallNextHookEx(this.hookHandle, code, wordParameter, longParameter);
}
#endregion
#region 메시지 후킹시 처리하기 - OnMessageHooked(message)
/// <summary>
/// 메시지 후킹시 처리하기
/// </summary>
/// <param name="message">메시지</param>
protected abstract void OnMessageHooked(TMessage message);
#endregion
#region 메시지 파싱하기 - ParseMessage(wordParameter, longParameter)
/// <summary>
/// 메시지 파싱하기
/// </summary>
/// <param name="wordParameter">WORD 매개 변수</param>
/// <param name="longParameter">LONG 매개 변수</param>
/// <returns>메시지</returns>
protected abstract TMessage ParseMessage(IntPtr wordParameter, IntPtr longParameter);
#endregion
////////////////////////////////////////////////////////////////////////////////////////// Private
//////////////////////////////////////////////////////////////////////////////// Event
#region 애플리케이션 종료시 처리하기 - Application_ApplicationExit(sender, e)
/// <summary>
/// 애플리케이션 종료시 처리하기
/// </summary>
/// <param name="sender">이벤트 발생자</param>
/// <param name="e">이벤트 인자</param>
private void Application_ApplicationExit(object sender, EventArgs e)
{
Dispose();
}
#endregion
//////////////////////////////////////////////////////////////////////////////// Function
#region 현재 모듈 핸들 구하기 - GetCurrentModuleHandle()
/// <summary>
/// 현재 모듈 핸들 구하기
/// </summary>
/// <returns>현재 모듈 핸들</returns>
private static IntPtr GetCurrentModuleHandle()
{
using(Process process = Process.GetCurrentProcess())
{
using(ProcessModule processModule = process.MainModule)
{
return WIN32Helper.GetModuleHandle(processModule.ModuleName);
}
}
}
#endregion
#region 메시지 쓰레드 처리하기 - ProcessMessageThread()
/// <summary>
/// 메시지 쓰레드 처리하기
/// </summary>
private void ProcessMessageThread()
{
while(this.isValid)
{
TMessage message = this.messageBlockingQueue.Dequeue();
OnMessageHooked(message);
}
}
#endregion
}
}