def _generate_windows_body(hooks):
    """Generate Windows specific functions.

    At the moment it implements load_impls_from_library, class destructor, and an utility function
    to convert from utf8 to wide-strings so we can use the wide family of windows
    functions that accept unicode.
    """
    result = ['public:', f'    ~HookCaller() {{',
        f'        for (auto handle : this->handles) {{',
        f'            FreeLibrary(handle);', f'        }}', f'    }}']
    result += [
        f'    void load_impls_from_library(const std::string& utf8_filename) {{'
        ,
        f'        std::wstring w_filename = utf8_to_wstring(utf8_filename);',
        f'        auto handle = LoadLibraryW(w_filename.c_str());',
        f'        if (handle == NULL) {{',
        f'            throw std::runtime_error("Error loading library " + utf8_filename + ": " + std::to_string(GetLastError()));'
        , f'        }}', f'        this->handles.push_back(handle);', '']
    for index, hook in enumerate(hooks):
        result += [
            f'        auto p{index} = GetProcAddress(handle, "{hook.function_name}");'
            , f'        if (p{index} != nullptr) {{',
            f'            this->append_{hook.name}_impl((uintptr_t)(p{index}));'
            , f'        }}', '']
    result.append('    }')
    result += ['', '', 'private:',
        f'    std::wstring utf8_to_wstring(const std::string& s) {{',
        f'        int flags = 0;',
        f'        int required_size = MultiByteToWideChar(CP_UTF8, flags, s.c_str(), -1, nullptr, 0);'
        , f'        std::wstring result;',
        f'        if (required_size == 0) {{',
        f'            return result;', f'        }}',
        f'        result.resize(required_size);',
        f'        int err = MultiByteToWideChar(CP_UTF8, flags, s.c_str(), -1, &result[0], required_size);'
        , f'        if (err == 0) {{',
        f'            // error handling: https://docs.microsoft.com/en-us/windows/desktop/api/stringapiset/nf-stringapiset-multibytetowidechar#return-value'
        , f'            switch (GetLastError()) {{',
        f'                case ERROR_INSUFFICIENT_BUFFER: throw std::runtime_error("utf8_to_wstring: ERROR_INSUFFICIENT_BUFFER");'
        ,
        f'                case ERROR_INVALID_FLAGS: throw std::runtime_error("utf8_to_wstring: ERROR_INVALID_FLAGS");'
        ,
        f'                case ERROR_INVALID_PARAMETER: throw std::runtime_error("utf8_to_wstring: ERROR_INVALID_PARAMETER");'
        ,
        f'                case ERROR_NO_UNICODE_TRANSLATION: throw std::runtime_error("utf8_to_wstring: ERROR_NO_UNICODE_TRANSLATION");'
        ,
        f'                default: throw std::runtime_error("Undefined error: " + std::to_string(GetLastError()));'
        , f'            }}', f'        }}', f'        return result;',
        f'    }}', f'', f'', f'private:', f'    std::vector<HMODULE> handles;']
    return result
