동적 링크 라이브러리 호출 Ffidl

admin의 아바타

http://www.elf.org/ffidl

Ffidl은 Tcl상에서 직접 동적 라이브러리 내부의 C루틴을 호출하기 위한 패키지입니다. 동적 라이브러리는 Windows의 DLL이나, Unix계열의 so 입니다.

동적 라이브러리 내부의 C루틴을 직접 호출함으로 몇가지 장점이 있다 생각됩니다.

  • Tcl확장을 만들지 않아도 C루틴을 호출할 수 있다.
  • 동적 라이브러리의 C루틴을 테스트할 수 있다.

::ffidl::callout

동적 라이브러리의 C루틴을 호출시는 ::ffidl::callout을 사용합니다. ::ffidl::callout은 미리 C루틴과의 인터페이스로 Tcl프로시져를 작성합니다. ::ffidl::callout 커맨드의 형식은 아래와 같습니다.

package require Ffidl
 
::ffidl::callout name {?arg_type1 ...?} return_type address ?protocol?

name은 Tcl프로시져의 이름을 지정합니다. arg_type은 C루틴의 인자 타입을 지정합니다. return_type은 C루틴 리턴값의 타입을 지정합니다. address는 다이나믹 링크 라이브러리의 load 어드레스를 지정합니다. protocol은 cdecl이나 stdcall을 지정합니다.(기본값은 cdecl)

arg_type과 return_type으로 지정할수 있는 타입은 아래의 표와 같습니다. +는 지정 가능, -는 지정 불가능한 타입입니다. 또한 elt는 그 타입이 구조체의 멤버에 이루어질수 있는지 없는지를 나타냅니다.

proc callback elt type definition
arg ret arg ret
- + - + - void void
+ + + + + int int
+ + + + + unsigned unsigned int
+ + + + + short signed short int
+ + + + + unsigned short unsigned short int
+ + + + + long signed long int
+ + + + + unsigned long unsigned long int
+ + + + + float float
+ + + + + double double
+ + + + + long double long double
+ + + + + sint8 signed 8 bit int
+ + + + + uint8 unsigned 8 bit int
+ + + + + sint16 signed 16 bit int
+ + + + + uint16 unsigned 16 bit int
+ + + + + sint32 signed 32 bit int
+ + + + + uint32 unsigned 32 bit int
+ + + + + sint64 signed 64 bit int
+ + + + + uint64 unsigned 64 bit int
+ + + + + pointer pointer as an integer value
+ + + + - pointer-obj pointer from Tcl_Obj
+ + + - - pointer-utf8 pointer from String
+ + + - - pointer-utf16 pointer from Unicode
+ - - - - pointer-byte pointer from ByteArray
+ - - - - pointer-var pointer from ByteArray stored in
variable. If the ByteArray is shared,
then an unshared copy is made and
stored back into the
variable.
+ - - - - pointer-proc pointer to callback function constructed to call a
Tcl proc.

아래의 예제는, Windows의 user32.dll내부의 GetWindowTextA 루틴을 호출합니다. GetWindowTextA 루틴은 int 타입의 값을 돌려주는 루틴으로, 인자에 윈도우 핸들, 문자열을 반환하기위한 버퍼의 포인터, 버퍼의 사이즈를 지정해줍니다.

package require Ffidl
 
::ffidl::callout  GetWindowText {int pointer-var int} \
    int [::ffidl::symbol user32.dll GetWindowTextA]
set buf [binary format x256]
GetWindowText 0x4804bc buf 256
binary scan $buf A* buf
puts stdout $buf 

실행후 buf 변수에는 윈도우 핸들로 지정한 윈도우 타이틀이 반환됩니다.

위의 그림은 도스창의 윈도우 핸들 값을 넣어 타이틀 텍스트를 얻어온것입니다. 테스트를 쉽게 하기위해서 여러분이 원하는 윈도우의 핸들값을 마우스 커서만 갖다대면 알려주는 유틸리티를 첨부파일에 올려놓았으니 다운받아 해보시기 바랍니다.

::ffidl::callback

::ffidl::callback은 ::ffidl::callout의 인자에 건네주기위한 콜백루틴을 생성합니다. 실제로 생성된 콜백루틴은 Tcl프로시져로 대치됩니다. ::ffidl::callback 커맨드의 형식은 아래와 같습니다.

package require Ffidl
::ffidl::callback name {?arg_type1 ...?} return_type ?protocol?

name은 Tcl프로시져의 이름을 지정합니다. arg_type은 C루틴의 인자 타입을 지정합니다. return_type은 C루틴의 리턴값 타입을 지정합니다. protocol은 cdecl이나 stdcall을 지정합니다.(기본은 cdecl)

아래의 예는 Windows의 EnumWindows 루틴에 건네주는 콜백루틴을 정의하고 있습니다. EnumWindows 프로시져는 콜백루틴 EnumWCB를 호출하고, 현재 열려있는 모든 윈도우의 핸들값을 표시합니다.

package require Ffidl
 
::ffidl::callout  EnumWindows {pointer-proc int} int \
    [ ::ffidl::symbol user32.dll EnumWindows]
::ffidl::callback  EnumWCB {int int} int
proc EnumWCB  {hwnd lparam} {
   puts stdout $hwnd
   return 1
}
EnumWindows  EnumWCB 0 

::ffidl::typedef

Ffidl은 구조체를 사용한 함수도 지원하고 있습니다. 기본 타입 외에도 사용자 정의 타입 구조체를 정의할수 있습니다.

예로 아래의 C언어 구조체는

typedef struct _OSVERSIONINFO{
    DWORD dwOSVersionInfoSize; /* 이 구조체의 사이즈 */
    DWORD dwMajorVersion;      /* major version */
    DWORD dwMinorVersion;      /* minor version */
    DWORD dwBuildNumber;       /* build 번호 */
    DWORD dwPlatformId;        /* platform 번호 */
    TCHAR szCSDVersion[ 128 ]; /* 추가 정보 */
} OSVERSIONINFO;

아래와 같이 ::ffidl::typedef 커맨드로 사용자정의 구조체를 정의할수 있습니다.

::ffidl::typedef verinfo unsigned unsigned unsigned unsigned unsigned

배열 부분은 길기 때문에 생략합니다.
아래의 예는 사용자 정의 타입 verinfo를 사용하여 Windows의 GetVersionExA로 OS의 버전을 얻습니다.

package require Ffidl
 
::ffidl::typedef verinfo unsigned unsigned \
    unsigned unsigned unsigned
::ffidl::callout  GetVersionEx {pointer-var} int \
    [ ::ffidl::symbol kernel32.dll GetVersionExA]
set buf [binary format x[::ffidl::info sizeof  verinfo]x128@0i 148]
GetVersionEx buf
binary scan $buf [::ffidl::info format verinfo ] size major minor build plat
puts stdout "version = $major.$minor.$build"

buf변수에는 구조체로부터 OS의 버전정보가 반환됩니다.

마치며

Ffidl을 사용하면 Tcl로부터 직접 동적 라이브러리내의 C루틴을 호출할수 있습니다. 상당히 쉬운 방법이지만, 인자의 지정에 실수를 하면 어플리케이션이 종료되는 현상이 발생할수 있으므로 Ffidl을 사용시 충분히 주의해야합니다.

첨부 파일파일 크기
166.jpg45.73 KB
167.jpg89.11 KB
168.jpg41.04 KB
minispy.zip13.86 KB