<?xml version="1.0" encoding="utf-8" ?>
<!-- // RSS2.0 -->
<rss version="2.0" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/">
<channel>
  <title><![CDATA[Delphi :: KURAPA.COM]]></title>
  <link><![CDATA[http://kurapa.com/xml-s192]]></link>
  <description><![CDATA[지앙춘윈(姜春云)의 종합 엔터테인먼트 서비스]]></description>
  <language>ko</language>
  <pubDate>2008-12-04T06:18:37+00:00</pubDate>
  <generator>KURAPA.COM RSS Gen v1.0</generator>
  <item>
    <title><![CDATA[태스크바에서 어플리케이션 숨기는 방법]]></title>
    <link>http://kurapa.com/content-a11570</link>
    <description><![CDATA[어플리케이션을 태스크바에서 숨기려면 다음의 함수를 사용한다.<br><br>ShowWindow(Application.Handle, SW_HIDE);<br><br>다시 보여주고 싶으면 다음의 함수를 사용한다.<br><br>ShowWindow(Application.Handle, SW_SHOW);]]></description>
    <category><![CDATA[Taskbar]]></category>
    <category><![CDATA[숨기기]]></category>
    <category><![CDATA[태스크바]]></category>
    <author><![CDATA[지앙춘윈]]></author>
    <guid><![CDATA[http://kurapa.com/content-a11570]]></guid>
    <pubDate>2008-10-09T04:30:20+00:00</pubDate>
  </item>
  <item>
    <title><![CDATA[TComboBox Readonly로 만드는 방법]]></title>
    <link>http://kurapa.com/content-a4840</link>
    <description><![CDATA[기본적으로 TComboBox는 Readonly를 지원하지 않는다.<br><br>윈도우 메시지로&nbsp; Fake하는 방법이 있어 소개한다.<br><br><br>TComboBox를 ReadOnly로 하려면 다음의 명령을 실행해주면 된다.<br><br><span style="font-family: Times New Roman; font-size: 12pt; color: rgb(0, 0, 0);"><span style="font-family: Courier New; font-size: 9pt; color: rgb(0, 0, 0);">SendMessage(GetWindow(ComboBox1.Handle,GW_CHILD),&nbsp;EM_SETREADONLY,&nbsp;<span style="font-family: Courier New; font-size: 9pt; color: rgb(128, 0, 0);">1</span></span></span><span style="font-family: Courier New; font-size: 9pt; color: rgb(0, 0, 0);">,&nbsp;<span style="font-family: Courier New; font-size: 9pt; color: rgb(128, 0, 0);">0</span></span><span style="font-family: Courier New; font-size: 9pt; color: rgb(0, 0, 0);">);<br><br><br><br>다시 원상복구 하려면 다음의 명령을 실행해 주기만 하면 된다.<br><br></span><span style="font-family: Courier New; font-size: 8pt; color: rgb(0, 0, 0);">SendMessage(GetWindow(ComboBox1.Handle,GW_CHILD),&nbsp;EM_SETREADONLY,&nbsp;<span style="font-family: Courier New; font-size: 8pt; color: rgb(128, 0, 0);">0</span></span><span style="font-family: Courier New; font-size: 8pt; color: rgb(0, 0, 0);">,&nbsp;<span style="font-family: Courier New; font-size: 8pt; color: rgb(128, 0, 0);">0</span></span><span style="font-family: Courier New; font-size: 8pt; color: rgb(0, 0, 0);">); </span><br />]]></description>
    <category><![CDATA[TComboBox]]></category>
    <category><![CDATA[ReadOnly]]></category>
    <author><![CDATA[췌엠]]></author>
    <guid><![CDATA[http://kurapa.com/content-a4840]]></guid>
    <pubDate>2007-08-09T01:31:36+00:00</pubDate>
  </item>
  <item>
    <title><![CDATA[델파이에서 URL Encoding 하는 방법]]></title>
    <link>http://kurapa.com/content-a4823</link>
    <description><![CDATA[다음의 함수를 사용해보라.<br><br><pre class="delphi"><span class="kw1">function</span> URLEncode<span class="br0">(</span><span class="kw2">const</span> S: <span class="kw4">string</span><span class="br0">)</span>: <span class="kw4">string</span>;<br><span class="kw2">var</span><br>  Idx: <span class="kw4">Integer</span>; <span class="co1">// loops thru characters in string</span><br><span class="kw1">begin</span><br>  Result := <span class="st0">''</span>;<br>  <span class="kw1">for</span> Idx := <span class="nu0">1</span> <span class="kw1">to</span> <span class="kw3">Length</span><span class="br0">(</span>S<span class="br0">)</span> <span class="kw1">do</span><br>  <span class="kw1">begin</span><br>    <span class="kw1">if</span> S<span class="br0">[</span>Idx<span class="br0">]</span> <span class="kw1">in</span> <span class="br0">[</span><span class="st0">'A'</span>..<span class="st0">'Z'</span>, <span class="st0">'a'</span>..<span class="st0">'z'</span>, <span class="st0">'0'</span>..<span class="st0">'9'</span>, <span class="st0">'-'</span>, <span class="st0">'='</span>, <span class="st0">'&amp;'</span>, <span class="st0">':'</span>, <span class="st0">'/'</span>, <span class="st0">'?'</span>, <span class="st0">';'</span>, <span class="st0">'_'</span>, <span class="st0">'.'</span><span class="br0">]</span> <span class="kw1">then</span><br>        Result := Result + S<span class="br0">[</span>Idx<span class="br0">]</span><br>    <span class="kw1">else</span><br>      Result := Result + <span class="st0">'%'</span> + <span class="kw3">IntToHex</span><span class="br0">(</span><span class="kw3">Ord</span><span class="br0">(</span>S<span class="br0">[</span>Idx<span class="br0">]</span><span class="br0">)</span>, <span class="nu0">2</span><span class="br0">)</span>;<br>  <span class="kw1">end</span>;<br><span class="kw1">end</span>;</pre><br />]]></description>
    <category><![CDATA[urlencode]]></category>
    <author><![CDATA[췌엠]]></author>
    <guid><![CDATA[http://kurapa.com/content-a4823]]></guid>
    <pubDate>2007-08-08T08:05:27+00:00</pubDate>
  </item>
  <item>
    <title><![CDATA[한글을 유니코드 또는 UTF-8포멧으로 변환하는 방법]]></title>
    <link>http://kurapa.com/content-a4821</link>
    <description><![CDATA[<font class="list_han">다음 함수를 확인해 보십시오.<br><br>function UnicodeToUtf8(Dest: PChar; Source: PWideChar; MaxBytes: Integer): Integer; overload; deprecated;<br><br>function Utf8ToUnicode(Dest: PWideChar; Source: PChar; MaxChars: Integer): Integer; overload; deprecated;<br><br>function UnicodeToUtf8(Dest: PChar; MaxDestBytes: Cardinal; Source: PWideChar; SourceChars: Cardinal): Cardinal; overload;<br><br>function Utf8ToUnicode(Dest: PWideChar; MaxDestChars: Cardinal; Source: PChar; SourceBytes: Cardinal): Cardinal; overload;<br><br>function UTF8Encode(const WS: WideString): UTF8String;<br><br>function UTF8Decode(const S: UTF8String): WideString;<br><br>function AnsiToUtf8(const S: string): UTF8String;<br><br>function Utf8ToAnsi(const S: UTF8String): string;</font>]]></description>
    <category><![CDATA[유니코드]]></category>
    <category><![CDATA[UTF-8]]></category>
    <category><![CDATA[UTF8String]]></category>
    <category><![CDATA[UTF8Encode]]></category>
    <category><![CDATA[UTF8Decode]]></category>
    <author><![CDATA[췌엠]]></author>
    <guid><![CDATA[http://kurapa.com/content-a4821]]></guid>
    <pubDate>2007-08-08T07:26:57+00:00</pubDate>
  </item>
  <item>
    <title><![CDATA[훅 인스턴스의 생성과 해제]]></title>
    <link>http://kurapa.com/content-a2249</link>
    <description><![CDATA[최근 본인은 이벤트를 감시하기 위해 훅을 사용하는 어플리케이션을 개발하고 있었다. 훅은 정말로 많은 작업에 있어서 유용하지만, 델파이에서는 직접적으로 지원되지 않기 때문에, 다음과 같이 Win32 API 호출을 사용해야 한다.<BR><P><BR><BR><FONT size=3><BR><UL><BR><TT>var</TT><BR><BR><TT>&nbsp; ThisHook : hHook;</TT><BR><BR><TT>.....</TT><BR><BR><TT>&nbsp; ThisHook := SetWindowsHookEx (WH_CALLWNDPROC,<BR>HookProc, HInstance, GetCurrentThreadID);</TT><BR></UL><BR></FONT><BR><BR><FONT size=2>이것은 WH_CALLWNDPORC 타입의 훅을 현재 어플리케이션의 인스턴스와 쓰레드에 걸어준다. 훅이 발생할 때 HookProc를 호출하는데, 그것의 다음과 같이 선언되어 있다.<BR><BR><BR><BR></FONT><UL><TT><FONT size=3>function HookProc (nCode: Integer; wParam, lParam:<BR>Longint): Longint; stdcall;</TT><BR><BR><TT>begin</TT><BR><BR><TT>//.... my code goes here</TT><BR><BR><TT>&nbsp; Result := CallNextHookEx (ThisHook, nCode,<BR>wParam, lParam);</TT><BR><BR><TT>//.... or goes in here</TT><BR><BR><TT>end;</TT></UL><BR></FONT><BR><BR><FONT size=2>여러분들은 볼랜드의 FORMS.PAS내에서 사용되는 이 호출들의 예를 발견할 수 있을 것이다. 또한 SetWindowsHookEx를 WIN32.HLP 파일에서 발견할 수 있으며 사용한 가능한 훅의 타입들에 대해서 볼 수 있을 것이다. 그러나, 이 예제에서 보듯이 훅을 사용하기 위해서는 전역 변수와 독립적으로 떨어져 나와 있는 함수를 가져야 한다는 것을 알아챘을 것이다. 이것은 동일한 어플리케이션내에서 여래개의 훅을 걸 때나, FORMS.PAS내의 경우와 같이 전역변수를 사용하지 않고 클래스내의 메소드나 변수를 참조하고 싶을 경우에는 문제가 된다.<BR></FONT><P><BR>동일한 문제가 클래스의 내부에 '윈도우 메세지 프로시져'를 작성하기를 원할 때 발생한다. 그러나, 볼랜드사는 클래스내의 '메소드'가 '윈도우 메세지 프로시져' 가 될 수 있도록 MakeObjectInstance(그리고 거기에 대응하는 FreeObjectInstace)를 제공해 주었다. 그래서, 본인은 동일한 기법으로 훅 함수로 메소드를 사용할 수 있지 않을까 하고 짐작했다. 그 결과가 되는 코드는 MakeObjectInstance를 위한 볼랜드가 제공했던 코드를 사용했지만 훅에 적절하도록 손을 봤다. 볼랜드의 원래 코드(Forms.Pas내에 있는 코드)에 약간의 변경을 가한 코드를 다음과 같이 제시한다.<BR><BR><FONT size=3><BR><UL><TT>unit</TT><BR><BR><TT>&nbsp; HookInst;</TT><TT></TT><BR><BR><P><TT>interface</TT><TT></TT><BR><BR><P><TT>uses</TT><BR><BR><TT>&nbsp; Windows;</TT><TT></TT><BR><BR><P><TT>type</TT><BR><BR><TT>&nbsp; THookCall = packed record</TT><BR><BR><TT>&nbsp;&nbsp;&nbsp; Code&nbsp;&nbsp; : integer;</TT><BR><BR><TT>&nbsp;&nbsp;&nbsp; WParam : WPARAM;</TT><BR><BR><TT>&nbsp;&nbsp;&nbsp; LParam : LPARAM;</TT><BR><BR><TT>&nbsp;&nbsp;&nbsp; Result : LResult</TT><BR><BR><TT>&nbsp; end;</TT><TT></TT><BR><BR><P><TT>&nbsp; THookMethod = procedure (var HookCall: THookCall)<BR>of object;</TT><TT></TT><BR><BR><P><TT>function&nbsp; MakeHookInstance (Method: THookMethod):<BR>pointer;</TT><BR><BR><TT>procedure FreeHookInstance (ObjectInstance: pointer);</TT><TT></TT><BR><BR><P><TT>implementation</TT><TT></TT><BR><BR><P><TT>const</TT><BR><BR><TT>&nbsp; InstanceCount = 313;&nbsp; // set so that<BR>sizeof (TInstanceBlock) &lt; PageSize</TT><TT></TT><BR><BR><P><TT>type</TT><BR><BR><TT>&nbsp; PObjectInstance = ^TObjectInstance;</TT><BR><BR><TT>&nbsp; TObjectInstance = packed record</TT><BR><BR><TT>&nbsp;&nbsp;&nbsp; Code: Byte;</TT><BR><BR><TT>&nbsp;&nbsp;&nbsp; Offset: Integer;</TT><BR><BR><TT>&nbsp;&nbsp;&nbsp; case Integer of</TT><BR><BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0: (Next: PObjectInstance);</TT><BR><BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1: (Method: THookMethod);</TT><BR><BR><TT>&nbsp; end;</TT><TT></TT><BR><BR><P><TT>type</TT><BR><BR><TT>&nbsp; PInstanceBlock = ^TInstanceBlock;</TT><BR><BR><TT>&nbsp; TInstanceBlock = packed record</TT><BR><BR><TT>&nbsp;&nbsp;&nbsp; Next: PInstanceBlock;</TT><BR><BR><TT>&nbsp;&nbsp;&nbsp; Code: array[1..2] of Byte;</TT><BR><BR><TT>&nbsp;&nbsp;&nbsp; WndProcPtr: Pointer;</TT><BR><BR><TT>&nbsp;&nbsp;&nbsp; Instances: array[0..InstanceCount]<BR>of TObjectInstance;</TT><BR><BR><TT>&nbsp; end;</TT><TT></TT><BR><BR><P><TT>var</TT><BR><BR><TT>&nbsp; InstBlockList : PInstanceBlock&nbsp; = nil;</TT><BR><BR><TT>&nbsp; InstFreeList&nbsp; : PObjectInstance = nil;</TT><TT></TT><BR><BR><P><TT>function StdHookProc (Code, WParam: WPARAM; LParam:<BR>LPARAM): LResult; stdcall; assembler;</TT><BR><BR><TT>asm</TT><BR><BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; XOR&nbsp;&nbsp;&nbsp;&nbsp;<BR>EAX,EAX</TT><BR><BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PUSH&nbsp;&nbsp;&nbsp;<BR>EAX</TT><BR><BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PUSH&nbsp;&nbsp;&nbsp;<BR>LParam</TT><BR><BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PUSH&nbsp;&nbsp;&nbsp;<BR>WParam</TT><BR><BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PUSH&nbsp;&nbsp;&nbsp;<BR>Code</TT><BR><BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MOV&nbsp;&nbsp;&nbsp;&nbsp;<BR>EDX,ESP</TT><BR><BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MOV&nbsp;&nbsp;&nbsp;&nbsp;<BR>EAX,[ECX].Longint[4]</TT><BR><BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CALL&nbsp;&nbsp;&nbsp;<BR>[ECX].Pointer</TT><BR><BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ADD&nbsp;&nbsp;&nbsp;&nbsp;<BR>ESP,12</TT><BR><BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; POP&nbsp;&nbsp;&nbsp;&nbsp;<BR>EAX</TT><BR><BR><TT>end;</TT><TT></TT><BR><BR><P><TT>{ Allocate a hook method instance }</TT><TT></TT><BR><BR><P><TT>function CalcJmpOffset(Src, Dest: Pointer): Longint;</TT><BR><BR><TT>begin</TT><BR><BR><TT>&nbsp; Result := Longint(Dest) - (Longint(Src) +<BR>5);</TT><BR><BR><TT>end;</TT><TT></TT><BR><BR><P><TT>function MakeHookInstance(Method: THookMethod): Pointer;</TT><BR><BR><TT>const</TT><BR><BR><TT>&nbsp; BlockCode: array [1..2] of Byte = ($59, $E9);</TT><BR><BR><TT>&nbsp; PageSize = 4096;</TT><BR><BR><TT>var</TT><BR><BR><TT>&nbsp; Block: PInstanceBlock;</TT><BR><BR><TT>&nbsp; Instance: PObjectInstance;</TT><BR><BR><TT>begin</TT><BR><BR><TT>&nbsp; if InstFreeList = nil then</TT><BR><BR><TT>&nbsp; begin</TT><BR><BR><TT>&nbsp;&nbsp;&nbsp; Block := VirtualAlloc (nil, PageSize,<BR>MEM_COMMIT, PAGE_EXECUTE_READWRITE);</TT><BR><BR><TT>&nbsp;&nbsp;&nbsp; Block^.Next := InstBlockList;</TT><BR><BR><TT>&nbsp;&nbsp;&nbsp; Move(BlockCode, Block^.Code, SizeOf(BlockCode));</TT><BR><BR><TT>&nbsp;&nbsp;&nbsp; Block^.WndProcPtr := Pointer(CalcJmpOffset(@Block^.Code[2],<BR>@StdHookProc));</TT><BR><BR><TT>&nbsp;&nbsp;&nbsp; Instance := @Block^.Instances;</TT><BR><BR><TT>&nbsp;&nbsp;&nbsp; repeat</TT><BR><BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Instance^.Code :=<BR>$E8;</TT><BR><BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Instance^.Offset :=<BR>CalcJmpOffset(Instance, @Block^.Code);</TT><BR><BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Instance^.Next :=<BR>InstFreeList;</TT><BR><BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; InstFreeList := Instance;</TT><BR><BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Inc(Longint(Instance),<BR>SizeOf(TObjectInstance));</TT><BR><BR><TT>&nbsp;&nbsp;&nbsp; until Longint(Instance) - Longint(Block)<BR>&gt;= SizeOf(TInstanceBlock);</TT><BR><BR><TT>&nbsp;&nbsp;&nbsp; InstBlockList := Block</TT><BR><BR><TT>&nbsp; end;</TT><BR><BR><TT>&nbsp; Result := InstFreeList;</TT><BR><BR><TT>&nbsp; Instance := InstFreeList;</TT><BR><BR><TT>&nbsp; InstFreeList := Instance^.Next;</TT><BR><BR><TT>&nbsp; Instance^.Method := Method</TT><BR><BR><TT>end;</TT><TT></TT><BR><BR><P><TT>{ Free a hook method instance }</TT><TT></TT><BR><BR><P><TT>procedure FreeHookInstance (ObjectInstance: Pointer);</TT><BR><BR><TT>begin</TT><BR><BR><TT>&nbsp; if ObjectInstance &lt;&gt; nil then</TT><BR><BR><TT>&nbsp; begin</TT><BR><BR><TT>&nbsp;&nbsp;&nbsp; PObjectInstance(ObjectInstance)^.Next<BR>:= InstFreeList;</TT><BR><BR><TT>&nbsp;&nbsp;&nbsp; InstFreeList := ObjectInstance</TT><BR><BR><TT>&nbsp; end</TT><BR><BR><TT>end;</TT><TT></TT><BR><BR><P><TT>end.</TT></P></UL><BR></FONT><BR><BR><FONT size=2>유닛의 문서화가 잘되지 못해 유감이다. 본인도 어떤 원리로 작동하고 있는지는 이해했지만, 이 코드를 완벽하게 이해하지는 못했다. 하지만 제대로 동작을 한다. <BR><BR><BR></FONT><P><B>어떻게 사용할 것인가?</B><BR><BR><P>여러분이 클래스내에서 WH_CALLWNDPROC를 사용하기를 원한다고 하자. 여러분의 훅 함수, 포인터 변수(메소드에 대한 포인터가 될 것임), 그리고 훅 핸들(훅 이벤트를 올바르게 연결할 수 있기 위해서임)을 선언하라.<BR><BR><UL><TT><FONT size=2>type</FONT></TT><BR><BR><TT><FONT size=2>&nbsp; TMyClass = class (TWhat)</FONT></TT><BR><BR><TT><FONT size=2>....</FONT></TT><BR><BR><TT><FONT size=2>&nbsp; private</FONT></TT><BR><BR><TT><FONT size=2>&nbsp;&nbsp;&nbsp; ThisHook : hHook;</FONT></TT><BR><BR><TT><FONT size=2>&nbsp;&nbsp;&nbsp; HookProc : pointer;</FONT></TT><BR><BR><TT><FONT size=2>....</FONT></TT><BR><BR><TT><FONT size=2>&nbsp;&nbsp; function MyHookProc (var HookCall: THookCall);</FONT></TT><BR><BR><TT><FONT size=2>....</FONT></TT><BR><BR><TT><FONT size=2>&nbsp; end;</FONT></TT></UL><BR><BR>그리고, 클래스의 생성자에서 클래스 메소드에 대한 포인터를 만들고, API 함수를 호출하여 훅을 구동시킨다. <BR><BR><UL><TT><FONT size=2>&nbsp; HookProc := MakeHookInstance (MyHookProc);</FONT></TT><BR><BR><TT><FONT size=2>&nbsp; ThisHook := SetWindowsHookEx (WH_CALLWNDPROC, HookProc, HInstance, GetCurrentThreadID);</FONT></TT></UL><BR><BR>그리고, 다음단계로 훅 함수를 선언해야 하는데, 요것은 클래스의 메소드가 된다. :)<BR><BR><UL><TT><FONT size=2>function TMyClass.MyHookProc (var HookCall: THookCall);</FONT></TT><BR><BR><TT><FONT size=2>begin</FONT></TT><BR><BR><TT><FONT size=2>....</FONT></TT><BR><BR><TT><FONT size=2>&nbsp; with HookCall do</FONT></TT><BR><BR><TT><FONT size=2>&nbsp;&nbsp;&nbsp; Result := CallNextHookEx (ThisHook,<BR>Code, wParam, lParam);</FONT></TT><BR><BR><TT><FONT size=2>....</FONT></TT><BR><BR><TT><FONT size=2>end;</FONT></TT></UL><BR><BR>마지막으로 소멸자에서 훅에 대한 해제 작업을 한다.<BR><BR><UL><TT><FONT size=2>&nbsp; UnhookWindowsHookEx (ThisHook);</FONT></TT><BR><BR><TT><FONT size=2>&nbsp; FreeHookInstance (HookProc);</FONT></TT></UL><BR><BR><BR><P><BR><B>그래서, 결론을 내리면... </B><BR><BR><P>전역 변수는 없다! 독립적으로 떨어져 나와 있는 훅 함수도 없다! 훅 함수는 쉽게 클래스 메소드를 참조할 수 있다.<BR></P>]]></description>
    <category><![CDATA[]]></category>
    <author><![CDATA[췌엠]]></author>
    <guid><![CDATA[http://kurapa.com/content-a2249]]></guid>
    <pubDate>2007-02-05T07:36:38+00:00</pubDate>
  </item>
  <item>
    <title><![CDATA[virtual와 override 문의 사용법에 대해서]]></title>
    <link>http://kurapa.com/content-a2248</link>
    <description><![CDATA[<P>객체지향에서 일종의 불문율같은 코딩 방식이 있습니다. 컴파일러가 반드시 그렇게 코딩해야 한다고 강제하지는 않으나, 반드시 그렇게 코딩하는 것이 옳다고 하는 방식입니다. 이는 오랜 동안 객체지향 기법을 연구한 끝에 여러 개발자들이 동의하는 사항들입니다. 그런 불문율 중에 몇가지를 논해 보려 합니다.</P><P>------------------------------------------------------------<BR>먼저 상위 클래스 메서드를 재정의할 때 사용하는 override문을 언제 사용하는가에 관한 것입니다. </P><P>이에 대한 룰은 다음과 같이 정리할 수 있습니다.</P><P>상위 클래스에서 virtual로 정의한 메서드를 하위 클래스에서 재정의할 때는 반드시 override 문을 사용하십시요.</P><P>이렇게 하지 않는 것은 virtual 메커니즘을 사용하는 근본 원칙을 위배하기 때문입니다. 따라서, 다음과 같은 코드로 작성하면, 상위 클래스의 virtual 메서드를 하위 클래스에서 일반 메서드로 바꾸는 것이 되기 때문에, 델파이 컴파일러는 경고를 날립니다.</P><P>TBase = class(TObject)<BR>&nbsp; public<BR>&nbsp;&nbsp;&nbsp; procedure VirtualMethod; virtual;<BR>&nbsp;&nbsp;&nbsp; procedure StaticMethod;<BR>&nbsp; end;</P><P>&nbsp; TDerived = class(TBase)<BR>&nbsp; public<BR>&nbsp;&nbsp;&nbsp; procedure VirtualMethod; // override; 오버라이드 문이 없으면 경고 발생<BR>&nbsp; end;<BR>&nbsp; <BR>&nbsp; 델파이 컴파일러 경고문.<BR>&nbsp; [Warning]Method 'VirtualMethod' hides virtual method of base type 'TBase';<BR>&nbsp; <BR>virtual메서드를 사용한다는 것은 하위 클래스에서 이 메서드를 재정의할 필요가 있다고 선언하는 것이기 때문에, override를 사용하지 않으면, 이 virtual 메커니즘이 무효화되어, 하위 클래스에서 VirtualMethod는 일반 메서드가 되어 버립니다. 이는 virtual 메서드가 실제 실행시 클래스에 따라서 호출된다는 메커니즘이 사라진다는 것이 됩니다. </P><P>------------------------------------------------------------<BR>그렇다면 상위 클래스의 일반 메서드를 하위 클래스에서 재정의하면 어떻게 될까요? 이 경우 델파이는 아무런 경고도 없지만, 이는 심각한 클래스 설계 오류일 수 있습니다. 상위 클래스와 하위 클래스에 동일한 메서드가 존재한다는 것은 사용법이 엄청 까다로운 클래스가 되기 때문입니다. </P><P>// 위험함 클래스 설계<BR>&nbsp; TBase = class(TObject)<BR>&nbsp; public<BR>&nbsp;&nbsp;&nbsp; procedure VirtualMethod; virtual;<BR>&nbsp;&nbsp;&nbsp; procedure StaticMethod;<BR>&nbsp; end;</P><P>&nbsp; TDerived = class(TBase)<BR>&nbsp; public&nbsp; <BR>&nbsp;&nbsp;&nbsp; procedure StaticMethod; // override; 이 경우는 override문을 사용하면 명백히 컴파일 에러입니다.<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // override는 상위 클래스에서 이 메서드가 virtual일때만 사용가능합니다&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp; end; </P><P>------------------------------------------------------------&nbsp; <BR>객체지향에서 virtual에 대한 또하나의 묵시적 코딩 룰은, 모든 파괴자 메서드는 반드시 virtual로 선언하라는 것입니다. 이는 대부분 객체들의 파괴자가, 실행시 클래스 타입에 따라서 올바른 파괴자가 호출되도록 보장하기 위함입니다. 여기서 그 이유를 설명하는 것은 복잡하므로 생략하지만, 이는 대부분 객체지향에 관한 책에서 필히 그렇게 할 것을 권하는 불문율입니다. 이 코딩 약속을 지켜야만 안전합니다. 이 때문에 파괴자를 재정의할 때 델파이 컴파일러는 강요하지 않지만, override를 반드시 사용해야만 합니다. 이 메커니즘을 깨면 엄청 골치 아픈 버그를 경험할 수 있으니 필히 지키길 권합니다.</P><P>&nbsp; TDerived = class(TBase)<BR>&nbsp; public<BR>&nbsp;&nbsp;&nbsp; destructor Destroy; override; // 만일 override문을 지우면, 저 앞과 비슷한 경고 발생<BR>&nbsp; end;</P><P>TObject.Destroy는 이미 virtual로 정의되어 있습니다. </P><P>------------------------------------------------------------<BR>객체를 파괴할 때는 반드시 Free메서드나 FreeAndNil함수를 사용하십시요. Destroy메서드를 직접 부르는 것은 위험한 상황을 초래할 수 있습니다. 그 이유는 델파이 매뉴얼에 잘 기술되어 있습니다.</P><P>------------------------------------------------------------<BR>생성자와 파괴자를 재정의할 때, 사용자가 추가하는 코드의 위치가 서로 반대입니다. </P><P>constructor TDerived.Create;<BR>begin&nbsp; <BR>&nbsp; // 간혹가다 사용자 코드를 여기에 둡니다.<BR>&nbsp; inherited;<BR>&nbsp; // 대부분의 경우 사용자 코드는 여기 둡니다. <BR>end;</P><P>destructor TDerived.Destroy;<BR>begin<BR>&nbsp; // 파괴자인 경우, 사용자 코드는 여기 둡니다. <BR>&nbsp; inherited;<BR>&nbsp; // 파괴자에서 이부분에 코드를 둬야 할 경우는 아직 저는 찾지 못했습니다. <BR>end;</P><P>inherited문은 상위 클래스에서 현재 메서드와 동일한 메서드를 호출하는 약어입니다. 생성자의 경우 사용자 코드를 필요에 따라서 inherited문을 아래위에 코드를 둘 수 있지만, 파괴자의 경우, 대부분 inherited 이전에 사용자 코드를 둡니다. 생성자는 자신의 최상위 클래스 생성자, 그 다음 상위 클래스 생성자 순으로 호출하지만, 파괴자는 이와 역순으로 상위 클래스의 파괴자를 호출하기 때문입니다.</P><P>따라서 명백한 이유가 없다면 생성자와 파괴자에 코드를 추가할 때는 반드시 이 원칙을 따르십시요</P><P>----------------------------------------------------------------<BR>C++과는 달리 델파이에는 클래스 레퍼런스 혹은 메타 클래스라는 개념이 있습니다. 이는 클래스 자체를, 즉 클래스 타입 자체를 함수나 메서드에 인수로 전달할 수 있는 기능입니다. 이는 C++의 템플리트 기능과 유사한 면이 많지만, 제 개인적으로는 C++ 템플리트보다 월등히 편하다고 생각합니다. 어쨋든 이 클래스 레퍼런스를 제대로 사용하려면, TObject의 생성자와 TComponent의 생성자의 모양이 좀 다르다는 점에 유의해야 합니다. </P><P>TObject의 생성자<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; constructor Create;</P><P>TComponent의 생성자<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; constructor Create(AOwner: TComponent); virtual;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>TObject와 달리 TComponent의 생성자는 virtual로 선언되어 있다는 점에 유의해야 합니다. 따라서 TComponent 이하의 클래스에서 상속받아 만든 클래스는 생성자를 재정의 할 때 반드시 override문을 사용해야 합니다. 이를 잊어먹으면 후일 그 클래스의 클래스 레퍼런스를 사용할 때 원치 않는 결과가 발생합니다. </P><P>&nbsp; TMyComponent = class(TComponent)<BR>&nbsp; public<BR>&nbsp;&nbsp;&nbsp; constructor Create(Owner:TComponent); override; // 반드시 override문을 사용 하십시요.&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; destructor Destroy; override;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 파괴자역시 반드시 override문을 사용하십시요.<BR>&nbsp; end;<BR>&nbsp; <BR>------------------------------------------------------------------<BR>virtual 메서드는 객체지향에서 매우 중요하며너 막강한 기능입니다.</P>]]></description>
    <category><![CDATA[]]></category>
    <author><![CDATA[췌엠]]></author>
    <guid><![CDATA[http://kurapa.com/content-a2248]]></guid>
    <pubDate>2007-02-05T07:33:47+00:00</pubDate>
  </item>
  <item>
    <title><![CDATA[델파이로 긁어오는 HTML 페이지]]></title>
    <link>http://kurapa.com/content-a2247</link>
    <description><![CDATA[<P>- 이 기사는 인터넷으로부터 HTML페이지를 다운로드하는데 요구되는 테크닉을 보여줍니다.<BR>어떤 페이지 스크래핑(패턴 매칭에 대한 정규식으로)하고 "situation-friendly" 방법으로 정보를<BR>보여주게 되죠.</P><P>여러분들이 이미 알고 있듯이, "About Delphi Programming"은 기사와, Chat, Forum, RTL Reference, <BR>Glossory, Free Source Code VCL과 더 많은 것들을 포함하는 Delphi/Kylix 개발의 모든 관점을 <BR>다루고 있습니다. 이 사이트는 매우 활발하게 운영되고 있습니다. 새로운 내용들이 델파이 <BR>초보자들을 위해 튜토리얼에서부터 더 빠르고 더 훌륭하며 더 견고한 개발을 위한 코드를 포함하는 <BR>더 개선된 기사들을 날마다 사이트에 추가되고 있습니다.</P><P>---------------------------<BR>주의: 우리는 이 사이트에 새 기사를 올리는 방법을 변경해 왔습니다. "What's new and Hot" 섹션이<BR>"In the Spotlight"로 변경되었습니다. 이 기사에서 그 생각이 "valid"일지라도, 만약 "About Delphi<BR>Programming" 사이트로부터 *Current Headlines*를 낚아채고 싶다면, "About Delphi Programming<BR>*Current Headlines* sticker" 페이지를 방문하기 바랍니다.<BR>---------------------------</P><P>이 사이트의 한 섹션에서 당신이 가져온 최신뉴스는 최신의 새롭고 변경된 것입니다. 이 페이지는<BR>"What's New and Hot"페이지에 위치해 있습니다. 여러분이 매번 방문할 때마다 필자는 여러분이 <BR>그 페이지를 열어볼 것을 추천하고 새로운 기사가 추가되었는지 확인하길 바랍니다.</P><P>이러한 페이지에 유용한 정보를 추가하기 위해 사이트 웹 마스터와 관련된 델파이 프로그래밍을 <BR>가능하게 하기 위해서 필자는 "About Delphi Programming *New and Hot* sticker"를 개발해 왔습니다.<BR>자바스크립트 파일이 그것이고 웹 마스터는 빈번히 업데이트 되는 프로페셔널 델파이 / 카일릭스 <BR>프로그래밍 컨텐츠의 소스를 뷰어에서 볼 수 있게 웹 페이지에 포함시킬 필요성을 충족시키게 됩니다.</P><P>자바스크립트 파일(.js)는 델파이로 개발된 유틸리티로 고안되었습니다. 이 어플리케이션은 <BR>이 사이트에서 What's New and Hot page를 다운로드 합니다. 그 다음은 페이지로부터 확실한 데이터를 <BR>추출하기 위한 정규식을 사용하게 됩니다. 마지막으로 추출된 데이터를 사용해서 .js 파일을 생성합니다.</P><P>이 기사의 논점은 인터넷에서 페이지를 다운로드하고 페이지를 스크래핑하여 더 "situation-friendly" <BR>방법에서 정보를 보여주는데 사용된 테크닉을 소개하는 것입니다.</P><P>이 기사에서 소개된 데이터 추출 방법의 키는 "situation-friendly" 소스로 HTML문서를 변환하는 것입니다.<BR>아래에 검토할 단계를 보겠습니다:</P><P>·HTML 소스 문서를 검색<BR>·HTML 문서를 처리하고 불필요한 데이터는 제거<BR>·결과를 문자열 타입의 변수로 전송<BR>·ListView로 추출된 정보를 출력</P><P>주의: 위에서 기술된 Sticker 자바스크립트 파일은 이 기사에서 검토한 테크닉을 사용합니다 - <BR>자바스크립트 document.write 메소드가 포맷된 HTML 코드를 문서에 추가한다는 사실에 따라 <BR>다양한 HTML 태그로 구성됩니다. 중요한 것은 Sticker핵인 .js가 필자가 추출하는 방법을 <BR>여러분에게 보여주는 데이터라는 것입니다.</P><P>Preparing the Delphi Project</P><P>이 기사를 기억에 남기기기 위해서 여러분들에게 Delphi를 시작해서 새 프로젝트를 생성할 것을 <BR>제안합니다. 폼 위에 TButton을 올려놓고 TListView도 올려놓습니다.</P><P>델파이 디폴트 컴포넌트 명을 그대로 사용하도록 합니다. 이제, Button1을 이용해서 인터넷으로부터 <BR>파일을 가져오고 정보검색을 해서 ListView1에 결과를 보여줍니다. 또한, ListView1에는 4개의 <BR>컬럼을 추가하도록 합니다: Title, URL, 설명, 언제/어디서. ListView1의 ViewStyle은 vsReport로 <BR>합니다.</P><P>· HTML 소스 문서를 검색</P><P>HTML 파일로부터 데이터를 추출하기 전에 확실히 해야할 것이 있습니다.<BR>여러분의 첫 작업은 인터넷에서 파일을 다운로드하는데 사용된 델파이 함수를 생성하는 것입니다.<BR>이 작업을 수행하는 방법은 WinInet API 호출을 사용하는 것이죠. 델파이는 HTTP나 FTP를 사용해서<BR>어느 웹 사이트나 접속하고 파일을 추출할 수 있도록 WinInet API(wininet.pas)를 전적으로 액세스<BR>하도록 하고 있습니다. 필자는 이미 "Get File From the Net"이라는 테크닉을 설명하는 기사를 쓴 <BR>적이 있습니다.</P><P>다른 접근 방법으로는 델파이 6를 사용한다면, TDownloadURL 객체를 사용하는 것입니다. <BR>객체는 ExtActns.pas 유닛에 적의되어 있는 TDownloadURL는 특정 URL에서 파일의 내용을 저장하기 위해<BR>고안된 것입니다. 여기 이 사이트에 "What's New and Hot" 페이지를 다운로드하는 TDownloadURL을 <BR>사용하는코드가 있습니다.</P><P>---------------------------------------------------------------------------<BR>function Download_HTM(const sURL, sLocalFileName:string): boolean;<BR>begin<BR>&nbsp; Result := True;<BR>&nbsp; with TDownLoadURL.Create(nil) do<BR>&nbsp; try<BR>&nbsp;&nbsp;&nbsp; URL := sURL;<BR>&nbsp;&nbsp;&nbsp; Filename := sLocalFileName;<BR>&nbsp;&nbsp;&nbsp; try<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ExecuteTarget(nil);<BR>&nbsp;&nbsp;&nbsp; except<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Result := False<BR>&nbsp;&nbsp;&nbsp; end;<BR>&nbsp; finally<BR>&nbsp;&nbsp;&nbsp; Free;<BR>&nbsp; end;<BR>end;<BR>---------------------------------------------------------------------------</P><P>Download_HTM 함수는 sURL 파라메터에서 지정된 URL로부터 파일을 다운로드하고 sLocalFileName 명으로<BR>PC에 파일을 저장합니다. 이 함수는 성공이면 True를, 그렇지 않으면 False를 리턴합니다.<BR>물론, 이 함수는 Button1 OnClick 이벤트 핸들러에서 호출됩니다. 아래에 코드가 있습니다. <BR>주의할 것은, 이 파일은 "C:\temp_adp.newandhot"으로 저장된다는 것입니다.</P><P>---------------------------------------------------------------------------<BR>procedure TForm1.Button1Click(Sender: TObject);<BR>const<BR>&nbsp; ADPNEWHOTURL = 'http://delphi.about.com/cs/newandhot/index.htm';<BR>&nbsp; TmpFileName = 'c:\temp_adp.newandhot';<BR>begin<BR>&nbsp; if NOT Download_HTM(ADPNEWHOTURL,TmpFileName) then<BR>&nbsp; begin<BR>&nbsp;&nbsp;&nbsp; ShowMessage('Error in HTML file download');<BR>&nbsp;&nbsp;&nbsp; Exit;<BR>&nbsp; end;</P><P>&nbsp; {<BR>&nbsp; more code to be added<BR>&nbsp; }</P><P>end;<BR>---------------------------------------------------------------------------</P><P>주의: 파일 받기 처리에서 TDownloadURL은 주기적으로 OnDownloadProgress 이벤트를 발생합니다. <BR>이는 처리과정을 사용자에게 피드백으로 제공하기 위해서 입니다. 필자는 여러분이 구현할 수 있도록 <BR>이 부분을 남겨둘 것입니다.</P><P>지금, 우리는 디스크에 HTM 페이지 파일을 갖고 있으며 오브젝트 파스칼 코드로 ASCII 핸들링하는<BR>테크닉을 사용할 수 있습니다.</P><P>· HTML 문서 처리</P><P>다음 단계는 HTML 문서 내부에 관심있는 데이터에 위치를 찾고 이를 추출하는 것 입니다. HTML 문서는<BR>순수 ASCII파일이기 때문에, 델파이에서 text파일로 작없하듯 액세스하면 됩니다.</P><P>Before we move on</P><P>이 기사에서 설명된 테크닉은 XSLT를 사용해서 XML로 HTML처럼 "new"라는 지능적인 정보검색 테크닉과는<BR>어째꺼나 상반된다는 것을 염두하는 것이 중요한데, 여러분이 이야기하는 것에 대해서 모르더라도 <BR>걱정은 안해도 됩니다.</P><P>웹페이지로부터 데이터를 성공적으로 추출하기 위해 패턴매칭의 몇가지 정규식이 필요합니다. 특히 <BR>이는 여러분이 만약 HTML 문서의 구조를 알고 있는 경우에 한해서 페이지를 스크래핑할 수 있을 <BR>것입니다.</P><P>이것은 여러분이 웹페이지를 생성하는 사람이라면 큰 문제는 아닙니다. 웹페이지 배후의 인물이 <BR>아니더라도 여러분은 패턴매칭을 할 수 있지만 HTML이 동적인 컨텐츠이고 문서구조가 다양한 배너광고 <BR>시스템과 동적인 서버 사이드 스크립트 엔진일 경우에 변화가 있을 수 있기때문에 종종 여러분의 <BR>코드를 체크해야만 합니다.</P><P>패턴 매칭결과를 도출못할 때의 상황에서 여러분은 HTML 문서를 XMl로 변환하는 것처럼 더 지능적인 <BR>해결책을 찾을 수 있습니다 - 구조화된 문서를 생성하는 표준: 그러나, 여기서 논의할 꺼리는 아닙니다.</P><P>여러분이 Notepad로 다운로드된 파일을 열어본다면, 우리가 추출하기 원하는 정보가 내부에 있는지 <BR>태크가 있는지 알아야 합니다. 해당 부분을 추출한 후, 서버/클라이언트 사이드 스크립트가 배제되도록 <BR>해야합니다 - 태그 사이에 있는 문자들. 남아있는 것은 다음처럼 10개의 포맷된 항목처럼 HTML <BR>코드입니다.</P><P>---------------------------------------------------------------------------<BR>&lt;a href="<A href='http://delphi.about.com/library/weekly/aa061802a.htm">A'>http://delphi.about.com/library/weekly/aa061802a.htm"&gt;A</A> Beginner's Guide to Delphi Programming: Chapter 5&lt;/a&gt;</P><P><BR>06/18 in BEGINNERS COURSE. Take a closer look at exactly what each keyword means by examining each line of the Delphi form unit source code. Interface, implementation, uses and other keywords explained in easy language!<BR>---------------------------------------------------------------------------<BR>(※ 위의 Html코드 예제가 제대로 보이지 않을수도 있습니다.)</P><P>자, 여기서 "item" 이라는 4가지 관심있는 정보가 나왔습니다. 코드에서 이 아이템은 ItemBuf 문자열<BR>변수에 저장됩니다. red 마크는 특별히 뉴스 아이템의 URL입니다. blue 마크는 아이템의 title 입니다.<BR>설명은 green마크이며 날짜나 위치는 maroon 마크입니다.</P><P>정보의 특정요소를 얻기위해서는 다음코드를 살펴봅니다:</P><P>---------------------------------------------------------------------------<BR>// title 찾기<BR>iStart := Pos('', ItemBuf) + Length('');<BR>iStop := Pos('', ItemBuf);<BR>sTitle := Copy(ItemBuf, iStart, iStop-iStart);<BR>---------------------------------------------------------------------------</P><P>마침내 여러분들이 각 아이템을 4개의 문자열 변수로 변환하고 ListView에 정보를 출력합니다.<BR>필자는 여러분에게 여기 상세한 프로젝트로 성가시게 하지 않을 것입니다. 전체코드를 확실히 <BR>본다면, 분석하는 만큼 많은것을 얻게 될 것입니다.</P><P>이 프로젝트는 실시간 동작합니다:</P><P><IMG src="http://kurapa.com/file_download.php?filename=ebe005c6aa63fa3a1b914aba18402d8f" border=0></P><P>여러분이 이 기사에 대하여 어떤 문의나 의견이 있다면, Delphi Programming Forum에 게시를 해주기를<BR>바랍니다.</P><P>p.s.<BR>Delphi IDE에 남겨지지 않은 최근의 새로운 기사가 여러분들을 도와줄 수 있는 expert, Delphi IDE <BR>add-on 개발하는 방법을 설명하는 기사를 확인해보세요. </P><P><BR>---------------------------------------------------------------------------<BR>From Zarko Gajic,<BR>Your Guide to Delphi Programming.<BR>FREE Newsletter. Stay up to date! </P><P>원문: <A href="http://delphi.about.com/od/internetintranet/l/aa062502a.htm">http://delphi.about.com/od/internetintranet/l/aa062502a.htm</A></P>]]></description>
    <category><![CDATA[스크랩]]></category>
    <category><![CDATA[scrap]]></category>
    <category><![CDATA[grab]]></category>
    <category><![CDATA[capture]]></category>
    <author><![CDATA[췌엠]]></author>
    <guid><![CDATA[http://kurapa.com/content-a2247]]></guid>
    <pubDate>2007-02-05T07:31:38+00:00</pubDate>
  </item>
  <item>
    <title><![CDATA[InternetOpenURL로 폼에다 데이터를 포스팅하는 방법]]></title>
    <link>http://kurapa.com/content-a831</link>
    <description><![CDATA[요즘은 블로그에다 글 올려주는 소프트웨어가 속속들이 나온다.<BR>델파이에서 이를 가장 쉽게 구현하는 방법중의 하나는 INET을 활용하는 것이다.<BR><BR>INET을 쓰면 브라우져 세팅이 어떻게 되어져 있는지 몰라도<BR>쉽게 이런 설정을 바로 적용하여 사용 할 수 있다.<BR><BR><BR>procedure doPost;<BR>var &nbsp;NetHandle: &nbsp; &nbsp; HINTERNET;<BR>&nbsp; &nbsp; &nbsp;hConnect: &nbsp; &nbsp; &nbsp;HINTERNET;<BR>&nbsp; &nbsp; &nbsp;hRequest: &nbsp; &nbsp; &nbsp;HINTERNET;<BR>begin<BR><BR>&nbsp; // Attempt the open<BR>&nbsp; NetHandle:=InternetOpen('Daemon', INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);<BR><BR>&nbsp; // Check the handle<BR>&nbsp; if Assigned(NetHandle) then<BR>&nbsp; begin<BR>&nbsp; &nbsp; &nbsp;// Resource protection<BR>&nbsp; &nbsp; &nbsp;try<BR>&nbsp; &nbsp; &nbsp; &nbsp; // NOTE ** <A onclick="return openNew(this.href);" href="http:///" target=_blank rel=nofollow>http:<WBR>/<WBR>/<WBR></A>&nbsp;is not valid as part of the name and will cause errors **<BR>&nbsp; &nbsp; &nbsp; &nbsp; hConnect:=InternetConnect(NetHandle, PChar('<A onclick="return openNew(this.href);" href="http://www.link.ru/" target=_blank rel=nofollow>www.<WBR>link.<WBR>ru</A>'), INTERNET_DEFAULT_HTTP_PORT, nil, nil, INTERNET_SERVICE_HTTP, 0, 1);<BR>&nbsp; &nbsp; &nbsp; &nbsp; // Check the connect handle<BR>&nbsp; &nbsp; &nbsp; &nbsp; if Assigned(hConnect) then<BR>&nbsp; &nbsp; &nbsp; &nbsp; begin<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// Resource protection<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;try<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Open the request<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; hRequest:=HttpOpenRequest(hConnect, PChar('POST'), PChar('/add.php'), nil, nil, PLPSTR(PChar('*/*')), INTERNET_FLAG_KEEP_CONNECTION, 1);<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Check handle<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if Assigned(hRequest) then<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; begin<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// Resource protection<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;try<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Send request - pass any additional headers and/or optional data here.<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // please be aware of the documented caveats:<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // The function also lets the client specify optional data to<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // send to the HTTP server immediately following the request headers.<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // This feature is generally used for "write" operations such as<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // PUT and POST. When using optional data, be aware of the<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // following caveat: the size of the optional data buffer,<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // specified with the dwOptionalLength parameter, must be<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // either less than 32 bytes or greater than approximately 150 bytes.<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Otherwise, the HttpSendRequest function fails. Another caveat requires<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // that the optional data buffer begin with '/r/n'. This ensures<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // that header and buffer is separated by '/r/n/r/n' as required by RFC 2068.<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if HttpSendRequest(hRequest, nil, 0, nil, 0) then<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// Success<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;MessageBox(0, 'Success', nil, MB_OK)<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; else<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// Display error code - check wininet for error codes, which start<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// at 12000 (INTERNET_ERROR_BASE)<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;MessageBox(0, PChar(IntToStr(GetLastError)), nil, MB_OK);<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;finally<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Close handle<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; InternetCloseHandle(hRequest);<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;end;<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; end;<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;finally<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Close handle<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; InternetCloseHandle(hConnect);<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;end;<BR>&nbsp; &nbsp; &nbsp; &nbsp; end;<BR>&nbsp; &nbsp; &nbsp;finally<BR>&nbsp; &nbsp; &nbsp; &nbsp; // Close handle<BR>&nbsp; &nbsp; &nbsp; &nbsp; InternetCloseHandle(NetHandle);<BR>&nbsp; &nbsp; &nbsp;end;<BR>&nbsp; end;<BR><BR>end;<br />]]></description>
    <category><![CDATA[inet]]></category>
    <author><![CDATA[췌엠]]></author>
    <guid><![CDATA[http://kurapa.com/content-a831]]></guid>
    <pubDate>2006-10-31T23:35:05+00:00</pubDate>
  </item>
  <item>
    <title><![CDATA[Web Browser Control에서 Enter키 먹게 하기]]></title>
    <link>http://kurapa.com/content-a417</link>
    <description><![CDATA[unit Unit1;<BR><BR>interface<BR><BR>uses<BR>ActiveX; // 추가<BR><BR>type<BR>TForm1 = class(TForm)<BR>&nbsp;&nbsp; Button1: TButton;<BR>&nbsp;&nbsp; ApplicationEvents1: TApplicationEvents;&nbsp;&nbsp;// &lt;- 추가<BR>&nbsp;&nbsp; WebBrowser1: TWebBrowser;<BR>&nbsp;&nbsp; procedure ApplicationEvents1Message(var Msg: tagMSG;<BR>&nbsp;&nbsp;&nbsp;&nbsp; var Handled: Boolean);<BR>&nbsp;&nbsp; procedure FormShow(Sender: TObject);<BR>private<BR>&nbsp;&nbsp; { Private declarations }<BR>public<BR>&nbsp;&nbsp; { Public declarations }<BR>&nbsp;&nbsp; FOleInPlaceActiveObject: IOleInPlaceActiveObject; // &lt;- 추가<BR><BR>end;<BR><BR>var<BR>Form1: TForm1;<BR><BR>implementation<BR><BR>{$R *.dfm}<BR><BR>procedure TForm1.ApplicationEvents1Message(var Msg: tagMSG;<BR>var Handled: Boolean);<BR>var<BR>iOIPAO: IOleInPlaceActiveObject;<BR>Dispatch: IDispatch;<BR>begin<BR>{ exit if we don't get back a webbrowser object }<BR>if (WebBrowser1 = nil) then<BR>begin<BR>&nbsp;&nbsp; Handled := False;<BR>&nbsp;&nbsp; Exit;<BR>end;<BR><BR>Handled := (IsDialogMessage(WebBrowser1.Handle, Msg) = True);<BR><BR>if (Handled) and (not WebBrowser1.Busy) then<BR>begin<BR>&nbsp;&nbsp; if FOleInPlaceActiveObject = nil then<BR>&nbsp;&nbsp; begin<BR>&nbsp;&nbsp;&nbsp;&nbsp; Dispatch := WebBrowser1.Application;<BR>&nbsp;&nbsp;&nbsp;&nbsp; if Dispatch &lt;&gt; nil then<BR>&nbsp;&nbsp;&nbsp;&nbsp; begin<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dispatch.QueryInterface (IOleInPlaceActiveObject, iOIPAO);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if iOIPAO &lt;&gt; nil then<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FOleInPlaceActiveObject := iOIPAO;<BR>&nbsp;&nbsp;&nbsp;&nbsp; end;<BR>&nbsp;&nbsp; end;<BR><BR>&nbsp;&nbsp; if FOleInPlaceActiveObject &lt;&gt; nil then<BR>&nbsp;&nbsp;&nbsp;&nbsp; if ((Msg.message = WM_KEYDOWN) or (Msg.message = WM_KEYUP)) and<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ((Msg.wParam = VK_BACK) or (Msg.wParam = VK_LEFT) or<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (Msg.wParam = VK_RIGHT)) then<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //nothing - do not pass on Backspace, Left or Right arrows<BR>&nbsp;&nbsp;&nbsp;&nbsp; else<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FOleInPlaceActiveObject.TranslateAccelerator (Msg);<BR>end;<BR>end;<BR><BR>procedure TForm1.FormShow(Sender: TObject);<BR>begin<BR>WebBrowser1.Navigate( 'http://yahoo.co.kr');<BR>end;<BR><BR>end.<br />]]></description>
    <category><![CDATA[]]></category>
    <author><![CDATA[췌엠]]></author>
    <guid><![CDATA[http://kurapa.com/content-a417]]></guid>
    <pubDate>2006-09-28T12:49:43+00:00</pubDate>
  </item>
  <item>
    <title><![CDATA[Application Event써서 Keyboard 받아내기]]></title>
    <link>http://kurapa.com/content-a416</link>
    <description><![CDATA[unit Unit1;<BR><BR>interface<BR><BR>uses<BR>Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,<BR>Dialogs, StdCtrls, AppEvnts, OleCtrls, SHDocVw;<BR><BR>type<BR>TForm1 = class(TForm)<BR>&nbsp;&nbsp; Button1: TButton;<BR>&nbsp;&nbsp; ApplicationEvents1: TApplicationEvents;<BR>&nbsp;&nbsp; WebBrowser1: TWebBrowser;<BR>&nbsp;&nbsp; procedure Button1Click(Sender: TObject);<BR>&nbsp;&nbsp; procedure ApplicationEvents1Message(var Msg: tagMSG;<BR>&nbsp;&nbsp;&nbsp;&nbsp; var Handled: Boolean);<BR>&nbsp;&nbsp; procedure FormShow(Sender: TObject);<BR>private<BR>&nbsp;&nbsp; { Private declarations }<BR>public<BR>&nbsp;&nbsp; { Public declarations }<BR>end;<BR><BR>var<BR>Form1: TForm1;<BR><BR>implementation<BR><BR>{$R *.dfm}<BR><BR>procedure TForm1.Button1Click(Sender: TObject);<BR>begin<BR>&nbsp;&nbsp; SetForegroundWindow( THandle(nil));<BR>end;<BR><BR>procedure TForm1.ApplicationEvents1Message(var Msg: tagMSG;<BR>var Handled: Boolean);<BR>begin<BR>if Msg.message=WM_KEYDOWN then<BR>begin<BR>&nbsp;&nbsp; if Msg.wParam=38 then<BR>&nbsp;&nbsp; begin<BR>&nbsp;&nbsp;&nbsp;&nbsp; Button1.Caption := 'UP!';<BR>&nbsp;&nbsp; end else begin<BR>&nbsp;&nbsp;&nbsp;&nbsp; Button1.Caption := IntToStr(Msg.wParam);<BR>&nbsp;&nbsp; end;<BR>&nbsp;&nbsp; WebBrowser1.SetFocus;<BR>end;<BR>end;<BR><BR>procedure TForm1.FormShow(Sender: TObject);<BR>begin<BR>WebBrowser1.Navigate( 'http://yahoo.co.kr');<BR>end;<BR><BR>end.<br />]]></description>
    <category><![CDATA[]]></category>
    <author><![CDATA[췌엠]]></author>
    <guid><![CDATA[http://kurapa.com/content-a416]]></guid>
    <pubDate>2006-09-28T12:49:24+00:00</pubDate>
  </item>
  <item>
    <title><![CDATA[Application defined window call procedure]]></title>
    <link>http://kurapa.com/content-a415</link>
    <description><![CDATA[** 메시지를 주고 받는 단위는 Form이다.<BR>**<BR>** Application Title을 적어줄 경우<BR>** 절대로 메시지 교환이 되지 않는다.<BR><BR>unit Unit1;<BR><BR>interface<BR><BR>uses<BR>Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,<BR>Dialogs, ExtCtrls, StdCtrls;<BR><BR>const<BR>WM_WAKEUP = WM_USER + 1992;<BR>LP_READPARAM = -1;<BR><BR>type<BR>TForm1 = class(TForm)<BR>&nbsp;&nbsp; Button1: TButton;<BR>&nbsp;&nbsp; Timer1: TTimer;<BR>&nbsp;&nbsp; Button2: TButton;<BR>&nbsp;&nbsp; procedure Button2Click(Sender: TObject);<BR>&nbsp;&nbsp; procedure Button1Click(Sender: TObject);<BR>&nbsp;&nbsp; procedure FormCreate(Sender: TObject);<BR>private<BR>&nbsp;&nbsp; { Private declarations }<BR>public<BR>&nbsp;&nbsp; { Public declarations }<BR>&nbsp;&nbsp; procedure WMWakeup(var Msg : TMessage); message WM_WAKEUP;<BR>end;<BR><BR>var<BR>Form1: TForm1;<BR><BR>AppDefWindowProc : function (h : HWND; uMsg : UINT; wp : WPARAM; lp : LPARAM): LRESULT; stdcall;<BR><BR>implementation<BR><BR>{$R *.dfm}<BR><BR>//------------------------------------------------------------------------------<BR>//&nbsp;&nbsp;AppWindowProc<BR>//------------------------------------------------------------------------------<BR>function AppWindowProc(h : HWND; uMsg : UINT; wp : WPARAM; lp : LPARAM): LRESULT; stdcall;<BR>begin<BR>Result := AppDefWindowProc(h, uMsg, wp, lp);<BR>end;<BR><BR>procedure TForm1.WMWakeup(var Msg : TMessage);<BR>begin<BR>// do something<BR>Color := clBlack;<BR>end;<BR><BR>procedure TForm1.Button2Click(Sender: TObject);<BR>var<BR>h:HWND;<BR>begin<BR>h := FindWindow( THandle(nil), 'bb');<BR>if (h=0) then<BR>begin<BR>&nbsp;&nbsp; ShowMessage( 'appliation not found');<BR>end else begin<BR>&nbsp;&nbsp; // Make top most window<BR>&nbsp;&nbsp; SetWindowPos( h, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE or SWP_NOZORDER or SWP_NOMOVE);<BR><BR>&nbsp;&nbsp; // Wake up window to do something<BR>&nbsp;&nbsp; SendMessage( h, WM_WAKEUP, 0, LP_READPARAM);<BR>end;<BR>end;<BR><BR>procedure TForm1.Button1Click(Sender: TObject);<BR>begin<BR>// Set Application Title<BR>Application.Title := 'bb';<BR>Caption := 'bb';&nbsp;&nbsp;// This will be used to find window<BR>end;<BR><BR>procedure TForm1.FormCreate(Sender: TObject);<BR>begin<BR>// Application Defined Window Procedure Call Registration<BR>AppDefWindowProc := Pointer(SetWindowLong(Application.Handle, GWL_WNDPROC, Integer(@AppWindowProc)));<BR>end;<BR><BR>end. <!--"<-->]]></description>
    <category><![CDATA[]]></category>
    <author><![CDATA[췌엠]]></author>
    <guid><![CDATA[http://kurapa.com/content-a415]]></guid>
    <pubDate>2006-09-28T12:48:52+00:00</pubDate>
  </item>
  <item>
    <title><![CDATA[윈도우 찾아서 Top Most로 올려주기]]></title>
    <link>http://kurapa.com/content-a414</link>
    <description><![CDATA[방법1)<BR>h := FindWindow( THandle(nil), '&lt;폼 이름&gt;');<BR>if (h&lt;&gt;0) then<BR>beign<BR>&nbsp;&nbsp; SetForegroundWindow(h);<BR>&nbsp;&nbsp; SetActiveWindow( h);<BR>end;<BR><BR><BR>방법2)<BR>h := FindWindow( THandle(nil), '&lt;폼 이름&gt;');<BR>if (h&lt;&gt;0) then<BR>beign<BR>&nbsp;&nbsp; SetForegroundWindow(h);<BR>&nbsp;&nbsp; SetActiveWindow( h);<BR>&nbsp;&nbsp; SetWindowPos( h, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE or SWP_NOZORDER or SWP_NOMOVE);<BR>end;<BR><br />]]></description>
    <category><![CDATA[Delphi]]></category>
    <author><![CDATA[췌엠]]></author>
    <guid><![CDATA[http://kurapa.com/content-a414]]></guid>
    <pubDate>2006-09-28T12:48:23+00:00</pubDate>
  </item>
  <item>
    <title><![CDATA[ActiveX의 등록 - Safe Code 넣어주기]]></title>
    <link>http://kurapa.com/content-a413</link>
    <description><![CDATA[들어가며...<BR><BR>얼마전 까지만 해도 ActiveX의 제작이나 사용은 나에겐 먼나라 같은 일이었다.<BR>그런데 그것이 현실로 다가올 줄이야. 흑흑...<BR>ActiveX의 제작까지는 델파이라는 훌륭한 매개체를 이용하여 손쉽게 작성할 수 있었다. 그런데 문제는 바로 이 제작된 ActiveX의 등록에 있었다. 이 ActiveX의 등록 방법에 대해 간단히 알아보자.<BR><BR><BR>AcitveX의 제작...<BR><BR>ActiveX의 제작은 이미 여러번 강좌나 기타 책등에서 많이 다룬 문제이므로 이곳에서는 생략하기로 하고 실제 이렇게 제작된 ActiveX를 어떻게 Web에 보이게 할 것인지 생각해 보자.<BR>제작된 ActiveX는 일단 등록을 해야 한다. 등록은 Regsvr32.exe를 이용해서 하게 되는데 일단 등록까지는 별 문제가 없을 것이다. <BR>이제 테스트용 HTML을 제작해서 ActiveX를 불러 오기만 하면 된다.<BR>이때 한가지 문제가 생긴다. 그냥 단순히 불러오기만 하는 것이라면 커다란 문제가 없다. 그런데 이 ActiveX의 내부 Procedure나 Event를 HTML Code와 Sink 시키려면 AcitveX의 Object ID가 있어야 한다. <BR>애석하게도 델파이에서 생성시켜 주는 테스트용 HTML에서는 결정적으로 이 Object ID를 생성해 주지 않는다. 하다 못해 VB도 생성해 주는 이 Object ID를 델파이에서 생성해 주지 않는다니 가슴이 몹시 아프다. 쩝...<BR><BR>우리가 할 수 있는 방법은 몇가지가 있다. 직접 이 ID를 생성해 주는 것이 첫번째이고, 두번째는 Edit Plus나 다른 툴을 이용하는 것이다. <BR>개발자들은 매우 게으른 사람들이란 얘기를 누군가 했었다. 그 말인 즉 개발자들은 어떤 문제를 손쉽게 해결해 줄 수 있는 도구를 먼저 찾고 없으면 그 도구를 만들어서라도 일을 손쉽게 해결하려는 경향이 있다는 것이다. 맞는 말이다. 본인 또한 이런 일을 해 줄수 있는 도구를 찾았으며 곧 MS에서 무료로 제공하는 두개의 툴을 구할 수 있었다. <BR>'ActiveX control Pad'란 녀석과 'ActiveX Lister'란 녀석들인데 이 툴을 사용하면 AtiveX를 개발 하고 나서 웹에 올리는데 드는 수고를 1/3로 줄여준다. <BR><BR>간단히 소개를 하자면... <BR><BR>'ActiveX Lister'란 녀석은 기존에 시스템에 등록되어있는 모든 ActiveX 컨트롤들은 일목 요연하게 표시하여 준다. 그중 하나를 선택하고 복사를 하면 Clipboard에 복사를 해 주는데... HTML로 붙이기를 해 보면 그 편리함에 새삼 놀라게 된다. <BR><BR>예로... 'Microsoft Forms 2.0 Image'라는 AcitveX를 선택하고 복사한 후 HTML에서 붙이기를 하면...<BR><BR>Id="Microsoft Forms 2.0 Image"<BR>Classid="clsid:4c599241-6926-101B-9992-00000B65C6F9"<BR>Width=80<BR>Height=30<BR><BR><BR>라고 만들어 준다. 간편하지 않은가? ^^<BR>우리가 원했던 Object ID까지 모두 생성해서 하나의 HTML 코드로 만들어 준다. 생활에 편리함을 주는 도구이다. <BR><BR>또다른 툴인 'ActiveX Control Pad'란 녀석은 그 기능에 몹시 놀라게 된다.<BR>Lister란 녀석은 간단히 Object를 생성하는 코드만을 만들어 주는데 반해 이 제어패드는 Object를 생성코드는 물론 ActiveX에 정의된 모든 Procedure와 Event를 보여주며 손쉽게 이 procedure와 Event를 HTML에 붙일 수 있도록 VBScript 코드를 자동으로 생성해 준다. 또 JAVAScript 코드로도 만들어 준단다...<BR>이 얼마나 고마운 녀석인가... 흑...<BR><BR>이 고마운 녀석들은 www.microsoft.com/intdev/sdk 에서 모두 구할 수 있다.<BR>참고하자...<BR><BR>이렇게 함으로써 Object ID를 생성하는 부분은 해결이 되었고...<BR>또다른 문제 하나... 바로 보안 경고 메시지가 뜬다는 것이다. <BR><BR>"이 페이지의 ActiveX는 잠재적 위험성이 있습니다. 초기화 및 스크립트 액세스를 허용하시겠습니까?" <BR><BR>라고 물어보는 Dialog 창이 매번 실행 할 때 마다 뜬다. 이 메시지는 IE의 옵션 조작으로도 안 보이게 할 방법이 없다. <BR><BR>이것은 이 ActiveX 안전하다는 것을 보장하는 코드가 빠져있기 때문에 나타나는 메시지인데. VC++에서는 *.RGS라는 파일을 만들어주며 이 파일의 내용은 registry에 Safe Key를 추가하는 역할을 하는 코드가 들어있다. <BR>이 Safe Key를 Registry에 추가해야만 이 메시지를 피할 수 있는데 이 또한 애석하게도 델파이에서는 생략되어 있다. <BR><BR>이것을 가능하게 해주는 'Safefactory.pas'란 파일의 풀 소스를 아래에 첨부한다.<BR><BR>- safefactory.pas -<BR><BR>unit SafeFactory;<BR>interface<BR>uses ComObj, ActiveX, AXCtrls;<BR><BR>const<BR>CATID_SafeForScripting: TGUID =<BR>'{7DD95801-9882-11CF-9FA9-00AA006C42C4}';<BR>CATID_SafeForInitializing: TGUID =<BR>'{7DD95802-9882-11CF-9FA9-00AA006C42C4}';<BR><BR>type<BR>TSafeActiveFormFactory = class( TActiveFormFactory )<BR>procedure UpdateRegistry( Register: Boolean ); override;<BR>end;<BR><BR>TSafeActiveXControlFactory = class( TActiveXControlFactory )<BR>procedure UpdateRegistry( Register: Boolean ); override;<BR>end;<BR><BR>implementation<BR><BR>procedure AddSafetyKeys( const ClassID: TGUID );<BR>var<BR>ClassKey: string;<BR>begin <BR>ClassKey := 'CLSID' + GUIDToString( ClassID );<BR>CreateRegKey( ClassKey + 'Implemented Categories','','');<BR>CreateRegKey( ClassKey + 'Implemented Categories'+GUIDToString(<BR>CATID_SafeForScripting ),'','');<BR>CreateRegKey( ClassKey + 'Implemented Categories'+GUIDToString(<BR>CATID_SafeForInitializing ),'','');<BR>end;<BR><BR>procedure RemoveSafetyKeys( const ClassID: TGUID );<BR>var<BR>ClassKey: string;<BR>begin <BR>ClassKey := 'CLSID' + GUIDToString( ClassID );<BR>DeleteRegKey( ClassKey + 'Implemented Categories'+GUIDToString(<BR>CATID_SafeForInitializing ));<BR>DeleteRegKey( ClassKey + 'Implemented Categories'+GUIDToString(<BR>CATID_SafeForScripting ));<BR>DeleteRegKey( ClassKey + 'Implemented Categories');<BR>end;<BR><BR>{TSafeActiveFormFactory}<BR>procedure TSafeActiveFormFactory.UpdateRegistry( Register: Boolean ); begin<BR>if Register then<BR>begin<BR>AddSafetyKeys( ClassID );<BR>inherited UpdateRegistry( Register );<BR>end<BR>else<BR>begin<BR>RemoveSafetyKeys( ClassID );<BR>inherited UpdateRegistry( Register );<BR>end;<BR>end;<BR><BR>{TSafeActiveXControlFactory}<BR>procedure TSafeActiveXControlFactory.UpdateRegistry( Register: Boolean );<BR>begin<BR>if Register then<BR>begin<BR>AddSafetyKeys( ClassID );<BR>inherited UpdateRegistry( Register );<BR>end<BR>else<BR>begin<BR>RemoveSafetyKeys( ClassID );<BR>inherited UpdateRegistry( Register );<BR>end;<BR>end;<BR><BR>end.<BR><BR>이 부분을 safefactory.pas로 저장하고... 기존에 만들었던 AcitveX의 initialization 부분의 TActiveFormFactory 를 TSafeActiveFormFactory로 고쳐준다.<BR><BR>예 )<BR><BR>initialization<BR>TeActiveFormFactory.Create(<BR>ComServer,<BR>TActiveFormControl,<BR>TMailServer,<BR>Class_MailServer,<BR>1,<BR>'',<BR>OLEMISC_SIMPLEFRAME or OLEMISC_ACTSLIKELABEL);<BR>end.<BR><BR>이렇게 고친다... ^^<BR><BR>initialization<BR>TSafeActiveFormFactory.Create(<BR>ComServer,<BR>TActiveFormControl,<BR>TMailServer,<BR>Class_MailServer,<BR>1,<BR>'',<BR>OLEMISC_SIMPLEFRAME or OLEMISC_ACTSLIKELABEL);<BR>end.<BR><BR>이렇게 함으로써 우리는 보안 메시지를 안뜨게 할 수 있게 되었다.<BR>이 Safefactory의 역할은 해당 CLSID에 Safe Key를 추가하는 역할을 하며 이 Key는 Registry에 등록되게 된다.<BR><BR>이제 거의 끝나간다... ^^<BR><BR>마지막으로 남은 문제 한가지... 바로 인증 부분이다. 이런 메시지를 본적이 있을 것이다. <BR><BR>"현재 보안 설정으로는 이 페이지의 ActiveX 컨트롤을 실행할 수 없습니다.<BR>그러므로, 이 페이지가 정상적으로 표시되지 않을 수 있습니다." <BR><BR>라는 메시지인데... 이 메시지는 인증키가 없는 ActiveX를 Load할 경우 나타내는 보안 메시지이다. 이 메시지를 안보이게 해결하려면 IE의 보안 수준을 "낮음"으로 설정함으로써 해결 할 수 있는데 모든 사용자에게 그렇게 설정하도록 하는것은 매우 위험한 발상이다. <BR><BR>이것을 또 해결해 보자... 쩝...<BR><BR>VerySign이란 인증 전문 회사를 들어보았을 것이다. 이 VerySign이란 할일없는 회사는 인증키만 내주고 할 일없이 200달러나 받아 챙긴다. 정말 칼만 안들었지 으...<BR><BR>우리같은 영세민이 어찌 간단한 ActiveX 하나때문에 200달러나 낼 수 있겠는가... <BR>그러나 이 인증 제도는 반드시 필요하다. ActiveX는 다른 VBScript나 JAVAScript 또 JAVA Applet과는 달리 시스템을 파괴할 수 있는 코드를 가지고 있을 수 있기 때문이다. 맘먹기 따라 남의 하드를 말끔히 포맷 시키는 것도 가능하다. 그렇기 때문에 안전하다는 인증을 받도록 한 것은 잘한일이다. <BR>그러나 역시 영세한 본인은 이 인증 코드를 직접 만들어야 했다. 테스트를 위해 인증키를 만들 수 있도록 MS에서는 INet SDK를 공개했다. 이것은 MSDN에서 구할 수 있는데... 이 INETSDK를 이용해서 우리는 우리가 만든 ActiveX에 인증을 할 수있다. <BR><BR>사용방법은... 먼저 받은 INET SDK를 설치한다. 그리고 나서 아래의 순서대로 차례차례 따라해보자... 반드시 Command Line에서 해야 한다는 것을 주의하자...<BR><BR>1. SETREG 1 True<BR><BR>2. MAKECERT -n "CN=영재의 보증서" -sv Test.Pvk Test.Cer<BR><BR>3. [Create Private Key Password] 대화상자가 뜨며 암호를 요구하면<BR>아무 거나 입력한다. 단 기억할 수 있는 암호로... ^^<BR><BR>4. CERT2SPC Test.cer Test.spc<BR><BR>5. 다시 대화상자가 뜨며 암호를 요구하면 아까 암호를 넣는다.<BR>단, 안 물어보는 경우도 있다는 사실... ^^<BR><BR>6. SIGNCODE -spc Test.spc -v Test.Pvk -n "Test" Test.OCX<BR>( 여기서 Test.OCX는 내가 만든 OCX의 이름으로 대체하면 된다. )<BR><BR>7. 또 대화상자가 뜨며 암호를 물어보면 아까 그 암호를 또 넣는다.<BR>단, 이것도 안물어 보는 경우가 있다는 사실...<BR><BR>이렇게 해서 내가 만든 ActiveX에 인증 코드를 추가하였다. 쩝... 처음이 어렵지 해 보면 무척 간단하다... 5번까지 해서 만들어둔 .spc파일과 .pvk 파일은 두고 두고 써먹을 수 있다는 사실... 매번 6번만 다시 실행해 주면 된다.<BR><BR>단, 6번은 델파이의 Web Deploy Option 대화상자에서 Code Sign Project를 Check 해주고... Code Sign 탭으로 가서 'Credential File' 항목에 .SPC 파일을 넣어주고 'Private Key' 항목에 .PVK 파일을 넣어줌으로써 생략할 수 있다. <BR><BR><BR><BR>마치며...<BR><BR>지금까지 말도 안되는 소릴 지껄여 봤당... 흐...<BR>혹시 본인과 같이 ActiveX 땜에 고민 했었던 분이 계시다면... 아마 큰 도움은 아니더라도 참고는 될 수 있을것이라 생각한다. 아직 못 다한 얘기가 있지만 ActiveX를 제작하고 Web에 올리는데 도움이 되는 얘기를 다음번에 한 번더 해 보도록 하자...<BR>그럼 안농히~ <!--"<-->]]></description>
    <category><![CDATA[Delphi]]></category>
    <category><![CDATA[DirectShow]]></category>
    <author><![CDATA[췌엠]]></author>
    <guid><![CDATA[http://kurapa.com/content-a413]]></guid>
    <pubDate>2006-09-28T12:47:28+00:00</pubDate>
  </item>
  <item>
    <title><![CDATA[ActiveX를 Safe한 Component로 등록하는 방법]]></title>
    <link>http://kurapa.com/content-a412</link>
    <description><![CDATA[Install the appropriate registry keys for your control. You can do this by replacing the factory creation code (at the bottom of your control's implementation unit) with a call that creates a special factory with the object safety registry entries. Basically this means replacing the code that says "TActiveXControlFactory.Create" with "TSafeActiveXControlFactory.Create", and USEing the following unit. <BR><BR><BR><BR>unit SafeFactory;<BR>interface<BR>uses ComObj, ActiveX, AXCtrls;<BR><BR>const<BR>CATID_SafeForScripting: TGUID = '{7DD95801-9882-11CF-9FA9-00AA006C42C4}';<BR>CATID_SafeForInitializing: TGUID =<BR>'{7DD95802-9882-11CF-9FA9-00AA006C42C4}';<BR><BR>type<BR>TSafeActiveFormFactory = class( TActiveFormFactory )<BR>&nbsp;&nbsp; procedure UpdateRegistry( Register: Boolean ); override;<BR>end;<BR><BR>TSafeActiveXControlFactory = class( TActiveXControlFactory )<BR>&nbsp;&nbsp; procedure UpdateRegistry( Register: Boolean ); override;<BR>end;<BR><BR>implementation<BR><BR>procedure AddSafetyKeys( const ClassID: TGUID );<BR>var<BR>ClassKey: string;<BR>begin&nbsp;&nbsp;<BR>ClassKey := 'CLSID\' + GUIDToString( ClassID );<BR>CreateRegKey( ClassKey + '\Implemented Categories','','');<BR>CreateRegKey( ClassKey + '\Implemented Categories\'+GUIDToString(<BR>&nbsp;&nbsp;&nbsp;&nbsp;CATID_SafeForScripting ),'','');<BR>CreateRegKey( ClassKey + '\Implemented Categories\'+GUIDToString(<BR>&nbsp;&nbsp;&nbsp;&nbsp;CATID_SafeForInitializing ),'','');<BR>end;<BR><BR>procedure RemoveSafetyKeys( const ClassID: TGUID );<BR>var<BR>ClassKey: string;<BR>begin&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>ClassKey := 'CLSID\' + GUIDToString( ClassID );<BR>DeleteRegKey( ClassKey + '\Implemented Categories\'+GUIDToString(<BR>&nbsp;&nbsp;&nbsp;&nbsp; CATID_SafeForInitializing ));<BR>DeleteRegKey( ClassKey + '\Implemented Categories\'+GUIDToString(<BR>&nbsp;&nbsp;&nbsp;&nbsp; CATID_SafeForScripting ));<BR>DeleteRegKey( ClassKey + '\Implemented Categories');<BR>end;<BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>{TSafeActiveFormFactory}<BR>procedure TSafeActiveFormFactory.UpdateRegistry( Register: Boolean );<BR>begin<BR>if Register then<BR>begin<BR>&nbsp;&nbsp; AddSafetyKeys( ClassID );<BR>&nbsp;&nbsp; inherited UpdateRegistry( Register );<BR>end<BR>else<BR>begin<BR>&nbsp;&nbsp; RemoveSafetyKeys( ClassID );<BR>&nbsp;&nbsp; inherited UpdateRegistry( Register );<BR>end;<BR>end;<BR><BR>{TSafeActiveXControlFactory}<BR>procedure TSafeActiveXControlFactory.UpdateRegistry( Register: Boolean );<BR>begin<BR>if Register then<BR>begin<BR>&nbsp;&nbsp; AddSafetyKeys( ClassID );<BR>&nbsp;&nbsp; inherited UpdateRegistry( Register );<BR>end<BR>else<BR>begin<BR>&nbsp;&nbsp; RemoveSafetyKeys( ClassID );<BR>&nbsp;&nbsp; inherited UpdateRegistry( Register );<BR>end;<BR>end;<BR><BR>end.<BR><br />]]></description>
    <category><![CDATA[activex]]></category>
    <category><![CDATA[safecode]]></category>
    <author><![CDATA[췌엠]]></author>
    <guid><![CDATA[http://kurapa.com/content-a412]]></guid>
    <pubDate>2006-09-28T12:46:31+00:00</pubDate>
  </item>
  <item>
    <title><![CDATA[투명 윈도우 구현 방법]]></title>
    <link>http://kurapa.com/content-a411</link>
    <description><![CDATA[투명 윈도우 만드는 방법<BR><BR>procedure TForm1.Button1Click(Sender: TObject);<BR>begin<BR>SetWindowLong(handle, GWL_EXSTYLE, GetWindowLong(handle, GWL_EXSTYLE) or WS_EX_LAYERED);<BR>SetLayeredWindowAttributes(handle, 0, Round((255 * 70 ) / 100), LWA_ALPHA);<BR>end;<BR><BR><BR><BR>이를 구현하기 위해서는 첨부되어져 있는 [win2k.pas]를 include하거나 다음을 상단부에 직접 define 한다.<BR><BR>uses<BR>Windows;<BR><BR>// Layered Window 관련<BR><BR>const<BR>{$EXTERNALSYM GWL_EXSTYLE}<BR>GWL_EXSTYLE = -20;<BR><BR>{$EXTERNALSYM WS_EX_LAYERED}<BR>WS_EX_LAYERED = $80000;<BR><BR><BR>{$EXTERNALSYM LWA_COLORKEY}<BR>LWA_COLORKEY = $00001;<BR>{$EXTERNALSYM LWA_ALPHA}<BR>LWA_ALPHA = $00002;<BR><BR>{$EXTERNALSYM ULW_COLORKEY}<BR>ULW_COLORKEY = $00001;<BR>{$EXTERNALSYM ULW_ALPHA}<BR>ULW_ALPHA = $00002;<BR>{$EXTERNALSYM ULW_OPAQUE}<BR>ULW_OPAQUE = $00004;<BR><BR>{$EXTERNALSYM UpdateLayeredWindow}<BR>function UpdateLayeredWindow(hwnd: HWND; hdcDst: HDC; pptDst: PPoint; psize: PSize;<BR>hdcsrc: HDC; pptsrc: PPoint; crKey: COLORREF; pblend: PBlendFunction;<BR>dwFlags: DWORD): BOOL; stdcall;<BR><BR>{$EXTERNALSYM SetLayeredWindowAttributes}<BR>function SetLayeredWindowAttributes(hwnd: HWND; crKey: COLORREF; bAlpha: Byte;<BR>dwFlags: DWORD): BOOL; stdcall;<BR><BR>implementation<BR><BR>{$EXTERNALSYM UpdateLayeredWindow}<BR>function UpdateLayeredWindow; external user32 name 'UpdateLayeredWindow';<BR>{$EXTERNALSYM SetLayeredWindowAttributes}<BR>function SetLayeredWindowAttributes; external user32 name 'SetLayeredWindowAttributes'<!--"<-->]]></description>
    <category><![CDATA[투명윈도우]]></category>
    <author><![CDATA[췌엠]]></author>
    <guid><![CDATA[http://kurapa.com/content-a411]]></guid>
    <pubDate>2006-09-28T12:45:46+00:00</pubDate>
  </item>
  <item>
    <title><![CDATA[웹브라우져에 붙이기 위한 ActiveX 프로그래밍 방법]]></title>
    <link>http://kurapa.com/content-a408</link>
    <description><![CDATA[<SPAN style="LINE-HEIGHT: 160%">&nbsp; <TABLE style="TABLE-LAYOUT: fixed" cellSpacing=0 cellPadding=0 width="100%" border=0><COLGROUP><COL width="100%"></COL><TBODY><TR><TD vAlign=top>- ActiveX의 등록<BR><BR><BR>들어가며...<BR><BR>얼마전 까지만 해도 ActiveX의 제작이나 사용은 나에겐 먼나라 같은 일이었다.<BR>그런데 그것이 현실로 다가올 줄이야. 흑흑...<BR>ActiveX의 제작까지는 델파이라는 훌륭한 매개체를 이용하여 손쉽게 작성할 수 있었다. 그런데 문제는 바로 이 제작된 ActiveX의 등록에 있었다. 이 ActiveX의 등록 방법에 대해 간단히 알아보자.<BR><BR><BR>AcitveX의 제작...<BR><BR>ActiveX의 제작은 이미 여러번 강좌나 기타 책등에서 많이 다룬 문제이므로 이곳에서는 생략하기로 하고 실제 이렇게 제작된 ActiveX를 어떻게 Web에 보이게 할 것인지 생각해 보자.<BR>제작된 ActiveX는 일단 등록을 해야 한다. 등록은 Regsvr32.exe를 이용해서 하게 되는데 일단 등록까지는 별 문제가 없을 것이다. <BR>이제 테스트용 HTML을 제작해서 ActiveX를 불러 오기만 하면 된다.<BR>이때 한가지 문제가 생긴다. 그냥 단순히 불러오기만 하는 것이라면 커다란 문제가 없다. 그런데 이 ActiveX의 내부 Procedure나 Event를 HTML Code와 Sink 시키려면 AcitveX의 Object ID가 있어야 한다. <BR>애석하게도 델파이에서 생성시켜 주는 테스트용 HTML에서는 결정적으로 이 Object ID를 생성해 주지 않는다. 하다 못해 VB도 생성해 주는 이 Object ID를 델파이에서 생성해 주지 않는다니 가슴이 몹시 아프다. 쩝...<BR><BR>우리가 할 수 있는 방법은 몇가지가 있다. 직접 이 ID를 생성해 주는 것이 첫번째이고, 두번째는 Edit Plus나 다른 툴을 이용하는 것이다. <BR>개발자들은 매우 게으른 사람들이란 얘기를 누군가 했었다. 그 말인 즉 개발자들은 어떤 문제를 손쉽게 해결해 줄 수 있는 도구를 먼저 찾고 없으면 그 도구를 만들어서라도 일을 손쉽게 해결하려는 경향이 있다는 것이다. 맞는 말이다. 본인 또한 이런 일을 해 줄수 있는 도구를 찾았으며 곧 MS에서 무료로 제공하는 두개의 툴을 구할 수 있었다. <BR>'ActiveX control Pad'란 녀석과 'ActiveX Lister'란 녀석들인데 이 툴을 사용하면 AtiveX를 개발 하고 나서 웹에 올리는데 드는 수고를 1/3로 줄여준다. <BR><BR>간단히 소개를 하자면... <BR><BR>'ActiveX Lister'란 녀석은 기존에 시스템에 등록되어있는 모든 ActiveX 컨트롤들은 일목 요연하게 표시하여 준다. 그중 하나를 선택하고 복사를 하면 Clipboard에 복사를 해 주는데... HTML로 붙이기를 해 보면 그&nbsp;&nbsp;편리함에 새삼 놀라게 된다. <BR><BR>예로... 'Microsoft Forms 2.0 Image'라는 AcitveX를 선택하고 복사한 후 HTML에서 붙이기를 하면...<BR><BR>&lt;Object<BR>&nbsp;&nbsp;Id="Microsoft Forms 2.0 Image"<BR>&nbsp;&nbsp;Classid="clsid:4c599241-6926-101B-9992-00000B65C6F9"<BR>&nbsp;&nbsp;Width=80<BR>&nbsp;&nbsp;Height=30<BR>&lt;/Object&gt;<BR><BR>라고 만들어 준다. 간편하지 않은가? ^^<BR>우리가 원했던 Object ID까지 모두 생성해서 하나의 HTML 코드로 만들어 준다. 생활에 편리함을 주는 도구이다. <BR><BR>또다른 툴인 'ActiveX Control Pad'란 녀석은 그 기능에 몹시 놀라게 된다.<BR>Lister란 녀석은 간단히 Object를 생성하는 코드만을 만들어 주는데 반해 이 제어패드는 Object를 생성코드는 물론 ActiveX에 정의된 모든 Procedure와 Event를 보여주며 손쉽게 이 procedure와 Event를 HTML에 붙일 수 있도록 VBScript 코드를 자동으로 생성해 준다. 또 JAVAScript 코드로도 만들어 준단다...<BR>이 얼마나 고마운 녀석인가... 흑...<BR><BR>이 고마운 녀석들은 www.microsoft.com/intdev/sdk 에서 모두 구할 수 있다.<BR>참고하자...<BR><BR>이렇게 함으로써 Object ID를 생성하는 부분은 해결이 되었고...<BR>또다른 문제 하나... 바로 보안 경고 메시지가 뜬다는 것이다. <BR><BR>"이 페이지의 ActiveX는 잠재적 위험성이 있습니다. 초기화 및 스크립트 액세스를 허용하시겠습니까?" <BR><BR>라고 물어보는 Dialog 창이 매번 실행 할 때 마다 뜬다. 이 메시지는 IE의 옵션 조작으로도 안 보이게 할 방법이 없다. <BR><BR>이것은 이 ActiveX 안전하다는 것을 보장하는 코드가 빠져있기 때문에 나타나는 메시지인데. VC++에서는 *.RGS라는 파일을 만들어주며 이 파일의 내용은 registry에 Safe Key를 추가하는 역할을 하는 코드가 들어있다. <BR>이 Safe Key를 Registry에 추가해야만 이 메시지를 피할 수 있는데 이 또한 애석하게도 델파이에서는 생략되어 있다. <BR><BR>이것을 가능하게 해주는 'Safefactory.pas'란 파일의 풀 소스를 아래에 첨부한다.<BR><BR>- safefactory.pas -<BR><BR>unit SafeFactory;<BR>interface<BR>uses ComObj, ActiveX, AXCtrls;<BR><BR>const<BR>CATID_SafeForScripting: TGUID =<BR>'{7DD95801-9882-11CF-9FA9-00AA006C42C4}';<BR>CATID_SafeForInitializing: TGUID =<BR>'{7DD95802-9882-11CF-9FA9-00AA006C42C4}';<BR><BR>type<BR>TSafeActiveFormFactory = class( TActiveFormFactory )<BR>&nbsp;&nbsp;procedure UpdateRegistry( Register: Boolean ); override;<BR>end;<BR><BR>TSafeActiveXControlFactory = class( TActiveXControlFactory )<BR>&nbsp;&nbsp;procedure UpdateRegistry( Register: Boolean ); override;<BR>end;<BR><BR>implementation<BR><BR>procedure AddSafetyKeys( const ClassID: TGUID );<BR>var<BR>ClassKey: string;<BR>begin&nbsp;&nbsp;<BR>ClassKey := 'CLSID\' + GUIDToString( ClassID );<BR>CreateRegKey( ClassKey + '\Implemented Categories','','');<BR>CreateRegKey( ClassKey + '\Implemented Categories\'+GUIDToString(<BR>CATID_SafeForScripting ),'','');<BR>CreateRegKey( ClassKey + '\Implemented Categories\'+GUIDToString(<BR>CATID_SafeForInitializing ),'','');<BR>end;<BR><BR>procedure RemoveSafetyKeys( const ClassID: TGUID );<BR>var<BR>ClassKey: string;<BR>begin&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>ClassKey := 'CLSID\' + GUIDToString( ClassID );<BR>DeleteRegKey( ClassKey + '\Implemented Categories\'+GUIDToString(<BR>CATID_SafeForInitializing ));<BR>DeleteRegKey( ClassKey + '\Implemented Categories\'+GUIDToString(<BR>CATID_SafeForScripting ));<BR>DeleteRegKey( ClassKey + '\Implemented Categories');<BR>end;<BR><BR>{TSafeActiveFormFactory}<BR>procedure TSafeActiveFormFactory.UpdateRegistry( Register: Boolean ); begin<BR>if Register then<BR>begin<BR>&nbsp;&nbsp;AddSafetyKeys( ClassID );<BR>&nbsp;&nbsp;inherited UpdateRegistry( Register );<BR>end<BR>else<BR>begin<BR>&nbsp;&nbsp;RemoveSafetyKeys( ClassID );<BR>&nbsp;&nbsp;inherited UpdateRegistry( Register );<BR>end;<BR>end;<BR><BR>{TSafeActiveXControlFactory}<BR>procedure TSafeActiveXControlFactory.UpdateRegistry( Register: Boolean );<BR>begin<BR>if Register then<BR>begin<BR>&nbsp;&nbsp;AddSafetyKeys( ClassID );<BR>&nbsp;&nbsp;inherited UpdateRegistry( Register );<BR>end<BR>else<BR>begin<BR>&nbsp;&nbsp;RemoveSafetyKeys( ClassID );<BR>&nbsp;&nbsp;inherited UpdateRegistry( Register );<BR>end;<BR>end;<BR><BR>end.<BR><BR>이 부분을 safefactory.pas로 저장하고... 기존에 만들었던 AcitveX의 initialization 부분의 TActiveFormFactory 를 TSafeActiveFormFactory로 고쳐준다.<BR><BR>예 )<BR><BR>initialization<BR>TeActiveFormFactory.Create(<BR>&nbsp;&nbsp;ComServer,<BR>&nbsp;&nbsp;TActiveFormControl,<BR>&nbsp;&nbsp;TMailServer,<BR>&nbsp;&nbsp;Class_MailServer,<BR>&nbsp;&nbsp;1,<BR>&nbsp;&nbsp;'',<BR>&nbsp;&nbsp;OLEMISC_SIMPLEFRAME or OLEMISC_ACTSLIKELABEL);<BR>end.<BR><BR>이렇게 고친다... ^^<BR><BR>initialization<BR>TSafeActiveFormFactory.Create(<BR>&nbsp;&nbsp;ComServer,<BR>&nbsp;&nbsp;TActiveFormControl,<BR>TMailServer,<BR>&nbsp;&nbsp;Class_MailServer,<BR>&nbsp;&nbsp;1,<BR>&nbsp;&nbsp;'',<BR>&nbsp;&nbsp;OLEMISC_SIMPLEFRAME or OLEMISC_ACTSLIKELABEL);<BR>end.<BR><BR>이렇게 함으로써 우리는 보안 메시지를 안뜨게 할 수 있게 되었다.<BR>이 Safefactory의 역할은 해당 CLSID에 Safe Key를 추가하는 역할을 하며 이 Key는 Registry에 등록되게 된다.<BR><BR>이제 거의 끝나간다... ^^<BR><BR>마지막으로 남은 문제 한가지... 바로 인증 부분이다. 이런 메시지를 본적이 있을 것이다. <BR><BR>"현재 보안 설정으로는 이 페이지의 ActiveX 컨트롤을 실행할 수 없습니다.<BR>그러므로, 이 페이지가 정상적으로 표시되지 않을 수 있습니다." <BR><BR>라는 메시지인데... 이 메시지는 인증키가 없는 ActiveX를 Load할 경우 나타내는 보안 메시지이다. 이 메시지를 안보이게 해결하려면 IE의 보안 수준을 "낮음"으로 설정함으로써 해결 할 수 있는데 모든 사용자에게 그렇게 설정하도록 하는것은 매우 위험한 발상이다. <BR><BR>이것을 또 해결해 보자... 쩝...<BR><BR>VerySign이란 인증 전문 회사를 들어보았을 것이다. 이 VerySign이란 할일없는 회사는 인증키만 내주고 할 일없이 200달러나 받아 챙긴다. 정말 칼만 안들었지 으...<BR><BR>우리같은 영세민이 어찌 간단한 ActiveX 하나때문에 200달러나 낼 수 있겠는가... <BR>그러나 이 인증 제도는 반드시 필요하다. ActiveX는 다른 VBScript나 JAVAScript 또 JAVA Applet과는 달리 시스템을 파괴할 수 있는 코드를 가지고 있을 수 있기 때문이다. 맘먹기 따라 남의 하드를 말끔히 포맷 시키는 것도 가능하다. 그렇기 때문에 안전하다는 인증을 받도록 한 것은 잘한일이다. <BR>그러나 역시 영세한 본인은 이 인증 코드를 직접 만들어야 했다. 테스트를 위해 인증키를 만들 수 있도록 MS에서는 INet SDK를 공개했다. 이것은 MSDN에서 구할 수 있는데... 이 INETSDK를 이용해서 우리는 우리가 만든 ActiveX에 인증을 할 수있다. <BR><BR>사용방법은... 먼저 받은 INET SDK를 설치한다. 그리고 나서 아래의 순서대로 차례차례 따라해보자... 반드시 Command Line에서 해야 한다는 것을 주의하자...<BR><BR>1. SETREG 1 True<BR><BR>2. MAKECERT -n "CN=영재의 보증서" -sv Test.Pvk Test.Cer<BR><BR>3. [Create Private Key Password] 대화상자가 뜨며 암호를 요구하면<BR>아무 거나 입력한다. 단 기억할 수 있는 암호로... ^^<BR><BR>4. CERT2SPC Test.cer Test.spc<BR><BR>5. 다시 대화상자가 뜨며 암호를 요구하면 아까 암호를 넣는다.<BR>단, 안 물어보는 경우도 있다는 사실... ^^<BR><BR>6. SIGNCODE -spc Test.spc -v Test.Pvk -n "Test" Test.OCX<BR>( 여기서 Test.OCX는 내가 만든 OCX의 이름으로 대체하면 된다. )<BR><BR>7. 또 대화상자가 뜨며 암호를 물어보면 아까 그 암호를 또 넣는다.<BR>단, 이것도 안물어 보는 경우가 있다는 사실...<BR><BR>이렇게 해서 내가 만든 ActiveX에 인증 코드를 추가하였다. 쩝... 처음이 어렵지 해 보면 무척 간단하다... 5번까지 해서 만들어둔 .spc파일과 .pvk 파일은 두고 두고 써먹을 수 있다는 사실... 매번 6번만 다시 실행해 주면 된다.<BR><BR>단, 6번은 델파이의 Web Deploy Option 대화상자에서 Code Sign Project를 Check 해주고... Code Sign 탭으로 가서 'Credential File' 항목에 .SPC 파일을 넣어주고 'Private Key' 항목에 .PVK 파일을 넣어줌으로써 생략할 수 있다. <BR><BR><BR><BR>마치며...<BR><BR>지금까지 말도 안되는 소릴 지껄여 봤당... 흐...<BR>혹시 본인과 같이 ActiveX 땜에 고민 했었던 분이 계시다면... 아마 큰 도움은 아니더라도 참고는 될 수 있을것이라 생각한다. 아직 못 다한 얘기가 있지만 ActiveX를 제작하고 Web에 올리는데 도움이 되는 얘기를 다음번에 한 번더 해 보도록 하자...<BR>그럼 안농히~ <!--"<--></TD></TR></TBODY></TABLE></SPAN>]]></description>
    <category><![CDATA[activex]]></category>
    <category><![CDATA[웹브라우져]]></category>
    <author><![CDATA[췌엠]]></author>
    <guid><![CDATA[http://kurapa.com/content-a408]]></guid>
    <pubDate>2006-09-28T12:45:07+00:00</pubDate>
  </item>
  <item>
    <title><![CDATA[how to record keyboard & mouse event]]></title>
    <link>http://kurapa.com/content-a407</link>
    <description><![CDATA[We will now present an example of using Windows API functions by creating an application that serves the useful function of hooking into the Windows messaging system, and recording any keyboard and mouse input to be used for later playback. We will call our macro recording application "Hookit!". Our "Hookit!" application is a great starting place for a full featured Windows macro recorder, reminiscent of the early days of DOS and my favorite "Sidekick's Sidekick", Borland's highly successful TSR program "SuperKey"! It's worth noting that unlike 16 bit Windows, neither Windows 95 or Windows NT shipped with a macro recording feature. <BR><BR>Since "Hookit!" will use system wide Windows hook functions for JournalRecord and JournalPlayback, we will also create a Windows dynamic link library along the way! Both the application and the dynamic link library will perform callbacks. <BR><BR>Pascal was originally designed to be a teaching language, thus the code example presented was written in Pascal, and can easily be ported to other languages such as "C". Note that the code cannot be ported to Visual Basic, since, as of this writing, Visual Basic does not support creating dynamic link libraries or callback functions. <BR><BR>The code presented may also be directly ported between 16 and 32 bit compilers, and will successfully compile with Borland's Turbo Pascal for Windows 1.5, Borland Pascal for Windows 7.0 / 7.01, Delphi 1.0 / 1.02, and Delphi 2.0. To make it easy to port to other languages, no Pascal or Delphi specific features are used. The code also serves as a model for creating a "traditional" Windows program in Delphi, and utilizing a dialog box template for the main window of a program. <BR><BR>Although "Hookit!" uses features designed to run on Windows 3.1 or above, the example code can be adapted to run on earlier versions of Windows (2.x and 3.0). <BR><BR><BR>The Journal Hooks: JournalRecord and JournalPlayback<BR>The Journal hook functions provide a easy way to record keyboard and mouse events on a system-wide basis, and play the events back at some later date. <BR><BR>You can install a JournalRecord or JournalPlayback hook callback function by calling the Windows API function SetWindowsHookEx(), and passing the address of your hook callback function. An instanced address is not needed, as both the JournalRecord and JournalPlayback callback functions are system wide hooks, and must reside in a dynamic link library. Calling SetWindowHookEx() will return a 32 bit handle to your hook callback function for you to identify yourself to other hook functions already in the "Hook Chain", and to remove your hook from the chain when you are done recording. <BR><BR>When you call SetWindowsHookEx() with the address of your JournalRecord callback function, Windows will return immediately, and the recording begins. Your JournalRecord callback function will get called with a code indicating the following conditions:<BR><BR><BR>There is a keyboard or mouse event to record. <BR>The system is entering a modal state. <BR>The system leaving a modal state. <BR>The system wants you to call the next hook in the hook chain. <BR>If there is a keyboard or mouse event, the code will equal "HC_ACTION", and you are free to record the event. A pointer to an EVENTMSG is passed to you in the lParam parameter of your JournalRecord callback function. The system time that the event was originally fired is contained in time parameter of the EVENTMSG structure. For playback purposes, you will need to make a copy of the EVENTMSG and change the time in the copy to reflect the net time into the recording, since you will need to synchronize the playback of the message to the system time at playback. This is done by getting the system time when you start the recording, and subtracting it from the message time. <BR><BR>If the system enters a modal state, the code will equal "HC_SYSMODALON", indicating something bad has happened to the system, and you should temporarily stop recording, and call the next message hook in the "Hook Chain", so hooks further down the chain will know what is going on. When the system returns from a modal state, the code will equal "HC_SYSMODALOFF", and you may resume recording. <BR><BR>Finally, if the code is less than zero, the system is asking you to call the next hook in the chain, and you should do so without further processing. When you are through recording, you can simply call the Windows API function UnHookWindowsHookEx() passing back the 32 bit handle given to you when you originally called the SetWindowsHookEx() function, and Windows will remove your hook callback function from the "Hook Chain". <BR><BR>When you are ready to playback your recording, you will call SetWindowsHookEx() again, passing the address of your JournalPlayback callback function. Windows will return immediately, and the playback begins. During playback, normal mouse and keyboard input is automatically disabled by the system. <BR><BR>Your JournalPlayback callback function will get called with a code indicating one the following conditions:<BR><BR><BR>HC_SKIP- You should retrieve the next message. If there are no more messages to play, then you may safely call the Windows API function UnHookWindowsHookEx(), passing the handle to your hook function to end the playback. <BR>HC_GETNEXT- You should play the current message. <BR>HC_SYSMODALON- The system is entering a modal state. This indicates something bad has happened. You should call the next hook in the hook chain, so other hooks will know something is up. <BR>HC_SYSMODALOFF- The system leaving a modal state, and Windows has unhooked your JournalPlayback callback procedure right out from under you. You are done. As your last act, you should call the next hook in the hook chain, so other hooks will know they are hosed as well. <BR>Code &lt; 0- The system wants you to call the next hook in the hook chain without further processing. <BR>You should go ahead and retrieve the first message before you call the SetWindowsHookEx() function, since the system will ask you to play the first message before requesting the next message. You will also need to get the system time, since you will need to "fix up" the playback time of each of your recorded messages to synchronize with the system time at playback. <BR><BR>Windows may ask you to play the same message more than once. The first time Windows asks you to play the current message, your JournalPlayback callback function should return the difference between the current time and the time the message is scheduled to play. If the difference between the current time and the time the message is scheduled to play is negative, your JournalPlayback callback function should return zero. The JournalPlayback callback function must also return zero if the same message is requested to be played more than once. <BR><BR><BR>How HOOKIT! works<BR>Our application (HOOKIT.EXE) will contain four buttons: <BR><BR>Start Recording <BR>Stop Recording <BR>Playback <BR>Done <BR>and one callback function: <BR><BR>PlaybackFinished() <BR>Our dynamic link library (HOOKLIB.DLL) will contain three functions that our calling application will use: <BR><BR>StartRecording() <BR>StopRecording() <BR>Playback() <BR>and two hook functions that Windows will use: <BR><BR>JournalRecordProc() <BR>JournalPlaybackProc() <BR><BR>HOOKIT! application logic: <BR><BR>Allow the program to start only if the version of Windows is equal or greater than Window's 3.1. <BR>We will need to supply a callback function to be called whenever a macro's playback has finished, since the Playback() function will return immediately, and our application will continue to execute during playback. We will need to call MakeProcInstance() to get the instanced address of our callback function PlaybackFinished() to pass to the Playback() function. Since we cannot call FreeProcInstance() in the middle of the callback, we must declare a global variable to hold the instanced address of PlaybackFinished() on program startup, and free it on exit from the program. <BR>Create a main window from a Dialog Box template. <BR>Upon creation of our main window, we will enable the "Start Recording" and "Done" button. We will disable the "Stop Recording" and "Playback" button, since we have no recording to stop or playback. <BR>When the "Done" button is selected we will free the instanced address of our callback function PlaybackFinished() and exit the program. <BR>When the "Start Recording" button is selected, we will disable the "Start Recording" and "Done" button, since we don't want to allow more than one recording at a time, and we don't want to allow the user to quit in the middle of recording. We will enable the "Stop Recording" button and call our StartRecording() function. If the StartRecoding() function returns an error, we will announce the error to the user, and reset the buttons to their default state before the "Start Recording" button was pressed. <BR>When the "End Recoding" button is pressed, We will call the StopRecording() function passing it the filename to store the macro in. Then we will enable both the "Done" and "StartRecording" buttons, since we can now allow the user to quit, and we want the user to have the option to record or rerecord the session. We will enable "Playback" button only if anything has successfully been recorded. <BR>When the "Playback" button is selected, we will disable all of our buttons, and call the Playback() function, passing it the filename of the macro to play, the instanced address of our callback function PlaybackFinished(), and a handle to our main window to be passed back to us as application defined data. When the macro playback is done, our hook function will callback to our program's PlaybackFinished() function, letting us know it has finished, and passing us back the handle to our main window as application data. We must do this since the Playback() function will return immediately, causing our program to continue to run during the macro's playback. This will allow us to know when it safe to enable our program's buttons again. If the Playback() function returns an error, we will reset the buttons to their default state before the "Playback" button was pressed. <BR>When our callback function PlaybackFininshed() is called, we can enable the "Start Recording", "Playback" and "Done" buttons. <BR><BR>HOOKLIB dynamic link library logic:<BR><BR><BR>If an error occurs during the call to StartRecord(), or, we are already recording or playing, we will return zero without further processing. <BR>During the recording process, we will save keyboard and mouse events to an array. For the sake of simplicity, we will limit the number of recorded events to what will fit in a 64k memory block. <BR>When StopRecording() is called, if any events have been recorded, we will write the recorded events to the disk for later playback. We will then unhook our JournalRecordProc() from the hook chain. We will return an error code of -1 if nothing is currently recording or -2 if there was trouble unhooking from the "Hook Chain". Otherwise we will return the number of messages recorded. <BR>When the Playback() function is called, we will start the playback. If an error occurs or their is nothing to playback, we will return zero without further processing. <BR>When the playback is finished, we will callback to the application announcing we are done playing back the macro. <BR>Since the SetWindowsHookEx() function does not allow us an appdata parameter for application defined data, we must declare several global variables for our hook callback functions, and avoid letting more than one application invoke either the StartRecord() and Playback() functions at any given time under Windows 3.1. <BR>We will define a global pointer called "PMsgBuff "that will point to an array of EventMsg structures to record to and playback from. Upon library startup, we will initialize this pointer to nil. Only when we are recording or playing a macro will this pointer actually point to a memory block, otherwise it will be nil. This will give us a way to determine whether or not we are in the process of recording or playing a macro. <BR>We will also need to define global variables for: <BR>"TheHook"- A 32 bit handle to our hook proc. <BR>"StartTime"- The starting time of the recording or playback. <BR>"MsgCount"- The total number of messages recorded. <BR>"CurrentMsg"- The current message playing. <BR>"ReportDelayTime"- If we should report a delay time. <BR>"SysModalOn"- If the system is currently in a "modal" state. <BR>"cbPlaybackFinishedProc"- The instanced address of the application's PlaybackFinished() callback function. <BR>"cbAppData"- The user defined application data parameter passed to our Playback() function. <BR><BR>Got-Ya's! <BR><BR>JournalRecordProc() is incorrectly documented as receiving a MSG structure. JournalRecordProc() actually receives the same EVENTMSG structure that is documented by JournalPlaybackProc(). <BR>The JournalRecordProc() and JournalPlaybackProc() hook procedures do not provide a user defined lpData parameter, so you must use global variables in your dynamic link library. <BR>Mouse events recorded with JournalRecord may not play back correctly if the display resolution has changed, or a window's position has changed. <BR>Keyboard events recorded with JournalRecord on a Windows system other than Windows NT will not play back correctly under Windows NT. Under Windows 3.1, the keyboard repeat count is stored in the ParamH parameter of the EVENTMSG structure. Under Windows NT, this parameter is always 1. The size of the EVENTMSG structure also changes under Win32. <BR>Your JournalPlaybackProc may be called many times with the HC_GETNEXT message. The system expects you to continue providing the same event to play until you receive a HC_SKIP message. <BR>Be sure to return a non-zero delay time only once for each unique event you process in your JournalPlaybackProc, else Windows NT may hang due to timing differences. If your JournalPlaybackProc() gets called with a code of HC_GETNEXT more than once for the same event, return zero from your JournalPlaybackProc. <BR>Interactive debugging of a journal hook cannot be done on a single machine. A Windows NT or Win32 application has an advantage that the system will send a WM_CANCELJOURNAL message to all applications when the system pulls the hook out from under the application when the user presses CTRL+ESC or CTRL+ALT+DEL. "HOOKIT!" handles this event gracefully. <BR>The SetWindowsHookEx() function will return immediately. This can be a problem if your program needs to know when the playback has finished. <BR>Since you will most likely use the Windows API function GetTickCount() for calculating messaging times, you always run the risk that the system time will wrap if windows has not been restarted in the last 49 days. If you account for the fact that not all compilers support unsigned 32 bit integers, the system time will appear to go negative sometime into the 24th day. <BR>The "handle" passed back from SetWindowsHookEx() is 32 bits wide, even in 16 bit environments. <BR>You may need to set your compiler's link buffer to compile to disk when building dynamic link libraries. If the file image is only created in memory, it will not be available to the program that uses it. <BR>JournalRecordProc() does not get along well with the new Windows 95 "StartKey" contained on some new keyboards. <BR><BR>Source code for HOOKLIB.DLL<BR>Note: Save the source as HOOKLIB.PAS for Pascal or HOOKLIB.DPR for Delphi. <BR><BR>{$C FIXED PRELOAD PERMANENT}<BR><BR>library HOOKLIB;<BR><BR>{$IFDEF Win32}<BR><BR>uses<BR>Windows;<BR><BR>type<BR>TwMsg = Longint;<BR>TwParam = Longint;<BR>TlParam = Longint;<BR><BR>{$ELSE}<BR><BR>uses<BR>{$IFDEF VER15}<BR>WinTypes, WinProcs, Win31;<BR>{$ELSE}<BR>{$IFDEF VER70}<BR>WinTypes, WinProcs, Win31;<BR>{$ELSE}<BR>WinTypes, WinProcs;<BR>{$ENDIF}<BR>{$ENDIF}<BR><BR>type<BR>TwMsg = Word;<BR>TwParam = Word;<BR>TlParam = Longint;<BR><BR>{$ENDIF}<BR><BR>const<BR>MAXMSG = 6500;<BR><BR>type<BR>PEventMsg = ^TEventMsg;<BR>TMsgBuff = Array[0..MAXMSG]<BR>of TEventMsg;<BR>TcbPlaybackFinishedProc<BR>= Procedure(AppData: Longint)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>{$IFDEF Win32} stdcall; {$ELSE} ; {$ENDIF}<BR><BR>var<BR>PMsgBuff: ^TMsgBuff;<BR>TheHook: HHook;<BR>StartTime: Longint;<BR>MsgCount: Longint;<BR>CurrentMsg: Longint;<BR>ReportDelayTime: Bool;<BR>SysModalOn: Bool;<BR>cbPlaybackFinishedProc:<BR>TcbPlaybackFinishedProc;<BR>cbAppData: Longint;<BR><BR><BR>{ ***********************************************************************<BR>}<BR>{ function JournalRecordProc(Code:<BR>Integer;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;wParam: TwParam;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;lParam: TlParam): Longint;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>{ Parameters: action to perform<BR>and message data.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>{ Returns: zero unless code<BR>&lt; 0, in which case return the result&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;from CallNextHookEx().<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>{ ***********************************************************************<BR>}<BR>function JournalRecordProc(Code:<BR>Integer;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>wParam: TwParam;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>lParam: TlParam): Longint<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>{$IFDEF Win32} stdcall; {$ELSE} ; export; {$ENDIF}<BR>begin<BR>JournalRecordProc := 0;<BR>case Code of<BR><BR>&nbsp;&nbsp; HC_ACTION: begin<BR>&nbsp;&nbsp;&nbsp;&nbsp; if SysModalOn then exit;<BR>&nbsp;&nbsp;&nbsp;&nbsp; if MsgCount &gt; MAXMSG<BR>then exit;<BR>&nbsp;&nbsp;&nbsp;&nbsp;{record the message}<BR>&nbsp;&nbsp;&nbsp;&nbsp; PMsgBuff^[MsgCount]<BR>:= PEventMsg(lParam)^;<BR>&nbsp;&nbsp;&nbsp;&nbsp;{set the delta time of<BR>the message}<BR>&nbsp;&nbsp;&nbsp;&nbsp; Dec(PMsgBuff^[MsgCount].Time,StartTime);<BR>&nbsp;&nbsp;&nbsp;&nbsp; Inc(MsgCount);<BR>&nbsp;&nbsp;&nbsp;&nbsp; exit;<BR>&nbsp;&nbsp; end;<BR><BR>&nbsp;&nbsp; HC_SYSMODALON: begin<BR>&nbsp;&nbsp;&nbsp;&nbsp; SysModalOn := True;<BR>&nbsp;&nbsp;&nbsp;&nbsp; CallNextHookEx(TheHook,<BR>Code, wParam, lParam);<BR>&nbsp;&nbsp;&nbsp;&nbsp; exit;<BR>&nbsp;&nbsp; end;<BR><BR>&nbsp;&nbsp; HC_SYSMODALOFF: begin<BR>&nbsp;&nbsp;&nbsp;&nbsp; SysModalOn := False;<BR>&nbsp;&nbsp;&nbsp;&nbsp; CallNextHookEx(TheHook,<BR>Code, wParam, lParam);<BR>&nbsp;&nbsp;&nbsp;&nbsp; exit;<BR>&nbsp;&nbsp; end;<BR><BR>end;<BR>if code &lt; 0 then<BR>&nbsp;&nbsp; JournalRecordProc := CallNextHookEx(TheHook,<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Code,<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; wParam,<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lParam);<BR>end;<BR><BR><BR>{ ***********************************************************************<BR>}<BR>{ function StartRecording:<BR>Integer;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>{ Parameters: none.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>{ Returns: non zero if successful.<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>{ ***********************************************************************<BR>}<BR>function StartRecording: Integer<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {$IFDEF<BR>Win32} stdcall; {$ELSE} ; export; {$ENDIF}<BR>begin<BR>StartRecording := 0;<BR>if pMsgBuff &lt;&gt; nil<BR>then exit;<BR>GetMem(PMsgBuff, Sizeof(TMsgBuff));<BR>if PMsgBuff = nil then exit;<BR>SysModalOn := False;<BR>MsgCount := 0;<BR>StartTime := GetTickCount;<BR>TheHook := SetWindowsHookEx(WH_JOURNALRECORD,<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp; JournalRecordProc,<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp; hInstance,<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp; 0);<BR>if TheHook &lt;&gt; 0 then<BR>begin<BR>&nbsp;&nbsp; StartRecording := 1;<BR>&nbsp;&nbsp; exit;<BR>end else begin<BR>&nbsp;&nbsp; FreeMem(PMsgBuff, Sizeof(TMsgBuff));<BR>&nbsp;&nbsp; PMsgBuff := nil;<BR>end;<BR>end;<BR><BR><BR>{ ***********************************************************************<BR>}<BR>{ function StopRecording(lpFileName:<BR>PChar): Integer;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>{ Parameters: pointer to filename<BR>to save to.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>{ Returns: number of records<BR>written.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-1 if not recording.<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-2 unable to unhook.<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>{ ***********************************************************************<BR>}<BR>function StopRecording(lpFileName:<BR>PChar): Longint<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {$IFDEF<BR>Win32} stdcall; {$ELSE} ; export; {$ENDIF}<BR><BR>var TheFile: File;<BR>begin<BR>if PMsgBuff = nil then begin<BR>&nbsp;&nbsp; StopRecording := -1;<BR>&nbsp;&nbsp; exit;<BR>end;<BR>if UnHookWindowsHookEx(TheHook)<BR>= False then begin<BR>&nbsp;&nbsp; StopRecording := -2;<BR>&nbsp;&nbsp; exit;<BR>end;<BR>TheHook := 0;<BR>if MsgCount &gt; 0 then<BR>begin<BR>&nbsp;&nbsp; Assign(TheFile, lpFileName);<BR>&nbsp;&nbsp;{$I-}<BR>&nbsp;&nbsp; Rewrite(TheFile, Sizeof(TEventMsg));<BR>&nbsp;&nbsp;{$I+}<BR>&nbsp;&nbsp; if IOResult &lt;&gt; 0<BR>then begin<BR>&nbsp;&nbsp;&nbsp;&nbsp; FreeMem(PMsgBuff, Sizeof(TMsgBuff));<BR>&nbsp;&nbsp;&nbsp;&nbsp; PMsgBuff := nil;<BR>&nbsp;&nbsp;&nbsp;&nbsp; StopRecording := 0;<BR>&nbsp;&nbsp;&nbsp;&nbsp; exit;<BR>&nbsp;&nbsp; end;<BR>&nbsp;&nbsp;{$I-}<BR>&nbsp;&nbsp; Blockwrite(TheFile, PMsgBuff^,<BR>MsgCount);<BR>&nbsp;&nbsp;{$I+}<BR>&nbsp;&nbsp; if IOResult &lt;&gt; 0<BR>then begin<BR>&nbsp;&nbsp;&nbsp;&nbsp; FreeMem(PMsgBuff, Sizeof(TMsgBuff));<BR>&nbsp;&nbsp;&nbsp;&nbsp; PMsgBuff := nil;<BR>&nbsp;&nbsp;&nbsp;&nbsp; StopRecording := 0;<BR>&nbsp;&nbsp;&nbsp;&nbsp;{$I-}<BR>&nbsp;&nbsp;&nbsp;&nbsp; Close(TheFile);<BR>&nbsp;&nbsp;&nbsp;&nbsp;{$I+}<BR>&nbsp;&nbsp;&nbsp;&nbsp; if IOResult &lt;&gt;<BR>0 then exit;<BR>&nbsp;&nbsp;&nbsp;&nbsp; exit;<BR>&nbsp;&nbsp; end;<BR>&nbsp;&nbsp;{$I-}<BR>&nbsp;&nbsp; Close(TheFile);<BR>&nbsp;&nbsp;{$I+}<BR>&nbsp;&nbsp; if IOResult &lt;&gt; 0<BR>then begin<BR>&nbsp;&nbsp;&nbsp;&nbsp; FreeMem(PMsgBuff, Sizeof(TMsgBuff));<BR>&nbsp;&nbsp;&nbsp;&nbsp; PMsgBuff := nil;<BR>&nbsp;&nbsp;&nbsp;&nbsp; StopRecording := 0;<BR>&nbsp;&nbsp;&nbsp;&nbsp; exit;<BR>&nbsp;&nbsp; end;<BR>end;<BR>FreeMem(PMsgBuff, Sizeof(TMsgBuff));<BR>PMsgBuff := nil;<BR>StopRecording := MsgCount;<BR>end;<BR><BR><BR>{ ***********************************************************************<BR>}<BR>{ function JournalPlaybackProc(Code:<BR>Integer;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;wParam: TwParam;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;lParam: TlParam): Longint;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>{ Parameters: action to perform<BR>and message data.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>{ Returns:&nbsp;&nbsp;if Code &lt; 0,<BR>returns the result from CallNextHookEx(),&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;otherwise returns<BR>the requested time to wait to fire&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;the next event<BR>or zero.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>{ ***********************************************************************<BR>}<BR>function JournalPlaybackProc(Code:<BR>Integer;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;wParam: TwParam;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;lParam: TlParam): Longint<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>{$IFDEF Win32} stdcall; {$ELSE} ; export; {$ENDIF}<BR>var<BR>TimeToFire: Longint;<BR>begin<BR>JournalPlaybackProc := 0;<BR>case Code of<BR><BR>&nbsp;&nbsp; HC_SKIP: begin<BR>&nbsp;&nbsp;&nbsp;&nbsp;{get the next message}<BR>&nbsp;&nbsp;&nbsp;&nbsp; Inc(CurrentMsg);<BR>&nbsp;&nbsp;&nbsp;&nbsp; ReportDelayTime := True;<BR>&nbsp;&nbsp;&nbsp;&nbsp;{are we finished?}<BR>&nbsp;&nbsp;&nbsp;&nbsp; if CurrentMsg &gt;=<BR>(MsgCount-1) then<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if TheHook &lt;&gt;<BR>0 then<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if UnHookWindowsHookEx(TheHook)<BR>= True then begin<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TheHook := 0;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FreeMem(PMsgBuff,<BR>Sizeof(TMsgBuff));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PMsgBuff := nil;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{callback to the<BR>application announcing we are finished}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cbPlaybackFinishedProc(cbAppData);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end;<BR>&nbsp;&nbsp;&nbsp;&nbsp; exit;<BR>&nbsp;&nbsp; end;<BR><BR>&nbsp;&nbsp; HC_GETNEXT: begin<BR>&nbsp;&nbsp;&nbsp;&nbsp;{play the current message}<BR>&nbsp;&nbsp;&nbsp;&nbsp; PEventMsg(lParam)^ :=<BR>PMsgBuff^[CurrentMsg];<BR>&nbsp;&nbsp;&nbsp;&nbsp; PEventMsg(lParam)^.Time<BR>:= StartTime + PMsgBuff^[CurrentMsg].Time;<BR>&nbsp;&nbsp;&nbsp;&nbsp;{if first time this message<BR>has played - report the delay time}<BR>&nbsp;&nbsp;&nbsp;&nbsp; if ReportDelayTime then<BR>begin<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ReportDelayTime :=<BR>False;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TimeToFire := PEventMsg(lParam)^.Time<BR>- GetTickCount;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if TimeToFire &gt;<BR>0 then JournalPlaybackProc := TimeToFire;<BR>&nbsp;&nbsp;&nbsp;&nbsp; end;<BR>&nbsp;&nbsp;&nbsp;&nbsp; exit;<BR>&nbsp;&nbsp; end;<BR><BR>&nbsp;&nbsp; HC_SYSMODALON:begin<BR>&nbsp;&nbsp;&nbsp;&nbsp;{something is wrong}<BR>&nbsp;&nbsp;&nbsp;&nbsp; SysModalOn := True;<BR>&nbsp;&nbsp;&nbsp;&nbsp; CallNextHookEx(TheHook,<BR>Code, wParam, lParam);<BR>&nbsp;&nbsp;&nbsp;&nbsp; exit;<BR>&nbsp;&nbsp; end;<BR><BR>&nbsp;&nbsp; HC_SYSMODALOFF:begin<BR>&nbsp;&nbsp;&nbsp;&nbsp;{we have been hosed by<BR>the system - our hook has been pulled!}<BR>&nbsp;&nbsp;&nbsp;&nbsp; SysModalOn := False;<BR>&nbsp;&nbsp;&nbsp;&nbsp; TheHook := 0;<BR>&nbsp;&nbsp;&nbsp;&nbsp; FreeMem(PMsgBuff, Sizeof(TMsgBuff));<BR>&nbsp;&nbsp;&nbsp;&nbsp; PMsgBuff := nil;<BR>&nbsp;&nbsp;&nbsp;&nbsp;{callback to the application<BR>announcing we are finished}<BR>&nbsp;&nbsp;&nbsp;&nbsp; cbPlaybackFinishedProc(cbAppData);<BR>&nbsp;&nbsp;&nbsp;&nbsp; CallNextHookEx(TheHook,<BR>Code, wParam, lParam);<BR>&nbsp;&nbsp;&nbsp;&nbsp; exit;<BR>&nbsp;&nbsp; end;<BR><BR>end;<BR>If code &lt; 0&nbsp;&nbsp;then<BR>&nbsp;&nbsp; JournalPlaybackProc :=<BR>CallNextHookEx(TheHook,<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Code,<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; wParam,<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lParam);<BR>end;<BR><BR><BR>{ ***********************************************************************<BR>}<BR>{ function Playback(lpFileName:<BR>PChar;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EndPlayProc:<BR>TcbPlaybackFinishedProc;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;AppData:<BR>Longint): Integer;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>{ Parameters: pointer to filename<BR>to play.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; application's<BR>EndPlay callback function.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; application's<BR>defined data.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>{ Returns: non zero if successful.<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>{ ***********************************************************************<BR>}<BR>function Playback(lpFileName:<BR>PChar;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EndPlayProc:<BR>TcbPlaybackFinishedProc;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;AppData:<BR>Longint): Integer<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {$IFDEF<BR>Win32} stdcall; {$ELSE} ; export; {$ENDIF}<BR>var<BR>TheFile: File;<BR>begin<BR>Playback := 0;<BR>If PMsgBuff &lt;&gt; nil<BR>then exit;<BR>GetMem(PMsgBuff, Sizeof(TMsgBuff));<BR>If PMsgBuff = nil then exit;<BR>Assign(TheFile, lpFileName);<BR>{$I-}<BR>Reset(TheFile, Sizeof(TEventMsg));<BR>{$I+}<BR>if IOResult &lt;&gt; 0 then<BR>begin<BR>&nbsp;&nbsp; FreeMem(PMsgBuff, Sizeof(TMsgBuff));<BR>&nbsp;&nbsp; PMsgBuff := nil;<BR>&nbsp;&nbsp; exit;<BR>end;<BR>{$I-}<BR>MsgCount := FileSize(TheFile);<BR>{$I+}<BR>if IOResult &lt;&gt; 0 then<BR>begin<BR>&nbsp;&nbsp; FreeMem(PMsgBuff, Sizeof(TMsgBuff));<BR>&nbsp;&nbsp; PMsgBuff := nil;<BR>&nbsp;&nbsp;{$I-}<BR>&nbsp;&nbsp; Close(TheFile);<BR>&nbsp;&nbsp;{$I+}<BR>&nbsp;&nbsp; if IOResult &lt;&gt; 0<BR>then exit;<BR>&nbsp;&nbsp; exit;<BR>end;<BR>if MsgCount = 0 then begin<BR>&nbsp;&nbsp; FreeMem(PMsgBuff, Sizeof(TMsgBuff));<BR>&nbsp;&nbsp; PMsgBuff := nil;<BR>&nbsp;&nbsp;{$I-}<BR>&nbsp;&nbsp; Close(TheFile);<BR>&nbsp;&nbsp;{$I+}<BR>&nbsp;&nbsp; if IOResult &lt;&gt; 0<BR>then exit;<BR>&nbsp;&nbsp; exit;<BR>end;<BR>{$I-}<BR>Blockread(TheFile, PMsgBuff^,<BR>MsgCount);<BR>{$I+}<BR>if IOResult &lt;&gt; 0 then<BR>begin<BR>&nbsp;&nbsp; FreeMem(PMsgBuff, Sizeof(TMsgBuff));<BR>&nbsp;&nbsp; PMsgBuff := nil;<BR>&nbsp;&nbsp;{$I-}<BR>&nbsp;&nbsp; Close(TheFile);<BR>&nbsp;&nbsp;{$I+}<BR>&nbsp;&nbsp; if IOResult &lt;&gt; 0<BR>then exit;<BR>&nbsp;&nbsp; exit;<BR>end;<BR>{$I-}<BR>Close(TheFile);<BR>{$I+}<BR>if IOResult &lt;&gt; 0 then<BR>begin<BR>&nbsp;&nbsp; FreeMem(PMsgBuff, Sizeof(TMsgBuff));<BR>&nbsp;&nbsp; PMsgBuff := nil;<BR>&nbsp;&nbsp; exit;<BR>end;<BR>CurrentMsg := 0;<BR>ReportDelayTime := True;<BR>SysModalOn := False;<BR>{save the application's callback<BR>procedure}<BR>cbPlaybackFinishedProc :=<BR>EndPlayProc;<BR>{save the application's defined<BR>data parameter}<BR>cbAppData := AppData;<BR>StartTime := GetTickCount;<BR>TheHook := SetWindowsHookEx(WH_JOURNALPLAYBACK,<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp; JournalPlayBackProc,<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp; hInstance,<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp; 0);<BR>if TheHook = 0 then begin<BR>&nbsp;&nbsp; FreeMem(PMsgBuff, Sizeof(TMsgBuff));<BR>&nbsp;&nbsp; PMsgBuff := nil;<BR>&nbsp;&nbsp; exit;<BR>end;<BR>Playback := 1;<BR>end;<BR><BR><BR>exports<BR>JournalRecordProc index<BR>1 name 'JOURNALRECORDPROC' resident,<BR>StartRecording index 2 name<BR>'STARTRECORDING' resident,<BR>StopRecording index 3 name<BR>'STOPRECORDING' resident,<BR>JournalPlayBackProc index<BR>4 name 'JOURNALPLAYBACKPROC' resident,<BR>Playback index 5 name 'PLAYBACK'<BR>resident;<BR><BR><BR>begin<BR>PMsgBuff := nil;<BR>end.<BR><BR>Resource statements to build:<BR>HOOKIT16.RES and HOOKIT32.RES <BR><BR>Note: Save the source as HOOKIT16.RC for 16 bit environments or HOOKIT32.RC for 32 bit environments. Use the Borland Resource Command Line Compiler BRCC.EXE to compile a 16 bit resource file and BRCC32.EXE to compile a 32 bit resource file. <BR><BR>HOOKIT DIALOG 15, 15, 63, 60<BR>STYLE WS_OVERLAPPED | WS_VISIBLE | WS_CAPTION | WS_MINIMIZEBOX<BR>CLASS "HOOKITDIALOGCLASS"<BR>CAPTION "HookIt!"<BR>BEGIN<BR>CONTROL "Start Recording",<BR>101, "BUTTON", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE<BR>| WS_GROUP | WS_TABSTOP, 0, 0, 63, 15<BR>CONTROL "Stop Recording",<BR>102, "BUTTON", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE<BR>| WS_GROUP | WS_TABSTOP, 0, 15, 63, 15<BR>CONTROL "PlayBack",<BR>103, "BUTTON", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE<BR>| WS_GROUP | WS_TABSTOP, 0, 30, 63, 15<BR>CONTROL "Done!",<BR>1, "BUTTON", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE |<BR>WS_GROUP | WS_TABSTOP, 0, 45, 63, 15<BR>END<BR><BR>Source code for HOOKIT.EXE<BR>Note: Save the source as HOOKIT.PAS for Pascal or HOOKIT.DPR for Delphi. <BR><BR>program HookIt;<BR><BR>{$D HookIt!}<BR><BR>{$C MOVEABLE PRELOAD PERMANENT}<BR><BR>{$IFDEF Win32}<BR><BR>{$R HOOKIT32.RES}<BR><BR>uses<BR>Windows, Messages;<BR><BR>type&nbsp;&nbsp;<BR>TwMsg = Longint;<BR>TwParam = Longint;<BR>TlParam = Longint;<BR><BR>{$ELSE}<BR><BR>{$R HOOKIT16.RES}<BR><BR>uses<BR>{$IFDEF VER15}<BR>WinTypes, WinProcs, Win31;<BR>{$ELSE}<BR>{$IFDEF VER70}<BR>WinTypes, WinProcs, Win31;<BR>{$ELSE}<BR>WinTypes, WinProcs, Messages;<BR>{$ENDIF}<BR>{$ENDIF}<BR><BR>type<BR>TwMsg = Word;<BR>TwParam = Word;<BR>TlParam = Longint;<BR><BR>{$ENDIF}<BR><BR>type<BR>TWinVersion = record<BR>&nbsp;&nbsp;WinMajor : Byte;<BR>&nbsp;&nbsp;WinMinor : Byte;<BR>&nbsp;&nbsp;DosMajor : Byte;<BR>&nbsp;&nbsp;DosMinor : Byte;<BR>end;<BR><BR>TcbPlaybackFinishedProc = Procedure(AppData: Longint)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {$IFDEF Win32} stdcall; {$ELSE} ; {$ENDIF}<BR><BR>const<BR>APPNAME = 'HookIt!';<BR>CLASSNAME ='HOOKITDIALOGCLASS';<BR>ID_BTN_START_RECORDING =<BR>101;<BR>ID_BTN_STOP_RECORDING =<BR>102;<BR>ID_BTN_PLAYBACK = 103;<BR>ID_BTN_DONE = IDOK;<BR>FILENAME = 'HOOKIT.MAC';<BR><BR>var<BR>PlaybackFinishedProc :TcbPlaybackFinishedProc;<BR><BR><BR>function StartRecording: Integer<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{$IFDEF<BR>Win32} stdcall; {$ELSE} ; far; {$ENDIF}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; external<BR>'HOOKLIB' index 2;<BR><BR><BR>function StopRecording(lpFileName:<BR>PChar): Integer<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {$IFDEF<BR>Win32} stdcall; {$ELSE} ; far; {$ENDIF}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;external<BR>'HOOKLIB' index 3;<BR><BR><BR>function Playback(lpFileName:<BR>PChar;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EndPlayProc:<BR>TcbPlaybackFinishedProc;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AppData:<BR>Longint): Integer<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{$IFDEF Win32}<BR>stdcall; {$ELSE} ; far; {$ENDIF}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; external<BR>'HOOKLIB' index 5;<BR><BR><BR>procedure PlaybackFinished(AppData:<BR>Longint)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>{$IFDEF Win32} stdcall; {$ELSE} ; export; {$ENDIF}<BR><BR>begin<BR>EnableWindow(GetDlgItem(hWnd(AppData),<BR>ID_BTN_START_RECORDING), True);<BR>EnableWindow(GetDlgItem(hWnd(AppData),<BR>ID_BTN_STOP_RECORDING), False);<BR>EnableWindow(GetDlgItem(hWnd(AppData),<BR>ID_BTN_PLAYBACK), True);<BR>EnableWindow(GetDlgItem(hWnd(AppData),<BR>ID_BTN_DONE), True);<BR>SetFocus(GetDlgItem(hWnd(AppData),<BR>ID_BTN_PLAYBACK));<BR>end;<BR><BR><BR>function HookitDialogProc(Dialog:<BR>HWnd;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>Msg: TwMsg;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>WParam: TwParam;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>LParam: TlParam): Longbool<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{$IFDEF<BR>Win32} stdcall; {$ELSE} ; export; {$ENDIF}<BR>begin<BR>HookitDialogProc := True;<BR>{do any default class handling<BR>here for HookItDlg}<BR>HookitDialogProc := Longbool(DefDlgProc(Dialog,<BR>Msg, WParam, LParam));<BR>end;<BR><BR><BR>function MainDlgProc(Dialog: HWnd;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Msg:TwMsg;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;WParam:TwParam;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LParam:TlParam): Bool<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {$IFDEF Win32} stdcall; {$ELSE} ; export; {$ENDIF}<BR>begin<BR>MainDlgProc := True;<BR>case Msg Of<BR><BR>&nbsp;&nbsp; WM_INITDIALOG: begin<BR>&nbsp;&nbsp;&nbsp;&nbsp; EnableWindow(GetDlgItem(Dialog,<BR>ID_BTN_START_RECORDING), True);<BR>&nbsp;&nbsp;&nbsp;&nbsp; EnableWindow(GetDlgItem(Dialog,<BR>ID_BTN_STOP_RECORDING), False);<BR>&nbsp;&nbsp;&nbsp;&nbsp; EnableWindow(GetDlgItem(Dialog,<BR>ID_BTN_PLAYBACK), False);<BR>&nbsp;&nbsp;&nbsp;&nbsp; EnableWindow(GetDlgItem(Dialog,<BR>ID_BTN_DONE), True);<BR>&nbsp;&nbsp;&nbsp;&nbsp; exit;<BR>&nbsp;&nbsp; end;<BR><BR>&nbsp;&nbsp; WM_COMMAND: begin<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp; case WParam of<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ID_BTN_START_RECORDING:<BR>begin<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EnableWindow(GetDlgItem(Dialog,<BR>ID_BTN_STOP_RECORDING), True);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SetFocus(GetDlgItem(Dialog,<BR>ID_BTN_STOP_RECORDING));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EnableWindow(GetDlgItem(Dialog,<BR>ID_BTN_START_RECORDING), False);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EnableWindow(GetDlgItem(Dialog,<BR>ID_BTN_PLAYBACK), False);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EnableWindow(GetDlgItem(Dialog,<BR>ID_BTN_DONE), False);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if StartRecording<BR>= 0 then begin<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EnableWindow(GetDlgItem(Dialog,<BR>ID_BTN_START_RECORDING), True);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SetFocus(GetDlgItem(Dialog,<BR>ID_BTN_START_RECORDING));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EnableWindow(GetDlgItem(Dialog,<BR>ID_BTN_STOP_RECORDING), False);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EnableWindow(GetDlgItem(Dialog,<BR>ID_BTN_PLAYBACK), False);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EnableWindow(GetDlgItem(Dialog,<BR>ID_BTN_DONE), True);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Messagebox(Dialog,<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'Unable<BR>to start recording!',<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;APPNAME,<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MB_OK);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exit;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ID_BTN_STOP_RECORDING:<BR>begin<BR