ما هو safecall؟

أنا أعمل على إنشاء ActiveX EXE باستخدام VB6 ، والمثال الوحيد الذي حصلت عليه هو مكتوب في دلفي.

عند قراءة رمز المثال ، لاحظت وجود بعض الوظائف التي تتبع توقيعاتها الكلمة الأساسية safecall . إليك مثال على ذلك:

function AddSymbol(ASymbol: OleVariant): WordBool; safecall;

ما هو الغرض من هذه الكلمة؟

0
وأضاف تحرير
الآراء: 2
<< a href = "http://en.wikipedia.org/wiki/X86_calling_conventions#safecall>" rel = "nofollow noreferrer"> en.wikipedia.org/wiki/X86_calling_conventions#safecall> ؛
وأضاف المؤلف Greg Hewgill, مصدر

4 إجابة

في COM ، كل أسلوب دالة تقوم بإرجاع HRESULT :

IThingy = interface
   ['{357D8D61-0504-446F-BE13-4A3BBE699B05}']
   function AddSymbol(ASymbol: OleVariant; out RetValue: WordBool): HRESULT; stdcall;
end;

هذه قاعدة مطلقة في COM:

  • لا توجد استثناءات في COM
  • كل شيء يعرض HRESULT
  • يشير HRESULT السلبي إلى فشل
  • بلغات أعلى مستوى ، يتم تعيين حالات الفشل إلى استثناءات

كان القصد من مصممي COM هو أن اللغات ذات المستوى الأعلى ستؤدي تلقائيًا إلى ترجمة الطرق الفاشلة إلى استثناء.

لذلك في لغتك الخاصة ، سيتم تمثيل استدعاء COM بدون HRESULT. على سبيل المثال:

  • Delphi-like: function AddSymbol(ASymbol: OleVariant): WordBool;
  • C#-like: WordBool AddSymbol(OleVariant ASymbol);

في دلفي ، يمكنك اختيار استخدام توقيع الدالة الأولية:

IThingy = interface
   ['{357D8D61-0504-446F-BE13-4A3BBE699B05}']
   function AddSymbol(ASymbol: OleVariant; out RetValue: WordBool): HRESULT; stdcall;
end;

والتعامل مع رفع الاستثناءات بنفسك:

bAdded: WordBool;
thingy: IThingy;
hr: HRESULT;

hr := thingy.AddSymbol('Seven', {out}bAdded);
if Failed(hr) then
    OleError(hr);

أو ما يعادله أقصر:

bAdded: WordBool;
thingy: IThingy;
hr: HRESULT;

hr := thingy.AddSymbol('Seven', {out}bAdded);
OleCheck(hr);

أو ما يعادله أقصر:

bAdded: WordBool;
thingy: IThingy;

OleCheck(thingy.AddSymbol('Seven'), {out}bAdded);

لم تنوي COM لك للتعامل مع HRESULTs

ولكن يمكنك أن تطلب من دلفي أن تخبرك عن ذلك ، حتى تتمكن من مواصلة البرمجة:

IThingy = interface
   ['{357D8D61-0504-446F-BE13-4A3BBE699B05}']
   function AddSymbol(ASymbol: OleVariant): WordBool); safecall;
end;

خلف الكواليس ، سيتحقق المحول البرمجي من الإرجاع HRESULT وإلقاء الاستثناء EOleSysError إذا أشار HRESULT إلى فشل (أي كان سالبة). إصدار برنامج safecall الذي يتم إنشاؤه بواسطة compiler مكافئ وظيفيًا لما يلي:

function AddSymbol(ASymbol: OleVariant): WordBool; safecall;
var
   hr: HRESULT;
begin
   hr := AddSymbol(ASymbol, {out}Result);
   OleCheck(hr);
end;

ولكنه يحررك من الاتصال ببساطة:

bAdded: WordBool;
thingy: IThingy;

bAdded := thingy.AddSymbol('Seven');

tl؛ dr: يمكنك استخدام إما:

function AddSymbol(ASymbol: OleVariant; out RetValue: WordBool): HRESULT; stdcall;
function AddSymbol(ASymbol: OleVariant): WordBool; safecall;

لكن يتطلب منك أولاً معالجة HRESULTs في كل مرة.

مكافأة الدردشة

تقريبًا أبداً لا تريد معالجة HRESULTs بنفسك؛ فإنه يخرق البرنامج مع ضوضاء لا يضيف شيئا. ولكن في بعض الأحيان قد ترغب في التحقق من HRESULT بنفسك (على سبيل المثال ، تريد التعامل مع فشل غير استثنائى). أبدا إصدارات من دلفي قد شملت بدءا ترجمة واجهات رأس ويندوز التي أعلن كلا الطريقتين:

IThingy = interface
   ['{357D8D61-0504-446F-BE13-4A3BBE699B05}']
   function AddSymbol(ASymbol: OleVariant; out RetValue: WordBool): HRESULT; stdcall;
end;

IThingySC = interface
   ['{357D8D61-0504-446F-BE13-4A3BBE699B05}']
   function AddSymbol(ASymbol: OleVariant): WordBool); safecall;
end;

أو من مصدر RTL:

  ITransaction = interface(IUnknown)
    ['{0FB15084-AF41-11CE-BD2B-204C4F4F5020}']
    function Commit(fRetaining: BOOL; grfTC: UINT; grfRM: UINT): HResult; stdcall;
    function Abort(pboidReason: PBOID; fRetaining: BOOL; fAsync: BOOL): HResult; stdcall;
    function GetTransactionInfo(out pinfo: XACTTRANSINFO): HResult; stdcall;
  end;

  { Safecall Version }
  ITransactionSC = interface(IUnknown)
    ['{0FB15084-AF41-11CE-BD2B-204C4F4F5020}']
    procedure Commit(fRetaining: BOOL; grfTC: UINT; grfRM: UINT); safecall;
    procedure Abort(pboidReason: PBOID; fRetaining: BOOL; fAsync: BOOL); safecall;
    procedure GetTransactionInfo(out pinfo: XACTTRANSINFO); safecall;
  end;

تقف SC لاحقة safecall . كلتا الواجهات متساوية ، ويمكنك اختيار أي إعلان متغير COM الخاص بك كما اعتماد على رغبتك:

//thingy: IThingy;
thingy: IThingySC;

يمكنك حتى القاء بينهما:

thingy: IThingSC;
bAdded: WordBool;

thingy := CreateOleObject('Supercool.Thingy') as TThingySC;

if Failed(IThingy(thingy).AddSymbol('Seven', {out}bAdded) then
begin
   //Couldn't seven? No sixty-nine for you
   thingy.SubtractSymbol('Sixty-nine');
end;

Extra مكافأة الدردشة - C#

C # بشكل افتراضي يفعل ما يعادل دلفي safecall ، باستثناء C #:

  • يجب إلغاء الاشتراك في تعيين safecall
  • بدلاً من الاشتراك

في C# سوف تعلن واجهة COM الخاصة بك على النحو التالي:

[ComImport]
[Guid("{357D8D61-0504-446F-BE13-4A3BBE699B05}")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IThingy
{
   WordBool AddSymbol(OleVariant ASymbol);
   WordBool SubtractSymbol(OleVariant ASymbol);
}

ستلاحظ أن HRESULT COM مخفي منك. سيقوم برنامج التحويل البرمجي C# ، مثل برنامج التحويل البرمجي Delphi ، بالتحقق من HRESULT التي تم إرجاعها تلقائيًا وإلقاء استثناء لك.

وفي C# ، كما هو الحال في دلفي ، يمكنك اختيار التعامل مع HRESULTs بنفسك:

[ComImport]
[Guid("{357D8D61-0504-446F-BE13-4A3BBE699B05}")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IThingy
{
   [PreserveSig]
   HRESULT AddSymbol(OleVariant ASymbol, out WordBool RetValue);

   WordBool SubtractSymbol(OleVariant ASymbol);
}

The [PreserveSig] tells the compiler to preserve the method signature exactly as is:

يشير إلى ما إذا كانت الطرائق غير المُدارة التي تحتوي على قيم الإرجاع HRESULT أو retval يتم ترجمتها مباشرةً أو HRESULT أو retval يتم تحويل قيم الإرجاع تلقائيًا إلى استثناءات.

0
وأضاف

Safecall يمر المعلمات من اليمين إلى اليسار ، بدلا من الباسكال أو التسجيل (الافتراضي) من اليسار إلى اليمين

مع safecall ، يزيل الإجراء أو الوظيفة المعلمات من المكدس عند الرجوع (مثل pascal ، ولكن ليس مثل cdecl حيث يصل إلى المتصل)

Safecall تنفذ استثناء "الجدران النارية" ؛ esp على Win32 ، بتنفيذ هذا الإعلام خطأ COM interprocess. من شأنه أن يكون مطابقا ل stdcall (اتفاقية الاتصال الأخرى المستخدمة مع الفوز api)

0
وأضاف

بالإضافة إلى ذلك ، تعمل جدران الحماية الاستثنائية عن طريق استدعاء SetErrorInfo() مع كائن يعتمد IErrorInfo بحيث يمكن الحصول على معلومات موسعة حول الاستدعاء. يتم ذلك بواسطة تجاوز TObject.SafeCallException في كل من TComObject و TAutoIntfObject. كلا هذين النوعين أيضاً بتنفيذ ISupportErrorInfo لتمييز هذه الحقيقة.

في حالة حدوث استثناء ، يمكن لمتصل طريقة safecall الاستعلام عن ISupportErrorInfo ، ثم الاستعلام عن الواجهة التي نتج عنها أسلوب فشل HRESULT (مجموعة بت عالية) ، وإذا كان ذلك يعيد S_OK ، يمكن الحصول GetErrorInfo() على معلومات الاستثناء (الوصف ، التعليمات ، إلخ ، في شكل تطبيق IErrorInfo التي تم تمريرها إلى SetErrorInfo() بواسطة Delphi RTL في تجاوز SafeCallException) .

0
وأضاف

What Francois said and if it wasn't for safecall your COM method call would have looked like below and you would have to do your own error checking instead of getting exceptions.

function AddSymbol(ASymbol: OleVariant; out Result: WordBool): HResult; stdcall;
0
وأضاف