«عندما نلصق جدولاً منسوخاً من Excel ينهار التنسيق. نريد أن يُلصَق كجدول.» «المحتوى الذي ننسخه في تطبيقنا يتحوّل إلى شيء غريب عندما نلصقه في Word.» «نريد أن نستطيع استقبال ملفّات بالسحب والإفلات.» ── في محادثات الاستشارة حول تغييرات تطبيقات الأعمال، الطلبات حول النسخ واللصق والسحب والإفلات (D&D) أمر ثابت.
لأنّ هذه بالضبط «ميزات يأخذها الجميع أمراً مسلَّماً»، كيف تعمل فعلاً معروف على نحو مفاجئ قليلاً. إن فكّرت في الحافظة كـ«صندوق تضع فيه قطعة بيانات واحدة»، لا تستطيع تفسير لماذا تنتج النسخة نفسها نتائج مختلفة بحسب مكان اللصق، أو لماذا يتوقّف اللصق عن العمل بعد أن تغلق تطبيق المصدر. الحافظة الحقيقيّة آليّة تضع المحتوى نفسه بعدّة تنسيقات دفعة واحدة، وتدع جانب اللصق يختار تنسيقاً يفهمه.
والسحب والإفلات، في الأساس، نقل بيانات OLE يسلّم تماماً تمثيل البيانات نفسه الذي للحافظة (IDataObject)، عبر واجهات COM. بعبارة أخرى، النسخ واللصق وD&D أشقاء: افهم واحداً بشكل صحيح والآخر هناك مباشرة.
هذا المقال موجَّه لموظّفي تقنيّة المعلومات في الشركات الصغيرة والمتوسّطة ولمطوّري تطبيقات Windows. يربط، في صورة واحدة، كيف تعمل تنسيقات الحافظة، وممارسات جانب اللصق وجانب النسخ، والطريقة الصحيحة لمراقبة الحافظة، وسياسات الإدارة لسجلّ الحافظة ومزامنة السحابة وRDP، وبنية سحب وإفلات OLE ومزالقه.
1. الخلاصة أوّلاً
- الحافظة منطقة واحدة تشترك فيها التطبيقات على سطح المكتب نفسه (محطّة نافذة)، وما يجلس هناك ليس «قطعة بيانات واحدة» بل المحتوى نفسه بعدّة تنسيقات دفعة واحدة. جلسة أخرى، مثل RDP، لها أصلاً حافظة مختلفة؛ ميزة إعادة التوجيه هي ما يجسر الاثنتين. لأنّ الوجهة تختار تنسيقاً تفهمه، تنتج النسخة نفسها نتائج مختلفة بحسب مكان اللصق.12
- للنصّ، استخدم CF_UNICODETEXT. CF_TEXT هو ANSI ومعتمد على صفحة الرموز، وعلى الأنظمة اليابانيّة مرتع للتشويه. يحوّل النظام بينهما ضمنيّاً، لكنّ الجانب القانونيّ هو يونيكود.3
- الملفّات تسافر كـCF_HDROP (مصفوفة مسارات منتهية بـNUL مزدوج)، والنصّ المنسَّق يستخدم التنسيق المسجَّل «HTML Format». لـHTML Format بنية غير معتادة: نصّ UTF-8 مع ترويسة إزاحات بايت.45
- السبب الحقيقيّ لـ«أغلقت تطبيق المصدر فلم أعد أستطيع اللصق» هو العرض المؤجَّل. آليّة تضع لا الحمولة بل وعداً فقط بـ«إنتاجها عند الطلب»؛ إن تخطّيت التجسيد عند الخروج (الردّ على WM_RENDERALLFORMATS، أو OleFlushClipboard لـOLE)، يتوقّف اللصق عن العمل.26
- عامل البيانات الملصوقة كدخل غير موثوق من الخارج. مايكروسوفت نفسها تنصّ بوضوح أنّ «بيانات الحافظة غير موثوقة. حلِّلها بعناية».7
- لمراقبة الحافظة، AddClipboardFormatListener + WM_CLIPBOARDUPDATE هو الخيار الوحيد. لا تستخدم الاستطلاع، ولا تستخدم SetClipboardViewer القديم (سلسلة العارض). تنسيقات مسجَّلة تُبقي الأسرار خارج السجلّ والمزامنة (ExcludeClipboardContentFromMonitorProcessing ورفاقه) موفَّرة أيضاً.81
- سجلّ الحافظة (Win+V) ومزامنة السحابة شأن إدارة تقنيّة معلومات. يمكنك التحكّم فيهما بـAllowClipboardHistory وAllowCrossDeviceClipboard عبر GPO / Intune (Policy CSP)، ولإعادة توجيه حافظة RDP سياسة مخصَّصة خاصّة بها.91011
- السحب والإفلات هو COM. يُسلَّم IDataObject نفسه الذي للحافظة بين IDropSource (مصدر السحب) وIDropTarget (هدف الإفلات) عبر حلقة DoDragDrop. يتطلّب RegisterDragDrop تهيئة بـOleInitialize (STA).1213
- لا تستطيع الإفلات من مستكشف الملفّات بصلاحيّة عاديّة على تطبيق مرتفع. UIPI (حجب الرسائل حسب مستوى السلامة) هو السبب، وهو قيد ينبغي أن تعرفه وقت التصميم.14
فيما يلي نمشي بهذا من أسس الحافظة صعوداً.
2. ما الحافظة حقّاً ── ليست «قطعة بيانات واحدة» بل «المحتوى نفسه بعدّة تنسيقات»
الحافظة آليّة مشاركة بيانات مشتركة تصل إليها كلّ تطبيق يشارك سطح المكتب نفسه (بدقّة أكبر، هي لكلّ محطّة نافذة: لجلسة مستخدم أخرى أو جلسة RDP كلّ منها حافظتها. يعمل النسخ واللصق عبر RDP لأنّ ميزة إعادة التوجيه تجسر الاثنتين ── الفصل 7). المبدأ الأوّل أنّها مدفوعة بالمستخدم: موقف التصميم الرسميّ أنّك لا تضع بيانات أو تخرجها خلف ظهر المستخدم.1
النقطة المهمّة أنّ النسخ لا يضع «قطعة بيانات واحدة». النافذة التي تنسخ تفرغ الحافظة ثمّ تضع عدّة تنسيقات على التوالي، تعبّر عن المحتوى نفسه من التنسيق الأقدر إلى الأقلّ قدرة.2 على سبيل المثال، عندما تنسخ جدولاً في ورقة حساب، يكون مفهوميّاً شيء كالآتي على الحافظة في الوقت نفسه.
| الأولويّة | التنسيق | المحتوى |
|---|---|---|
| 1 | تنسيق خاصّ بالتطبيق | تمثيل داخليّ كامل، بما في ذلك الصيغ والتنسيق (للصق مرّة أخرى في التطبيق نفسه) |
| 2 | HTML Format | جزء HTML يحفظ بنية الجدول والتنسيق |
| 3 | CSV | نصّ مفصول بالخلايا |
| 4 | CF_UNICODETEXT | نصّ بسيط مفصول بعلامات جدولة |
| 5 | تنسيق صورة | صورة نقطيّة لكيفيّة ظهور الجدول |
جانب اللصق يختار من هذه القائمة تنسيقاً يفهمه ويستخرجه. الصق في Word فتحصل على جدول منسَّق؛ الصق في Notepad فتحصل على نصّ مفصول بعلامات جدولة ── لأنّ الاثنين اختارا تنسيقين مختلفين. «النتيجة تعتمد على مكان اللصق» ليس خطأ؛ إنّه النتيجة العاديّة لهذا التصميم.
flowchart TB
accTitle: لماذا تنتج النسخة نفسها نتائج مختلفة بحسب مكان اللصق
accDescr: جانب النسخ يضع المحتوى نفسه على الحافظة بعدّة تنسيقات، وجانب اللصق يختار تنسيقاً يفهمه، فيحصل Word على جدول منسَّق وNotepad على نصّ مفصول بعلامات جدولة
copy["نسخ: ورقة حساب"] --> cb["الحافظة(تنسيقات كثيرة)"]
cb --> rich["تنسيقات أغنى"]
cb --> plain["تنسيقات أبسط"]
rich --> f1["خاصّ بالتطبيق"]
rich --> f2["HTML Format"]
plain --> f3["CSV"]
plain --> f4["CF_UNICODETEXT"]
f2 -->|"Word"| word["جدول منسَّق"]
f4 -->|"Notepad"| notepad["نصّ مفصول بعلامات جدولة"]
بعبارة أخرى، الشكاوى الافتتاحيّة ── «ينهار التنسيق»، «يُلصَق شيء غريب» ── تكاد كلّها تختزل إلى مشكلة كيفيّة اختيار أحد الجانبين للتنسيقات، أو كيفيّة عرض الجانب الآخر لها. الفصل 4 يغطّي جانب اللصق؛ الفصل 5 يغطّي جانب النسخ.
3. التنسيقات القياسيّة والتنسيقات المسجَّلة ── CF_UNICODETEXT، CF_HDROP، HTML Format
3.1. التنسيقات القياسيّة ── استخدم جانب يونيكود للنصّ
التنسيقات التي يعرّفها نظام التشغيل مسبقاً تُدعى تنسيقات قياسيّة. التي تظهر باستمرار في تطبيقات الأعمال هي الآتية.3
| التنسيق | القيمة | المحتوى |
|---|---|---|
| CF_TEXT | 1 | نصّ ANSI (معتمد على صفحة الرموز) |
| CF_UNICODETEXT | 13 | نصّ يونيكود. هذا التنسيق القانونيّ للنصّ |
| CF_HDROP | 15 | قائمة مسارات ملفّات (مقبض HDROP) |
| CF_DIB | 8 | صورة نقطيّة مستقلّة عن الجهاز |
| CF_LOCALE | 16 | معرّف الإعدادات المحلّيّة المرتبط بالنصّ |
يُحوَّل CF_TEXT وCF_UNICODETEXT أحدهما إلى الآخر ضمنيّاً من النظام (تنسيقات مُركَّبة). يستخدم تحويل رموز الأحرف صفحة الرموز المرتبطة بـCF_LOCALE.3 الاعتماد على ذلك التحويل يُسقِط أحرفاً لا يستطيع ANSI تمثيلها (مثلاً رموز يونيكود فقط وأحرف التركيب)، لذا القاعدة وحيد ما يقرأه التطبيق ويكتبه على CF_UNICODETEXT (DataFormats.UnicodeText في .NET).
flowchart TB
accTitle: التحويل الضمنيّ بين CF_UNICODETEXT وCF_TEXT
accDescr: التطبيق يقرأ ويكتب CF_UNICODETEXT فقط؛ والنظام يركِّب CF_TEXT بتحويل ضمنيّ بصفحة رموز CF_LOCALE. الأحرف التي لا يستطيع ANSI تمثيلها تُسقَط في ذلك التحويل
apprw["التطبيق يقرأ ويكتب"] --> uni["CF_UNICODETEXT"]
uni <-->|"تحويل CF_LOCALE"| ansi["CF_TEXT(ANSI)"]
ansi -.-> loss["الأحرف غير القابلة للتمثيل تُسقَط"]
3.2. CF_HDROP ── الملفّات تسافر كـ«قائمة مسارات»
CF_HDROP هو ما يُستخدَم عندما تنسخ ملفّات في مستكشف الملفّات، أو عندما تسحب ملفّات وتفلتها. الحمولة ليست الملفّات نفسها؛ إنّها كتلة ذاكرة ترصّ مصفوفة «منتهية بـNUL مزدوج»: بعد ترويسة بنية DROPFILES، سلاسل مسار كامل مفصولة بأحرف NUL، وسلسلة فارغة في النهاية. pFiles للترويسة هو إزاحة بداية قائمة المسارات، وfWide يقول ما إذا كانت السلاسل يونيكود.4
[DROPFILES header: pFiles=start offset of the path list, fWide=1(Unicode)]
C:\data\a.txt(NUL)C:\data\b.txt(NUL)(NUL)
في الشيفرة الأصليّة تسحبها واحداً واحداً بـDragQueryFile؛ في .NET تستقبلها كـstring[] عبر DataFormats.FileDrop. حقيقة أنّ «ما يسافر هو المسارات فقط، لا الملفّات نفسها» ستهمّ مرّة أخرى في D&D للفصلين 8 و9.
flowchart TB
accTitle: تخطيط كتلة الذاكرة لـCF_HDROP
accDescr: بنية DROPFILES تجلس في بداية الذاكرة العموميّة؛ pFiles إزاحة بداية قائمة المسارات وfWide يقول ما إذا كانت يونيكود. ثمّ تتبع مسارات كاملة، مفصولة بـNUL، وتنتهي الكتلة بسلسلة فارغة(NUL مزدوج). ما يسافر هو المسارات فقط، لا الملفّات نفسها
hdr["DROPFILES(pFiles / fWide)"] --> p1["C:\\data\\a.txt + NUL"]
p1 --> p2["C:\\data\\b.txt + NUL"]
p2 --> tail["سلسلة فارغة(NUL مزدوج)"]
hdr -.-> note["المسارات فقط تسافر، لا الملفّات"]
3.3. التنسيقات المسجَّلة ── RegisterClipboardFormat و«HTML Format»
للبيانات التي لا تستطيع التنسيقات القياسيّة التعبير عنها، يستطيع تطبيق اختيار اسم وتسجيل تنسيقه. مرِّر اسماً إلى RegisterClipboardFormat فتحصل على معرّف تنسيق؛ التسجيل بالاسم نفسه من تطبيق مختلف يعيد المعرّف نفسه، لذا متى اتّفقتم على الاسم يمكنكم مشاركة البيانات بين التطبيقات.1 عندما تمرّر بيانات منظَّمة بين مجموعة تطبيقاتك، استخدم اسماً لن يتصادم، مثل KomuraSoft.Report.RowData.
التنسيق المسجَّل التمثيليّ هو «HTML Format»، للنصّ المنسَّق (مع RTF، أحد تنسيقي النصّ الغنيّ الرئيسيّين). الحمولة نصّ UTF-8، لكن لها بنية غير معتادة: ترويسة تسرد إزاحات بايت تُرفَق في المقدّمة.5
Version:0.9
StartHTML:<byte offset of the start of the whole HTML>
EndHTML:<byte offset of the end of the whole HTML>
StartFragment:<byte offset of the start of the fragment>
EndFragment:<byte offset of the end of the fragment>
<html><body>
<!--StartFragment--><b>bold</b> fragment text<!--EndFragment-->
</body></html>
كلّ إزاحة هي موضع بايت من بداية البيانات، بما في ذلك الترويسة نفسها؛ الممارسة المعتادة حجز عرض ثابت (مثلاً 10 أرقام) وكتابة القيم المقاسة بعد بناء الجسم. StartFragment/EndFragment يعلّمان بداية ونهاية «الجزء الذي اختاره المستخدم فعلاً» بالبايت (لا بالأحرف). في UTF-8 الذي يشمل اليابانيّة، يتباعد عدد الأحرف وعدد البايتات، لذا إن أخطأت حساب الإزاحة هذا، يسقط اللصق في تطبيق آخر البداية أو النهاية. إن ولَّدت HTML Format بنفسك، يجب أن تملأ الترويسة بمواضع بايت مقاسة بعد الترميز إلى UTF-8.5
flowchart TB
accTitle: كيف تتعلّق ترويسة HTML Format بالإزاحات
accDescr: StartHTML وEndHTML للترويسة يشيران إلى الـHTML كلّه، وStartFragment وEndFragment يشيران إلى الجزء الذي اختاره المستخدم، كلاهما كمواضع بايت من بداية البيانات. لأنّ عدد الأحرف وعدد البايتات يتباعدان في UTF-8، املأ الترويسة بمواضع بايت مقاسة بعد الترميز
header["ترويسة(إزاحات بايت)"] --> html["الـHTML كلّه"]
html --> frag["الجزء المختار"]
header -.-> byte["الإزاحات بايتات بعد UTF-8"]
CSV (DataFormats.CommaSeparatedValue في .NET) يُستخدَم أيضاً شيوعاً للبيانات الجدوليّة. للتوافق مع Excel، عرض HTML Format (مع تنسيق)، وCSV (قيم فقط)، وCF_UNICODETEXT (مفصول بعلامات جدولة) معاً يعني أنّك لا تحتاج إلى اختيار وجهة لصق واحدة.
4. ممارسات جانب اللصق ── أولويّة التنسيق والتحقّق
4.1. انظر من التنسيقات الغنيّة نزولاً
التنسيقات على الحافظة مصطفّة بالترتيب الذي وضعها به جانب النسخ (أي من الأكثر تعبيراً إلى الأقلّ). خطّ أساس جانب اللصق النظر، بين التنسيقات التي تستطيع معالجتها، بدءاً من الذي فيه أكثر معلومات. في Win32 إمّا تعدّد بـEnumClipboardFormats وتستخدم أوّل تنسيق تتعرّف عليه، أو تمرّر قائمة أولويّتك إلى GetPriorityClipboardFormat وتدعها تختار.2
في .NET يبدو التفريع شيئاً كالآتي.
// Pasting a table: look from rich to plain
var data = Clipboard.GetDataObject();
if (data is null) return;
// Advertising a format does not guarantee the payload is a string. Use this
// branch only when the type also checks out; otherwise fall through to the next candidate
if (data.GetDataPresent(DataFormats.Html)
&& data.GetData(DataFormats.Html) is string html)
{
// Validate the HTML Format header, then import as a table
}
else if (data.GetDataPresent(DataFormats.CommaSeparatedValue))
{
// Import as CSV
}
else if (data.GetDataPresent(DataFormats.UnicodeText))
{
// Import as tab-separated text
}
تلك إجابة الشكوى الافتتاحيّة، «لصق جدول Excel ينهار». تطبيق يقرأ النصّ البسيط فقط لا يستقبل أبداً بنية الجدول. إلى أيّ مدى نزولاً في قائمة التنسيقات تقبل قرار تصميم في جانب اللصق.
flowchart TB
accTitle: تفريع لصق ينظر من التنسيقات الغنيّة نزولاً
accDescr: إن وُجد HTML Format وكانت الحمولة أيضاً سلسلة، استورد كجدول؛ وإلا جرّب CSV؛ إن غاب ذلك أيضاً فانزل إلى نصّ مفصول بعلامات جدولة. إن لم يحضر أيّ مرشّح، ارفض
startsel["بدء اللصق"] --> h{"HTML Format + سلسلة؟"}
h -->|"نعم"| useh["تحقّق الترويسة → جدول"]
h -->|"لا"| c{"CSV حاضر؟"}
c -->|"نعم"| usec["استيراد كـCSV"]
c -->|"لا"| t{"UnicodeText؟"}
t -->|"نعم"| uset["نصّ مفصول بعلامات جدولة"]
t -->|"لا"| giveup["رفض"]
4.2. البيانات الملصوقة دخل خارجيّ
يسهل تفويته، لكنّ محتوى الحافظة بيانات من الخارج، ولا تعرف أيّ تطبيق وضعها. تحذّر مايكروسوفت أيضاً، في وثائق حافظة OLE، أنّ «بيانات الحافظة غير موثوقة. حلِّلها بعناية قبل أن تستخدمها في التطبيق».7
- تحقّق أنّ إزاحات ترويسة HTML Format لا تشير خارج المخزن (توجد تطبيقات تُصدِر ترويسات مكسورة).
- القيم التي تستوردها كأرقام أو تواريخ أو رموز ينبغي أن تمرّ بالتحقّق نفسه الذي لدخل الشاشة.
- ضع دفاعاً ضدّ بيانات ضخمة. حتّى إن لصق أحدهم صورة مئات الميغابايتات أو ملايين أسطر النصّ، لا تحجب واجهة المستخدم، وارفض متى تجاوزت حدّاً. تحفّظ: GetData في .NET، في لحظة استدعائه، يجسِّد الحمولة كلّها إلى سلسلة مُدارة (ويعمل العرض المؤجَّل كجزء من ذلك)، لذا وضع فحص حجم بعد GetData ليس دفاعاً. في Win32، فحص GlobalSize على HGLOBAL الذي يعيده GetClipboardData يعطيك دفاعاً في مرحلة «لا تمضِ إلى التحويل والتحليل كسلسلة مُدارة»، لكن لتنسيقات العرض المؤجَّل GetClipboardData نفسه يطلق العرض، لذا ما زلت لا تستطيع منع التجسيد في جانب مصدر النسخ. كي لا تتجمّد واجهة المستخدم، انقل الجلب عن مؤشّر ترابط الواجهة (وحتّى حينئذ، لأنّ Clipboard في .NET يتطلّب STA، افعله على مؤشّر مخصَّص مضبوط على STA، لا على مؤشّر مجمع Task.Run (MTA) ── القسم 5.1).
فكرة أنّ «قيمة تصل من الخارج، أيّاً كان المسار، تُتحقَّق قبل أن تستخدمها» هي الفكرة نفسها المعروضة في «Never Use a QR Code’s Decoded Value As-Is». افتراض أنّ اللصق آمن لأنّه فعل مستخدم هو كيف تبدأ الحوادث.
flowchart TB
accTitle: تحقّق من البيانات الملصوقة قبل أن تستخدمها
accDescr: البيانات المأخوذة من الحافظة تمرّ بحضور التنسيق، ونوع الحمولة، وحدّ الحجم، وتحقّق المحتوى بذلك الترتيب؛ أفشل أياً منها فارفض أو انزل إلى التنسيق المرشَّح التالي
present["التنسيق حاضر؟"] --> type["نوع الحمولة سليم؟"]
type --> size["الحجم ضمن الحدّ؟"]
size --> content["تحقّق المحتوى"]
content --> ok["استيراد"]
type -.->|"نوع خاطئ"| rej["رفض / التنسيق التالي"]
size -.->|"كبير جدّاً"| rej
content -.->|"غير صالح"| rej
5. ممارسات جانب النسخ ── عرض عدّة تنسيقات دفعة واحدة، والعرض المؤجَّل
5.1. ضع عدّة تنسيقات دفعة واحدة
ممارسة جانب النسخ عكس 4.1: اعرض تنسيقاً غنيّاً وتنسيقاً بسيطاً في الوقت نفسه. مع DataObject في WinForms/WPF تستطيع كتابتها في بضعة أسطر.15
// WinForms (System.Windows.Forms). WPF is the same shape with System.Windows DataObject/Clipboard
var data = new DataObject();
data.SetData(DataFormats.Html, htmlFormatText); // HTML Format string including the header
data.SetData(DataFormats.CommaSeparatedValue, csv); // CSV
data.SetData(DataFormats.UnicodeText, plainText); // Plain text
Clipboard.SetDataObject(data, copy: true); // copy:true = keep after the app exits
ملاحظتان. أولاً، صنف Clipboard في .NET لا يُستخدَم إلا من مؤشّر ترابط STA.15 مؤشّر واجهة WinForms/WPF هو STA بسبب [STAThread]، لذا هذا عادة ليس مشكلة، لكن لمسه من مؤشّر خلفيّ يفشل (أساسيّات STA/MTA في «أساسيات COM STA/MTA - نماذج مؤشّرات الترابط وكيفية تجنّب حالات التعليق (Hang)»). ثانياً، معنى copy: true مرتبط بالعرض المؤجَّل في القسم التالي.
5.2. العرض المؤجَّل ── لماذا «تغلق المصدر فلا تستطيع اللصق»
بناء حمولة كبيرة بعدّة تنسيقات في كلّ مرّة مُهدِر، لذا للحافظة آليّة تُدعى العرض المؤجَّل. مرِّر NULL كمقبض بيانات إلى SetClipboardData، وبدل الحمولة يُسجَّل وعد فقط بـ«إنتاجها عند الطلب»؛ عندما يطلب أحد ذلك التنسيق، يصل WM_RENDERFORMAT إلى مصدر النسخ، وعندها فقط تُولَّد البيانات.2
نتيجة هذا التصميم هي الافتتاح «أغلقت تطبيق المصدر فلم أعد أستطيع اللصق». قبل أن يخرج، يستقبل مصدر النسخ WM_RENDERALLFORMATS وهو مسؤول عن تجسيد كلّ تنسيق لم يُعرَض بعد؛ اخرج دون فعل ذلك فيُفقَد التنسيق.2
flowchart TB
accTitle: العرض المؤجَّل ولماذا يفشل الإغلاق ثمّ اللصق
accDescr: مصدر النسخ يسجّل وعداً فقط بمقبض NULL، ويجسِّد عند الطلب عبر WM_RENDERFORMAT. عند الخروج هو مسؤول عن تجسيد كلّ تنسيق بـWM_RENDERALLFORMATS؛ تخطَّ ذلك فيُفقَد التنسيق
promise["SetClipboardData NULL = وعد"] --> req["جانب اللصق يطلبه"]
req --> render["WM_RENDERFORMAT → ابنِ الآن"]
promise --> quit["مصدر النسخ على وشك الخروج"]
quit -->|"RENDERALLFORMATS"| ok["اللصق يعمل بعد الخروج"]
quit -->|"تخطّي التجسيد"| lost["التنسيق يُفقَد بعد الإغلاق"]
على حافظة OLE (الأسلوب الذي يضع IDataObject بـOleSetClipboard)، هذه العلاقة أوضح بعد. كلّ ما تمسكه الحافظة مؤشّر إلى كائن البيانات، واستدعاء OleFlushClipboard عند خروج التطبيق يجسِّد البيانات على الحافظة، لذا اللصق ما زال يعمل بعد الخروج.6 Clipboard.SetDataObject(data, copy: true) في .NET هو ما يحدّد سلوك «أبقِه بعد الخروج» هذا.
عندما تنسخ نطاقاً كبيراً في Excel وتحاول الخروج، المطالبة «هناك كمّيّة كبيرة من المعلومات على الحافظة. هل تريد أن تتمكّن من لصق هذه المعلومات في برنامج آخر لاحقاً؟» هي بالضبط تأكيد ما إذا كان سيُشغَّل هذا التجسيد (الـflush). إن استخدمت العرض المؤجَّل في تطبيقك، تذكّر أنّ التجسيد عند الخروج جزء من المجموعة نفسها. العرض المؤجَّل تحسين أداء، ولأنّ طلب العرض يعمل تزامنيّاً داخل معالجة الرسائل، للبيانات التي يستغرق توليدها وقتاً طويلاً مقايضة تجميد الواجهة.2
6. ممارسات مراقبة الحافظة ── المستمع، وإعادة المحاولة، والاستبعاد من السجلّ
6.1. استخدم AddClipboardFormatListener
متطلّبات مثل «نريد اكتشاف قيمة قارئ باركود أو نسخة من نظام الأعمال واستيرادها تلقائيّاً» تحتاج إلى مراقبة تغيّرات الحافظة. تاريخيّاً ثمّة ثلاث طرق؛ اليوم الإجابة الصحيحة واحدة.8
| الطريقة | التقييم |
|---|---|
| قراءة على مؤقّت (استطلاع) | مُهدِرة، وقد تفوّت تحديثات. لا تستخدمها |
| SetClipboardViewer (سلسلة عارض) | خطأ في تطبيق واحد في السلسلة يكسر السلسلة كلّها. تُحفَظ فقط للتوافق الخلفيّ |
| AddClipboardFormatListener | موصى بها. يصل WM_CLIPBOARDUPDATE إلى النافذة المسجَّلة |
flowchart TB
accTitle: تدفّق مراقبة الحافظة
accDescr: سجّل بـAddClipboardFormatListener عند إنشاء المقبض، ويصل WM_CLIPBOARDUPDATE أيّاً كان التطبيق الذي نسخ. اقرأ بإعادة محاولة، وألغِ التسجيل تناظريّاً بـRemoveClipboardFormatListener عند إتلاف المقبض
created["AddClipboardFormatListener"] --> wait["انتظار"]
anyapp["تطبيق ما ينسخ"] --> notify["WM_CLIPBOARDUPDATE"]
wait --> notify
notify --> readtry["قراءة بإعادة محاولة(6.2)"]
readtry --> wait
destroyed["RemoveClipboardFormatListener"] -.->|"إلغاء التسجيل"| created
// Minimal WinForms implementation
public partial class MainForm : Form
{
[DllImport("user32.dll", SetLastError = true)]
static extern bool AddClipboardFormatListener(IntPtr hwnd);
[DllImport("user32.dll", SetLastError = true)]
static extern bool RemoveClipboardFormatListener(IntPtr hwnd);
const int WM_CLIPBOARDUPDATE = 0x031D;
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
AddClipboardFormatListener(Handle);
}
protected override void OnHandleDestroyed(EventArgs e)
{
// Unregister symmetrically to match handle destruction / recreation
RemoveClipboardFormatListener(Handle);
base.OnHandleDestroyed(e);
}
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_CLIPBOARDUPDATE)
{
// Read Clipboard.GetDataObject() here and import if the format is one you need
}
base.WndProc(ref m);
}
}
6.2. أعد المحاولة عندما لا تستطيع الفتح
نافذة واحدة فقط في الوقت نفسه تستطيع فتح الحافظة؛ بينما عمليّة أخرى تفتحها، يفشل OpenClipboard.2 مباشرة بعد WM_CLIPBOARDUPDATE، غالباً ما يزال مصدر النسخ أو مراقب آخر يعمل، لذا فشل قراءة مؤقّت حدث عاديّ. ضع دائماً بضع إعادة محاولات بانتظار قصير (عشرات الملّي ثانية) بينها. لاحظ أنّ زيادات تحميل Clipboard في .NET التي تسمح بتحديد عدد إعادة المحاولة والفاصل موجودة فقط في جانب الكتابة، SetDataObject. لا مكافئ في جانب القراءة (GetDataObject ورفاقه)، لذا تكتب catch-wait-retry بنفسك ── ExternalException في WinForms، وCOMException في WPF.
flowchart TB
accTitle: تدفّق إعادة محاولة قراءة الحافظة
accDescr: نافذة واحدة فقط في الوقت نفسه تستطيع فتح الحافظة، لذا قراءة مباشرة بعد إشعار التغيير قد تفشل بالسباق مع عمليّة أخرى. عند استثناء، انتظر عشرات الملّي ثانية وأعد المحاولة؛ إن بلغت الحدّ، تخلَّ هذه المرّة والتقطه في التحديث التالي
upd["WM_CLIPBOARDUPDATE"] --> tryread["محاولة قراءة"]
tryread -->|"نجاح"| useok["استيراد(فحوصات الفصل 4)"]
tryread -->|"مستخدَم"| waitretry["انتظار عشرات الملّي ثانية"]
waitretry -->|"إعادة محاولة"| tryread
waitretry -->|"حدّ"| giveup2["تخلَّ هذه المرّة"]
6.3. أبقِه خارج السجلّ والمزامنة ── عناية بميزات النسخ التي تعالج أسراراً
لـWindows سجلّ حافظة (Win+V) ومزامنة عبر الأجهزة (حافظة السحابة)، والبيانات التي يضعها تطبيق في نطاق كليهما افتراضيّاً. تطبيق يضع أسراراً مثل كلمات مرور أو أرقام حسابات على ميزة نسخ يضع أيضاً تنسيقاً مسجَّلاً يستبعد المحتوى من السجلّ والمزامنة.1
- ExcludeClipboardContentFromMonitorProcessing: ضع هذا ومحتوى تلك النسخة لا يُدرَج لا في السجلّ ولا في المزامنة.
- CanIncludeInClipboardHistory (DWORD 0): اكبت السجلّ فقط.
- CanUploadToCloudClipboard (DWORD 0): اكبت المزامنة عبر الأجهزة فقط.
سبب أنّ كلمة مرور نسخها مدير كلمات مرور لا تبقى على Win+V هو هذه الآليّة. تحصل على معرّف تنسيق بتمرير الاسم إلى RegisterClipboardFormat وتضبطه إلى جانب البيانات العاديّة، لذا يستحقّ التنفيذ في أيّ تطبيق أعمال يعالج أسراراً.
7. الحافظة من منظور تقنيّة المعلومات ── السجلّ، ومزامنة السحابة، وضوابط RDP
خطوة قليلاً بعيداً عن التطوير، إليك النقاط التي تهمّ المسؤول. يتراكم سجلّ الحافظة النسخ الأخيرة، وتزامن حافظة السحابة النسخ عبر أجهزة مسجَّل الدخول إليها بالحساب نفسه من Microsoft / Microsoft Entra.10 مريح كما هو، ينتج أيضاً بقايا وتسرّباً: معلومات شخصيّة منسوخة من نظام أعمال تتراكم في السجلّ، ومحتوى منسوخ على حاسوب عمل يزامن إلى حاسوب شخصيّ.
السياستان اللتان تستخدمهما للتحكّم في هذا في منظّمة هما الآتيتان.
| ما تتحكّم فيه | GPO (Computer Configuration > Administrative Templates > System > OS Policies) | Policy CSP (Intune) | الافتراضيّ |
|---|---|---|---|
| سجلّ الحافظة | Allow Clipboard History | Experience/AllowClipboardHistory | مسموح |
| المزامنة عبر الأجهزة | Allow Clipboard synchronization across devices | Privacy/AllowCrossDeviceClipboard | مسموح |
كلاهما متاح من Windows 10 الإصدار 1809 فصاعداً؛ عطِّلهما فتصبح العناصر المقابلة في تطبيق الإعدادات رماديّة، وتسري السياسة فوراً.910
الثابت الآخر إعادة توجيه حافظة RDP (سطح المكتب البعيد). افتراضيّاً، يعمل النسخ واللصق بين الحاسوب المحلّي والجلسة البعيدة، لذا يمكن أن يصبح مساراً لإخراج أسرار من خادم. سياسة «Do not allow clipboard redirection» (قيمة السجلّ fDisableClip) تستطيع حجب الاتجاهين.11 إصدارات Windows Server / Windows 11 الحديثة أضافت أيضاً سياسات أدقّ، مثل تقييد اتجاه الخادم-إلى-العميل بالنصّ فقط. ما إذا حظرت تماماً أو قيّدت على مراحل توازن بين التشغيل والأمان.
flowchart TB
accTitle: المسارات التي يمكن أن ينتشر عبرها محتوى الحافظة، ونقاط التحكّم
accDescr: المحتوى المنسوخ في نطاق السجلّ ومزامنة السحابة افتراضيّاً، وعلى RDP يسافر إلى جلسة أخرى عبر إعادة التوجيه. كلّ مسار يمكن التحكّم فيه بسياسة، وجانب التطبيق يستطيع استبعاد نفسه من السجلّ والمزامنة بتنسيقات الاستبعاد
cb["الحافظة"] --> hist["السجلّ(Win+V)"]
cb --> cloud["مزامنة السحابة"]
cb --> rdp["إعادة توجيه RDP"]
hist -.-> p1["AllowClipboardHistory"]
cloud -.-> p2["AllowCrossDeviceClipboard"]
rdp -.-> p3["fDisableClip"]
cb -.-> p4["تنسيقات استبعاد التطبيق(6.3)"]
8. السحب والإفلات هو COM ── IDataObject + IDropSource + IDropTarget
8.1. البيانات نفسها التي للحافظة، طريقة حمل مختلفة
يعمل سحب وإفلات OLE بالأدوار الثلاثة الآتية.12
| الدور | من ينفّذه | العمل |
|---|---|---|
| IDataObject | مصدر السحب | الحمولة المحمولة. كائن البيانات المتعدّد التنسيقات نفسه الذي للحافظة |
| IDropSource | مصدر السحب | تقرير ما إذا استمرّ السحب أو أُلغي، وملاحظات المؤشّر |
| IDropTarget | هدف الإفلات | إعلان قبول/رفض في DragEnter/DragOver/DragLeave/Drop، واستقبال الإفلات |
يستدعي مصدر السحب DoDragDrop، تبدأ حلقة السحب، وعندما يدخل الفأرة نافذة هدف إفلات يُشعَر ذلك IDropTarget؛ عند الإفلات، يُسلَّم IDataObject. تقول الوثائق الرسميّة أيضاً أنّ «D&D يوفّر تماماً الوظيفة نفسها التي لنسخ ولصق الحافظة. إن كان تطبيق ينفّذ أصلاً النسخ واللصق، فالإضافة صغيرة».12 بعبارة أخرى، كائن البيانات المتعدّد التنسيقات الذي بنيته في الفصول 2 إلى 5 يصبح حمولة D&D كما هو.
flowchart TB
accTitle: تدفّق سحب وإفلات OLE
accDescr: مصدر السحب يضع IDataObject في الحمولة ويستدعي DoDragDrop لبدء حلقة السحب؛ IDropTarget لهدف الإفلات يعلن قبولاً/رفضاً في DragEnter وDragOver، وعند Drop يختار تنسيقاً من IDataObject ويستخرجه
src["IDataObject + IDropSource"] -->|"DoDragDrop"| loop["حلقة السحب"]
loop -->|"دخول الفأرة"| enter["DragEnter/Over: Effect"]
enter -->|"رفع الزرّ"| drop["IDropTarget.Drop"]
drop --> data["اختيار تنسيق واستخراج"]
8.2. OleInitialize (STA) مطلوب
نافذة ستكون هدف إفلات تسجّل بـRegisterDragDrop، وهنا مزلق كلاسيكيّ. إن هيّأت COM بـCoInitialize/CoInitializeEx، يفشل RegisterDragDrop دائماً بـE_OUTOFMEMORY؛ يجب أن تهيّئ بـOleInitialize.13 OleInitialize يهيّئ COM كـSTA، لأنّ D&D ميزة متجذّرة في عالم STA للنوافذ ومضخّة الرسائل. يجب أيضاً أن يعمل مؤشّر الاستدعاء مضخّة رسائل؛ تخطَّ ذلك فتعلّق تطبيقات أخرى أثناء السحب.13 الخلفية هنا تماماً نقاش نموذج مؤشّرات الترابط في «أساسيات COM STA/MTA - نماذج مؤشّرات الترابط وكيفية تجنّب حالات التعليق (Hang)».
في تطبيق WinForms/WPF يتولّى الإطار تهيئة OLE وتنفيذات الواجهات، لذا على المطوّر فقط كتابة الأحداث.
// WinForms: accept dropped files
listView1.AllowDrop = true;
listView1.DragEnter += (s, e) =>
{
// Also check that the source allows Copy (some sources only allow Move/Link)
e.Effect = e.Data.GetDataPresent(DataFormats.FileDrop)
&& (e.AllowedEffect & DragDropEffects.Copy) == DragDropEffects.Copy
? DragDropEffects.Copy // Accept: receive as a copy
: DragDropEffects.None; // Do not accept
};
listView1.DragDrop += (s, e) =>
{
// Drag data is also untrusted input. Even if it advertises FileDrop, the payload
// can be null or a different type, and GetData itself can fail
object data;
try { data = e.Data.GetData(DataFormats.FileDrop); }
catch (COMException) { return; }
if (data is not string[] paths) return;
foreach (var path in paths)
{
// Validate the path before importing (Section 9.3)
}
};
الشكل نفسه في WPF: تستقبل بـAllowDrop="True" وأحداث DragOver/Drop على العنصر، وتستخرج مصفوفة المسارات بـe.Data.GetData(DataFormats.FileDrop). إعلان قبول/رفض (Effect) في كلّ DragEnter/DragOver هو عرف IDropTarget؛ تخطَّه فتحصل على الخطأ الذي يبقى فيه المؤشّر على «غير مسموح» ولا يتغيّر أبداً.
9. مزالق D&D ── الارتفاع، والنقل، والتحقّق من المسار
9.1. لا تستطيع الإفلات على تطبيق مرتفع كمسؤول
أفلت ملفّاً من مستكشف الملفّات على تطبيق أُطلق بـ«التشغيل كمسؤول» ولا يحدث شيء ── هذا ليس خطأ تنفيذ، إنّه سلوك نظام التشغيل. UIPI (User Interface Privilege Isolation) يحجب افتراضيّاً الرسائل من عمليّة سلامة أدنى إلى نافذة سلامة أعلى، لذا إشعارات الإفلات من مستكشف الملفّات بصلاحيّة عاديّة (سلامة متوسّطة) لا تصل أبداً إلى تطبيق مرتفع.14
flowchart TB
accTitle: كيف يحجب UIPI الإفلات على تطبيق مرتفع
accDescr: إشعارات الإفلات من مستكشف الملفّات بسلامة متوسّطة إلى تطبيق مرتفع بسلامة عالية تُحجَب افتراضيّاً بـUIPI ولا تصل أبداً. أبقِ الواجهة بصلاحيّة عاديّة واعزل العمل المميَّز، فيصل الإفلات
explorer["المستكشف(متوسّط)"] -->|"إشعار إفلات"| uipi{"UIPI"}
uipi -->|"محجوب"| elevated["تطبيق مرتفع: لا إفلات"]
uipi -->|"يمرّ"| normal["واجهة عاديّة: يصل الإفلات"]
normal -.->|"تفويض العمل المميَّز"| broker["عمليّة مرتفعة معزولة"]
حلّ التفافيّ يسمح برسائل محدَّدة مثل WM_DROPFILES فرديّاً بـChangeWindowMessageFilterEx معروف،14 لكنّ ما يمرّره هو إشعار الإفلات الأقدم (WM_DROPFILES)؛ لا يحلّ D&D لـOLE ككلّ. التوجيه العمليّ واضح: توقّف عن تصميم التطبيق ليعمل مرتفعاً طوال الوقت. اعزل العمل الذي يحتاج الارتفاع فقط إلى عمليّة منفصلة، والواجهة نفسها تستطيع البقاء بصلاحيّة عاديّة واستقبال D&D (تصميم العزل مغطّى بالتفصيل في «كيف تعزل العمل الذي يحتاج إلى المسؤول فقط داخل تطبيقات Windows»).
9.2. معنى DragDropEffects ── Move عقد أنّ «الأصل يذهب»
Copy/Move/Link على DragDropEffects ليست زينة؛ إنّها عقد بين مصدر السحب وهدف الإفلات. يعلن مصدر السحب مجموعة التأثيرات التي يسمح بها في DoDragDrop، ويختار هدف الإفلات التأثير الفعليّ، وعندما ينجح Move، يحذف مصدر السحب البيانات (الملفّ) ── ذلك العرف. إن أعاد جانب الاستقبال Move بلا تفكير، تحصل على الحادثة «أفلتُّه واختفى الملفّ الأصليّ». لاستخدام استيراد تطبيق أعمال، جانب الاستقبال الذي يعلن Copy هو الافتراضيّ الآمن.
flowchart TB
accTitle: عقد DragDropEffects ── Move يحذف الأصل
accDescr: مصدر السحب يعلن مجموعة التأثيرات المسموحة في DoDragDrop، وهدف الإفلات يختار التأثير الفعليّ. عندما ينجح Move يحذف مصدر السحب الملفّ، لذا للاستيراد ينبغي أن يعلن جانب الاستقبال Copy
srcdecl["المصدر: تأثيرات مسموحة"] --> tgtsel["الهدف: اختيار Effect"]
tgtsel -->|"Copy"| copyok["الأصل يبقى(استيراد)"]
tgtsel -->|"Move"| moveact["المصدر يحذف الملفّ"]
9.3. التحقّق من مسار مُفلَت
ما يسافر في CF_HDROP/FileDrop هو المسار فقط (القسم 3.2). قبل أن تستورد، مرِّره بتحقّق الدخل غير الموثوق نفسه الذي للصق.
- ملفّ أو مجلّد: قرّر كمواصفة ما يحدث عندما يُفلَت مجلّد كامل (تكرار واستيراد، أو رفض).
- عناصر نائبة OneDrive: قد يوجد المسار بينما جسم الملفّ ليس محلّيّاً ── ملفّ عند الطلب. لحظة فتحه يبدأ تنزيل، وبدون اتّصال يفشل. السلوك والإجراءات المضادّة في «OneDrive “Files On-Demand” and Business Apps».
- مسارات طويلة ومسارات غير معتادة: مسارات فوق MAX_PATH، ومسارات شبكة (UNC)، ومسارات على وسائط قابلة للإزالة ينبغي قبولها فقط بعد أن تؤكّد أنّ المعالجة اللاحقة تستطيع التعامل معها.
- العدد والحجم الإجماليّ: كي لا يجمّد إفلات آلاف الملفّات الواجهة، اجعل الاستيراد غير متزامن وضع حدّاً وعرض تقدّم.
10. الخلاصة
- الحافظة آليّة تضع المحتوى نفسه بعدّة تنسيقات دفعة واحدة في منطقة واحدة مشتركة داخل سطح المكتب نفسه (محطّة نافذة). جانب اللصق يختار التنسيق، لذا تنتج النسخة نفسها نتائج مختلفة.
- النصّ هو CF_UNICODETEXT، والملفّات CF_HDROP، والنصّ المنسَّق التنسيق المسجَّل HTML Format (ترويسة إزاحات بايت + UTF-8).
- جانب اللصق ينظر من الغنيّ إلى البسيط ويعامل الحمولة كدخل خارجيّ. جانب النسخ يعرض عدّة تنسيقات دفعة واحدة، وإن استخدم العرض المؤجَّل ينفّذ أيضاً التجسيد عند الخروج (WM_RENDERALLFORMATS / OleFlushClipboard).
- المراقبة هي AddClipboardFormatListener + WM_CLIPBOARDUPDATE. استعد لسباقات OpenClipboard بإعادة محاولة، وأبقِ الأسرار خارج السجلّ والمزامنة بـExcludeClipboardContentFromMonitorProcessing ورفاقه.
- تستطيع تقنيّة المعلومات التحكّم في سجلّ الحافظة ومزامنة السحابة وإعادة توجيه RDP بـGPO / Intune. الافتراضيّ مسموح للجميع، لذا قرّر عمداً في البيئات التي تعالج أسراراً.
- D&D هو COM: IDropSource/IDropTarget يسلّمان IDataObject نفسه الذي للحافظة. يتطلّب RegisterDragDrop OleInitialize (STA).
- الإفلات على تطبيق مرتفع يحجبه UIPI. Move على DragDropEffects عقد أنّ «الأصل يذهب»؛ تحقّق من مسار مُفلَت قبل أن تستورده.
النسخ واللصق وD&D، للمستخدم، ميزات ينبغي أن تُشعَر كالهواء. لهذا بالضبط «لا أستطيع اللصق»، و«ينهار»، و«اختفى» تؤذي التجربة بهذا القدر ── ولهذا تطبيق يعرض عدّة تنسيقات ويعالج الإفلات كما ينبغي يجعل العمليات اليوميّة أسلس من تلقاء نفسه. آمل أن يكون هذا مادّة مفيدة عندما تقرّر ما تصلحه أوّلاً.
مقالات ذات صلة
- ما هي COM / ActiveX / OCX - دليل عمليّ للفروق والعلاقات بينها
- أساسيات COM STA/MTA - نماذج مؤشّرات الترابط وكيفية تجنّب حالات التعليق (Hang)
- Windows Shell Integration Today — Context Menus, File Associations, and What Changed in Windows 11
- مشكلة بقاء EXCEL.EXE عند التعامل مع Excel من C# ── أنماط تحرير مراجع COM وقرار الاستبدال
- طريقة التفكير في تصميم UX لتطبيقات Windows - جدول قرار لتحديد ما يُعطى الأولوية في ToC / ToB / المراقبة / الأجهزة الميدانية / الأدوات المقيمة
- OneDrive “Files On-Demand” and Business Apps — The Assumptions Placeholders Break and How to Deal with Them
مجالات الاستشارة ذات الصلة
تتعامل شركة كومورا سوفت ذ.م.م. مع تصميم وتنفيذ دعم النسخ واللصق والسحب والإفلات في تطبيقات الأعمال (عرض عدّة تنسيقات، والتوافق مع Excel، واستيراد ملفّات مُفلَتة)، وتحقيق السبب الجذريّ لمشكلات مثل «ينهار عندما ألصق» أو «تختفي النسخة»، وأتمتة إدخال تراقب الحافظة، وتنفيذات تُبقي البيانات السرّيّة خارج السجلّ والمزامنة. الحالات التي تتضمّن الطبقات الأدنى من COM وOLE مرحَّب بها حتّى إن بدأت من عزل العَرَض.
روابط مرجعيّة
-
Microsoft Learn, Clipboard Formats. حول إمكان نافذة وضع المعلومات نفسها بعدّة تنسيقات حافظة؛ والتنسيقات المسجَّلة عبر RegisterClipboardFormat (تسجيل الاسم نفسه يعيد القيمة نفسها، لذا تستطيع التطبيقات مشاركتها)؛ والتنسيقات المركَّبة؛ واستبعاد المحتوى من سجلّ الحافظة / مزامنة السحابة بـExcludeClipboardContentFromMonitorProcessing وCanIncludeInClipboardHistory وCanUploadToCloudClipboard. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, Clipboard Operations. حول إمكان نافذة واحدة فقط في الوقت نفسه فتح الحافظة؛ ووضع التنسيقات من الأكثر تعبيراً إلى الأقلّ عند النسخ؛ واختيار التنسيق عند اللصق بـEnumClipboardFormats / GetPriorityClipboardFormat؛ والعرض المؤجَّل بتمرير NULL إلى SetClipboardData ومسؤوليّات WM_RENDERFORMAT / WM_RENDERALLFORMATS؛ ومقايضات العرض المؤجَّل. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8
-
Microsoft Learn, Standard Clipboard Formats. حول تعريفات التنسيقات القياسيّة CF_TEXT (ANSI)، وCF_UNICODETEXT، وCF_HDROP، وCF_DIB، وCF_LOCALE، وحول تحويل النظام ضمنيّاً بين CF_TEXT وCF_UNICODETEXT باستخدام صفحة الرموز المرتبطة بـCF_LOCALE. ↩ ↩2 ↩3
-
Microsoft Learn, Shell Clipboard Formats. حول تركيب CF_HDROP من بنية DROPFILES زائد مصفوفة سلاسل مسار كامل منتهية بـNUL مزدوج؛ واسترجاع المسارات الفرديّة بـDragQueryFile؛ وتنسيقات صدفة CFSTR_ التي تتطلّب تسجيلاً عبر RegisterClipboardFormat. ↩ ↩2
-
Microsoft Learn, HTML Clipboard Format. حول كون الاسم المسجَّل «HTML Format»؛ وبنية الترويسة بإزاحات بايت مثل Version وStartHTML وEndHTML وStartFragment وEndFragment؛ وكون الترميز دائماً UTF-8؛ وعرف تعليقات StartFragment/EndFragment. ↩ ↩2 ↩3
-
Microsoft Learn, OleFlushClipboard function (ole2.h). حول جعل OleSetClipboard الحافظة تمسك مؤشّراً فقط إلى كائن البيانات؛ وتجسيد OleFlushClipboard البيانات على الحافظة بحيث يعمل اللصق بعد خروج التطبيق؛ وإفراغ الحافظة بـOleSetClipboard(NULL) عندما لا تحتاج إلى إبقائها عند الخروج. ↩ ↩2
-
Microsoft Learn, OleGetClipboard function (ole2.h). حول كيفيّة الحصول على IDataObject من الحافظة، والتحذير أنّ بيانات الحافظة غير موثوقة وينبغي تحليلها بعناية قبل أن يستخدمها التطبيق. ↩ ↩2
-
Microsoft Learn, Using the clipboard. حول مقارنة الطرق الثلاث لمراقبة الحافظة (نوافذ عارض، وأرقام تسلسل، ومستمعي تنسيق)؛ وتوقّع أن تستخدم البرامج الجديدة مستمعاً عبر AddClipboardFormatListener؛ وهشاشة سلسلة العارض عندما تكون صيانة السلسلة ناقصة؛ وكون أرقام التسلسل ليست شيئاً ينبغي استطلاعه. ↩ ↩2
-
Microsoft Learn, Policy CSP - Experience. حول السماح بسجلّ الحافظة أو رفضه بسياسة Experience/AllowClipboardHistory؛ والتوفّر من Windows 10 الإصدار 1809 فصاعداً؛ وكون الافتراضيّ مسموحاً؛ وتعيين GPO تحت «System > OS Policies» مع سريان التغييرات فوراً. ↩ ↩2
-
Microsoft Learn, Policy CSP - Privacy. حول السماح بمزامنة الحافظة عبر الأجهزة أو رفضها بسياسة Privacy/AllowCrossDeviceClipboard؛ وحدوث المزامنة بين أجهزة مسجَّل الدخول إليها بالحساب نفسه من Microsoft / Microsoft Entra؛ وكون الافتراضيّ مسموحاً. ↩ ↩2 ↩3
-
Microsoft Learn, Policy CSP - ADMX_TerminalServer. حول إمكان TS_CLIENT_CLIPBOARD («Do not allow clipboard redirection»، قيمة السجلّ fDisableClip) منع مشاركة الحافظة بين المحلّي والبعيد في جلسة سطح مكتب بعيد، وحول كون إعادة التوجيه مسموحة افتراضيّاً. ↩ ↩2
-
Microsoft Learn, Drag and Drop (COM). حول عمل سحب وإفلات OLE بالثلاثة IDropSource (مصدر السحب)، وIDropTarget (هدف الإفلات)، وDoDragDrop (الحلقة التي يوفّرها OLE)؛ وتوفير الوظيفة نفسها التي لنسخ ولصق الحافظة، بحيث يحتاج تطبيق ينفّذ أصلاً النسخ واللصق إضافة صغيرة فقط؛ وأنواع الملاحظات. ↩ ↩2 ↩3
-
Microsoft Learn, RegisterDragDrop function (ole2.h). حول تسجيل نافذة هدف إفلات مع IDropTarget؛ والفشل دائماً بـE_OUTOFMEMORY إن هُيِّئ COM بـCoInitialize/CoInitializeEx، لذا يُطلَب OleInitialize؛ وتعليق تطبيق مصدر السحب إن لم يكن مؤشّر الاستدعاء يشغّل مضخّة رسائل. ↩ ↩2 ↩3
-
Microsoft Learn, ChangeWindowMessageFilterEx function (winuser.h). حول كون UIPI آليّة أمان تحجب افتراضيّاً استقبال رسائل من مرسل سلامة أدنى، وحول السماح برسائل محدَّدة لكلّ نافذة بمرشّح رسائل (MSGFLT_ALLOW). ↩ ↩2 ↩3
-
Microsoft Learn, How to add data to the Clipboard (Windows Forms). حول وضع بيانات بعدّة تنسيقات دفعة واحدة بـDataObject وClipboard.SetDataObject؛ والإضافة بعدّة تنسيقات كي تتمكّن تطبيقات أخرى من التعرّف عليها؛ وكون صنف Clipboard قابلاً للاستخدام فقط من مؤشّر ترابط STA، لذا يُطلَب [STAThread]. ↩ ↩2
مقالات ذات صلة
أحدث المقالات التي تشترك في نفس الوسوم. عمّق فهمك بمواضيع مرتبطة.
التعهيد الخارجي والتطوير التعاقدي لتطبيقات Windows: ما ينبغي تنظيمه قبل الطلب
قبل تكليف جهة خارجية بتطوير تطبيق Windows تعاقديّاً، إليك النقاط التي ينبغي تنظيمها: تعديل البرمجيّات القائمة، وتكامل الأجهزة، وCOM/Activ...
ما الذي يعنيه «لا يستجيب» حقّاً ── كيف يقرِّر Windows أنّ تطبيقاً قد تجمّد، وكيف تصمِّم تطبيقات لا تتجمّد
«لا يستجيب» في Windows آليّة يحكم فيها نظام التشغيل أنّ نافذة لم تسترجع رسالة لمدّة 5 ثوانٍ ويستبدلها بنافذة شبح. يغطّي المقال دواخل ذلك ...
CI/CD عمليّ لتطبيقات WinForms / WPF ── أتمتة من البناء إلى التوقيع والتوزيع عبر GitHub Actions
دليل عمليّ لبناء CI/CD لتطبيقات WinForms / WPF عبر GitHub Actions. يشمل YAML الأدنى للبناء + الاختبار على windows-latest، وترقيم الإصدار ...
أيقونات علبة النظام والإشعارات المنبثقة في تطبيقات Windows — مزالق NotifyIcon وكيفية اختيار AppNotification المناسب
دليل عملي لإبقاء تطبيق Windows الخاص بالأعمال مقيمًا في علبة النظام (منطقة الإشعارات) وإخطار المستخدم عبر الإشعارات المنبثقة (Toast). يتن...
تعدد اللغات في تطبيقات WinForms/WPF ── الممارسة العملية لملفات resx وتجميعات الأقمار الصناعية (Satellite Assemblies) وتبديل الثقافة (Culture)
نستعرض من منظور عملي تعدد اللغات في تطبيقات سطح المكتب على Windows، بما في ذلك الفرق بين CurrentCulture وCurrentUICulture، وآلية الموارد ...
أين يتصل هذا الموضوع
ترتبط هذه المقالة بشكل طبيعي بصفحات الخدمات التالية.
تطوير تطبيقات ويندوز
ندعم تطوير برامج ويندوز للأعمال، وتكامل الأجهزة، وأدوات التواصل.
دعم إعادة استخدام الأصول القديمة وترحيلها
ندعم إعادة استخدام وترحيل الأصول التي تحمل قيود COM / ActiveX / OCX أو 32bit / 64bit.
الأسئلة الشائعة
أسئلة شائعة حول موضوع هذه المقالة.
- لماذا ينهار تنسيق جدول منسوخ من Excel عندما ألصقه في تطبيقي؟
- الحافظة لا تحتفظ «بقطعة بيانات واحدة». يُوضَع المحتوى نفسه بعدّة تنسيقات دفعة واحدة (التنسيق الخاصّ لتطبيق المصدر، وHTML Format، وCSV، ونصّ يونيكود، وما شابه)، ويختار تطبيق الوجهة تنسيقاً يفهمه ويستخرجه. عندما ينهار التنسيق، السبب النموذجيّ أنّ الوجهة تقرأ النصّ البسيط فقط (CF_UNICODETEXT). إن أردت بنية الجدول أيضاً، نفِّذ جانب اللصق بحيث يفضّل HTML Format أو CSV. وبالعكس، إن أردت أن تلصق تطبيقات أخرى بشكل صحيح من نسخة صُنعت في تطبيقك، فاعرض عند النسخ تنسيقاً غنيّاً وتنسيقاً بسيطاً معاً.
- لماذا لا أستطيع اللصق بعد أن أغلقت التطبيق الذي نسخت منه؟
- لأنّ المصدر يستخدم العرض المؤجَّل. التطبيقات التي تعالج بيانات كبيرة لا تضع الحمولة عند النسخ؛ تسجّل على الحافظة وعداً فقط بأنّها «ستنتجها عند الطلب». إن خرج المصدر بعد ذلك دون تجسيد البيانات استجابةً لـWM_RENDERALLFORMATS عند الإغلاق، يُفقَد كلّ تنسيق لم يُعرَض بعد. تطبيق يستخدم حافظة OLE (IDataObject) يمكنه إبقاء اللصق بعد الخروج باستدعاء OleFlushClipboard عند الإغلاق لتجسيد البيانات.
- كيف يراقب تطبيقي الحافظة بحثاً عن تغييرات؟
- الطريقة الموصى بها حالياً تسجيل نافذتك مستمعاً بـAddClipboardFormatListener ومعالجة رسالة WM_CLIPBOARDUPDATE التي تصل في كلّ مرّة يتغيّر المحتوى. استطلاع المحتوى على مؤقّت يهدر عملاً وقد يفوّت تحديثات، وسلسلة العارض القديمة القائمة على SetClipboardViewer تُحفَظ فقط للتوافق الخلفيّ، لأنّ خطأً في تطبيق واحد في السلسلة يكسر السلسلة كلّها. لاحظ أيضاً أنّ OpenClipboard عند قراءة قد يفشل لأنّ عمليّة أخرى تمسك الحافظة، لذا نفِّذ إعادة محاولة بانتظار قصير إن أردت القراءة مستقرّة.
- هل ثمّة طريقة لإبقاء أسرار مثل كلمات المرور خارج سجلّ الحافظة (Win+V)؟
- هناك رافعتان، واحدة في جانب التطبيق وواحدة في جانب السياسة. في جانب التطبيق، إن وضعت أيضاً التنسيق المسجَّل ExcludeClipboardContentFromMonitorProcessing عند النسخ، فإنّ ذلك المحتوى لا يُدرَج لا في السجلّ ولا في المزامنة عبر الأجهزة. يمكنك أيضاً التحكّم في كلّ منهما مستقلّاً بـCanIncludeInClipboardHistory (السجلّ فقط) وCanUploadToCloudClipboard (المزامنة فقط). هذا الآليّة التي تستخدمها مدراء كلمات المرور. إن أردت إيقافه للمنظّمة كلّها، يمكنك تعطيل السجلّ ومزامنة السحابة نفسيهما بـAllowClipboardHistory وAllowCrossDeviceClipboard عبر نهج المجموعة أو Intune (Policy CSP).
- لماذا لا أستطيع سحب ملفّ وإفلاته على تطبيق يعمل كمسؤول؟
- لأنّ آليّة أمان تُدعى UIPI (User Interface Privilege Isolation) تحجب تسليم الرسائل من عمليّة سلامة أدنى إلى نافذة سلامة أعلى. مستكشف الملفّات يعمل بصلاحيّة عاديّة (سلامة متوسّطة)، لذا إشعارات السحب والإفلات لا تصل أبداً إلى نافذة تطبيق مرتفع. حلّ التفافيّ يسمح برسائل مثل WM_DROPFILES فرديّاً بـChangeWindowMessageFilterEx معروف، لكنّه ينطبق فقط على إشعار الإفلات الأقدم. الإصلاح الحقيقيّ التوقّف عن تصميم التطبيق ليعمل مرتفعاً طوال الوقت، وعزل العمل الذي يحتاج الارتفاع فقط إلى عمليّة منفصلة.
الملف الشخصي للمؤلف
صفحة الملف الشخصي لمؤلف المقالة.
غو كومورا
مؤسّس شركة كومورا سوفت ذ.م.م.
يركّز على تطوير برامج ويندوز، والاستشارات التقنية، والتحقيق في الأخطاء، ويتميّز في المشاريع التي تبقى فيها الأصول القديمة ناشطة، وفي تشخيص الأعطال التي يصعب تحديد سببها.