[Excel] VBA: Ini via API lesen, Typen unverträglich

amn.ssy

Erfahrenes Mitglied
Hallo,

Um automatisiiert aus anderen Excelfiles bestimmte Spalten an eine "Sammelmappe" zu übertragen habe ich mir folgendes gebastelt, was mit den Abfragen via Inputbox sehr gut funktioniert.
Da in manchen Fällen die Eingabe routinemäßig immer die gleichen sind möchte ich in den jeweiligen Verzeichnissse Ini-Dateien hinterlegen, die dann ggf. erst gelesen werden.
Zum Testen möchte ich erstmal einen Wert lesen und laufe promt in eine Typenunverträglichkeit rein, die es mir bislang nicht gelang auf zu lösen. Hat jemand 'ne Idee - wo ist mein Fehler?

Tabelle1:
Visual Basic:
Sub CopySource()
Dim lLRowS As Long
Dim lLRowD As Long
Dim sColS As String
Dim sColD As String
Dim iShNumS As Integer
Dim iNum As Integer
Dim i As Integer

ReadIni

    'iNum = Application.InputBox(Prompt:="", Title:="Number of Files?", Type:=1)
    
    For i = 1 To iNum

        'iShNumS = Application.InputBox(Prompt:="", Title:="Sheetnumber Source?", Type:=1)
        'sColS = Application.InputBox(Prompt:="", Title:="Column Source?", Type:=2)
        'sColD = Application.InputBox(Prompt:="", Title:="Column Destination?", Type:=2)
    
        lLRowD = Cells(Rows.Count, sColD).End(xlUp).Row
    
            file = Application.GetOpenFilename()
                If file = False Then Exit Sub
            Workbooks.Open file
    
        With Worksheets(iShNumS)
            lLRowS = .Cells(Rows.Count, sColS).End(xlUp).Row
            .Range(sColS & "2:" & sColS & lLRowS).Copy ThisWorkbook.ActiveSheet.Range(sColD & lLRowD + 1)
            ActiveWorkbook.Close False
        End With
        
    Next i
    
End Sub

Sub ReadIni()
    config$ = "C:\Users\xxx\Desktop\xxx_auto\config.ini"
    If Dir$(config$, vbNormal) <> "" Then
        iNum = GetIniString(config$, "NumFiles", "numf", 0)
        iShNumS = GetIniString(config$, "NumSheet", "shnum", 0)
        sColS = GetIniString(config$, "ColSource", "cols", "")
        sColD = GetIniString(config$, "ColDest", "cold", "")
    End If
End Sub

Modul1:
Visual Basic:
Option Explicit

Declare Function WritePrivateProfileString Lib "kernel32" _
    Alias "WritePrivateProfileStringA" ( _
    ByVal lpApplicationName As String, _
    ByVal lpKeyName As Any, _
    ByVal lpString As Any, _
    ByVal lplFileName As String) As Long
    
Declare Function GetPrivateProfileString Lib "kernel32" _
    Alias "GetPrivateProfileStringA" ( _
    ByVal lpApplicationName As String, _
    ByVal lpKeyName As Any, _
    ByVal lpDefault As String, _
    ByVal lpReturnedString As String, _
    ByVal nSize As Long, _
    ByVal lpFileName As String) As Long
    
Public Function GetIniString( _
    ByVal INIFile As String, _
    ByVal Section As String, _
    ByVal Titel As String, _
    ByVal Property As Integer, _
    Optional ByVal nSize As Integer = 256) As String
 
  Dim lResult As Long
  Dim sValue As String
  Dim iValue As Integer
 
  sValue = Space$(nSize)
  
  lResult = GetPrivateProfileString(INIFile, Section, Titel, _
  Property, sValue, nSize)
  If IsNumeric(sValue) Then
    iValue = CInt(sValue)
    GetIniString = Left$(iValue, lResult)
  Else
    GetIniString = Left$(sValue, lResult)
  End If
End Function

Die Config.ini sieht so aus:
Visual Basic:
[NumFiles]
numf = 1
 
[NumSheet]
shnum = 1

[ColSource]
cols = g

[ColDest]
cold = d

Zum Testen habe ich absichtlich Property und sValue auf Integer gesetzt. Das hat aber genauso wenig gebracht wie das casten mit CInt.
Wäre schon mal toll wenn's bisdahin laufen würde, obgleich ich noch nicht wirklich Plan habe wie ich z.B. die Ini-Dateien flexibel ablegen kann bzw. das ganze an das Fileopen hänge und wie ich die Ini einlese wenn mehrere Durchläufe gemacht werden sollen?
So könnte z.B. numf auch auf 3 (Durchläufe) stehen, was bedeutet, daß in den folgenden Sektionen eben auch je 3 Werte stehen.

Grüße
_opiwahn_
 
Der Vertrag zwischen der Windows-API und Clients, welche darauf zugreifen wollen, besagt eindeutig, dass wenn die API einen String erwartet, du auch einen zu liefern hast (als Variable).
In deinem Fall übergibst du in deinem Call als 4. Parameter den Wert 0, während die API einen String erwartet (bzw. besser gesagt: Die Adresse zu einem String).

Ändere deinen 4. Parameter auf String
Ausserdem: Niemals reservierte Wörter als Variablen-Namen verwenden. "Property" ist ein reserviertes Wort.

Falls du ohne Konvertierungsgedöns Integers aus ner INI lesen willst, schau dir mal die API GetPrivateProfileInt an.
 
Hallo Zvoni!
Danke für deine Rückmeldung.
Daß eigendlich ein String erwartet wird ist mir schon klar.
Ursprünglich hatte ich auch die Übergabeparameter auf String gesetzt.
Da bin ich aber schon in die erste Unverträglichkeit reingelaufen.
Das Problem ist der "Property"-Wert (ich werds ändern) in den ersten beiden Fällen (numf + shnum) ein Integer liefert und erst in den folgenden ein String.
numf = Anzahl der abzuarbeitenden Dateien bzw. Zähler für die Schleife
shnum = Nummer der Tabelle z.B. Sheet(1)
cols = Spalte Quelle
cold = Spalte Ziel

grüße
_opiwahn_
 
Zuletzt bearbeitet:
Ich sehe gerade noch nen weiteren Fehler:

lResult = GetPrivateProfileString(INIFile, Section, Titel, Property, sValue, nSize)

Ändere das mal wie folgt ab:
Visual Basic:
sValue=Space(nSize)

lResult = GetPrivateProfileString(Section, Titel, "Fehler", sValue, Len(sValue), INIFile)

Falls sValue einen Integer als String zurückgibt (den du auch eventuell erwartest), kannst du ja bequem

iNum=CInt(sValue)

machen.

Siehe auch: http://www.vbarchiv.net/api/api_getprivateprofilestring.html
 
Zuletzt bearbeitet:
Super Idee - funktioniert :)
Zumindest in der Lokalen Ausgabe bzw. im Debugfenster.
Innerhalb der Funktion sind die Werte alle korrekt.
Sobald ich jedoch aus der Funktion rauskomme und es an die Schleife geht sind alle Werte wieder verschwunden :-(
Was mich noch interessieren würde:
Wieso steht der Ini-String am Schluss - muß keine Reihenfolge eingehlten werden?
Das Zusammenspiel zwischen "Fehler" (anstelle von Propty (hab's umbenannt)) und LEN(sValue) ist mir ebenfalls ein kleines Rätsel? LEN steht doch für Lenght (of sValue) - oder?

Danke erstmal ...

Grüße
_opiwahn_
 
Bei API-Calls muss zwingend die Reihenfolge eingehalten werden. Ansonsten lies dir den Link durch, den ich dir oben reingehängt habe. Da steht ne genaue Beschreibung welches Feld welche Bedeutung hat (u.a. eben auch "Fehler")

Visual Basic:
Private Declare Function GetPrivateProfileString Lib "kernel32" _
  Alias "GetPrivateProfileStringA" ( _
  ByVal lpApplicationName As String, _
  ByVal lpKeyName As Any, _
  ByVal lpDefault As String, _
  ByVal lpReturnedString As String, _
  ByVal nSize As Long, _
  ByVal lpFileName As String) As Long
Beschreibung:
Liesst einen Stringwert (Text) aus einer (INI-)Datei und gibt diesen als Rückgabewert zurück.

Parameter:
lpApplicationName Namen des Abschnittes in der (INI-)Datei. Übergibt man hier ein "VBNullString"-Zeichen so liefert die Funktion die Namen aller Sektionen.
lpKeyName Schlüssel des zu lesenden Eintrags. Übergibt man hier ein "VBNullString"-Zeichen und existiert ein Abschnitt "lpApplicationName", so erhält man alle im Abschnitt gespeicherten Schlüssel.
nDefault Vorgabe-/Standardwert, der zurückgegeben werden soll, wenn kein Eintrag vorhanden ist.
lpReturnedString Puffer, der den Rückgabewert enthält (muss mit ausreichend Leerstellen gefüllt sein)
nSize Größe des Puffers in Bytes.
lpFileName Pfadangabe der (INI-)Datei
 
Es ist logisch warum deine variablen leer sind: deine variablen iNum usw. Sind im bereich CopySource deklariert. Schieb die mal nach draussen.
 
Ok - verstanden, angepasst und weiter getestet.
Jetzt hab ich aber plötzlich ein Problem mit einer einer undefinierten Objektvariablen (ich nehme an file)
Das ganze trat in dem Moment auf als ich die Variablen Global setze (jetzt verliert auch auch nicht mehr die Werte :)). Damit habe ich mir aber wohl das nächste Fass aufgemacht.

Dim file as Object

scheint alleine wohl nicht zu reichen?

Außerdem versuche ich gerade den Pfad zu Ini auswählbar zu machen, was auch funktioniert, jedoch wird gleich danach "file open" gesprungen ohne die Ini abzuarbeiten oder eben die Input-Boxen einzublenden.


Visual Basic:
Option Explicit

Public lLRowS As Long
Public lLRowD As Long
Public sColS As String
Public sColD As String
Public iShNumS As Integer
Public iNum As Integer
Public i As Integer
Public sCPath As String
Public sConfig As String

Public objShell As New Shell32.Shell
Public objFolder As Shell32.Folder


Sub CopySource()

Dim file As Object

ReadIni

If iNum < 1 Then

    iNum = Application.InputBox(Prompt:="", Title:="Number of Files?", Type:=1)

Else

    For i = 1 To iNum
    
    If iShNumS < 0 Or sColS = "" Or sColD = "" Then

        iShNumS = Application.InputBox(Prompt:="", Title:="Sheetnumber Source?", Type:=1)
        sColS = Application.InputBox(Prompt:="", Title:="Column Source?", Type:=2)
        sColD = Application.InputBox(Prompt:="", Title:="Column Destination?", Type:=2)
        
    Else
    
        lLRowD = Cells(Rows.Count, sColD).End(xlUp).Row
    
            file = Application.GetOpenFilename()
                If file = False Then Exit Sub
            Workbooks.Open file
    
        With Worksheets(iShNumS)
            lLRowS = .Cells(Rows.Count, sColS).End(xlUp).Row
            .Range(sColS & "2:" & sColS & lLRowS).Copy ThisWorkbook.ActiveSheet.Range(sColD & lLRowD + 1)
            ActiveWorkbook.Close False
        End With
        
    End If
    
    Next i
    
End If

End Sub

Sub ReadIni()

    sCPath = SelectFolder("Select Folder", "")
    
    If Len(sCPath) Then
    sConfig = sCPath & "\config.ini"
        If Dir$(sConfig, vbNormal) <> "" Then
            iNum = CInt(GetIniString(sConfig, "NumFiles", "numf", 0))
            iShNumS = CInt(GetIniString(sConfig, "NumSheet", "shnum", 0))
            sColS = GetIniString(sConfig, "ColSource", "cols", "")
            sColD = GetIniString(sConfig, "ColDest", "cold", "")
        End If
    Else
        MsgBox "Cancel was pressed"
    End If
    
End Sub

Function SelectFolder( _
    Optional Title As String, _
    Optional TopFolder _
    As String) As String
    
    Set objFolder = objShell.BrowseForFolder(0, Title, 1, TopFolder)
    If Not objFolder Is Nothing Then
        SelectFolder = objFolder.Items.Item.Path
    End If
End Function
 
Zeile 41
Muss es nicht "set file=application ...... Blalala" heissen?

Sofern application.getopenfilename ein objekt zurück gibt. Wenn ich mich aber richtig erinner gibt die funktion einen string zurück, oder?
 

Neue Beiträge

Zurück