سؤال

بعد الكثير من التجارب ، وجدت طريقة لتبادل PCHAR من DLL Freepascal مع Delphi المترجمة EXE. أنا مسؤول عن كل من رمز مصدر DLL و EXE ولكن يجب أن يكون المرء في Freepascal والآخر في Delphi. يتضمن حلي الأساليب التالية في DLL:

function GetAString(): PChar;
var aString: string;
begin
  aString := 'My String';
  result := StrAlloc(length(aString) + 1);
  StrPCopy(result, aString); 
end;

procedure FreeString(aString: PChar);
begin
  StrDispose(aString); 
end;

ومن Delphi Exe ، لاستدعاء طريقة getastring ، أحتاج إلى استدعاء طريقة getastring ، وإنقاذ PCHAR إلى سلسلة Delphi الفعلية واستدعاء طريقة freestring.

هل هذه هي أفضل طريقة لتبادل سلسلة من DLL Freepascal مع Delphi Exe؟ هل يمكنني تجنب الدعوة إلى Freestring من Delphi؟

وأخيرًا ، إذا كان هذا هو الحل الصحيح ، فكيف سيتصرف مع Delphi 2010 و WideString افتراضيًا: هل أحتاج إلى إجبار Widepchar في Freepascal أيضًا؟

هل كانت مفيدة؟

المحلول

تتمثل إحدى طرق تبادل السلاسل بين تطبيق DLL و Delphi دون استخدام مكالمة Freestring في أخذ المخزن المؤقت للسلسلة من تطبيق الاتصال كـ PCHAR ، وملء المخزن المؤقت في DLL. هذه هي الطريقة التي تعمل بها وظائف Windows API عندما تحتاج إلى تبادل السلاسل مع تطبيقات الاتصال.

للقيام بذلك ، يقوم تطبيق الاتصال الخاص بك بإنشاء مخزن مؤقت للسلسلة ، ويرسل PCHAR يشير إلى هذا المخزن المؤقت مع حجم المخزن المؤقت إلى وظيفة DLL الخاصة بك. إذا كان حجم المخزن المؤقت أصغر من السلسلة الفعلية التي يجب على DLL إرسالها إلى التطبيق ، فيمكن أن ترسل وظيفة DLL الحجم الفعلي المطلوب للمخزن المؤقت إلى تطبيق الاتصال.

كيف سيتصرف مع Delphi 2010 و WideString افتراضيًا: هل أحتاج إلى إجبار Widepchar في Freepascal أيضًا؟

في Delphi 2009 و Delphi 2010 ، PCHAR يساوي Pwidechar. في الإصدارات السابقة من Delphi ، وبقدر ما أعرف ، في Freepascal ، PCHAR يساوي Pansichar. لذا ، إذا قمت بإرجاع PCHAR من DLL ، فلن يعمل الكود بشكل صحيح في Delphi 2010. يجب عليك استخدام Pansichar أو Pwidechar بشكل صريح. يمكنك مرة أخرى متابعة وظائف Windows API. إنها توفر نسختين من العديد من وظائف API ، واحدة لها دعم widechar الذي يحتوي اسمه على حرف W كلاحقة ، والآخر يحمل دعم ANSI الذي يحتوي اسمه على شخصية كلاحقة.

سيكون إعلان وظيفة DLL الخاص بك شيئًا من هذا القبيل:

function AStringFuncW(Buffer: PWideChar; var BufferSize: Integer): Boolean;

function AStringFuncA(Buffer: PAnsiChar; var BufferSize: Integer): Boolean;

تعديل:

هنا رمز عينة:

1- وظيفة DLL الخاصة بك لـ Widechar ستكون هكذا:

function AStringFuncW(Buffer: PWideChar; var BufferSize: Integer): Boolean; stdcall;
var
  MyOutputStr : WideString;
begin
  Result := False;

  // Calculate your output string here.
  MyOutputStr := 'This is a sample output';

  // Check if buffer is assigned, and its given length is enough
  if Assigned(Buffer) and (BufferSize >= Length(MyOutputStr) + 1) then
  begin
    //Copy output string into buffer
    StrPCopy(Buffer,MyOutputStr);
    Result := True;
  end;
  //Return actual size of output string.
  BufferSize := Length(MyOutputStr) + 1;
end;

بالنسبة لإصدار Ansichar ، يمكنك استخدام نفس الرمز مع Ansistring و Pansichar ، أو تحويل معلمة سلسلة ANSI إلى Unicode ، واستدعاء AstringFuncw داخل وظيفة AstringFunca ، ثم تحويل سلسلة الإرجاع من AstringFuncw.

2- إليك كيفية تحديد هذه الوظائف في وحدة الواجهة الخاصة بك لاستخدامها من قبل عملاء DLL:

unit TestDLLIntf;

interface
const
  TestDll = 'Test.dll';

  function AStringFuncW(Buffer: PWideChar; var BufferSize: Integer): Boolean; stdcall;
  function AStringFuncA(Buffer: PWideChar; var BufferSize: Integer): Boolean; stdcall;
  function AStringFunc(Buffer: PWideChar; var BufferSize: Integer): Boolean; stdcall;

implementation

function AStringFuncW; external TestDll name 'AStringFuncW';
function AStringFuncA; external TestDll name 'AStringFuncA';
{$IFDEF UNICODE}
 function AStringFunc; external TestDll name 'AStringFuncW';
{$ELSE}
 function AStringFunc; external TestDll name 'AStringFuncA';
{$ENDIF}

end.

في الكود أعلاه ، يتم الإعلان عن كل من وظائف AstringFuncw و AstringFunca كوظائف خارجية. تشير وظيفة AstringFunc إلى إصدار Widechar في Delphi 2009 - 2010 ، وتشير إلى إصدار Ansichar في الإصدارات الأخرى.

3- هنا يمكنك أن ترى كيف يمكن لعملاء DLL استخدام وظيفتك:

procedure TForm1.Button1Click(Sender: TObject);
var
  Str : string;
  Size : Integer;
begin
  // Retrieve required buffer size
  AStringFunc(nil,Size);
  // Set buffer
  SetLength(Str,Size);
  // Retrieve output string from DLL function.
  if AStringFunc(PChar(Str),Size) then
    ShowMessage(Str);
end;

في الكود أعلاه ، يحصل تطبيق العميل أولاً على حجم الإخراج الفعلي من AstringFunc ، ثم يقوم بتعيين مخزن مؤقت للسلسلة ، ويسترجع سلسلة الإخراج من DLL. لاحظ أن نفس الرمز يجب أن يعمل في كل من الإصدارات Unicode وغير unicode من Delphi ، لأن AstringFunc يشير إما إلى AstringFunca أو AstringFuncw داخل DLL الخاص بك اعتمادًا على ما إذا كان المترجم يدعم Unicode أم لا.

نصائح أخرى

اعتمادًا على البيانات التي تمر بها ، قد تكون قادرًا على استخدام Widestrings بدلاً من ذلك. تم تخصيصها على كومة Windows ، لذلك إذا قمت بتخصيص واحدة في DLL وحررها في Exe ، فسيذهب كلاهما عبر مدير الذاكرة نفسه.

يجب أن تكون قادرًا على استخدام إعلان الوظيفة مثل هذا:

procedure GetAString(var Result: WideString); stdcall;

وسوف يتعامل كل من Delphi و Freepascal مع التخصيص والتحرير تلقائيًا. WideString هو نفسه في Delphis الممكّن من Unicode مثل تلك التي تمهيدي ، لذلك لن تحتاج إلى تغيير الأشياء لذلك.

قاعدة الإبهام لتخصيص/توصيل الذاكرة في EXE و DLL هي: الوحدة النمطية التي تخصص الذاكرة هي المسؤولة عن توصيل الذاكرة نفسها. في مثالك ، فإن DLL هو الذي يخصص الذاكرة وتوصيلها ، لذلك فهي صحيحة.

هناك استثناء واحد من القاعدة العامة. يقوم كل من Delphi وأحدث إصدارات Pascal المجانية بتنفيذ WideStrings كـ BSTR - نوع بيانات سلسلة يستخدمها COM. يتم تخصيص WideStrings وتحريرها بواسطة Windows ، وليس بواسطة Delphi (أو Free Pascal) Manager. لذلك ليست هناك حاجة لاستخدام pwidechar لتمرير الحجج الواسعة بين EXE و DLL - يمكن تمرير Widestrings مباشرة.


محدث:

لقد اختبرت الرمز التالي:

Pascal Free (الإصدار 2.2.4) DLL:

library TestLib1;

{$mode objfpc}{$H+}

{$IFDEF WINDOWS}{$R TestLib1.rc}{$ENDIF}

procedure GetAStringProc(var S: WideString); stdcall;
begin
  S:= '12345';
end;

function GetAStringFunc: WideString; stdcall;
begin
  Result:= '12345';
end;

exports
  GetAStringProc, GetAStringFunc;

begin
end.

Delphi 2009 Exe (الشكل الرئيسي):

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    S: WideString;
  end;

var
  Form1: TForm1;

type
  TGetAStringProc = procedure(var S: WideString); stdcall;
  TGetAStringFunc = function: WideString; stdcall;

var
  GetAStringProc: TGetAStringProc;
  GetAStringFunc: TGetAStringFunc;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
    GetAStringProc(S);
    Caption:= S;
    S:= '';
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
    S:= GetAStringFunc;
    Caption:= S;
    S:= '';
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  LibHandle: THandle;

begin
  LibHandle:= LoadLibrary('TestLib1.dll');
  if LibHandle <> 0 then begin
    @GetAStringProc:= GetProcAddress(LibHandle, 'GetAStringProc');
    @GetAStringFunc:= GetProcAddress(LibHandle, 'GetAStringFunc');
  end;
  if not Assigned(GetAStringProc) or not Assigned(GetAStringFunc) then
      ShowMessage('Error!');
end;

end.

يبدو أن الإجراء GETARCTRINGPROC يعمل بشكل جيد بينما تؤدي الوظيفة GETARCTRINGFUNC إلى الوصول إلى انتهاك. يبدو أن Delphi و Free Pascal لإرجاع نتيجة نوع السلسلة بشكل مختلف.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top