לדלג לתוכן

פתרון המעטפת וחיי המחשב

פתרון התרגיל הראשון

הפונקציה הבאה מצטרפת לClipboardScope מההרצאה. היא מחזיקה את הלוח רק לאורך ההעתקה. הבדיקה אינה מניחה שהאפליקציה האחרת יצרה טקסט תקין רק מפני שבחרה את שם הפורמט הנכון.

#include <algorithm>
#include <string>

std::wstring read_text(HWND owner) {
    ClipboardScope clipboard(owner);
    if (!IsClipboardFormatAvailable(CF_UNICODETEXT))
        throw std::runtime_error("Unicode text unavailable");
    HGLOBAL block = GetClipboardData(CF_UNICODETEXT);
    if (!block) throw std::runtime_error("GetClipboardData failed");
    const SIZE_T bytes = GlobalSize(block);
    if (bytes < sizeof(wchar_t) || bytes > 1024 * 1024 || bytes % sizeof(wchar_t))
        throw std::runtime_error("invalid clipboard text size");
    const auto text = static_cast<const wchar_t*>(GlobalLock(block));
    if (!text) throw std::runtime_error("GlobalLock failed");
    struct Unlock {
        HGLOBAL block;
        ~Unlock() { GlobalUnlock(block); }
    } unlock{block};
    const auto limit = text + bytes / sizeof(wchar_t);
    const auto end = std::find(text, limit, L'\0');
    if (end == limit) throw std::runtime_error("missing terminator");
    return std::wstring(text, end);
}

הterminator קובע את סוף הטקסט בפורמט הזה. אפס פנימי מפסיק את הטקסט, בשונה מBSTR שהאורך השמור שלו יכול לתאר תוכן נוסף. אם צריך לאמת UTF-16 ולדחות surrogateים לא תקינים, נוסיף בדיקה כזאת לפני העברה למערכת שמניחה Unicode תקין; מגבלת גודל אינה בדיקת קידוד.

פתרון התרגיל השני

הקובץ הבא הוא רכיב ספרייה לבנייה עם /c, ונדרש link עם ole32.lib וuuid.lib ביישום שמפעיל אותו. הcaller מאתחל STA ומוסר HWND חי. אנחנו מחזירים HRESULT ושומרים את המחרוזת ריקה בכל מסלול שאינו הצלחה.

#include <windows.h>
#include <shobjidl.h>
#include <wrl/client.h>
#include <memory>
#include <string>
#include <new>

HRESULT choose_file(HWND owner, std::wstring& path) noexcept {
    path.clear();
    Microsoft::WRL::ComPtr<IFileOpenDialog> dialog;
    HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog, nullptr, CLSCTX_INPROC_SERVER,
                                  IID_PPV_ARGS(dialog.GetAddressOf()));
    if (FAILED(hr)) return hr;
    FILEOPENDIALOGOPTIONS options{};
    hr = dialog->GetOptions(&options);
    if (FAILED(hr)) return hr;
    hr = dialog->SetOptions(options | FOS_FORCEFILESYSTEM | FOS_FILEMUSTEXIST);
    if (FAILED(hr)) return hr;
    hr = dialog->Show(owner);
    if (FAILED(hr)) return hr;
    Microsoft::WRL::ComPtr<IShellItem> item;
    hr = dialog->GetResult(item.GetAddressOf());
    if (FAILED(hr)) return hr;
    PWSTR raw = nullptr;
    hr = item->GetDisplayName(SIGDN_FILESYSPATH, &raw);
    if (FAILED(hr)) return hr;
    struct FreeTaskMemory { void operator()(wchar_t* p) const { CoTaskMemFree(p); } };
    std::unique_ptr<wchar_t, FreeTaskMemory> memory(raw);
    if (!raw) return E_UNEXPECTED;
    try { path.assign(raw); }
    catch (const std::bad_alloc&) { return E_OUTOFMEMORY; }
    catch (...) { return E_FAIL; }
    return S_OK;
}

הביטול מתקבל בדרך כלל כHRESULT_FROM_WIN32(ERROR_CANCELLED) מתוך Show. הcaller יכול לזהות אותו ולחזור למצב ההמתנה בלי להציג שגיאה חמורה. המסלול שמחזיר pathname עדיין אינו מבטיח שהקובץ יישאר קיים או זהה עד הפתיחה; נבדוק את הפעולה האמיתית על הקובץ בהמשך.

פתרון התרגיל השלישי

התוכנית הבאה אינה משנה מצב power בעצמה. היא רק מודדת בין שתי נקודות שהמשתמש בוחר. את ניסוי השינה מבצעים בVM שבה מותר לעצור את הפעילות.

#include <windows.h>
#include <cstdio>

int main() {
    std::puts("Press Enter to start.");
    std::getchar();
    ULONGLONG active_start = 0;
    if (!QueryUnbiasedInterruptTime(&active_start)) return 1;
    const ULONGLONG elapsed_start = GetTickCount64();
    std::puts("Press Enter to finish.");
    std::getchar();
    ULONGLONG active_end = 0;
    if (!QueryUnbiasedInterruptTime(&active_end)) return 2;
    const ULONGLONG elapsed_end = GetTickCount64();
    std::printf("elapsed_ms=%llu active_ms=%llu\n",
        static_cast<unsigned long long>(elapsed_end - elapsed_start),
        static_cast<unsigned long long>((active_end - active_start) / 10000));
}

במערכת שלא ישנה נצפה להפרשים דומים במסגרת רזולוציית הדגימה והזמן בין הקריאות. בזמן שינה נצפה להבחנה לפי החוזה של שני השעונים. השהיית VM על ידי hypervisor אינה בהכרח אותו מצב power כמו שינה בתוך Windows, ולכן נרשום איזה מנגנון עצר את המכונה. לא נכריז שהAPI שגוי על סמך ניסוי שלא הפעיל את המצב שהתכוונו לבדוק.