This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision | ||
tutorial:dlllibrary [2018/09/21 10:32] admin |
tutorial:dlllibrary [2019/03/15 10:42] (current) admin |
||
---|---|---|---|
Line 8: | Line 8: | ||
| Linux | .so | | | Linux | .so | | ||
| MacOS X | .dylib | | | MacOS X | .dylib | | ||
- | \\ \\ | + | \\ |
=====การสร้าง Shared Library===== | =====การสร้าง Shared Library===== | ||
ใน Lazarus IDE เราสามารถสร้าง Shared Library ไว้ใช้งานสำหรับ Window, Linux และ MacOS X ได้ทั้งหมดครับ โดยการเลือกเมนู \\ | ใน Lazarus IDE เราสามารถสร้าง Shared Library ไว้ใช้งานสำหรับ Window, Linux และ MacOS X ได้ทั้งหมดครับ โดยการเลือกเมนู \\ | ||
Line 37: | Line 37: | ||
end; | end; | ||
- | exports FunctionName1; | + | exports FunctionName; |
begin | begin | ||
Line 47: | Line 47: | ||
Run >> Compile | Run >> Compile | ||
- | เมื่อทำการ compile เสร็จเรียบร้อย เราจะได้ไฟล์ .dll ที่พร้อมใช้งานครับ | + | เมื่อทำการ compile เสร็จเรียบร้อย เราจะได้ไฟล์ .dll ที่พร้อมใช้งานครับ ให้นำไฟล์ .dll ไปไว้ใน folder เดียวกับไฟล์ Project เราได้เลย |
<hidden Example-1: Create .dll Library> | <hidden Example-1: Create .dll Library> | ||
Line 59: | Line 59: | ||
SysUtils, Classes, Math; | SysUtils, Classes, Math; | ||
- | function PipeArea1(const DOut,DIn : double) : double; | + | function PipeArea1(const DOut,DIn : double) : double; {Calling;} |
begin | begin | ||
result := (PI/4)*(DOut*DOut - DIn*DIn); | result := (PI/4)*(DOut*DOut - DIn*DIn); | ||
end; | end; | ||
- | function PipeArea2(const DOut,tw : double) : double; | + | function PipeArea2(const DOut,tw : double) : double; {Calling;} |
begin | begin | ||
result := (PI/4)*(DOut*DOut - (DOut-2*tw)*(DOut-2*tw)); | result := (PI/4)*(DOut*DOut - (DOut-2*tw)*(DOut-2*tw)); | ||
end; | end; | ||
- | exports PipeArea1, | + | exports PipeArea1,PipeArea2; |
- | PipeArea2; | + | |
begin | begin | ||
Line 80: | Line 79: | ||
=====การเรียกใช้งาน Shared Library===== | =====การเรียกใช้งาน Shared Library===== | ||
การเรียกใช้งาน Shared Library สามารถเรียกได้ 2 แบบ ดังนี้ | การเรียกใช้งาน Shared Library สามารถเรียกได้ 2 แบบ ดังนี้ | ||
- | *Static Calling คือการเรียกมาในขณะ Compile-time นั่นหมายถึงเวลาที่เรานำไฟล์ .exe ไปแจกจ่ายนั้น ไม่จำเป็นต้องแนบไฟล์ .dll ไปใน Folder เนื่องจาก code ต่างๆใน Shared Library ได้ถูก compiled ไว้ในไฟล์ .exe เรียบร้อยแล้ว วิธีเรียกแบบนี้มีข้อดีคือไม่ซับซ้อน แต่มีข้อเสียคือจะทำให้ไฟล์ของโปรแกรมที่เราเขียนขึ้น มีขนาดใหญ่ | + | *Static Calling คือการเรียกทุก function จาก Library มาเก็บไว้ในโปรแกรมหลักตั้งแต่ต้น ยกตัวอย่าง เช่น เราต้องการใช้งาน function ทั้งหมด 5 ตัว ทุกครั้งที่มีการรัน .exe function ทั้ง 5 ตัวดังกล่าว ก็จะถูกเรียกมาเก็บในโปรแกรมเราตั้งแต่ต้น |
- | *Dynamic Calling คือการเรียกใช้ ในขณะ Run-time นั่นคือเวลาที่เรานำไฟล์ .exe ไปแจกจ่ายนั้น จำเป็นต้องแนบไฟล์ .dll ไปใน Folder ทุกครั้ง เพราะตัว Shared Library ไม่ได้ถูก Compiled รวมเขากับไฟล์ .exe ดังนั้นจึงถูกเรียกมาใช้เฉพาะหน้าแทน วิธีเรียกแบบนี้จะต้องเขียน code ที่ค่อนข้างยุ่งยาก แต่ข้อดีคือจะทำให้ขนาดของโปรแกรมเราไม่ใหญ่มากและเรายังสามารถ Update ไฟล์ .dll ได้ในภายหลัง | + | *Dynamic Calling คือการเรียกใช้ function จาก Library เฉพาะตัวที่ถูกใช้งานจริง ไม่ได้เรียกมาใช้ทั้งหมดแต่แรก อย่างไรก็ตาม วิธีนี้จะยุ่งยากกว่าวิธีแรกพอสมควร เพราะต้องเขียน procedure/function ขึ้นมาเป็นพิเศษเพื่อเรียกใช้งาน function เหล่านั้นทีละตัว ของใครของมัน\\ |
+ | หมายเหตุ: ทั้งสองแบบ ต้องการไฟล์ .dll แนบไปใช้งานกับไฟล์ .exe ด้วยทุกครั้ง | ||
<hidden Example-2: Static Calling .dll Library> | <hidden Example-2: Static Calling .dll Library> | ||
- | **ตัวอย่าง** การเรียก Library ชื่อว่า Pipe เพื่อเก็บฟังก์ชั่นคำนวณหาพื้นที่หน้าตัดท่อ\\ | + | **ตัวอย่าง** การเรียก Library ชื่อว่า Pipe (จาก Example-1) เพื่อเก็บฟังก์ชั่นคำนวณหาพื้นที่หน้าตัดท่อ\\ |
- | __ข้อสังเกต__: ... | + | __ข้อสังเกต__: ชื่อตัวแปร Argument ของในโปรแกรมนี้ ไม่ตรงกับใน .dll \\ |
+ | *ไฟล์นี้ ใช้ PipeArea1(const D,Di:double):double \\ | ||
+ | *แต่ในไฟล์ .dll ใช้ PipeArea1(const DOut,DIn : double) : double; \\ | ||
+ | จะเห็นว่าชื่อตัวแปรสามารถตั้งให้ต่างกันได้ เพียงแต่ต้องเป็นชนิดเดียวกัน | ||
<sxh delphi;> | <sxh delphi;> | ||
program StaticCalling_PIPEDLL; | program StaticCalling_PIPEDLL; | ||
Line 96: | Line 99: | ||
Classes; | Classes; | ||
- | function PipeArea1(const D,Di:double):double; external 'Pipe.dll'; | + | function PipeArea1(const D,Di:double):double;{calling;} external 'Pipe.dll'; |
- | function PipeArea2(const D,t:double):double; external 'Pipe.dll'; | + | function PipeArea2(const D,t:double):double;{calling;} external 'Pipe.dll'; |
Var D,Di,t:double; | Var D,Di,t:double; | ||
Line 110: | Line 113: | ||
readln; | readln; | ||
+ | end. | ||
+ | </sxh> | ||
+ | </hidden> | ||
+ | |||
+ | <hidden Example-3: Dynamic Calling .dll Library> | ||
+ | **ตัวอย่าง** การเรียก Library ชื่อว่า Pipe (จาก Example-1) เพื่อเก็บฟังก์ชั่นคำนวณหาพื้นที่หน้าตัดท่อ\\ | ||
+ | __ข้อสังเกต__: มีดังนี้ \\ | ||
+ | *จะต้องมี unit ชื่อ Dynlibs\\ | ||
+ | *จะต้องมีการเรียกใช้ function เป็นตัวแปร หรือที่เรียกว่า Procedural Type \\ | ||
+ | <sxh delphi;highlight: [8,13-14,20-35,42]> | ||
+ | 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. | end. | ||
</sxh> | </sxh> | ||
Line 127: | Line 183: | ||
| oldfpccall | RTR | Callee | | | oldfpccall | RTR | Callee | | ||
- | แต่สำหรับการไม่ระบุนั้น compiler จะรู้ได้เองว่าเรากำลังใช้แบบ default ซึ่งในที่นี้ก็คือ register | + | จากตารางข้างบนอธิบายถึงความต่างของแต่ละ 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 | ||
+ | <hidden Example-4: Static Calling .dll Library using stdcall as calling convention> | ||
+ | **ตัวอย่าง** การเรียก Library ชื่อว่า Pipe เพื่อเก็บฟังก์ชั่นคำนวณหาพื้นที่หน้าตัดท่อ\\ | ||
+ | __ข้อสังเกต__: เราจะใช้ Calling ที่ชื่อว่า stdcall ซึ่งจะต้องมีการระบุ Calling Convention ทั้งใน .dll และโปรแกรมหลัก \\ | ||
+ | |||
+ | <sxh delphi;highlight: [8,13]> | ||
+ | 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. | ||
+ | </sxh> | ||
+ | |||
+ | <sxh delphi;highlight: [10-11]> | ||
+ | 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. | ||
+ | </sxh> | ||
+ | </hidden> | ||
\\ \\ | \\ \\ | ||
=====References===== | =====References===== | ||
https://www.freepascal.org/docs-html/prog/progsu7.html \\ | https://www.freepascal.org/docs-html/prog/progsu7.html \\ | ||
https://www.freepascal.org/docs-html/prog/progse22.html#x173-1760006.3 | https://www.freepascal.org/docs-html/prog/progse22.html#x173-1760006.3 |