User Tools

Site Tools


Sidebar


Introduction


Basic Tutorials


Advance Tutorials


Useful Techniques


Examples

  • Simple Pipe Weight Calculator
  • Unit Convertor

Sidebar

tutorial:dlllibrary

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 ที่พร้อมใช้งานครับ ให้นำไฟล์ .dll ไปไว้ใน folder เดียวกับไฟล์ Project เราได้เลย

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 คือการเรียกทุก function จาก Library มาเก็บไว้ในโปรแกรมหลักตั้งแต่ต้น ยกตัวอย่าง เช่น เราต้องการใช้งาน function ทั้งหมด 5 ตัว ทุกครั้งที่มีการรัน .exe function ทั้ง 5 ตัวดังกล่าว ก็จะถูกเรียกมาเก็บในโปรแกรมเราตั้งแต่ต้น
  • Dynamic Calling คือการเรียกใช้ function จาก Library เฉพาะตัวที่ถูกใช้งานจริง ไม่ได้เรียกมาใช้ทั้งหมดแต่แรก อย่างไรก็ตาม วิธีนี้จะยุ่งยากกว่าวิธีแรกพอสมควร เพราะต้องเขียน procedure/function ขึ้นมาเป็นพิเศษเพื่อเรียกใช้งาน function เหล่านั้นทีละตัว ของใครของมัน

หมายเหตุ: ทั้งสองแบบ ต้องการไฟล์ .dll แนบไปใช้งานกับไฟล์ .exe ด้วยทุกครั้ง

Example-2: Static Calling .dll Library

Example-2: Static Calling .dll Library

ตัวอย่าง การเรียก Library ชื่อว่า Pipe (จาก Example-1) เพื่อเก็บฟังก์ชั่นคำนวณหาพื้นที่หน้าตัดท่อ
ข้อสังเกต: ชื่อตัวแปร 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. 

Example-3: Dynamic Calling .dll Library

Example-3: Dynamic Calling .dll Library

ตัวอย่าง การเรียก Library ชื่อว่า Pipe (จาก Example-1) เพื่อเก็บฟังก์ชั่นคำนวณหาพื้นที่หน้าตัดท่อ
ข้อสังเกต: มีดังนี้

  • จะต้องมี unit ชื่อ Dynlibs
  • จะต้องมีการเรียกใช้ function เป็นตัวแปร หรือที่เรียกว่า Procedural Type

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

{$mode objfpc}{$H+}

uses
  Classes,Dynlibs;

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

//Define procedural type for Dynamic Calling
type
  TMyDLL = function(const D,Di:double):double;{calling;}



Var D,Di:double;

  function CallDLib(const D,Di:double):double;
  var
    MyHandle: TLibHandle;
    MyDLLFunc: TMyDLL;
  begin
    MyHandle := SafeLoadLibrary('Pipe.dll');
    if MyHandle<>0 then
      begin
        MyDLLFunc := TMyDLL(GetProcedureAddress(MyHandle,'PipeArea1'));
        if Assigned(MyDLLFunc) then
          result:=MyDLLFunc(D,Di);
      end
    else
      result:=10;
    FreeLibrary(MyHandle);
  end;

begin
  writeln('Example for calling .dll library named Pipe.dll');
  D:=0.25;
  Di:=0.23;
  writeln('Static Calling PipeArea1(D,Di): ',PipeArea1(D,Di));
  writeln('Dynamic Calling PipeArea1(D,Di): ',CallDLib(D,Di));
  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 calling convention สำหรับ FPC คือ register

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

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

ตัวอย่าง การเรียก Library ชื่อว่า Pipe เพื่อเก็บฟังก์ชั่นคำนวณหาพื้นที่หน้าตัดท่อ
ข้อสังเกต: เราจะใช้ Calling ที่ชื่อว่า stdcall ซึ่งจะต้องมีการระบุ 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.txt · Last modified: 2019/03/15 10:42 by admin