User Tools

Site Tools


Sidebar


Introduction


Basic Tutorials


Advance Tutorials


Useful Techniques


Examples

  • Simple Pipe Weight Calculator
  • Unit Convertor

Sidebar

tutorial:dlllibrary

This is an old revision of the document!


Shared Library

Shared Library คืออะไร

Shared Library คือ แหล่งเก็บ subprogram หรือ Procedure/function ที่ได้ถูก compiled เรียบร้อยแล้วและพร้อมให้ถูกเรียกใช้งาน
นามสกุลของ Shared Library จะแตกต่างกันไป ขึ้นอยู่กับ Operating System สรุปได้ตามตารางต่อไปนี้

Operating System (OS) Library Extension
Window .dll
Linux .so
MacOS X .dylib



การสร้าง Shared Library

ใน Lazarus IDE เราสามารถสร้าง Shared Library ไว้ใช้งานสำหรับ Window, Linux และ MacOS X ได้ทั้งหมดครับ โดยการเลือกเมนู

File >> New >> Library

นามสกุลของ Shared Library ที่ถูก Compiled แล้วจะเป็นไปตาม OS ที่เราใช้งานในขณะนั้น
อย่างไรก็ตาม ในส่วนของรายละเอียดการสร้างและใช้งาน library ในที่นี้จะขอพูดถึงแต่ .dll ละกันครับ

ข้อควรระวัง - เนื่องจาก Shared Library เป็น Code ที่ถูก Complied เรียบร้อยแล้ว ดังนั้นหากท่านใช้ Lazarus IDE เวอร์ชั่น 32bit สร้าง library ขึ้นมาสักอัน Library ดังกล่าวจะไม่สามารถเรียกใช้งานบน Lazarus IDE - 64bit ได้ครับ

สำหรับ Lazarus IDE บน Window หลังจากสร้างไฟล์ใหม่ที่เป็น Library ขึ้นมาแล้ว จะมีหน้าต่าง text editor ขึ้นมาให้พิมพ์ code ได้เหมือนกับการสร้างโปรเจคต์แบบ Program หรือ Simple Program ครับ แต่ต่างกันคือจะมีคำว่า Library ขึ้นมาแทนคำว่า Program

การเขียน Library ไม่ยากครับ ให้ยึดรูปแบบดังนี้ครับ

library LibraryName;

{$mode objfpc}{$H+}

uses
  SysUtils, Classes;

function FunctionName(Arg1, Arg2:real):real; {Calling Convention}
begin
  result := Arg1 + Ag2;
end;

exports FunctionName;

begin
end.

หลังจากที่เราเขียน code ลงไปในไฟล์ Library แล้ว ก็ให้ไปที่เมนูแล้วเลือก

Run >> Compile

เมื่อทำการ compile เสร็จเรียบร้อย เราจะได้ไฟล์ .dll ที่พร้อมใช้งานครับ

Example-1: Create .dll Library

Example-1: Create .dll Library

ตัวอย่าง การสร้าง Library ชื่อว่า Pipe เพื่อเก็บฟังก์ชั่นคำนวณหาพื้นที่หน้าตัดท่อ

library Pipe;

{$mode objfpc}{$H+}

uses
  SysUtils, Classes, Math;

function PipeArea1(const DOut,DIn  : double) : double; {Calling;}
begin
  result := (PI/4)*(DOut*DOut - DIn*DIn);
end;

function PipeArea2(const DOut,tw  : double) : double; {Calling;} 
begin
  result := (PI/4)*(DOut*DOut - (DOut-2*tw)*(DOut-2*tw));
end;

exports PipeArea1,PipeArea2;

begin
end.



การเรียกใช้งาน Shared Library

การเรียกใช้งาน Shared Library สามารถเรียกได้ 2 แบบ ดังนี้

  • Static Calling คือการเรียกมาในขณะ Compile-time นั่นหมายถึงเวลาที่เรานำไฟล์ .exe ไปแจกจ่ายนั้น ไม่จำเป็นต้องแนบไฟล์ .dll ไปใน Folder เนื่องจาก code ต่างๆใน Shared Library ได้ถูก compiled ไว้ในไฟล์ .exe เรียบร้อยแล้ว วิธีเรียกแบบนี้มีข้อดีคือไม่ซับซ้อน แต่มีข้อเสียคือจะทำให้ไฟล์ของโปรแกรมที่เราเขียนขึ้น มีขนาดใหญ่
  • Dynamic Calling คือการเรียกใช้ ในขณะ Run-time นั่นคือเวลาที่เรานำไฟล์ .exe ไปแจกจ่ายนั้น จำเป็นต้องแนบไฟล์ .dll ไปใน Folder ทุกครั้ง เพราะตัว Shared Library ไม่ได้ถูก Compiled รวมเขากับไฟล์ .exe ดังนั้นจึงถูกเรียกมาใช้เฉพาะหน้าแทน วิธีเรียกแบบนี้จะต้องเขียน code ที่ค่อนข้างยุ่งยาก แต่ข้อดีคือจะทำให้ขนาดของโปรแกรมเราไม่ใหญ่มากและเรายังสามารถ Update ไฟล์ .dll ได้ในภายหลัง

Example-2: Static Calling .dll Library

Example-2: Static Calling .dll Library

ตัวอย่าง การเรียก Library ชื่อว่า Pipe เพื่อเก็บฟังก์ชั่นคำนวณหาพื้นที่หน้าตัดท่อ
ข้อสังเกต: ชื่อตัวแปร Argument ของในโปรแกรมนี้ ไม่ตรงกับใน .dll

  • ไฟล์นี้ ใช้ PipeArea1(const D,Di:double):double
  • แต่ในไฟล์ .dll ใช้ PipeArea1(const DOut,DIn : double) : double;

จะเห็นว่าชื่อตัวแปรสามารถตั้งให้ต่างกันได้ เพียงแต่ต้องเป็นชนิดเดียวกัน

program StaticCalling_PIPEDLL;
//This example demonstrates the simple use of static calling .dll library.
//For calling convention, register is used by default.

{$mode objfpc}{$H+}

uses
  Classes;

function PipeArea1(const D,Di:double):double;{calling;} external 'Pipe.dll';
function PipeArea2(const D,t:double):double;{calling;} external 'Pipe.dll';

Var D,Di,t:double;

begin
  writeln('Example for calling .dll library named Pipe.dll');
  D:=0.25;
  Di:=0.23;
  t:=0.013;
  writeln('Calling PipeArea1(D,Di): ',PipeArea1(D,Di));
  writeln('Calling PipeArea2(D,t): ',PipeArea2(D,t));
  readln;

end. 



การระบุ Calling Convention

ในทุกๆการใช้งาน Shared Library จะต้องมีการระบุรูปแบบการเรียกใช้งาน หรือ Calling Convention เสมอ โดย Calling Convention สำหรับ FPC สรุปได้ดังนี้

Keyword Variable Push Order Stack Cleaned by
default LTR Callee
register LTR Callee
cdecl RTR Caller
interupt RTR Callee
pascal LTR Callee
safecall RTR Callee
stdcall RTR Callee
oldfpccall RTR Callee

จากตารางข้างบนอธิบายถึงความต่างของแต่ละ Calling Convention คือทิศทางการใส่ค่าตัวแปร และการกำจัดค่าของตัวแปรใน Ram จะเห็นได้ว่า default, register และ pascal จะมีการใส่ค่าตัวแปรแบบ “ซ้ายไปขวา” หรือ LTR (Left-to-Right) ในขณะที่ cdecl, interupt, safecall, stdcall และ oldfpccall จะใส่ค่าตัวแปรแบบ “ขวาไปซ้าย” หรือ RTL (Right-to-Left)

ข้อควรระวัง - หากเราระบุ Calling Convention ใน .dll ไม่ตรงกับการเรียกใช้ในไฟล์ .exe ก็อาจทำให้เกิดการใส่ตัวแปรในแต่ละ Argument ของ Procedure/Function สลับกันได้ จะนำไปสู่การคำนวณที่ผิดพลาดอย่างแน่นอน

หมายเหตุ - การไม่ระบุ Calling Convention นั้น จะหมายถึงการเรียกใช้งานแบบ default ซึ่งตัว default สำหรับ FPC คือ register

Example-3: Static Calling .dll Library using stdcall as calling convention

Example-3: Static Calling .dll Library using stdcall as calling convention

ตัวอย่าง การเรียก Library ชื่อว่า Pipe เพื่อเก็บฟังก์ชั่นคำนวณหาพื้นที่หน้าตัดท่อ
ข้อสังเกต: จะต้องมีการระบุ Calling Convention ทั้งใน .dll และโปรแกรมหลัก

library Pipe;

{$mode objfpc}{$H+}

uses
  SysUtils, Classes, Math;

function PipeArea1(const DOut,DIn  : double) : double; stdcall;
begin
  result := (PI/4)*(DOut*DOut - DIn*DIn);
end;

function PipeArea2(const DOut,tw  : double) : double; stdcall;
begin
  result := (PI/4)*(DOut*DOut - (DOut-2*tw)*(DOut-2*tw));
end;

exports PipeArea1,
PipeArea2;

begin
end.

program StaticCalling_PIPEDLL;
//This example demonstrates the simple use of static calling .dll library.
//For calling convention, register is used by default.

{$mode objfpc}{$H+}

uses
  Classes;

function PipeArea1(const D,Di:double):double; stdcall; external 'Pipe.dll';
function PipeArea2(const D,t:double):double; stdcall; external 'Pipe.dll';

Var D,Di,t:double;

begin
  writeln('Example for calling .dll library named Pipe.dll');
  D:=0.25;
  Di:=0.23;
  t:=0.013;
  writeln('Calling PipeArea1(D,Di): ',PipeArea1(D,Di));
  writeln('Calling PipeArea2(D,t): ',PipeArea2(D,t));
  readln;

end. 



References

tutorial/dlllibrary.1540692730.txt.gz · Last modified: 2018/10/28 09:12 by admin