يمكنك PInvoke متعدد البايت ANSI إلى varargs؟ ما الخطأ الذي افعله؟

StackOverflow https://stackoverflow.com/questions/825844

سؤال

و** MAJOR UPDATE ** لقد ارتكبت خطأ طفيفة ولكن ما زلت غريبة عن ما يحدث بالضبط.

وظيفة أدعو هو في الواقع "fooV"، وهي وظيفة مع هذا التوقيع:

foo(const char *, const char *, EnumType, va_list)

وهذا من تلقاء نفسه في AccessViolationExceptions الأول هو الحصول على، ولكن لا يفسر لماذا تعمل بارامس المعلمات لكل نوع. NET الأخرى باستثناء السلاسل التي سيكون لديك لتحويلها إلى أحرف متعددة البايت ANSI. انا ذاهب للحصول على مطور DLL لفضح الإصدار الذي يستخدم في الواقع ... في قائمة المعلمة، أي تلميحات عن PInvoking إذا va_list هي المعلمة؟

و** آخر العمر **

وهذا مرتبط، ولكنها مختلفة من السؤال الأخير سألت .

ولدي لاستخدام PInvoke استدعاء دالة مكتبة C الذي يحتوي على توقيع التالية:

foo(const char *, const char *, EnumType, ...)

وظيفة بتكوين مورد بطريقة تختلف حسب StructType. حالة أنا مهتم بتكوين شيء تتوقع varargs أن تكون سلسلة متعدد البايت ANSI واحدة. أنا استخدم هذا التوقيع PInvoke لاستدعاء الدالة:

    [DllImport(DllName, CharSet = CharSet.Ansi, EntryPoint = "foo")]
    public static extern int Foo(string s1, string s2, EnumType st1, params string[] s3);

ووparams string[] يبدو غريبا بالنسبة لي، ولكن التوقيعات السابقة التي دعت هذه الوظيفة تم استخدامه لأنواع أخرى مثل bool ذلك تابعت النمط.

وأنا التفاف هذا مع طريقة صافي ودا:

public void Foo(string s1, string s2, string s3)
{
    int error = Dll.Foo(s1, s2, EnumType.Foo, s3);
    // handle errors
}

ومؤخرا غيرت التوقيع لتشمل "BestFitMapping = كاذبة، ThrowOnUnmappableChar = صحيح" في DLLImport السمة ليتوافق مع اقتراحات FxCop ل. هذا هو ذر الرماد في العيون كما سأذكر لاحقا.

وماذا أتوقع من إجراء هذا التغيير هو دعم محدود ليونيكود. على سبيل المثال، على جهاز مع الشفرة الإنجليزية سيتم طرح استثناء إذا كنت تمر السلسلة التي تحتوي على حرف اليابانية. على جهاز اليابانية، كنت أتوقع أن تكون قادرة على تمرير سلسلة اليابانية إلى وظيفة. عملت اختبار اللغة الإنجليزية كما هو متوقع، ولكن الاختبار الياباني يلقي System.Runtime.InteropServices.COMException مع HRESULT 0x8007007A. يحدث هذا حتى من دون إعدادات BestFitMapping وThrowOnUnmappableChar.

ولقد فعلت قليلا ونظروا حولهم، وشهدت بعض المواقع التي اقترح هل يمكن PInvoke varargs فقط عن طريق تحديد الحجج العادية، لذلك حاولت هذا التوقيع:

[DllImport(DllName, CharSet = CharSet.Ansi, EntryPoint = "foo")]
public static extern int Foo(string s1, string s2, EnumType st1, string s3);

وهذا يطرح AccessViolationException عندما استخدامها على إما الإنجليزية أو اليابانية آلة.

وأنا لا يمكن استخدام UTF-8 أو ترميز Unicode آخر لأن هذا مكتبة C تتوقع ومقابض متعددة البايت ANSI الوحيد. لقد وجدت أن تحديد CharSet.Unicode لهذه المهمة يعمل، ولكن أنا قلقة انها فقط بالصدفة وليس شيئا عليها وأود أن الاعتماد. لقد فكرت في تحويل السلسلة إلى [] بايت باستخدام الشفرة للنظام، ولكن لا يمكن معرفة كيفية تحديد أود أن تمرير صفيف بايت إلى المعلمة varargs.

ما الذي يحدث؟ على الجهاز الإنجليزية، الحروف الانجليزية العمل charactes الجميلة واليابانية بطرح ArgumentException كما هو متوقع. على الجهاز اليابانية، الأحرف الإنجليزية تعمل بشكل جيد والأحرف اليابانية رمي COMException. هل هناك شيء خاطئ مع توقيعي PInvoke؟ لقد حاولت استخدام السمة MarshalAs لتحديد نوع LPArray ونوع فرعي من LPStr، ولكن هذا فشل في بنفس الطريقة. UnmanagedType.LPStr يشير إلى أنه عبارة عن سلسلة ANSI بايت واحد؛ هل هناك طريقة لتحديد سلسلة متعدد البايت ANSI؟

و** تحديث ** وإليك إضافة الاشياء أخذ تعليقات الحالية في الاعتبار.

إذا كنت تحديد اصطلاح استدعاء من CDecl واتخاذ المعايير العادية من هذا القبيل:

[DllImport(DllName, CharSet = CharSet.Ansi, EntryPoint = "foo", CallingConvention = CallingConvention.Cdecl)]
public static extern int Foo(string s1, string s2, EnumType e1, string s3) 

وعند استخدام هذه المواصفات، أحصل على AccessViolationException. لذلك حاولت هذه:

[DllImport(DllName, CharSet = CharSet.Ansi, EntryPoint = "foo", CallingConvention = CallingConvention.CDecl)]
public static extern int Foo(string s1, string s2, EnumType e1, string s3) 

وهذا يعمل للغة الإنكليزية ويلقي COMException عند استخدام الأحرف اليابانية.

والشيء الوحيد الذي كنت قد وجدت أن العكانساس باستمرار هو هذا:

[DllImport(DllName, CharSet = CharSet.Ansi, EntryPoint = "foo", BestFitMapping = false, ThrowOnUnmappableChar = true)]
public static extern int Foo(string s1, string s2, EnumType e1, params IntPtr[] s3)

لجعل هذا العمل، وأنا استخدم Marshal.StringToHGlobalAnsi () وتمرير هذا المؤشر. هذا يعمل للغة الإنكليزية واليابانية. أنا لست مرتاحا ليس مفهوما لماذا هذا هو الحل، لكنه يعمل.

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

المحلول

وربما كنت في حاجة أيضا إلى تحديد CallingConvention=CallingConvention.Cdecl منذ وظيفة varargs سوف تستخدم اتفاقية _cdecl استدعاء (الافتراضي هو WINAPI التي في التخلف بدوره إلى STDCALL).

وأنت قد تحتاج إلى شيء آخر، ولكن أنا متأكد من أنك سوف تحتاج إلى ما لا يقل عن ذلك.

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