Mehrdeutigkeit 'ULONG_PTR' und 'Wmplib_tlb::ULONG_PTR'



  • Hallo,

    ich habe den WindowsMediaPlayer als ActiveX Komponente hinzugefügt, zudem FTP und HTTP.

    Ich will eine Datei auf dem Server auslesen, darin enthalten sind Links zu Videos, die dann über den MediaPlayer innerhalb der Form abgespielt werden.

    BCB 2009, Projektbedingungen Strict

    Die Liste der Fehler ist lang, erster Punkt:
    winsock2.h
    ULONG_PTR Key;

    Kann mir jemand helfen ?

    #include <iostream.h>
    #include <cstdlib>
    #include <iostream>
    #include <stdio.h>
    //---------------------------------------------------------------------------
    #pragma package(smart_init)
    #pragma link "WMPLib_OCX"
    #pragma link "ResizeKit"
    #pragma link "IdBaseComponent"
    #pragma link "IdComponent"
    #pragma link "IdExplicitTLSClientServerBase"
    #pragma link "IdFTP"
    #pragma link "IdHTTP"
    #pragma link "IdTCPClient"
    #pragma link "IdTCPConnection"
    #pragma resource "*.dfm"

    [BCC32 Fehler] winsock2.h(1094): E2015 Mehrdeutigkeit zwischen 'ULONG_PTR' und 'Wmplib_tlb::ULONG_PTR'
    [BCC32 Fehler] iosfwd(254): E2238 Bezeichner 'char_traits<wchar_t>::int_type' mehrfach deklariert
    ...


  • Mod

    Nach ein bisschen wühlen durch hauptsächlich chinesische Google-Ergebnisse, würde ich als Lösung eine Änderung der Include-Reihenfolge vorschlagen, so dass WMPLib_OCX als letztes kommt. Wohl gesagt: Include. Wie Pragma Link und Include miteinander wechselwirken kann ich dir aber leider nicht sagen, weil ich nie Pragma Link benutzte. Das musst du selber heraus finden, was das jeweils für die Reihenfolge bedeutet.



  • Das Problem ist die völlig bescheuerte Delphi-Unart, alles in namespaces zu verpacken und die im gleichen Header am Ende per using-Direktive wieder in scope zu bringen.

    Der wmplib_tlib.hpp Header sieht vermutlich so oder so ähnlich aus:

    #ifndef Wmplib_tlbH
    #define Wmplib_tlbH
    
    namespace Wmplib_tlb
    {
       ...
       typedef ... ULONG_PTR ...
    };
    
    #if !defined(DELPHIHEADER_NO_IMPLICIT_NAMESPACE_USE) && !defined(NO_USING_NAMESPACE_WMPLIB_TLIB)
    using namespace Wmplib_tlb
    #endif
    

    Wenn das so aussieht hast du Glück und kannst das Verhalten steuern, indem du nicht direkt die wmplib_tlb.hpp inkludierst, sondern eine eigene Header Datei erstellst, die vor dem Inkludieren das Symbol NO_USING_NAMESPACE_WMPLIB_TLIB definiert:

    #ifndef MYWMPLIBH
    #define MYWMPLIBH
    
    #define NO_USING_NAMESPACE_WMPLIB_TLIB
    #include <wmplib_tlb.hpp>
    
    #endif
    

    Alternativ kannst du auch in den Projektoptionen das Symbol NO_USING_NAMESPACE_WMPLIB_TLIB definieren.

    Wenn der #if !defined(...) Block nicht existiert, musst du die using namespace Direktive aus der .hpp Datei entfernen und möglicherweise mehr Quelltext korrigieren, bis es wieder kompiliert.



  • DocShoe schrieb:

    Das Problem ist die völlig bescheuerte Delphi-Unart, alles in namespaces zu verpacken und die im gleichen Header am Ende per using-Direktive wieder in scope zu bringen.

    Daß alle verwendeten Namespaces standardmäßig geöffnet werden, ist weder unüblich noch verwerflich, sondern halt eine Designentscheidung; C# macht das auch so.

    Trotzdem gibt es hier einige Probleme:

    • Delphi übergeht mehrdeutige Symbolreferenzen stillschweigend und nimmt einfach das zuletzt definierte Symbol, was zur Folge hat, daß es auf die Reihenfolge der Module in der uses -Liste ankommt. Das halte ich für einen Designfehler.
    • der Formdesigner erzeugt Code ohne explizite Namespace-Bezüge. (In Delphi führt das zusammen mit dem the last definition wins-"Feature" zu dem folgenden sehr gängigen Pattern: wenn du nur für deine Formklasse eine Komponente subclassen willst, kannst du einfach vor die Form-Definition ein type TButton = class(StdCtrls.TButton) ... end; setzen; der Formdesigner braucht so nicht zu wissen, daß du eine neue Komponente definiert hast.)
    • weil Delphi keine Headerdateien lesen kann, gibt es Delphi-Units für die meisten Headerdateien aus dem Windows-SDK. Da wiederum C++Builder die SDK-Header verwenden kann, muß in den entsprechenden Delphi-Units durch spezielle Direktiven dafür gesorgt werden, daß die im dort definierten Typen nicht in die automatisch generierte Headerdatei exportiert werden (und daß Mangling und Objektlayout kompatibel sind), sondern daß stattdessen Verweise auf die SDK-Typen erzeugt werden. Deshalb ist das using namespace für diese Header i.d.R. kein Problem.

    Das Problem entsteht hier, weil die Typbibliothek als Delphi-Unit importiert wurde, und weil aus Gründen der Abwärtskompatibilität dann eben dieses using namespace in den Header eingefügt wird wie für jedes andere Delphi-Unit. Weil das Unit aber automatisch erzeugt wurde, hat natürlich niemand darauf achten können, daß keine Symbolkonflikte mit SDK-Typen entstehen.

    Es gibt also zwei Möglichkeiten:

    1. du importierst die Typbibliothek als C++-Unit. Der TLB-Importer sollte eine überschaubare Menge an SDK-Typen erkennen (wie hoffentlich auch ULONG_PTR ) und für diese keine Definition erzeugen.

    2. oder du definierst in den Projektoptionen das Symbol NO_USING_NAMESPACE_WMPLIB_TLIB , wie DocShoe vorschlug.



  • audacia schrieb:

    C# macht das auch so.

    Nein, man muß auch in C# explizit

    using System; // oder anderer Namespace
    

    schreiben.



  • Th69 schrieb:

    Nein, man muß auch in C# explizit

    using System; // oder anderer Namespace
    

    schreiben.

    Na eben, das ist doch genau wie in Delphi:

    uses
      SysUtils;
    


  • Aber du hast geschrieben:

    audacia schrieb:

    Daß alle verwendeten Namespaces standardmäßig geöffnet werden, ist weder unüblich noch verwerflich, ...

    Hier ging es doch um den von Delphi erzeugten C++-Code, d.h. durch das Einbinden der Header wird automatisch der Namespace geöffnet (wenn man nicht die Defines definiert hat). Und in C# ist ja ein Namespace und dessen Typen (Klassen, Interfaces, ...) alleine durch dessen Einbinden in das Projekt bekannt. Das "using ..." ist dann nur noch dafür da, damit man den Namespace nicht explizit immer hinschreiben muß, d.h. auch ohne kann man auf die enthaltenen Typen zugreifen.
    In Delphi muß man aber explizit die "uses"-Klausel hinschreiben, damit das Modul im Source bekannt gemacht wird (quasi "#include" und "using" in einem) -> also im Unterschied zu C#!



  • Du hast recht; ich habe mich da schlecht ausgedrückt, und ich kann das bei erneutem Durchsehen auch inhaltlich nicht so stehenlassen.

    DocShoe schrieb von der "Delphi-Unart", und ich hatte mich mit meinem Einwand auch auf die Sprache Delphi beziehen wollen. Die Konsequenzen für die Headerdateien habe ich ja separat aufgeführt. Aber wie du sagst: es ist eben auch nicht genau wie in Delphi.

    Also, von vorne. An eine moderne Programmiersprache könnte man folgende Ansprüche stellen:

    1. daß Schnittstellendefinitionen einander nicht beeinflussen
    2. daß die Bedeutung eines Programms nicht von volatilen Umständen wie der Reihenfolge der Schnittstelleneinbindung beeinflußt wird
    3. daß das Öffnen eines Namensraums nur eine lokale Schreiberleichterung ist und die Schnittstelle nicht verändert
    4. daß auch Symbole aus nicht geöffneten Namensräumen erreichbar sind

    C# erfüllt alle vier Ansprüche. Das bedeutet, wir können Namensräume, mit denen wir arbeiten, einfach öffnen und Symbole nur dann qualifizieren, wenn es Mehrdeutigkeiten gibt, und wir tun das guten Gewissens, weil der Compiler im Zweifel die Qualifizierung erzwingt. Wir können auch mit Namensräumen arbeiten, die wir nicht öffnen wollen (z.B. weil sie eine wünschensunwerte Menge an Konflikten verursachen würden).

    Delphi erfüllt #1 und #3, aber nicht #2 (weil das zuletzt referenzierte Unit bevorzugt wird) und nicht #4. Wie in C# kann man alle Namensräume öffnen, mit denen man arbeitet, und nur explizit qualifizieren, um Mehrdeutigkeiten auszuweichen. Aber ob das gut geht, ist ein bißchen Glückssache, und man muß auch nicht genehme Namensräume öffnen, um mit ihnen zu arbeiten.

    C++ mit Headerdateien erfüllt nur #4: Headerdateien können einander in die Quere kommen; die Headerreihenfolge kann auch die Bedeutung eines Programms verändern; und das Öffnen eines Namensraums ist nicht nur eine lokale Schreiberleichterung, sondern verändert den umliegenden Namensraum. Deshalb ist die übliche Arbeitsweise mit C# und Delphi, möglichst viele Namensräume zu öffnen, in C++-Headerdateien gefährlich und verpönt.

    Was ich also ausdrücken wollte: das standardmäßige Öffnen von Namensräumen ist an sich keine Unart, wenn die Sprache dazu paßt. Aber weil C++ die meisten meiner obigen Kriterien nicht erfüllt, sollte man es in Headerdateien eben bleibenlassen. Deshalb fand ich, daß "Delphi-Unart" das Problem nicht trifft. Ich würde "C++Builder-Unart" vorschlagen 🙂



  • OK, so stimme ich dir vollkommen zu. 👍



  • audacia schrieb:

    ...
    Deshalb fand ich, daß "Delphi-Unart" das Problem nicht trifft. Ich würde "C++Builder-Unart" vorschlagen 🙂

    Das ist auch genau das, was ich eigentlich sagen wollte.


Log in to reply