سؤال

ما هو "مقبض" عند مناقشة الموارد في ويندوز؟ كيف يعملون؟

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

المحلول

إنها قيمة مرجعية مجردة لمورد، وغالبا ما تكون ذاكرة أو ملف مفتوح، أو أنبوب.

على وجه صحيح, في Windows، (وعموما في الحوسبة) مقبض هو تجريد يخفي عنوان ذاكرة حقيقية من مستخدم API، مما يسمح للنظام بإعادة تنظيم الذاكرة الفعلية بشفافية إلى البرنامج. حل مقبض في مؤشر أقفال الذاكرة، وإصدار المقبض يبطل المؤشر. في هذه الحالة، فكر في الأمر كمفهر في جدول مؤشرات ... يمكنك استخدام الفهرس لمكالمات API النظام، ويمكن للنظام تغيير المؤشر في الجدول في الإرادة.

بدلا من ذلك، قد يتم إعطاء مؤشر حقيقي كمؤكلا عندما يعتزم كاتب API أن مستخدم API معزول من تفاصيل ما تم إرجاع العنوان إليه؛ في هذه الحالة، يجب اعتباره أن ما يشير إليه المقبض قد يتغير في أي وقت (من إصدار API إلى الإصدار أو حتى من الاتصال إلى Call of the API الذي يقوم بإرجاع المقبض) - يجب أن يتم علينا معالجة المقبض بنفس قيمة مبهمة ذو معنى فقط إلى API.

يجب أن أضيف ذلك في أي نظام تشغيل حديث، حتى ما لا يزال ما يسمى "المؤشرات الحقيقية" لا تزال معتمة في مساحة الذاكرة الظاهرية للعملية، مما يتيح O / S لإدارة وإعادة ترتيب الذاكرة دون إبطال المؤشرات داخل العملية وبعد

نصائح أخرى

أ HANDLE هو معرف فريد من نوعه السياق. عن طريق السياق الخاصة، أعني أنه لا يمكن استخدام مقبض تم الحصول عليه من سياق واحد بالضرورة في أي سياق تمريصي آخر يعمل أيضا HANDLEس.

علي سبيل المثال، GetModuleHandle إرجاع معرف فريد لوحدة محملة حاليا. يمكن استخدام المقبض الذي تم إرجاعه في وظائف أخرى تقبل مقابض الوحدة النمطية. لا يمكن منحها للوظائف التي تتطلب أنواع أخرى من المقابض. على سبيل المثال، لا يمكنك إعطاء مقبض عاد من GetModuleHandle ل HeapDestroy ونتوقع أن تفعل شيئا معقولا.

ال HANDLE نفسها مجرد نوع لا يتجزأ. عادة، ولكن ليس بالضرورة، هو مؤشر لبعض أنواع الأسالطة أو موقع الذاكرة. على سبيل المثال، HANDLE عاد من قبل GetModuleHandle هو في الواقع مؤشر إلى عنوان الذاكرة الظاهرية الأساسية للوحدة. ولكن لا توجد قاعدة تفيد بأن المقابض يجب أن تكون مؤشرات. يمكن أن يكون المقبض أيضا عددا صحيحا بسيطا (يمكن استخدامه من قبل بعض API Win32 كوسيط في صفيف).

HANDLES هي تمثيلات مبهمة عمدا التي توفر التغليف والتجريد من موارد Win32 الداخلية. وبهذه الطريقة، يمكن ل APIS Win32 أن يغير النوع الأساسي وراء المقبض، دون أنه يؤثر على رمز المستخدم بأي طريقة (على الأقل هذه هي الفكرة).

النظر في هذه التطبيقات الثلاثة المختلفة المختلفة ل API Win32 التي صنعت للتو، وافترض ذلك Widget هو struct.

Widget * GetWidget (std::string name)
{
    Widget *w;

    w = findWidget(name);

    return w;
}
void * GetWidget (std::string name)
{
    Widget *w;

    w = findWidget(name);

    return reinterpret_cast<void *>(w);
}
typedef void * HANDLE;

HANDLE GetWidget (std::string name)
{
    Widget *w;

    w = findWidget(name);

    return reinterpret_cast<HANDLE>(w);
}

يعرض المثال الأول التفاصيل الداخلية حول API: إنها تسمح بمعرفة رمز المستخدم بذلك GetWidget إرجاع مؤشر إلى struct Widget. وبعد هذا له عواقب

  • يجب أن يكون رمز المستخدم الوصول إلى ملف الرأس الذي يحدد Widget بنية
  • يمكن أن يؤدي رمز المستخدم إلى تعديل الأجزاء الداخلية من المرتجعة Widget بنية

كل من هذه النتائج قد يكون غير مرغوب فيه.

المثال الثاني يخفي هذه التفاصيل الداخلية من رمز المستخدم، عن طريق العودة فقط void *. وبعد لا يحتاج رمز المستخدم إلى الوصول إلى الرأس الذي يحدد Widget بنية.

المثال الثالث هو بالضبط نفس الثانية، لكننا نسمي فقط void * أ HANDLE في حين أن. ربما هذا لا يشجع كود المستخدم من محاولة معرفة بالضبط ما void * نقاط ل.

لماذا تمر عبر هذه المشكلة؟ النظر في هذا المثال الرابع من إصدار أحدث من هذه API نفسها:

typedef void * HANDLE;

HANDLE GetWidget (std::string name)
{
    NewImprovedWidget *w;

    w = findImprovedWidget(name);

    return reinterpret_cast<HANDLE>(w);
}

لاحظ أن واجهة الوظيفة مطابقة للمثال الثالث أعلاه. هذا يعني أن رمز المستخدم يمكن أن يستمر في استخدام هذا الإصدار الجديد من API، دون أي تغييرات، على الرغم من تغيير تنفيذ "وراء الكواليس" لاستخدام NewImprovedWidget بنية بدلا من ذلك.

المقابض الموجودة في هذه الأمثلة هي حقا مجرد صديقة، من المفترض، الاسم ل void *, ، وهذا هو بالضبط ما HANDLE في Win32 API (انظر الأمر في MSDN.). يوفر جدارا رائعا بين كود المستخدم والتمثيل الداخلي لمكتبة Win32 الذي يزيد من إمكانية النقل، بين إصدارات Windows، من التعليمات البرمجية التي تستخدم API Win32.

يعد مقبض في برمجة Win32 رمزا يمثل موردا يديره نواة Windows Kernel. يمكن أن يكون مقبض في نافذة، ملف، إلخ.

تعالج المقابض عبارة عن طريقة لتحديد مورد الجسيمات التي ترغب في العمل باستخدام Apis Win32.

لذلك على سبيل المثال، إذا كنت ترغب في إنشاء نافذة، وإظهارها على الشاشة، فيمكنك القيام بما يلي:

// Create the window
HWND hwnd = CreateWindow(...); 
if (!hwnd)
   return; // hwnd not created

// Show the window.
ShowWindow(hwnd, SW_SHOW);

في المثال أعلاه HWND يعني "مقبض إلى نافذة".

إذا كنت معتادا على لغة موجهة للكائنات، فيمكنك التفكير في مقبض كمثال من الفئة بدون أساليب من ولاية الدولة فقط قابلة للتعديل من خلال وظائف أخرى. في هذه الحالة فرجي شباك تعمل الوظيفة على تعديل حالة مقبض النافذة.

يرى مقابض وأنواع البيانات للمزيد من المعلومات.

مقبض هو معرف فريد لكائن يديره Windows. إنه مثل المؤشر, ، لكن ليس مؤشر في SECENT ليس عنوانا يمكن تغييره بواسطة رمز المستخدم للوصول إلى بعض البيانات. بدلا من ذلك، سيتم تمرير مقبض إلى مجموعة من الوظائف التي يمكن أن تؤدي الإجراءات الموجودة على الكائن، يحدد المقبض.

يعد مقبض مثل القيمة الرئيسية الأساسية لسجل في قاعدة بيانات.

تحرير 1: حسنا، لماذا يحدد المخالفة القصيرة، وهو مفتاح أساسي على سجل قاعدة بيانات بشكل فريد، ويحدد مقبض في نظام Windows بشكل فريد نافذة، ملف مفتوح، إلخ، هذا ما أقوله.

لذلك على المستوى الأساسي مقبض أي فرز هو مؤشر إلى مؤشر أو

#define HANDLE void **

الآن لماذا تريد استخدامه

يتيح أخذ برنامج الإعداد:

class Object{
   int Value;
}

class LargeObj{

   char * val;
   LargeObj()
   {
      val = malloc(2048 * 1000);
   }

}

void foo(Object bar){
    LargeObj lo = new LargeObj();
    bar.Value++;
}

void main()
{
   Object obj = new Object();
   obj.val = 1;
   foo(obj);
   printf("%d", obj.val);
}

لذلك نظرا لأن OBJ تم تمريره بالقيمة (قم بعمل نسخة وإعطاء ذلك إلى الوظيفة) إلى FOO، ستطبع PrintF القيمة الأصلية من 1.

الآن إذا قمنا بتحديث FOO إلى:

void foo(Object * bar)
{
    LargeObj lo = new LargeObj();
    bar->val++;
}

هناك فرصة أن تقوم الطباعة بطباعة القيمة المحدثة 2. ولكن هناك أيضا احتمال أن تسبب FOO في شكل تلف ذاكرة أو استثناء.

السبب في ذلك، بينما تستخدم الآن مؤشرا لتمرير OBJ إلى الوظيفة التي تقوم أيضا بتخصيص 2 ميجا من الذاكرة، فقد يتسبب ذلك في نقل نظام التشغيل الخاص بنقل الذاكرة حول تحديث موقع OBJ. نظرا لأنك قد اجتازت المؤشر حسب القيمة، إذا تم نقل OBJ ثم يقوم نظام التشغيل بتحديث المؤشر ولكن ليس النسخة في الوظيفة وربما تسبب مشاكل.

تحديث أخير إلى FOO من:

void foo(Object **bar){
    LargeObj lo = LargeObj();
    Object * b = &bar;
    b->val++;
}

سيؤدي هذا دائما إلى طباعة القيمة المحدثة.

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

أي أنواع معينة من المقابض (HWND، ملف، إلخ) هي محددة للنطاق وإشارة إلى نوع معين من الهيكل للحماية من تلف الذاكرة.

فكر في النافذة في Windows باعتبارها هيكل تصفها. هذه الهيكل جزء داخلي من Windows ولا تحتاج إلى معرفة تفاصيل ذلك. بدلا من ذلك، يوفر Windows TYPEDEF للمؤشر إلى بنية لتلك الهيكل. هذا هو "المقبض" الذي يمكنك من خلاله تعليقه على النافذة.،

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