פתרון אוטומציה וממשקי זמן ריצה¶
פתרון התרגיל הראשון¶
העתקת BSTR דרך VariantCopy יוצרת בעלות נפרדת על תוכן המחרוזת. אפשר לשנות את יחידת UTF-16 הראשונה במקור ולראות שההעתק אינו משתנה. בשני הvariantים שמכילים VT_BYREF נשמרת כתובת לאותו משתנה, ולכן שינוי שלו נצפה משניהם. העטיפות אינן מאריכות את חייו.
ההבדל אינו "העתקה עמוקה תמיד" מול "העתקה שטוחה תמיד". הפעולה תלויה בטיפוס. עבור interface pointer מוסיפים reference לאותו אובייקט, ולכן שינוי state של האובייקט יכול להיות גלוי גם דרך ההעתק.
פתרון התרגיל השני¶
הפונקציה הבאה היא קובץ ספרייה שניתן לבנות עם /c. היא אינה משנה את המערך. מספר האיברים מוגבל לפני הלולאה, והאינדקס מחושב ב64 ביט כדי לא לבצע overflow בסוף טווח של LONG.
#include <windows.h>
#include <oleauto.h>
#include <cstdint>
HRESULT sum_array(SAFEARRAY* array, std::int64_t* output) noexcept {
if (!array || !output) return E_POINTER;
*output = 0;
if (SafeArrayGetDim(array) != 1) return E_INVALIDARG;
VARTYPE type = VT_EMPTY;
HRESULT hr = SafeArrayGetVartype(array, &type);
if (FAILED(hr)) return hr;
if (type != VT_I4) return DISP_E_TYPEMISMATCH;
LONG low = 0, high = 0;
hr = SafeArrayGetLBound(array, 1, &low);
if (FAILED(hr)) return hr;
hr = SafeArrayGetUBound(array, 1, &high);
if (FAILED(hr)) return hr;
const std::int64_t count = static_cast<std::int64_t>(high) - low + 1;
if (count < 0 || count > 1000000) return E_INVALIDARG;
std::int64_t sum = 0;
for (std::int64_t offset = 0; offset < count; ++offset) {
LONG index = static_cast<LONG>(static_cast<std::int64_t>(low) + offset);
LONG value = 0;
hr = SafeArrayGetElement(array, &index, &value);
if (FAILED(hr)) return hr;
sum += value;
}
*output = sum;
return S_OK;
}
המגבלה של מיליון ערכי LONG מבטיחה שהסכום מתאים ל64 ביט. אם נסיר את המגבלה או נחליף את טיפוס האיבר ל64 ביט, נצטרך בדיקת overflow חדשה. זהו חלק מחוזה הקלט, לא רק optimization.
פתרון התרגיל השלישי¶
בשרת הבדיקה GetIDsOfNames יחזיר DISP_E_UNKNOWNNAME לשם שאינו מוכר. בתוך Invoke נבדוק תחילה שהDISPID והflags מתאימים, שיש שני ארגומנטים ושאין named arguments שאיננו תומכים בהם. לאחר מכן נדרוש VT_I4 בשני הסלוטים, או נבצע המרה מפורשת בהתאם למדיניות שנבחרה.
החישוב יהיה args[1].lVal - args[0].lVal, עם הרחבה ל64 ביט לפני החיסור ובדיקת טווח לפני החזרה לVT_I4. אם נכשל טיפוס, נמלא puArgErr באינדקס של המערך, ולא במספר הפרמטר לפי סדר הקריאה המקורי. אם התוצאה מחוץ לטווח, נחזיר DISP_E_OVERFLOW.
לקוח ABI בודק HRESULT ומנקה outputים בהתאם לחוזה, גם במסלול שגיאה. לקוח C++/WinRT בדרך כלל מקבל hresult_error מתוך הprojection. בתוך מימוש רכיב אסור לתת לחריגת C++ לחצות ABI שאינו מגדיר זאת; הprojection או קוד הגבול מתרגמים אותה חזרה לHRESULT. שתי הצורות צריכות לתאר אותה משמעות של כשל.