ผู้เขียน : โซว เฟย
อีเมล: [email protected]
หน้าแรก: http://www.atechsoft.com/people/zouf/ ในกระบวนการใช้ Delphi คุณจะต้องพบกับปัญหาแปลก ๆ มากมายอย่างหลีกเลี่ยงไม่ได้และเอกสารของ Delphi ก็มีจำนวนน้อยจนน่าประหลาดใจเช่นกันฉันจึงได้แต่สรุปช้า ๆ ด้วยตัวเอง ก็มีครับ (เนื่องจากเป็นเพียงรายละเอียดกระจัดกระจาย ผมเลยไม่ได้ใช้ความพยายามในการเขียนมากนัก อาจจะยุ่งนิดหน่อย แต่ก็ควรจะเข้าใจได้ ;-)) หาก: 1. หากคุณมีวิธีแก้ไขปัญหาต่อไปนี้ที่ดีกว่า โปรดบอกฉันและเพื่อนๆ ของฉันใน csdn 2. หากคุณมีคำถามอื่นๆ โปรดระบุปัญหาและคำตอบของคุณ แล้วบอกฉันและเพื่อนๆ ของฉันใน csdn การสื่อสารสร้างทุกสิ่ง!
ข้อความ: ถาม: หากแบบฟอร์มที่สร้างใน Delphi DLL เป็น ShowModal ใน Exe ไอคอนสองไอคอนจะปรากฏขึ้นบนทาสก์บาร์ จะแก้ไขปัญหานี้ได้อย่างไร? ตอบ: ต่อไปนี้เป็นวิธีการทั่วไปในการวางแบบฟอร์มใน DLL: DLL: function ShowFrm: TModalResult; stdcall;beginForm1 := TForm1.Create(Nil); tryForm1.ShowModal; ในที่สุด ฟังก์ชั่น ShowFrm: TModalResult; ภายนอก 'TestDLL.dll';…begin…ShowFrm;…end แบบฟอร์มใน DLL ที่สร้างขึ้นในลักษณะนี้จะแสดงไอคอนอื่นพร้อมกับแอปพลิเคชันหลัก เหตุผลก็คือแอปพลิเคชันอื่นจะถูกสร้างขึ้นสำหรับ DLL ใน Delphi และแต่ละรายการ แอปพลิเคชันจะแสดงไอคอนแถบงาน วิธีแก้ไข: ส่งแอปพลิเคชันของ EXE หลักไปยัง DLL ในแอปพลิเคชันหลัก ดังนี้: DLL: function ShowFrm(app: TApplication): TModalResult; stdcall;varoldApp: TApplication;beginoldApp := Application;Application := app;Form1 : = TForm1.Create (ไม่มี); tryForm1.ShowModal; ในที่สุดForm1.Free; สิ้นสุด; แอปพลิเคชัน := oldApp;end;Main EXE: function ShowFrm(app: TApplication): TModalResult; stdcall; external 'TestDLL.dll';…begin…ShowFrm(Application);…end ยังคงมีความแตกต่างอยู่บ้าง ดูโค้ดใน Forms.pas: Constructor TApplication.Create(AOwner: TComponent);begin…ถ้าไม่ใช่ IsLibrary ให้ CreateHandle;…end;เป็นที่ทราบได้ว่าแอปพลิเคชันใน DLL ไม่มีตัวจัดการ ดังนั้นการประมวลผลลูปข้อความจะไม่ถูกดำเนินการ ซึ่งก็ถูกต้องเช่นกัน ถาม: ปัญหามักเกิดขึ้นกับ DLL ใน Delphi! ตอบ: สาเหตุที่เกิดปัญหาขึ้นนั้นเป็นเพราะกลไกการจัดการหน่วยความจำของ Delphi เอง ตัวอย่างเช่น: สร้างอ็อบเจ็กต์ใน DLL: x := TClass.Create(Application); ในขณะนี้ Delphi จะปล่อยพื้นที่ว่างโดยอัตโนมัติ อาจหมดอายุแล้ว (ระบบปฏิบัติการที่แตกต่างกันจะแตกต่างกัน) ดังนั้นการดำเนินการปล่อยของ x จะ ทำให้เกิดข้อยกเว้น อีกตัวอย่างหนึ่ง: สร้างอ็อบเจ็กต์ใน EXE และส่งผ่าน DLL เป็นตัวแปรในเครื่องใน DLL เมื่อ DLL ถูกทำลาย Delphi จะปล่อยตัวแปรทั้งหมดที่อยู่นอกขอบเขตโดยอัตโนมัติ ดังนั้นหากคุณใช้สิ่งนี้ใน EXE อีกครั้ง ข้อยกเว้นจะถูกส่งออกไป โดยทั่วไปแล้ว ปัญหาเกิดจากกลไกการจัดการหน่วยความจำของคอมไพเลอร์ Delphi "อัจฉริยะ" และกลไกการเพิ่ม/ยกเลิกการโหลด DLL ของ Windows ซึ่งนำไปสู่ข้อขัดแย้งในการเข้าถึงหน่วยความจำใน DLL และ EXE วิธีแก้ไข: (ปัญหาส่วนใหญ่สามารถหลีกเลี่ยงได้ตราบใดที่คุณปฏิบัติตามหลักการต่อไปนี้) 1. ระหว่าง DLL และ EXE พยายามอย่าใช้กลไกการจัดการหน่วยความจำอัตโนมัติของ Delphi โปรแกรมเมอร์มีหน้าที่รับผิดชอบวงจรชีวิตของออบเจ็กต์ ตัวอย่างเช่น: สำหรับ x := TClass.Create(Application) ข้างต้น ให้เปลี่ยนเป็น: x : = TClass.Create(nil); ด้วยวิธีนี้ Application จะไม่ทำให้ว่างอีกต่อไป แน่นอนว่าโปรแกรมเมอร์จะต้องปลดปล่อยมันเอง 2. พยายามหลีกเลี่ยงการมีตัวชี้ที่แตกต่างกันที่ชี้ไปยังวัตถุเดียวกันระหว่าง DLL และ EXE ตัวอย่างเช่น ใน DLL นั้น x ชี้ไปที่วัตถุ TClass และใน EXE y ชี้ไปที่วัตถุ TClass ด้วยวิธีนี้ การเผยแพร่หน่วยความจำในด้านใดด้านหนึ่งจะทำให้หน่วยความจำในอีกด้านหนึ่งไม่ถูกต้อง 3. อื่นๆ... ถาม: เธรดที่ทำงานเป็นระยะต้องหยุดชั่วคราวแล้วจึงทำงานต่อไป แต่จะเกิดอะไรขึ้นหากจำเป็นต้องหยุดเธรดในเวลานี้ (เช่น กระบวนการสิ้นสุดลง) ตอบ: โซลูชันที่ 1: ทำการวนซ้ำเป็นระยะผ่านโหมดสลีปในเธรด (หากเธรดถูกหยุดชั่วคราวผ่านโหมดสลีป เธรดจะไม่สามารถฟื้นคืนชีพผ่านประวัติย่อและวิธีการอื่น ๆ ได้) สิ้นสุดเธรดผ่าน KillThread นี่เป็นวิธีที่ง่ายที่สุด แต่หยาบคายเกินไปและอาจทำให้เกิดปัญหาได้ (KillThread เป็น API ที่ไม่แนะนำสำหรับ Windows) โซลูชันที่ 2: หยุดชั่วคราวในเธรด ส่งตัวจับเวลาไปนอกเธรด และดำเนินการต่อเป็นระยะ ๆ รหัสดังต่อไปนี้: // ThreadPRocedure Execute;begin While not Terminated dobegin... // Processing code Suspend;end;end;// Outside // Timer Procedure OnTimer(Sender: Tobject);beginthd.Resume;end;// หากต้องการสิ้นสุดเธรด Place...thd.Resume;thd.Terminate;thd.WaitFor; // โดยทั่วไป หลังจากสิ้นสุดเธรด คุณต้องใช้ WaitFor เพื่อยืนยันว่าเธรดสิ้นสุดแล้วจริงๆ ...ปัญหา: ข้อต่อระหว่างเกลียวกับด้านนอกแรงเกินไป และแม้แต่รอบการทำงานของเกลียวก็ต้องถูกกำหนดโดยใช้ตัวจับเวลาภายนอก แนวทางที่สาม (นี่เป็นวิธีที่ดีที่สุดที่ฉันคิด): หยุดเธรดชั่วคราวผ่านสัญญาณ // ThreadTMyThread = คลาส (TThread) เหตุการณ์ส่วนตัว: TEvent; ขั้นตอนการป้องกัน ดำเนินการ; แทนที่; ตัวสร้างสาธารณะ (loginInfo: TLoginInfo); แทนที่; แทนที่; ขั้นตอน SetEvent; end; { TMyThread } ตัวสร้าง TMyThread.Create (loginInfo: TLoginInfo); เริ่มต้นเหตุการณ์ := TEvent.Create (ไม่มี, True, True, 'EventName');end;destructor TMyThread.Destroy;begin Event.Free;end;procedure TMyThread.Execute;begin inherited; ในขณะที่ยังไม่สิ้นสุด ให้เริ่ม // ... Event.ResetEvent; end;end;procedure TMyThread.SetEvent;begin Event.SetEvent;end;สำหรับโปรแกรมที่ต้องการขัดจังหวะเธรด เพียงแค่โค้ดต่อไปนี้ก็เพียงพอแล้ว: beginning …thd.Terminate;thd.SetEvent;thd.WaitFor;…สิ้นสุด;