Übersicht Windows Media Player

  • VB.NET

Es gibt 63 Antworten in diesem Thema. Der letzte Beitrag () ist von kafffee.

    @-Franky-

    Wow das ist schon mal einiges übersichtlicher :)

    Aber du überschätzt mich. Ich verstehe den Code nicht, hauptsächlich in den Regionen Functions und Delegates... Ich muss dazu sagen, dass ich bisher noch nichts mit Pointern oder Delegaten gemacht hab...

    Könntest du mir das anhand einer Funktion, Schritt für Schritt, in Stichworten erklären und/oder den Code ein bisschen auskommentieren?

    Ich wär dir sehr dankbar :)

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „kafffee“ ()

    Hi

    Da musst Du Dich im Moment ein bissel gedulden. Bin derzeit im Urlaub. Sozusagen PC und Laptop freie Zeit. Danach kann ich gern etwas Licht ins Dunkle bringen. Denke aber das auch @RodFromGermany, @VaporiZed und vllt. auch andere schon was dazu sagen könnten wie das funktioniert.
    Mfg -Franky-
    @-Franky-

    Ja kein Problem. Geniess die freie Zeit :)

    Hab noch mehrere Baustellen in meinem Projekt, dann mach ich da weiter und werd vllt auch die andern mal zwischendurch fragen...

    Meine DLL ist quasi auch schon fertig ohne dass ich auf grössere Probleme gestossen bin... Nur eben die Überprüfung ob die Daten auf die Disc passen fehlt eben noch...

    Bis dahin,

    kafffee :)
    @kafffee

    So, Urlaub zu Ende. Weiter gehts. :) Ich versuche das mal so einfach wie Möglich zu erklären. Ein Delegate beschreibt die Signatur einer Interface Funktion und zwar so wie diese in der C++ Headerdatei imapi2.h im Abschnitt "typedef struct [Interface]Vtbl" eines Interfaces steht. Hier mal das Interface IDiscMaster2 aus der C++ Headerdatei.

    Spoiler anzeigen

    C-Quellcode

    1. EXTERN_C const IID IID_IDiscMaster2;
    2. #if defined(__cplusplus) && !defined(CINTERFACE)
    3. MIDL_INTERFACE("27354130-7F64-5B0F-8F00-5D77AFBE261E")
    4. IDiscMaster2 : public IDispatch
    5. {
    6. public:
    7. virtual /* [helpstring][restricted][hidden][id][propget] */ HRESULT STDMETHODCALLTYPE get__NewEnum(
    8. /* [retval][ref][out] */ __RPC__deref_out_opt IEnumVARIANT **ppunk) = 0;
    9. virtual /* [helpstring][id][propget] */ HRESULT STDMETHODCALLTYPE get_Item(
    10. /* [in] */ LONG index,
    11. /* [retval][ref][out] */ __RPC__deref_out_opt BSTR *value) = 0;
    12. virtual /* [helpstring][id][propget] */ HRESULT STDMETHODCALLTYPE get_Count(
    13. /* [retval][ref][out] */ __RPC__out LONG *value) = 0;
    14. virtual /* [helpstring][id][propget] */ HRESULT STDMETHODCALLTYPE get_IsSupportedEnvironment(
    15. /* [retval][ref][out] */ __RPC__out VARIANT_BOOL *value) = 0;
    16. };
    17. #else /* C style interface */
    18. typedef struct IDiscMaster2Vtbl
    19. {
    20. BEGIN_INTERFACE
    21. HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
    22. __RPC__in IDiscMaster2 * This,
    23. /* [in] */ __RPC__in REFIID riid,
    24. /* [annotation][iid_is][out] */
    25. __RPC__deref_out void **ppvObject);
    26. ULONG ( STDMETHODCALLTYPE *AddRef )(
    27. __RPC__in IDiscMaster2 * This);
    28. ULONG ( STDMETHODCALLTYPE *Release )(
    29. __RPC__in IDiscMaster2 * This);
    30. HRESULT ( STDMETHODCALLTYPE *GetTypeInfoCount )(
    31. __RPC__in IDiscMaster2 * This,
    32. /* [out] */ __RPC__out UINT *pctinfo);
    33. HRESULT ( STDMETHODCALLTYPE *GetTypeInfo )(
    34. __RPC__in IDiscMaster2 * This,
    35. /* [in] */ UINT iTInfo,
    36. /* [in] */ LCID lcid,
    37. /* [out] */ __RPC__deref_out_opt ITypeInfo **ppTInfo);
    38. HRESULT ( STDMETHODCALLTYPE *GetIDsOfNames )(
    39. __RPC__in IDiscMaster2 * This,
    40. /* [in] */ __RPC__in REFIID riid,
    41. /* [size_is][in] */ __RPC__in_ecount_full(cNames) LPOLESTR *rgszNames,
    42. /* [range][in] */ __RPC__in_range(0,16384) UINT cNames,
    43. /* [in] */ LCID lcid,
    44. /* [size_is][out] */ __RPC__out_ecount_full(cNames) DISPID *rgDispId);
    45. /* [local] */ HRESULT ( STDMETHODCALLTYPE *Invoke )(
    46. IDiscMaster2 * This,
    47. /* [in] */ DISPID dispIdMember,
    48. /* [in] */ REFIID riid,
    49. /* [in] */ LCID lcid,
    50. /* [in] */ WORD wFlags,
    51. /* [out][in] */ DISPPARAMS *pDispParams,
    52. /* [out] */ VARIANT *pVarResult,
    53. /* [out] */ EXCEPINFO *pExcepInfo,
    54. /* [out] */ UINT *puArgErr);
    55. /* [helpstring][restricted][hidden][id][propget] */ HRESULT ( STDMETHODCALLTYPE *get__NewEnum )(
    56. __RPC__in IDiscMaster2 * This,
    57. /* [retval][ref][out] */ __RPC__deref_out_opt IEnumVARIANT **ppunk);
    58. /* [helpstring][id][propget] */ HRESULT ( STDMETHODCALLTYPE *get_Item )(
    59. __RPC__in IDiscMaster2 * This,
    60. /* [in] */ LONG index,
    61. /* [retval][ref][out] */ __RPC__deref_out_opt BSTR *value);
    62. /* [helpstring][id][propget] */ HRESULT ( STDMETHODCALLTYPE *get_Count )(
    63. __RPC__in IDiscMaster2 * This,
    64. /* [retval][ref][out] */ __RPC__out LONG *value);
    65. /* [helpstring][id][propget] */ HRESULT ( STDMETHODCALLTYPE *get_IsSupportedEnvironment )(
    66. __RPC__in IDiscMaster2 * This,
    67. /* [retval][ref][out] */ __RPC__out VARIANT_BOOL *value);
    68. END_INTERFACE
    69. } IDiscMaster2Vtbl;



    Die Funktion CreateInterface gibt Dir einen Pointer auf ein Interface zurück. Welches Interface es sein soll wird über die IID und CLSID des Interfaces festgelegt. Diese Werte (GUID) stehen ebenfalls in der C++ Headerdatei. Ich habe hier die klassische Methode per API CoCreateInstance gewählt weil der übliche Weg per .NET Funktion Activator.CreateInstance nicht so wollte wie ich das erwartet hätte.

    Die Enum VTable_Interfaces enthält die Funktionsnummern einer Funktion eines Interfaces. Du zählst im Abschnitt "typedef struct [Interface]Vtbl" einfach die Funktionen, beginnend mit 0 (QueryInterface), durch und kommst so z.b. für IDiscMaster2.get_Item auf 8. Hier ist es wichtig das die Funktionsnummer stimmt den sonst liefert Dir die Funktion GetMethodPtr nachher einen Pointer zu einer anderen Interface Funktion zurück.

    Die Funktion GetMethodPtr liefert Dir einen Pointer auf die gesuchte Interface Funktion. Also wenn für pInterface ein Pointer auf das Interface IDiscmaster2 und für intMethodNumber die 8 (get_Item) übergeben wird, bekommst Du den Pointer auf die entsprechende Funktion get_Item aus dem Interface IDiscMaster2 zurück geliefert.

    Die Funktionen in der Region "Interface Functions" führen halt die Funktion eines Interfaces aus. Zuerst holen wir uns vom Pointer auf ein Interface (wird wie in C++ mit "this" angegeben) und der Funktionsnummer (Enum VTable_Interfaces) den Pointer auf die entsprechende Funktion. Diesen Pointer auf die Funktion verbinden wir nun mit dem passenden Delegate der ja die Funktionssignatur darstellt (Marshal.GetDelegateForFunctionPointer) und übergeben die entsprechenden Parameter (this = Pointer auf das Interface das ja die Funktion über den Funktionspointer und somit dem Delegate, bereitstellt).

    Ich hoffe ich habe das halbwegs einfach und verständlich erklärt wie das ganze funktioniert.
    Mfg -Franky-

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „-Franky-“ ()

    @-Franky-

    So bin jetzt endlich mal dazu gekommen, mir das genauer anzuschauen, diese Streaming-Geschichte hat mich mehr in Beschlag genommen als ich dachte, und am Ende lags bloss an meinem Router...
    _____________________________________

    Nun zum Thema:

    Wow also insbesondere der C-Code macht mir echt zu schaffen... Aber ich probiers mal...

    -Franky- schrieb:

    Diese Werte (GUID) stehen ebenfalls in der C++ Headerdatei.


    Wo finde ich denn diese C++ Header Datei?

    -Franky- schrieb:

    Die Enum VTable_Interfaces enthält die Funktionsnummern einer Funktion eines Interfaces


    Wie komme ich dann auf die Funktionsnummer von GetVolumeFreeSectors? Wo muss ich suchen?

    kafffee schrieb:

    Wo finde ich denn diese C++ Header Datei?


    Du kannst den header file herausfinden indem du z.B. nach einen Funktionsnamen suchst, dann bei MS schauen welcher header.

    Beispiel GetWindowrect:
    docs.microsoft.com/en-us/windo…/nf-winuser-getwindowrect
    also winuser.h
    Nun 3 möglichkeiten:

    1->In einem C++ Projekt, include den header, in dieser Zeile ein rechtsklick, dann F12 oder zum Dokument wechseln klicken
    2->Die SDK ordner durchsuchen (winuser.h hab ich in "C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\um\WinUser.h")
    3->Mit google den Header finden, (github winuser.h) github.com/tpn/winsdk-10/blob/…10.0.10240.0/um/WinUser.h
    Bilder
    • Unbenannt.jpg

      41,01 kB, 869×413, 19 mal angesehen
    @kafffee

    @Takafusa hat es treffend beschrieben. Du kannst nach der Funktion über Google suchen. Oder auch nach dem Interface das die Funktion enthält. Jedenfalls steht dann in der MS-Doku in welchem C++ Headerfile das dann zu finden ist. Für Dich ist das die imapi2.h. Die suchst auf Deiner Platte oder über Google auf GitHub wobei Du auf GitHub drauf achten müsstest, das Du die für Windows anklickst. Auf der Platte kannst die Headerdatei auch einfach mit dem Notepad einsehen und darin entsprechende Stellen suchen.

    Und wenn Du über Google mit den passenden Begriffen suchst, läuft Dir auch sowas über den Weg: github.com/awalsh128/IMAPI2

    Edit: Eigentlich hast Du doch die kompletten Interfaces in meinem ursprünglichen Beispiel. Da kannst Dir die entsprechenden Funktionen entnehmen und ein Delegate draus machen (this As IntPtr als ersten Parameter hinzufügen). Da kannst auch gleich die Funktiosnummer auszählen. Die erste Funktion im Interface hat die 3. QueryInterface(0), AddRef(1) und Release(2) vom Interface IUnknown siehst Du da ja nicht.
    Mfg -Franky-

    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „-Franky-“ ()

    -Franky- schrieb:

    Eigentlich hast Du doch die kompletten Interfaces in meinem ursprünglichen Beispiel.


    Hab mir das Projekt mal durchgeschaut, aber hab keine Datei imapi2.h gefunden...

    Was ich gefunden hab:

    VB.NET-Quellcode

    1. <ComImport>
    2. <InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>
    3. <Guid(IID_IDiscFormat2Data)>
    4. Public Interface IDiscFormat2Data


    Also das dürfte das richtige Interface sein oder? Und die GUID Werte find ich dann in der imapi2.h?
    Hab leider weder im Netz noch auf meiner Platte eine imapi2.h gefunden...

    Den C++ Workload hab ich leider auch nicht installiert....

    Das hier hab ich auch gefunden: github.com/awalsh128/IMAPI2
    Aber da hab ich auch vergeblich nach einer imapi2.h gesucht...

    Hat einer von euch die Datei vielleicht auf Platte oder einen Download Link?

    Und die Funktion darin:

    VB.NET-Quellcode

    1. <PreserveSig> Function get_FreeSectorsOnMedia(<Out> ByRef value As Integer) As Integer


    Einfach dann bis zu dieser Funktion hochzählen, hab ich das richtig verstanden? Das dürfte dann 18 sein, sehe ich das richtig?
    @kafffee Die imapi2.h hab ich mir irgendwann mal aus dem Internet gezogen. Sollte aber reichen. Ich kann leider gerade mein Visual Studio nicht starten um da nochmal nachzuschauen. Die Funktion get_FreeSectorsOnMedia gibt es in verschiedenen IMAPI2 Interfaces. Da Du hauptsächlich von Audio gesprochen hast, müsstest dann die Funktion aus dem IDiscFormat2TrackAtOnce Interface nehmen.
    Dateien
    • imapi2.h.txt

      (327,72 kB, 7 mal heruntergeladen, zuletzt: )
    Mfg -Franky-
    @Takafusa Die 3 Dateien dürften die imapi.h, die imapi2.h und die imapi2fs.h sein. Die imapi.h enthält ältere Interfaces mit den nan nur CDs (keine DVDs) und AudioCDs brennen kann. Die imapi2fs.h enthält zusätzliche Interfaces für die Interfaces in imapi2.h. Die braucht man wenn man zB. eine DatenCD brennen/auslesen oder auch wenn man ISO Dateien erstellen oder brennen möchte.
    Mfg -Franky-
    @-Franky-

    Nein sind alles imapi2.h, sind aus verschiedenen Windows 10 SDK Versionen, sind 3 bei mir vorhanden. Wobei ich laut VS Installer nur 2 Windows 10 SDKs installiert hab. Daher hab ich inner ZIP die Ordnerstruktur beibehalten, so kann Kaffee bei Bedarf feststellen(Ordnername) was aus welcher SDK Version stammt.

    Müsste man mal die Hashes berechnen, um festzustellen ob die Dateien identisch sind, aber da war ich zu faul für.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Takafusa“ ()

    Hi

    Ah, ok. :) Habn wa trotzdem was dazu gelernt. ;) Ich glaub da gibt es so gut wie keine Unterschiede. Wenn dann würde ich sowieso die aus dem neuesten SDK nehmen.
    Mfg -Franky-

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „-Franky-“ ()

    So ich hab mal gemacht soweit ich gekommen bin:

    (1) In der Region Enums GetVolumeFreeSectors zugefügt
    (2) In der Region Const die zwei Konstanten mit CLSID und IID zugefügt
    (3) In der Region Structure eine Variable dafür zugefügt
    (4) In der Region Properties eine entsprechende Eigenschaft zugefügt
    (5) In der Region Interface Delegates eine Funktion zugefügt
    (6) In der Region Interface Functions eine Funktion zugefügt

    >> bei (6) in Zeile 367 (hier 3) zeigt er mir leider folgenden Laufzeitfehler an:

    System.Runtime.InteropServices.MarshalDirectiveException
    HResult=0x80131535
    Nachricht = "parameter #2" kann nicht gemarshallt werden: Ungültige verwaltete/nicht verwaltete Typenkombination (Int32/UInt32 muss mit I4, U4 oder "Error" kombiniert werden)..
    Quelle = mscorlib

    VB.NET-Quellcode

    1. If CType(Marshal.GetDelegateForFunctionPointer(pMethodPtr,
    2. GetType(IDiscRecorder2_GetVolumeFreeSectors)),
    3. IDiscRecorder2_GetVolumeFreeSectors)(this, value) = S_OK Then Return value


    Damit kann ich so nichts anfangen... Kann mir jemand auf die Sprünge helfen?

    Gruss, kafffee

    PS: Anbei das Testprogramm
    Dateien
    • VBN_IMAPI2.zip

      (45,96 kB, 4 mal heruntergeladen, zuletzt: )
    Hi

    Ich glaub da bist ein wenig durcheinander gekommen. IDiscRecorder2 besitzt so eine Funktion nicht. IDiscFormat2TrackAtOnce hat eine Funktion get_FreeSectorsOnMedia. Marshallen musst da nichts.

    Ich komme erst am Montag wieder dazu deinen Code zu checken.
    Mfg -Franky-
    Ja das kann gut sein. Ich guck mirs nochmal an

    Edit:

    Also hab mirs nochmal angeschaut. Ich glaube ich hab da bloss die Funktion falsch benamt, weil ich mir nicht sicher war. Hab da ein bisschen geraten an der Stelle muss ich zugeben. Die Werte für CLSID, IID sollten richtig sein...

    Vom Marshallen hab ich gar keine Ahnung...

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „kafffee“ ()

    @kafffee So, mal fix über Deinen Code geschaut und meine Vermutung war schon richtig. Ach weiste was, da ich weis das das, wenn man damit noch nie zu tun hatte, schon sehr verwirrend sein kann. Daher, bitte schön. :) Hoffe da ist alles drin was Du benötigst. Ist jetzt auch nur auf die schnelle zusammen gebastelt und müsstest evtl. an Deine Bedürfnisse anpassen.
    Dateien
    • VBN_IMAPI2.zip

      (16,89 kB, 3 mal heruntergeladen, zuletzt: )
    Mfg -Franky-

    Neu

    @-Franky-

    Hey Franky recht herzlichen Dank :thumbsup:

    Ich hab mich jetzt dazu entschlossen, die Anzahl an freien Bytes als Property der DLL zur Verfügung zu stellen, sodass der User ein bisschen freie Hand hat, wie er die Länge bzw. Grösse der Audiodaten denn nun ermittelt...

    Da hast die Zahl der Sektoren ja einfach durch 512 geteilt und hast dann die Größe in MB ausgegeben. Hat dann halt so einige Nachkommastellen...

    Jetzt meine Frage:

    Ich hab bisschen recherchiert, wieviel Bytes denn nun ein Sektor hat und bin auf widersprüchliche Aussagen gestossen...: 3234 oder auch 2048... Beides zum Thema CD-ROM

    Macht es denn einen Unterschied, ob Audio oder Daten-CD? Bei der Audio-CD gibt es ja glaube ich noch so einen TZOC (Table Of Contents).... Und wenn man sich für eine Daten-CD entscheidet, kann man dann 1:1 die Dateigrösse der Daten auf HDD zu der Grösse der Daten auf der CD nehmen?

    Dass es bei DVDs anders sein wird brauch ich wohl nicht zu fragen...

    Ich hab das Ganze mal ausgiebig getestet und bin zu dem Schluss gekommen:

    Man kann Audio- oder MP3-CDs brennen und man kann MP3-DVDs brennen...

    Neu

    kafffee schrieb:

    Hat dann halt so einige Nachkommastellen...

    In meinem anderen Testcode gebe ich das ganze per Math.Ceiling aus und komme auf folgendes bei einer neuen CD:
    TotalSectorsOnMedia: 359999 Sectors = 704 MB
    FreeSectorsOnMedia: 359847 Sectors = 703 MB
    UsedSectorsOnMedia: 0 Sectors = 0 MB

    Kommt auch gut hin bei einer 700MB CD-Rohling. Ein bissel mehr geht immer denke ich. CDBurnerXP gibt im Prinzip das gleiche aus wenn man eine AudioCD brennen wollte (siehe Screenshot). Von daher passt das schon. Mit anderen Medien (DVDs usw) müsste man das mal testen ob da die Werte passen. Hab nur keine DVD Rohlinge mehr.

    Ja die TOC brauch wohl auch ein bissel was an Platz auf der CD. Frag mich aber nicht wieviel. Die TOC dürfte aber nicht so groß sein. So tief hab ich mich in das Thema IMAPI2 bzw. im allgemeinen zum Thema brennen von CDs/DVDs nicht reingearbeitet. Aber da sollte man doch Infos zu finden.

    Edit: Eine AudioCD kann wohl im Standard max. 74min Audio enthalten und mit ein wenig überbrennen, kommt wohl auch auf den Brenner an ob der das Unterstützt, wohl bis max. 80min. Vllt kann man ja die 74min als Wert nehmen der die die gesamtlänge der zu brennenden Songs nicht überschreiten sollte.
    Bilder
    • CDBurnerXP.png

      18,11 kB, 420×467, 6 mal angesehen
    Mfg -Franky-

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „-Franky-“ ()

    Neu

    So nach einem halben Jahr ist die DLL nun endlich fertig. Hab mich nun doch zu einer Property, die die Anzahl Sektoren und nicht die Anzahl Bytes ausgibt, entschieden, einfach aus dem Grund dass das aufrufende Programm wohl besser weiss, welches Medium eingelegt ist, und wie lange die Tracks (wenn man eine Audio-CD brennt) in Minuten/Sekunden dauern...
    Hierzu kann man, wenn man das WMP Control eh schon nutzt, die Property WMP.CtlControls.currentItem.duration benutzen, um die Länge eines Tracks zu ermitteln. Dann sollte man noch einen kleinen Puffer für das TOC und die Zeit zwischen den Tracks einbauen, wenn es eine Audio-CD sein soll...

    Das Ganze ist eigentlich selbsterklärend, aber ich reisse es trotzdem mal kurz an:

    -Public Function CDBurners gibt eine Auflistung der Laufwerksbuchstaben zurück, die zum Brennen geeiginet sind (also z.B: D:)
    -Public Function GetFreeSectors gibt die auf dem eingelegten Medium freien Sektoren zurück. Als Argument muss man den Laufwerksindex übergeben, ich gehe mal stark davon aus, dass z.B. D: >> Index 0 und E: >> Index 1 sein würde, also dass das Ganze alphabetisch ist...
    -Public Function BurnCD startet dann das eigentliche Brennen. Als Argumente übergibt man:
    -Playlist: DIe einzelnen zu brennenden Tracks als Dateinamen
    -CDDrive: Den Laufwerksbuchstaben des Brenners, also z.B. D:
    -BurnAsMP3CD: True, wenn man eine MP3- bzw. Daten-CD/DVD brennen möchte
    -CDName: So wird die CD dann benannt
    -Es gibt ausserdem 3 Events:
    -NewBurnStatus Wenn sich der Brennstatus ändert
    -NewNotification Wenn es eine Programmmeldung gibt, dieses kann z.B. dazu verwendet werden, um die Property Statusmeldung in einer MessageBox auszugeben
    -NewProgress Wenn sich der Brennfortschritt ändert, kann man dazu benutzen, um die Property Fortschritt z.B. in einer TrackBar anzuzeigen

    Wichtig: Als Verweise braucht die DLL die AxInterop.WMPLib.dll und die Interop.WMPLib.dll

    @Elephant

    Wenn du noch Fragen hast, kannst dich gerne bei mir/uns melden....

    Ansonsten wünsch ich euch viel Spass mit der DLL, hat mich gefreut :)

    Hab alles schon durchgetestet, bis auf die GetFreeSectors, sollte aber funktionieren...

    Edit: Ich hatte noch vergessen zu erwähnen:

    Wenn das Brennen abgschlossen ist (erkennt man am Brennstatus) sollte noch WrapUp aufgerufen werden

    Edit2: Ich hab in der GetFreeSectors doch noch ein Bug entdeckt, ist aber korrigiert und Update ist in Post 64 dann zum Download erhältlich und in Kürze auch im Sourcecode-Austausch...

    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „kafffee“ ()