vb@rchiv
VB Classic
VB.NET
ADO.NET
VBA
C#

https://www.vbarchiv.net
Rubrik: Allgemein   |   VB-Versionen: VB5, VB601.03.04
Erstellen von TypeLib's

Dieser Workshop befasst sich mit dem Erstellen von Type-Libraries, sogenannten TypeLib's (TLB). Anhand eines einfachen Beispiels wird gezeigt, wie man beispielsweise den Root-Pfad im Ordner-Auswahl-Dialog individuell festlegen kann, so dass sich Ordner nur unterhalb des angegebenen Root-Verzeichnisses auswählen lassen.

Autor:  Ralf SchoenBewertung:  Views:  28.638 

Dieser Workshop befasst sich mit dem Erstellen von Type-Libraries, sogenannten TypeLib's (TLB). Anhand eines einfachen Beispiels wird gezeigt, wie man beispielsweise den Root-Pfad im Ordner-Auswahl-Dialog individuell festlegen kann, so dass sich Ordner nur unterhalb des angegebenen Root-Verzeichnisses auswählen lassen.

API ist nicht alles.....

Wollen wir ein wenig mehr aus VB herausholen und die Interfaces nutzen die uns zur Verfügung stehen, dann sollte man sich mit der Erstellung von Type Libraries auseinandersetzen. 

Wofür braucht man diese TypLib´s ? 

TypLib´s können Deklarationen, Definitionen, Aufzählungen sowie Schnittstellen-Definitionen enthalten. Sobald wir eine Referenz auf so eine TypLib erstellt haben, können wir diese Informationen in dem Objekt-Betrachter sehen und uns stehen diese Definitionen zur Verfügung. 

Um eine TypLib erstellen zu können, brauchen wir einen GUID Generator (UUID.EXE )und das Programm MKTYPLIB.EXE. Diese werden bei der Installation von Visual C++ mitgeliefert. Ein von mir entwickelter GUID Generator liegt dem Beispiel Projekt bei. Auch von Vorteil ist der Einsatz von OLEVIEW. Wer ihn nicht hat, kann ihn sich unter   www.microsoft.com/com besorgen. 

In diesem Workshop werden wir eine TypLib erstellen, die wir benötigen, um bei BrowseForFolder den Anfangspfad einstellen zu können. (Dies war eine Frage aus dem Forum.) 

....We bring the Full Power to our Apps.....

TypLib´s werden in einer Scripting Sprache geschrieben, der IDL (Interface Definiton Language). IDL ist eine sehr mächtige Sprache, aber da wir uns in einer Umgebung von Visual Basic bewegen, können wir hier nicht alles ausnutzen. Doch schon die Basic´s reichen für uns völlig aus. Normalerweise werden TypLib's mit dem Tool MIDL von Microsoft compiliert. Dieses Tool wird jedoch nicht mit VB ausgeliefert. Aber es gibt ja noch das MKTYPLIB. MKTYPLIB ist ein ODL (Object Definition Language) Compiler. Der Unterschied zwischen diesen beiden Compilern liegt darin, das ODL eine Teilmenge von IDL ist und daher auch nicht vollwertig mit IDL. 

Der Aufbau einer TypLib....

[
    uuid([UUID]),
    version(1.0),
    helpstring("vbArchiv SF_ParseDisplayName")
]
library [LibraryName]
{
           // Libraries die eingebunden werden sollen 
           importlib("stdole2.tlb");

uuid ist unsere neue GUID die wir nun erstellen müssen. Entweder über UUID oder über das kleine Tool welches ich mitgeliefert habe. Version und helpstring sollte klar sein. Wer sich die stdole2.tlb einmal genauer anschauen möchte, der kann dieses über das Programm OLEVIEW tun. Gefunden wird diese TLB unter TypeLibraries und dann OLE Automation. Über diese TypLib wird auch das IUNKNOWN Interface mit eingebunden, darum brauchen wir uns also nicht mehr zu kümmern.

LibraryName ist der Name unserer Library . Denken Sie sich einen schönen aus  

Weiter geht's ... 

   // Ab hier beginnt der Inferface Block 
   // Interfaces die sich in dieser TypLib befinden 
   interface IShellFolder

    // Typen Deklaration
    typedef unsigned char BYTE;
    typedef long LPITEMIDLIST;
    typedef long LPTSTR;
    typedef long ULONG;
    typedef short USHORT;

    typedef struct SHITEMID         
    {
	USHORT      cb;          
	BYTE        abID[1];    
    } SHITEMID;
    
    typedef struct ITEMIDLIST       
    {
       SHITEMID    mkid;
    } ITEMIDLIST;

Jetzt kommt das Interface IShellFolder. Wir geben diesem Interface den Namen SF_ParseDisplayName. Normalerweise würde hier nur IShellfolder Interface stehen. 

[
        uuid(000214e6-0000-0000-c000-000000000046),
        helpstring("SF_ParseDisplayName Interface"),
        odl
    ]

    interface IShellFolder : IUnknown
    {
     [helpstring("ParseDisplayName")]
       long    ParseDisplayName(
                [in]     long          hwndOwner,        
                [in]     long          pbcReserved,     
                [in]     LPSTR        lpszDisplayName, 
                [in,out] ULONG         *pchEaten,        
                [in,out] LPITEMIDLIST  *ppidl,           
                [in,out] ULONG         *pdwAttributes);  
    };	
}

Dies ist die gesamte TypLib die man braucht, um z. B. die Funktion ParseDisplayName im Interface IShellFolder benutzen zu können. Natürlich ist das Interface IShellFolder in Wirklichkeit viel größer, doch für unsere Zwecke reicht es erstmal. Deshalb auch der andere Name. 

Viele werden sich jetzt fragen, ja schön, aber wie komme ich an diese Informationen? Über die Uuid wird das Interface identifiziert. Hier muss man ein wenig suchen. MSDN, OLEVIEW oder die Registry werden Abhilfe schaffen. Der Parameter ODL ist für mktyplib notwendig, hat aber weiter keine Bedeutung. 

Dann noch ein Blick in die MSDN. Unter IShellFolder:

Inherits from : IUnknown   

Ok, haben wir gemacht... interface IShellFolder: IUnknown bedeutet also nichts anderes als definiere mir das Interface IShellFolder welches sich von IUnknown ableitet. 

Schauen wir uns die ParseDisplayName an...

HRESULT ParseDisplayName( 
    HWND hwnd,
    LPBC pbc,
    LPOLESTR pwszDisplayName,
    ULONG *pchEaten,
    LPITEMIDLIST *ppidl,
    ULONG *pdwAttributes
);

Dies sind alle Informationen die wir brauchen um dieses Interface einzubinden. Ob es sich um einen in, out oderin,out handelt steht in der Beschreibung der einzelnen Parameter.

Nun haben wir alles, um die TypLib erstellen zu können. Hierzu verwenden wir MKTYPLIB.EXE ...

Der Parameter /nocpp muss verwendet werden wenn kein C preprozessor installiert ist, was wohl bei den meisten der Fall sein wird. Damit kann man dann auch keine preprozessor Direktiven verwenden.

Mktyplib /nocpp test.odl

Nun sollte eine Meldung erscheinen die uns sagt, das die TypLib erfolgreich erstellt worden ist. Wenn dies der Fall ist, befindet sich im selben Verzeichnis wie die .odl eine neue Datei mit der Extension .tlb. Herzlichen Glückwunsch, soeben haben Sie ihre erste TypLib erstellt.

Wie bindet man nun diese TypLib in VB ein?

Eine TypLib muss auch beim System angemeldet (registriert) werden. Dafür gibt es auch kleine Tools die uns diese abnehmen (z.B. regtlib ist ein solch kleiner Helfer). Unter VB können wir uns das aber sparen. Gehen Sie unter Projekte->Verweise und dann auf den Button "Durchsuchen". Wechseln Sie in das Directory wo sich die TypLib befindet und wählen Sie diese aus. Ab nun steht die TypLib zur Verfügung. 

Wenn wir nun einmal den Objekt Browser öffnen, sehen wir, dass uns die Schnittstelle die wir eben erstellt haben zur Verfügung steht. 

BrowseForFolder - Root festelegen... 

Nun wollen wir auch unsere TypLib einsetzen und dazu schauen wir uns die Struktur von BrowseForFolder einmal genauer an.

Private Declare Function SHBrowseForFolder Lib "shell32" _
  Alias "SHBrowseForFolderA" ( _
  lpbi As BROWSEINFO) As Long

Die Funktion SHBrowseForFolder verlangt als Übergabe die BROWSEINFO Struktur:

Private Type BROWSEINFO
   hwndOwner As Long
   pIDLRoot As Long
   pszDisplayName As Long
   lpszTitle As Long
   ulFlags As Long
   lpfnCallback As Long
   lParam As Long
   iImage As Long
End Type

Dort sehen wir, dass es eine pIDLRoot gibt. In der MSDN steht, Pointer auf die Item Identifier List (PIDL) welche auf den Root Folder zeigt. Wenn man hier nichts angibt, dann ist der Startpunkt der Desktop. (Root vom ShellNamespace) 

Ok, versuchen wir erst mal, ein wenig mehr über die PIDL zu erfahren und was wir wirklich brauchen um den Root festzulegen. Es gibt eine Funktion SHGetSpecialFolderLocation, die uns eine PIDL zurückliefert.

Private Declare Function SHGetSpecialFolderLocation Lib "shell32.dll" ( _ 
  ByVal hwndOwner As Long, _
  ByVal nFolder As Long, _
  pidl As ITEMIDLIST) As Long

Unter nFolder gibt man dann die CSIDL an. Eine CSIDL ist eine eindeutige Nummer, die auf jedem System gleich ist. Die Location kann aber eine andere sein. Auf einigen System gibt es halt c:\Windows und auf anderen c:\WinNT. Mit der CSIDL "CSIDL_WINDOW" würde man dann diesen Pfad erhalten. 

Hier die CSIDL´s die es so gibt.

 CSIDL_FLAG_CREATE = &H8000
 CSIDL_ADMINTOOLS = &H30
 CSIDL_ALTSTARTUP = &H1D
 CSIDL_APPDATA = &H1A
 CSIDL_BITBUCKET = &HA
 CSIDL_CDBURN_AREA =  &H3B
 CSIDL_COMMON_ADMINTOOLS = &H2F
 CSIDL_COMMON_ALTSTARTUP = &H1D
 CSIDL_COMMON_APPDATA = &H23
 CSIDL_COMMON_DESKTOPDIRECTORY = &H19
 CSIDL_COMMON_DOCUMENTS = &H2E
 CSIDL_COMMON_FAVORITES = &H1F
 CSIDL_COMMON_MUSIC = &H35
 CSIDL_COMMON_PICTURES = &H36
 CSIDL_COMMON_PROGRAMS = &H17
 CSIDL_COMMON_STARTMENU = &H16
 CSIDL_COMMON_STARTUP = &H18
 CSIDL_COMMON_TEMPLATES = &H2D
 CSIDL_COMMON_VIDEO = &H37
 CSIDL_CONTROLS = &H3
 CSIDL_COOKIES = &H21
 CSIDL_DESKTOP = &H0
 CSIDL_DESKTOPDIRECTORY = &H10
 CSIDL_DRIVES = &H11
 CSIDL_FAVORITES = &H6
 CSIDL_FONTS = &H14
 CSIDL_HISTORY = &H22
 CSIDL_INTERNET = &H1
 CSIDL_INTERNET_CACHE = &H20
 CSIDL_LOCAL_APPDATA = &H1C	
 CSIDL_MYDOCUMENTS = &HC
 CSIDL_MYMUSIC  = &HD
 CSIDL_MYPICTURES = &H27
 CSIDL_MYVIDEO = &HE
 CSIDL_NETHOOD = &H13
 CSIDL_NETWORK = &H12
 CSIDL_PERSONAL = &H5
 CSIDL_PRINTERS = &H4
 CSIDL_PRINTHOOD = &H1B
 CSIDL_PROFILE = &H28
 CSIDL_PROGRAM_FILES = &H26
 CSIDL_PROGRAM_FILES_COMMON = &H2B
 CSIDL_PROGRAMS = &H2
 CSIDL_RECENT = &H8
 CSIDL_SENDTO = &H9
 CSIDL_STARTMENU = &HB
 CSIDL_STARTUP = &H7
 CSIDL_SYSTEM = &H25
 CSIDL_TEMPLATES = &H15
 CSIDL_WINDOWS = &H24

Wofür diese einzelnen CSIDL´s stehen und was dabei zu beachten ist, kann man unter  http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/shell/reference/enums/csidl.asp nachlesen. Würde man nun diese pidl, die uns durch die Funktion SHGetSpecialFolder zurückgeliefert wurde, bei pIDLRoot übergeben, würde das Startverzeichnis geändert.

Wie man sieht, gibt es eine Möglichkeit die PIDL eines bestimmen Ordners zu bekommen. Aber wie sieht es mit einem Path aus der nicht in der CSIDL Auflistung steht. Also schauen wir uns noch mal die IShellFolder an. Dort gibt es die ParseDisplayName und deren Beschreibung lautet:übersetzt den Directory Namen in eine IDL, und genau das brauchen wir ja auch.

PIDL:

Wenn man sich die Shell Funktionen anschaut, findet man dort eine SHSimpleIDListFromPath. Hey, eine IDL von einem Path? Genau das ist es doch, oder nicht? Warum Simple? 

Es gibt zwei Arten von PIDL Eine sogenannte Simple PIDL und eine Komplex PIDL. Der Unterschied liegt darin, das eine Simple PIDL nur auf eine ITEMIDLIST zeigt und die Komplex PIDL auf mehrere. (Man könnte es auch als ein Array betrachten) Und da wir keine Simple brauchen, können wir auch nicht auf diese Funktion zurückgreifen. Näher gehe ich jetzt nicht auf die beiden Unterschiede ein, da es sonst den Workshop ein wenig sprengen würde. 

Also kommt jetzt doch die TypLib zum Einsatz. 

Nachdem wir oben bereits die ParseDisplayName in unserer TypLib eingebunden haben, wollen wir uns noch kurz die Parameter anschauen:

Hwnd

Hier übergeben wir das Handle unserer Anwendung

pbc

Hier der Context von dem aus wir suchen wollen. Hier kann auch ein Null-Wert übergeben werden. Odereben eine PIDL

pwszDisplayName

Hier geben wir den Pfad an, den wir suchen (UNICODE)

pchEaten

Gibt die Anzahl der Zeichen vom DisplayName zurück. Wenn man diese Info nicht braucht, kann auch ein NULL-Wert übergeben werden)

ppidl

Hier kommt unsere PIDL zurück (Komplex) (ITEMIDLIST)

pdwAttributes

Hier können wieder verschiede Attribute gesetzt werden. Infos gibt es in der MSDN

Ok, dann haben wir ja alles zusammen um diese Funktion endlich mal zu testen. 

Zuerst erstellen wir ein Projekt (Standard - EXE), das nur dazu verwendet wird um den BrowseForFolder Dialog aufzurufen. Dann erstellen wir noch eine ActiveX.DLL. Die Eingenschaften der DLL können alle so bleiben wie sie sind. 

Fangen wir mit der DLL an. Deklarationen, Konstanten und API Aufrufe die wirbrauchen:

Private Const MAX_PATH = 260
 
Private Type BROWSEINFO
    hwndOwner As Long
    pidlRoot As Long
    pszDisplayName As Long
    lpszTitle As Long
    ulFlags As Long
    lpfn As Long
    lParam As Long
    iImage As Long
End Type
 
' Shell
Private Declare Function SHBrowseForFolder Lib "shell32.dll" _
  Alias "SHBrowseForFolderA" ( _ 
  lpbi As BROWSEINFO) As Long
 
Private Declare Function SHGetSpecialFolderLocation Lib "shell32.dll" ( _ 
  ByVal hwndOwner As Long, _
  ByVal nFolder As Long, _
  pidl As Long) As Long
 
Private Declare Function SHGetDesktopFolder Lib "shell32.dll" ( _
  ppshf As IShellFolder) As Long
 
Private Declare Function SHGetPathFromIDList Lib "shell32.dll" _
  Alias "SHGetPathFromIDListA" ( _
  ByVal pidl As Long, _
  ByVal pszPath As String) As Long
 
Private Declare Function GetFullPathName Lib "kernel32" _
  Alias "GetFullPathNameA" ( _ 
  ByVal lpFileName As String, _
  ByVal nBufferLength As Long, _
  ByVal lpBuffer As String, _
  ByVal lpFilePart As String) As Long
 
' BROWSEINFO Values
Private Const BIF_RETURNONLYFSDIRS = &H1
Private Const BIF_RETURNFSANCESTORS = &H8
 
' andere
Private Declare Sub CopyMemoryLpToStr Lib "kernel32" _
  Alias "RtlMoveMemory" ( _ 
  ByVal lpvDest As String, _
  lpvSource As Long, _
  ByVal cbCopy As Long)
 
Private Declare Function lstrlenptr Lib "kernel32" _
  Alias "lstrlenA" ( _
  ByVal lpString As Long) As Long
 
Private Declare Function lstrlen Lib "kernel32" _
  Alias "lstrlenA" ( _
  ByVal lpString As String) As Long
 
' Members
Private m_OwnerHwnd As Long
Private m_sRootDir As String
 
' hier unsere pidlRoot
Private m_pidlRoot As Long

Jetzt kommen einfach nur zwei Properties die wir benutzen um von der Standard-EXE zwei Werte zusetzen:

Public Property Get OwnerHwnd() As Long
   OwnerHwnd = m_OwnerHwnd
End Property
 
Public Property Let OwnerHwnd(ByVal lOwnerHwnd As Long)
   m_OwnerHwnd = lOwnerHwnd
End Property
Public Property Get RootDir() As String
   RootDir = m_sRootDir
End Property
 
Public Property Let RootDir(ByVal sDir As String)
   m_sRootDir = sDir
End Property

Sollte nicht der großen Aufklärung benötigen, was hier passiert. OwnerHwnd ist eben die hwnd unserer Form, die den Dialog aufruft und RootDir ist der Pfad, den wir als Root haben wollen.

Modifizierte BrowseForFolder-Funktion

Nun kommt die BrowseForFolder Function, die wir aufrufen um den Dialog anzuzeigen und die uns den ausgewählten Pfad wieder zurückgibt:

Public Function BrowseForFolder() As String
  Dim tBrowseInfo As BROWSEINFO
 
  ' Titel der angezeigt werden soll
  Dim sTitle As String
  ' sollte klar sein
  Dim pidlRoot As Long
  ' hier kommt die pidl rein die wir von BFF zurückbekommen
  Dim pidlFromBFF As Long
  ' Verzeichnis, das ausgewählt wurde, nicht der komplette Path
  Dim m_sDisplayName As String
 
  ' in Unicode umwandeln
  sTitle = StrConv("Ordern wählen", vbFromUnicode)
 
  ' Platz schaffen
  m_sDisplayName = String$(MAX_PATH, 0)
 
  ' wird übergeben m_OwnerHwnd
  tBrowseInfo.hwndOwner = m_OwnerHwnd
 
  ' hier müssen wir auf StrPtr zurückgreifen
  ' da wir einen Pointer brauchen
  tBrowseInfo.pszDisplayName = StrPtr(m_sDisplayName)
 
  ' ebenfalls einen Pointer übergeben
  tBrowseInfo.lpszTitle = StrPtr(sTitle)
 
  ' Flags setzen
  tBrowseInfo.ulFlags = BIF_RETURNONLYFSDIRS Or BIF_RETURNFSANCESTORS
  tBrowseInfo.iImage = 0
 
  ' hier jetzt schauen, ob wir eine andere pidl brauchen
  If Len(m_sRootDir) <> 0 Then
    pidlRoot = GetPidlFromPath(m_sRootDir)
  Else
    pidlRoot = 0
  End If
 
  ' root setzen
  tBrowseInfo.pidlRoot = pidlRoot
 
  ' Dialog aufrufen
  pidlFromBFF = SHBrowseForFolder(tBrowseInfo)
 
  ' wenn man nur den Namen des ausgewählten Verzeichnisses braucht
  m_sDisplayName = PointerToString(tBrowseInfo.pszDisplayName)
 
  ' hier jetzt den Pfad holen, der sich in der zurückgegebenen
  ' PIDL befindet
  BrowseForFolder = PathFromPidl(pidlFromBFF)
 
End Function

Die Kommentare im Source sollten für diese Routine ausreichen. Wichtig ist, dass lpzDisplayName und lpszTitle als Pointer übergeben werden müssen. Dann natürlich noch worauf wir alle gewartet haben: Die Funktion GetPidlFromPath(m_sRootDir) 

Private Function GetPidlFromPath(sPath As String) As Long
   Dim folder As IShellFolder
   Dim ppidl As Long
   Dim pchEaten As Long
   Dim pAtt As Long
   Dim lFilePos As Long
   Dim sReturn As String
   Dim lenPath As Long
 
   sReturn = String$(MAX_PATH, 0)
   lenPath = GetFullPathName(sPath, MAX_PATH, sReturn, lFilePos)
   If lenPath <> 0 Then
      sPath = Left$(sReturn, lenPath)
      Set folder = GetDesktopFolder
      If folder.ParseDisplayName(0&, 0&, StrConv(sPath, vbUnicode), pchEaten, ppidl, pAtt) = 0 Then
         GetPidlFromPath = ppidl
      End If
   End If 
End Function

Hier sehen wir jetzt, dass unsere TypLib zum Einsatz kommt:

Dim folder as IShellFolder

Jetzt noch ein gültiges Objekt erzeugen:

Set folder = GetDesktopFolder

Und nun können wir auf die ParseDisplayName zugreifen. Hier auch wieder darauf achten, dass der Path als Unicode erwartet wird. Bei Erfolg wird uns eine pidl zurückgeliefert. Das war es auch schon. Diese wird dann der pidlRoot zugewiesen. Hier müssen keine Konvertierungen mehr vorgenommen werden, da es sich hier schon um Pointer handelt. Wir erinnern uns: eine PIDL ist ein Pointer.

Die GetDesktopFolder liefert uns die IShellFolder für den Desktop zurück. Wie wir ja wissen: der Root vom ShellNamespace.

Private Function GetDesktopFolder() As IShellFolder
  Dim lR As Long
  lR = SHGetDesktopFolder(GetDesktopFolder)
End Function

Dann noch zwei Hilfsfunktionen:

Private Function PointerToString(Pointer As Long) As String
  Dim lLen As Long
  Dim sReturn As String
 
  lLen = lstrlenptr(Pointer)
  sReturn = String$(lLen, 0)
  CopyMemoryLpToStr sReturn, ByVal Pointer, lLen
  PointerToString = sReturn
End Function
Private Function PathFromPidl(ByVal pidl As Long) As String
  Dim sPath As String
  Dim lR As Long
 
  sPath = String$(MAX_PATH, 0)
  lR = SHGetPathFromIDList(pidl, sPath)
  If lR <> 0 Then
    PathFromPidl = Left$(sPath, lstrlen(sPath))
  End If
End Function

Jetzt noch der Aufruf aus unserem Standard Projekt. Dort befindet sich nur ein Button über den wir den Dialog aufrufen und eine TextBox in der wir den RootPfad eintragen können. Nicht vergessen, den Verweis auf unsere DLL hinzuzufügen. (Projekt->Verweise)

Option Explicit
 
Private BFF As cBFF
 
Private Sub Command1_Click()
  Dim s As String
 
  BFF.OwnerHwnd = Me.hWnd
  ' hier den Root Path setzen
  BFF.RootDir = txtPfad
  s = BFF.BrowseForFolder
 
  MsgBox s
End Sub
 
Private Sub Form_Load()
  Set BFF = New cBFF
End Sub

Das war es dann auch schon .......



Anzeige

Kauftipp Unser Dauerbrenner!Diesen und auch alle anderen Workshops finden Sie auch auf unserer aktuellen vb@rchiv  Vol.6
(einschl. Beispielprojekt!)

Ein absolutes Muss - Geballtes Wissen aus mehr als 8 Jahren vb@rchiv!
- nahezu alle Tipps & Tricks und Workshops mit Beispielprojekten
- Symbol-Galerie mit mehr als 3.200 Icons im modernen Look
Weitere Infos - 4 Entwickler-Vollversionen (u.a. sevFTP für .NET), Online-Update-Funktion u.v.m.
 
 
Copyright ©2000-2024 vb@rchiv Dieter OtterAlle Rechte vorbehalten.


Microsoft, Windows und Visual Basic sind entweder eingetragene Marken oder Marken der Microsoft Corporation in den USA und/oder anderen Ländern. Weitere auf dieser Homepage aufgeführten Produkt- und Firmennamen können geschützte Marken ihrer jeweiligen Inhaber sein.