النصوص داخل نظام تشغيل ويندوز لها نوعين فقط و هم Null-Terminated و Binary String و لكل نوع منهم صفات و خصائص و اسلوب معين فى الترميز داخل الذاكره حيث
النصوص Null-Terminated : قد تكون Unicode (و تسمى ايضا Wide String) و تستهلك 2 بايت للحرف الواحد او ANSI و تستهلك 1 بايت من الذاكره، و فى كلتا الحالتين النص لابد من ان ينتهى بالرقم صفر (الرقم صفر و ليس الحرف صفر).
النصوص Binary String: يطلق عليها ايضا Basic String و Length Prefixed String و هذا النوع من النصوص دائما ما يستهلك 2 بايت للحرف الواحد اما اسلوب ترميزها داخل الذاكره فشكله كالتالى:
- اول 4 بايت هم رقم Integer و يمثل عدد الحروف داخل النص (و بالطبع لا يتم حساب مساحة هذا الرقم).
- النص نفسه بمساحة 2 بايت للحرف و عدد الحروف يماثل الرقم المذكور سابقا
- فى النهايه يتم وضع 2 بايت يحتووا على الرقم صفر
عندما تقوم بإستخدام دوال API من داخل الدوت نت يتصادف وجود معاملات نصيه، و لتقوم بتضمين الداله بشكل سليم داخل الدوت نت سيتطلب منك العمل فى اتجاه من اثنين و هم:
- استخدام المواصفه MarshalAsAttribute على معامل من نوع String و تمرر للمواصفه اى من القيم LPStr او LPWStr او BStr للأنواع المذكوره سابقا على التوالى.
- استخدام دوال النصوص الموجوده بالفئه Marshal على معامل من نوع IntPtr و ذلك للتحكم فى نوع النص.
لكل من الطرق السابقه مزايا و عيوب و لكن الطريقه الثانيه (الفئه Marshal) تستخدم بكثره لمرونتها لذا قمت بعمل الفئه TString و هى محور هذا الموضوع.
النوع Null-Terminated Unicode يرمز له بالإسم LPWSTR اما النوع Null-Terminated ANSI فيرمز له بالإسم LPSTR.
داخل نظام التشغيل تم صنع نسختين من الدوال التى تتعامل مع النصوص الـ Null-Terminated احداهما للـ Unicode و الأخرى للـ ANSI و للتسهيل على مبرمجى السى فقد تم كتابة الداله مرة واحده و تم استبدال النوعين بالنوع LPTSTR.
فى الواقع النوع LPTSTR هو ليس نوع و انما يمكن اعتباره على انه متغير موجه للمترجم حيث اذا رأى المترجم ان المشروع يستخدم نصوص Unicode فسيتم استبدال LPTSTR بـ LPWSTR اما اذا كان المشروع يستخدم نصوص ANSI فسيتم استبدال LPTSTR بـ LPSTR
داخل الـ VB يمكن اعتبار هذا الشرط يتم كالتالى
فى البدايه يتم تعريف ثابت و ليكن اسمه Unicode داخل مكان ثوابت الترجمه المشروطه بخصائص المشروع و داخل الكود
#If UNICODE Then
Public Const LPTSTR As LPWSTR
#Else
Public Const LPTSTR As LPSTR
#End Ifبالطبع الصيغه السابقه هى صيغه مجازيه فقط لتقريب الصوره.
بعد تعريف الثابت الموجود بالأعلى يمكن كتابة دالة بالشكل التالى
Public Sub SayMyName(ByVal MyName As LPSTR)
End Subكما ترى اذا تم تعريف الثابت Unicode فسيصبع المعامل MyName من نوع Unicode String و غير ذلك سيصبح من نوع ANSI String.
الفئه TString تمثل نص من اى نوع من انواع النصوص الـ 3 المستخدمه داخل نظام التشغيل و شكلها كالتالى
Public Enum StringType
BStr
NullTerminated
End Enum
Public Enum StringFormat
Unicode
Ansi
End Enum
Public NotInheritable Class TString
Implements IDisposable
Public Sub New(ByVal value As String)
Public Sub New(ByVal value As String, ByVal type As StringType, ByVal format As StringFormat)
Public ReadOnly Property Handle() As IntPtr
Public ReadOnly Property BufferSize() As Integer
Public ReadOnly Property StringLength() As Integer
Public ReadOnly Property Type() As StringType
Public ReadOnly Property Format() As StringFormat
Public Sub ChangeType(ByVal type As StringType, ByVal format As StringFormat)
Public Overrides Function ToString() As String
Public Shared Narrowing Operator CType(ByVal Obj As TString) As IntPtr
Public Shared Narrowing Operator CType(ByVal value As String) As TString
End Classالنسخه الكامله منها فى المرفقات.
شرح الفئه TString
تحتوى الفئه على مشيدين فقط احداهما يقبل النوع String و يجعل الفئه تمثل النص المرر للمشيد كأنه نص Null Terminated ANSI و المشيد الأخر يتيح لك تحديد نوع و طبيعة النص حيث ان نوع النص هو NullTerminated او BStr و طبيعة النص هى Unicode او Ansi و جدير بالذكر هنا انه فى حال اختيار نوع النص BStr فلابد من ان تكون طبيعة النص Unicode حيث داخل ويندوز النص هو فقط امتداد للـ Unicode.
تحتوى الفئه على 5 خصائص للقراءة فقط و هم Handle و تعود لك بمقبض النص داخل الذاكره، BufferSize و تعود لك بالمساحة التى يحجزها النص من الذاكره و المساحه بالبايت، StringLength و تعود لك بعدد الحروف الموجوده بالنص، Type و تعود لك بنوع النص من حيث كونه BStr او NullTerminated و اخيرا الخاصيه Format و تعود لك بطبيعة النص من حيث كونه Unicode او ANSI.
كل نسخه من الفئه TString تقام على نص واحد فقط و بمجرد ادخالك له لا يمكنك تغييره و لكن يمكنك تغيير نوعه او طبيعته.
تحتوى الفئه على دالتين و هم ChangeType و تسمح لك بتغيير نوع و طبيعة النص و من الهام ان تعرف ان الـ Handle يتغير بتغييرك للنوع او طبيعة النص، ايضا توجد الداله ToString و التى تعود لك بالنص الذى تمثله الفئه كـ String.
داخل الفئه تم اعادة تعريف المعامل CType مرتين اولهما لتحويل الفئه TString إلى IntPtr و الأخر لتحويل النوع String إلى النوع TString و ستعرف فائدهم قريبا.
كيفية استخدام الفئه TString
يمكنك استخدام الفئه TString بإسلوبين اولهما عن طريق استخدام الكلمه المحجوزه New مع اى من المشيدين الموجودين بالفئه كالتالى
Dim s1 As New TString("This is by default ansi string")
Dim S2 As New TString("A unicode string", StringType.NullTerminated, StringFormat.Unicode)
Dim S3 As New TString("A BStr string", StringType.BStr, StringFormat.Unicode)الإسلوب الثانى عن طريق علامة التساوى (تماما كما تستخدم النوع String)
Dim s as TString = "This is by default ansi string"
هنا سيتم استخدام المعامل CType ألذى تم اعادة تعريفه داخل الفئه و النسخه التى سيتم استدعائها هى التى تقبل String و تعود بـ TString و عندما يتم استدعاء المعامل CType سيقوم بدور بإنشاء نسخه جديده من الفئه TString و استدعاء المشيد ذو المعامل الواحد.
لإستخدام الفئه مع دوال الـ API فقط قم بجعل المعامل النصى من نوع IntPtr و قم بتمرير نسخه مؤقته من الفئه TString بالنص الذى تريد و ايضا بالخصائص التى تريدها:
مثلا فالنفترض انه لديك الداله PassStr و التى تأخذ معامل واحد من نوع Null-Terminated Unicode String لذا تعريف الداله سيكون كالتالى
Public Declare Function PassStr Lib "TestDll.dll" (ByVal hStr As IntPtr) As Boolean
و ستقوم بإستدعائها كالتالى
PassStr(New TString("My unicode string", StringType.NullTerminated, StringFormat.Unicode))كل ما يحدث هنا هو انشاء نسخه جديده من الفئه ثم الحصول على مقبض النص الذى تمثله هذه الفئه و تمريره للداله PassStr و ذلك يتم عن طريق المعامل CType ألذى يقبل TString و يعود بـ IntPtr بعد ذلك يتم تمرير المقبض للداله لتقوم بعملها بعدها يتم استدعاء الداله Dispose بشكل تلقائى ليتم تحرير الذاكره التى حجزتها الفئه و السبب فى ذلك ان المتغير الذى تم تمريره هو Temporary و لذلك يتم تدمير الفئه بمجرد انتهاء الإستدعاء.
اتمنى لكم الإستفادة منها.
و الله ولى التوفيق