WinApi Funktion in C++ "aendern"



  • Hallo Leute, folgende IP Funktion von der MSDN Seite ist gegeben (Ausschnitt)

    1.Declare a pointer to an IP_ADAPTER_INFO variable called pAdapterInfo, and a ULONG variable called ulOutBufLen. These variables are passed as parameters to the GetAdaptersInfo function. Also create a DWORD variable called dwRetVal (for error checking).

    IP_ADAPTER_INFO  *pAdapterInfo;
    ULONG            ulOutBufLen;
    DWORD            dwRetVal;
    

    2.Allocate memory for the structures.

    pAdapterInfo = (IP_ADAPTER_INFO *) malloc( sizeof(IP_ADAPTER_INFO) );
    ulOutBufLen = sizeof(IP_ADAPTER_INFO);
    

    3.Make an initial call to GetAdaptersInfo to get the size needed into the ulOutBufLen variable.
    Note This call to the function is meant to fail, and is used to ensure that the ulOutBufLen variable specifies a size sufficient for holding all the information returned to pAdapterInfo. This is a common programming model for data structures and functions of this type.

    if (GetAdaptersInfo( pAdapterInfo, &ulOutBufLen) != ERROR_SUCCESS) {
        free (pAdapterInfo);
        pAdapterInfo = (IP_ADAPTER_INFO *) malloc ( ulOutBufLen );
    }
    

    Ich verstehe die ganze Sache wohl nicht richtig. Es wird doch mit dem Malloc im Prinzip auch nur Speicher für 1x struct IP_ADAPTER_INFO angefordert oder ?

    Könnte ich nicht einfach ein

    pAdapterInfo = new IP_ADAPTER_INFO ; 
    
    if (!pAdapterInfo){
             do_sm_Error_Handling();
    }
    

    machen ? Oder hab ich das falsch verstanden ? Sollte man die Funktionen eher C mäßig 1:1 von der MSDN Seite übernehmen ?



  • Um anderen das googlen zu ersparen: es geht um diese MSDN Seite:
    https://msdn.microsoft.com/de-de/library/windows/desktop/aa366314%28v=vs.85%29.aspx

    c_to_cpp schrieb:

    Ich verstehe die ganze Sache wohl nicht richtig. Es wird doch mit dem Malloc im Prinzip auch nur Speicher für 1x struct IP_ADAPTER_INFO angefordert oder ?

    Ja: im ersten Durchgang schon.
    Der Puffer wird aber fast sicher zu klein sein.
    Dann schlägt GetAdaptersInfo() zwar fehl, aber immerhin steht danach in ulOutBufLen wie groß der Puffer denn mindestens sein muss, damit das nicht wieder passiert.
    Also wird der anfängliche (zu kleine) Puffer freigegeben, ein ausreichender neuer alloziert und das ganze wiederholt.

    c_to_cpp schrieb:

    Sollte man die Funktionen eher C mäßig 1:1 von der MSDN Seite übernehmen ?

    IMHO ja. Pack das Ding in eine Funktion und lass Dir einen vielleicht einen smartpointer zurückgeben, wenn Du mehr C++ drinhaben willst. new/delete statt malloc/free macht es noch einfacher...

    Übrigens ist GetAdaptersInfo() seit XP(!) deprecated, wenn ich das richtig lese.
    https://msdn.microsoft.com/de-de/library/windows/desktop/aa365917%28v=vs.85%29.aspx



  • new/delete statt malloc/free macht es noch einfacher...

    Wie wende ich dann ein new mit der Buffer Größe an ?

    pAdapterInfo = new IP_ADAPTER_INFO ; 
    
    if (!pAdapterInfo){
             do_sm_Error_Handling();
    }
    

    => Das wuerde ja nur für 1x Struct Speicher anfordern....



  • c_to_cpp schrieb:

    new/delete statt malloc/free macht es noch einfacher...

    Wie wende ich dann ein new mit der Buffer Größe an ?

    char* p=new char[N];
    //...
    delete []p;
    


  • Ok, das war mir schon klar, ich meinte wie ich es in diesem Fall auf

    pAdapterInfo = (IP_ADAPTER_INFO *) malloc ( ulOutBufLen );
    

    anwende. Vor dem Malloc ist ja ein Cast, wie setzte ich das auf new / delete um ?


  • Mod

    c_to_cpp schrieb:

    Vor dem Malloc ist ja ein Cast, wie setzte ich das auf new / delete um ?

    Genauso?



  • Du kannst auch mit char* arbeiten und erst später casten, wenn Dir gleich casten nicht gefällt 🙂

    Auch auf die Gefahr hin mich zu wiederholen:
    MSDN schlägt selber vor GetAdaptersAddresses() statt GetAdaptersInfo() zu verwenden.
    Das Procedere ist aber gleich:

    unique_ptr<IP_ADAPTER_ADDRESSES>
    get_adapters_addresses(ULONG family, ULONG flags) {
      using ptr_t = std::unique_ptr<IP_ADAPTER_ADDRESSES>;
      ULONG size = 15*1024;  // lt. doku
      PIP_ADAPTER_ADDRESSES addr = (PIP_ADAPTER_ADDRESSES)new char[size];
      ULONG rc;
      while((rc=GetAdapterAddresses(family, flags, nullptr, addr, &size))==ERROR_BUFFER_OVERFLOW){
        delete[]addr;
        addr=(PIP_ADAPTER_ADDRESSES)new char[size];
      }
      if(rc==ERROR_SUCCESS)
        return ptr_t{addr};
      // Hier koennte Ihre Fehlerbehandlung stehen...
      delete[]addr;
      switch(rc){
      case ERROR_NOT_ENOUGH_MEMORY:
        throw bad_alloc{};
      // ....
      default:
        throw runtime_error{"What the zuny!?"};
      }
    }
    

    So ungefähr könnte das aussehen - hab aber keine Windows-Mühle.


  • Mod

    Benutz doch in solchen Beispielen konsequent die Smartpointer. So wie jetzt ist es doch bloß C mit new statt malloc und der Threadersteller fragt sich, wo überhaupt der Unterschied zwischen den Sprachen ist.



  • Ja, Sire! Sofort Sire!

    unique_ptr<IP_ADAPTER_ADDRESSES>
    get_adapters_addresses(ULONG family, ULONG flags) {
      using ptr_t = std::unique_ptr<IP_ADAPTER_ADDRESSES>;
      ULONG size = 15*1024;  // lt. doku
      auto addr = ptr_t{(PIP_ADAPTER_ADDRESSES)new char[size]};
      ULONG rc;
      while((rc=GetAdapterAddresses(family, flags, nullptr, addr.get(), &size))==ERROR_BUFFER_OVERFLOW)
        addr=ptr_t{(PIP_ADAPTER_ADDRESSES)new char[size]};
      if(rc==ERROR_SUCCESS)
        return addr;
      // Hier koennte Ihre Fehlerbehandlung stehen...
      switch(rc){
      case ERROR_NOT_ENOUGH_MEMORY:
        throw bad_alloc{};
      // ....
      default:
        throw runtime_error{"What the zuny!?"};
      }
    }
    

    @c_to_cpp: wie Du siehst sind nach SeppJs Einwand und einer weiteren Iteration des Codes alle händischen Speicherfreigaben verschwunden. Der Code ist kürzer und klarer.



  • @c_to_cpp
    Arbeite erstmal mit malloc/free, ist für den Anfang das einfachste.

    @Furble Wurble
    Darf man denn überhaupt ein mit new char[] besorgtes char-Array mit delete (POD_TYPE*)addressOfFirstElement freigeben?
    Wenn nicht, was ich vermute, dann bekommt man mit unique_ptr<POD_TYPE> ein bisschen ein UB Problem.
    Ich hab' das bisher immer mit malloc + shared_ptr<POD_TYPE> + custom deleter der free aufruft gemacht.

    (Man kann natürlich auch nen unique_ptr<POD_TYPE, CLibFreeDeleter> o.ä. verwenden. Wenn man vorwiegend auto verwendet ist das OK, aber sonst... bäh.)

    ps.: So:

    #include <memory>
    #include <cassert>
    
    template <class T>
    std::shared_ptr<T> AllocateSizedPod(size_t size)
    {
        assert(size >= sizeof(T));
        std::shared_ptr<T> const ptr(static_cast<T*>(std::calloc(1, size)), std::free);
        if (!ptr)
            throw std::bad_alloc();
    
        return ptr;
    }
    


  • hustbaer schrieb:

    @Furble Wurble
    Darf man denn überhaupt ....

    Nein. Das ist mir auch aufgefallen - sonst würde ich auch schon pofen. 😞

    Tatsächlich stimme ich Dir zu malloc/free ist einfacher korrekt hinzubekommen, als new/delete.

    Ich bastel gerade noch an einem wrapper, aber der nimmt einen vector<char> als Speicher...ist aber nur für mich, damit ich gleich schlafen kann. Ohne schlechtes Gewissen... 🙂



  • Nimm new und delete, das macht es noch leichter!
    ~ Furble Wurble

    Ich <- doof!

    Mal eben so aus dem Lamäng einen Code hinpfuschen...Nie wieder! 🕶



  • Danke erstmal. Der Wrapper von Furble wäre evtl. noch interessant... ?
    Ich werde es erstmal bei malloc / free belassen. Habe noch 2 Fragen :

    1. Wenn ich das ganze mit den Smart_Pointern usw. weglasse (Habe mich damit noch zuwenig befasst) müsste folgender Code aber gehen oder ?
    PIP_ADAPTER_ADDRESSES addr = (PIP_ADAPTER_ADDRESSES)new char[size];
    
    while((rc=GetAdapterAddresses(family, flags, nullptr, addr, &size)==ERROR_BUFFER_OVERFLOW){
       delete[]addr; 
       addr=(PIP_ADAPTER_ADDRESSES)new char[size]; 
    }
    

    Hier müsste ja wie im Original code nach dem 1. Aufruf (wenn dieser denn Failt) die "neue" Speichergroesse angefordert werden. Kann es hier ein Problem geben ? Ich weiß nicht ob GetAdapterAdresses addr evtl. ändert, ich würde den dann noch wegspeichern davor , damit delete wieder den Originalpointer erhält?

    2.)
    auf der MSDN Seite ( Google: site:msdn.microsoft.com https://msdn.microsoft.com/de-de/library/windows/desktop/aa366314%28v=vs.85%29.aspx )

    wird der Speicher so freigegeben ... ??? :

    //6.Free any memory allocated for the pAdapterInfo structure.

    if (pAdapterInfo)
            free(pAdapterInfo);
    

    ==>> pAdapterInfo ist doch eigentlich ne Linked-List, ist das Speicherfreigeben so nicht falsch ? Der gibt doch nur für ein Element frei ? ? ?



  • c_to_cpp schrieb:

    Ich weiß nicht ob GetAdapterAdresses addr evtl. ändert, ich würde den dann noch wegspeichern davor, damit delete wieder den Originalpointer erhält?

    GetAdaptersAddresses() kann den Wert (die Adresse auf die der Zeiger zeigt) des Parameters AdapterAddresses nicht ändern, da er per value übergeben wird.

    c_to_cpp schrieb:

    ==>> pAdapterInfo ist doch eigentlich ne Linked-List, ist das Speicherfreigeben so nicht falsch ? Der gibt doch nur für ein Element frei ? ? ?

    Das delete[] gibt allen Speicher frei, den du mit new T[N] angefordert hast. IP_ADAPTER_ADDRESSES ist zwar eine linked list, "lebt" aber nur in dem von dir bereitgestelltem Speicher. Eine Liste ist es deshalb, weil die einzelnen IP_ADAPTER_ADDRESSES unterschiedlich groß sein können. Du hättest sonst keine Möglichkeit festzustellen, wo ein IP_ADAPTER_ADDRESSES endet und ein neues anfängt.



  • Wenn man die PIP_ADAPTER_ADDRESSES nur lokal braucht kann man das auch ganz ohne Pointer per std::vector<char> lösen (ohne Sicherheitsgurte und Helm):

    #include <vector>
    
    void f()
    {
       ULONG RequiredSize = 0;
       ::GetAdaptersAddresses( AF_INET, 0, NULL, NULL, &RequiredSize );
       if( RequiredSize > 0 )
       {
          std::vector<char> Buffer( RequiredSize );
          PIP_ADAPTER_ADDRESSES Addresses = reinterpret_cast<PIP_ADAPTER_ADDRESSES>( &Buffer[0] );
    
          ::GetAdaptersAddresses( AF_INET, 0, NULL, Addresses, RequiredSize );
       }
    }
    


  • GetAdaptersAddresses() kann den Wert (die Adresse auf die der Zeiger zeigt) des Parameters AdapterAddresses nicht ändern, da er per value übergeben wird.

    Stimmt, peinlich.
    Ok, dann müsste das mit meinen paar Zeilen eigentlich funktionieren...


  • Mod

    c_to_cpp schrieb:

    GetAdaptersAddresses() kann den Wert (die Adresse auf die der Zeiger zeigt) des Parameters AdapterAddresses nicht ändern, da er per value übergeben wird.

    Stimmt, peinlich.
    Ok, dann müsste das mit meinen paar Zeilen eigentlich funktionieren...

    Bloß, dass man dann eben im Falle einer Exception ein Problem hat.



  • c_to_cpp schrieb:

    Danke erstmal. Der Wrapper von Furble wäre evtl. noch interessant... ?

    Na klar. Ich habe ja gerade eine richtige Serie von guten Ideen in diesem Thread 🙂
    So habe ich mir das minimal gedacht:

    template<typename T>
    class wrapper : private vector<char> {
      using impl_t = vector<char>;
    public:
      using pointer = T;
      using impl_t::resize;
      wrapper() = default;
      wrapper(size_t sz)
        : impl_t(sz) { }
      pointer get() { return reinterpret_cast<pointer>(&(*this)[0]); }
    };
    
    using pip_adapter_addresses = wrapper<PIP_ADAPTER_ADDRESSES>;
    
    pip_adapter_addresses
    get_adapters_addresses(ULONG family, ULONG flags) {
      ULONG size = 15*1024;  // lt. doku
      pip_adapter_addresses addr(size);
      ULONG rc;
      while((rc=GetAdaptersAddresses(family, flags, nullptr, addr.get(), &size))==ERROR_BUFFER_OVERFLOW)
        addr.resize(size);
      if(rc==ERROR_SUCCESS)
        return addr;
      // Hier koennte Ihre Fehlerbehandlung stehen...
      switch(rc){
      case ERROR_NOT_ENOUGH_MEMORY:
        throw bad_alloc{};
      // ....
      default:
        throw runtime_error{"What the zuny!?"};
      }
    }
    


  • DocShoe schrieb:

    [...]

    yay! 👍 😋


Log in to reply