2017-12-28 50 views
0

Ist es irgendwie möglich, eine Vorlage für WinAPI-Funktionen zu erstellen? Beispielsweise gibt es zwei ähnliche Funktionen (LookupPrivilegeName und LookupPrivilegeDisplayName), jedoch mit unterschiedlichen Parametern. Ich rufe beide Funktionen wie folgt auf: der erste Aufruf ruft die erforderliche Puffergröße ab, der zweite Aufruf gibt den gewünschten Wert zurück. Wenn dies nicht möglich ist, gibt es alternative Möglichkeiten, den Code kompakter zu gestalten?Vorlage für WinAPI-Funktionen

+0

Sie könnten eine Metaprogrammierung erstellen, um eine Reihe von Parametertypen einer winapi-Funktion zuzuordnen, um eine Überladungsgruppe zu erstellen, die Sie selbst auflösen, aber dafür gibt es keine magische Lösung. –

+0

Ja, es ist eine gängige Praxis, handliche Wrapper zu erstellen, die eine einfachere Schnittstelle bieten, Eingabeparameter validieren, Fehlercodes überprüfen usw. – VTT

+0

Danke für die Antworten, aber wie wäre es mit einem Beispiel? Oder kannst du mich auf die nützlichen Links zu diesem Thema hinweisen? Ich muss das wirklich verstehen. – kate

Antwort

0

Sie wollen wahrscheinlich so etwas wie:

template<typename CharT> 
bool LookupPriviledgeDisplayName(CharT const *, CharT const *, DWORD *, std::basic_string<CharT> & strDisplayName); 
template<> 
bool LookupPriviledgeDisplayName<char>(char const * pszSystem, char const * pszName, DWORD * pdwLangID, std::basic_string <char> & strDisplayName) 
{ 
    DWORD dwLength = 0; 
    if(!LookupPrivilegeDisplayNameA(pszSystem, pszName, nullptr, &dwLength, pdwLangID)) 
     return false; 

    std::vector<char> buffer(dwLength + 1); 
    if(!LookupPrivilegeDisplayNameA(pszSystem, pszName, &buffer[0], &dwLength, pdwLangID)) 
     return false; 

    strDisplayName.assign(&buffer[0], &buffer[0] + dwLength); 

    return true; 
} 
template<> 
bool LookupPriviledgeDisplayName<wchar_t>(wchar_t const * pszSystem, wchar_t const * pszName, DWORD * pdwLangID, std::basic_string <wchar_t> & strDisplayName) 
{ 
    DWORD dwLength = 0; 
    if(!LookupPrivilegeDisplayNameW(pszSystem, pszName, nullptr, &dwLength, pdwLangID)) 
     return false; 

    std::vector<wchar_t> buffer(dwLength + 1); 
    if(!LookupPrivilegeDisplayNameW(pszSystem, pszName, &buffer[0], &dwLength, pdwLangID)) 
     return false; 

    strDisplayName.assign(&buffer[0], &buffer[0] + dwLength); 

    return true; 
} 
+0

Dies ist eine Definition von Copy-Pasta, auch gibt es keine Notwendigkeit, 'A' Varianten zu nennen. – VTT

+0

weil 'LookupPrivilege [Display] NameW' ist sehr schwer Call (benutzter RPC Aufruf an lsass) viel besser verwenden Sie einige zugewiesene Puffer im ersten Aufruf, stattdessen 0. und nur wenn es fehlschlägt, neu zugewiesenen Puffer für zweiten Aufruf – RbMm

+0

Vielleicht ich bin nicht so richtig ausdrücken meine Gedanken. Ich meine, gibt es eine Möglichkeit, eine einzige Vorlage für zwei Winapi-Funktionen mit variablen Parametern zu erstellen? Etwas wie '' 'Vorlage ' '' wobei TRet der Typ der Funktion zurückgegeben wird, TVal ist der gewünschte Wert, den ich abrufen möchte, und TArgs ist eine Menge von Parametern. – kate

0

Etwas Ähnliches. Die Grundidee besteht darin, Code nach Funktionalität zu trennen, um Doppelarbeit zu vermeiden.

#include <memory> 
#include <string> 
#include <iostream> 
#include <system_error> 
#include <cassert> 

// Error handler. 
void 
On_Error(const ::DWORD error_code) 
{ 
    throw ::std::system_error{static_cast<int>(error_code), ::std::system_category()}; 
} 

// Error check. 
void 
Validate_LookupPrivilegeNameSuccess(const ::BOOL result, const bool expect_insufficient_buffer) 
{ 
    if(FALSE == result) 
    { 
     auto const error_code{::GetLastError()}; 
     if((ERROR_INSUFFICIENT_BUFFER == error_code) && (!expect_insufficient_buffer)) 
     { 
      On_Error(error_code); 
     } 
    } 
} 

enum class 
t_PrivilegeNameCategoryId 
{ 
    name 
, display_name 
}; 

// Helper class calling WinAPI methods, checking input and output. 
template<t_PrivilegeNameCategoryId> class 
t_LookupPrivilegeNameImpl; 

template<> class 
t_LookupPrivilegeNameImpl<t_PrivilegeNameCategoryId::name> final 
{ 
    public: static ::std::size_t 
    Lookup(const ::LPCWSTR psz_system_name, ::LUID & luid, const ::LPWSTR p_buffer, ::DWORD buffer_capacity_items_count) 
    { 
     assert((0 == buffer_capacity_items_count) || (nullptr != p_buffer)); 
     Validate_LookupPrivilegeNameSuccess 
     (
      ::LookupPrivilegeNameW 
      (
       psz_system_name 
      , ::std::addressof(luid) 
      , p_buffer 
      , ::std::addressof(buffer_capacity_items_count) 
      ) 
     , nullptr == p_buffer 
     ); 
     return(buffer_capacity_items_count); 
    } 
}; 

template<> class 
t_LookupPrivilegeNameImpl<t_PrivilegeNameCategoryId::display_name> final 
{ 
    public: static ::std::size_t 
    Lookup(const ::LPCWSTR psz_system_name, const ::LPCWSTR psz_display_name, const ::LPWSTR p_buffer, ::DWORD buffer_capacity_items_count) 
    { 
     assert(psz_display_name); 
     assert(L'\0' != psz_display_name[0]); 
     assert((0 == buffer_capacity_items_count) || (nullptr != p_buffer)); 
     ::DWORD language_id{}; 
     Validate_LookupPrivilegeNameSuccess 
     (
      ::LookupPrivilegeDisplayNameW 
      (
       psz_system_name 
      , psz_display_name 
      , p_buffer 
      , ::std::addressof(buffer_capacity_items_count) 
      , ::std::addressof(language_id) 
      ) 
     , nullptr == p_buffer 
     ); 
     return(buffer_capacity_items_count); 
    } 
}; 

// Lookup function implementing get size -> resize buffer -> get data algorithm. 
template<t_PrivilegeNameCategoryId name_category_id, typename... TArgs> void 
Lookup_PrivilegeName(::std::wstring & name, TArgs &&... args) 
{ 
    try 
    { 
     name.resize(t_LookupPrivilegeNameImpl<name_category_id>::Lookup(::std::forward<TArgs>(args)..., ::LPWSTR{}, ::DWORD{})); 
     t_LookupPrivilegeNameImpl<name_category_id>::Lookup(::std::forward<TArgs>(args)..., name.data(), static_cast<::DWORD>(name.size())); 
     if(name.empty() || (L'\0' != name.back())) 
     { 
      On_Error(ERROR_UNIDENTIFIED_ERROR); 
     } 
     name.pop_back(); 
    } 
    catch(...) 
    { 
     name.clear(); 
     throw; 
    } 
} 

int main() 
{ 
    ::LPCWSTR psz_system_name{}; 
    ::LUID privilege_luid{5}; // a wild guess 
    ::std::wstring privilege_name{}; 
    Lookup_PrivilegeName<t_PrivilegeNameCategoryId::name>(privilege_name, psz_system_name, privilege_luid); 
    ::std::wstring privilege_display_name{}; 
    Lookup_PrivilegeName<t_PrivilegeNameCategoryId::display_name>(privilege_display_name, psz_system_name, privilege_name.c_str()); 
    ::std::wcout << privilege_name << L"\n" << privilege_display_name << ::std::endl; 
    return(0); 
} 
+0

Sie vermeiden den doppelten Code für die Pufferverwaltung, aber Sie tauschen das für höhere Code-Komplexität. Meiner Meinung nach ist dies ein Beispiel, bei dem [DRY] (https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) nicht so weit wie möglich verfolgt werden sollte, da es den Code schwieriger zu verstehen macht. Ich wette, Sie sparen nicht einmal viele LOC im Vergleich zu zwei einfachen Überladungen. – zett42

0

Mein russischer Freund erklärte mir die Einschränkungen von Vorlagen. Statt dessen schlug er vor, Funktionen mit Kontextwechsel zu verwenden. In meiner Situation wird es so etwas wie diese:

BOOL LookupData(DWORD context, PLUID luid, wstring input, wstring & output) { 
    BOOL status = TRUE; 
    DWORD buflen = 0, lang = 0; 
    switch (context) { 
    case 0: status = LookupPrivilegeName(NULL, luid, NULL, &buflen); break; 
    case 1: status = LookupPrivilegeDisplayName(NULL, input.c_str(), NULL, &buflen, &lang); break; 
    default: return FALSE; 
    } 

    if (!status && ERROR_INSUFFICIENT_BUFFER != GetLastError()) return status; 

    auto buffer = make_unique<wchar_t[]>(buflen); 
    switch (context) { 
    case 0: status = LookupPrivilegeName(NULL, luid, buffer.get(), &buflen); break; 
    case 1: status = LookupPrivilegeDispayName(NULL, input.c_str(), buffer.get(), &buflen, &lang); break; 
    } 

    if (!status) { 
     buf.release(); 
     return status; 
    } 

    output = buf.get(); 
    buf.release(); 

    return status; 
} 

Jetzt kann ich in -Schreibzyklus nach TOKEN_PRIVILEGES Strukturen erhalten:

std::wstring name, desription; 
if (!LookupData(0, tp->Privipeges[i].Luid, L"", name)) break; 
if (!LookupData(1, NULL, name, description)) break; 
std::wcout << name << L" - " << std::endl; 

Natürlich, es ist schmutziger Trick, aber es funktioniert.