November 25, 2020, 05:45:52

Neuigkeiten:

Wenn ihr euch für eine gute Antwort bedanken möchtet, im entsprechenden Posting einfach den Knopf "sag Danke" drücken!


Access .Feld("Dateiname") <> Windows Dateiname

Begonnen von maddhin, Oktober 27, 2020, 10:39:59

⏪ vorheriges - nächstes ⏩

maddhin

Hallo,

ich lade mit Access einige Dateien herunter. Dazu mache ich "Webscraping" und speichere mit
.fields("Title")= IEElement.title
einen Dateinamen in einer Tabelle. Via
sPath = psPath & "\" & rs!title
...
URLDownloadToFile 0, psURL, sPath, 0, 0

(lade ich herunter und) schreibe ich die Datei. Und mit
If FSO.FileExists(sPath) = True Then
                .Edit
                .Fields("FileDownloaded") = True
                .Update
            End If

möchte ich prüfen, ob die Datei auch auf der Platte gelandet ist und setze ein Häckchen.

So weit, so gut und funktioniert...

...fast einwandfrei:

mein Problem ist, dass bei einigen Dateinamen bzw. Dateien "FSO.FileExists(sPath) = false" bleibt, obwohl die Datei heruntergeladen ist, d.h. FSO.FileExists(sPath) müsste entsprechend "true" sein. D.h. der Vergleich zwischen dem Namen in der Tabelle und dem Namen in Windows schlägt fehl.

Nach endlosem Probieren meine ich das Problem auf einen Bindestrich reduziert zu haben, der eigentlich chr(45) sein sollte, aber nach meinen Experimenten keinem chr() zuzuordnen ist. Kurioserweise lieft ein asc() des Bindestrichs den Wert 45 = ASCII Bindestrich. Allerdings erkennt VBA das Zeichen nicht als chr(45) (z.B. in Replace). Die Webseite wo "Title" herkommt ist in UTF-8.

Ich bin jetzt mit meinem Latein am Ende. Nach stundenlangem Googlen scheint dies ein "encoding" Problem zu sein, ich sehe im Moment aber nicht, wie ich mein Problem konkret lösen kann.

Hat jemand eine Idee?

maddhin

Neuigkeiten: ich habe inzwischen den (vermeidlichen!) Übeltäter als U+2122 Unicode-Bindestrich identifiziert...

Gibt es eine Funktion, die "Web"-Strings (vermutlich UTF-8) so in Access speichert bzw. konvertiert, dass man problemlos damit arbeiten kann?

In meinem konkreten Fall waren in fast allen der 845 heruntergeladenen Dateien mehrere chr(45)-Bindestriche und nur 9 Dateien mit U+2122 anstatt chr(45) haben Probleme bereitet.

Ich würde jetzt gerne den VBA Code so schreiben, dass sowas nicht mehr passieren kann.

Lieben Dank für jeglichen Input im Voraus!

markus888

Vielleicht zeigst du mal, was die Funktion URLDownloadToFile macht.
Falls das eine Windows Funktion ist - > welche Version verwendest du - ANSI/Unicode?
10 Jahre Access

maddhin

Zitat von: markus888 am Oktober 27, 2020, 13:07:44Vielleicht zeigst du mal, was die Funktion URLDownloadToFile macht.
Falls das eine Windows Funktion ist - > welche Version verwendest du - ANSI/Unicode?

URLDownloadToFile ist die Windows Funktion und ich verwende sie wie dargestellt:
Function dlfile(psURL As String, psPath As String)
    Dim sPath As String
    sPath = psPath
    Debug.Print "Saving to: " & sPath
    Debug.Print "------------------"
    URLDownloadToFile 0, psURL, sPath, 0, 0
End Function
psURL ist die URL und sPath der vollständige Pfad. Was die Nullen machen, weiß ich nicht wirklich :-[

Ich lerne gerade, dass es URLDownloadToFileW (Unicode) and URLDownloadToFileA (ANSI) gibt. Ggf. ist das hilfreich... :)

Blöde Frage: welche Version wäre denn in meinem Fall die richtige?

markus888

Zitat von: maddhin am Oktober 27, 2020, 13:19:16welche Version wäre denn in meinem Fall die richtige?

Da Access alles in Unicode macht, verwende ich bei win API immer nur die unicode Versionen.
10 Jahre Access

maddhin

Zitat von: markus888 am Oktober 27, 2020, 13:27:20Da Access alles in Unicode macht, verwende ich bei win API immer nur die unicode Versionen.

Nachdem ich mit "URLDownloadToFileW" als Funktion gescheitert bin, habe ich nochmal genau hingesehen und
Private Declare PtrSafe Function URLDownloadToFile Lib "urlmon" _
    Alias "URLDownloadToFileA" (ByVal pCaller As Long, ByVal szURL As String, _
    ByVal szFileName As String, ByVal dwReserved As Long, ByVal lpfnCB As Long) As Long
gefunden... Das hatte ich vergessen, ich hatte ganz am Anfang etwas c&p aus dem Internet gemacht... Sorry.

Ich habe jetzt URLDownloadToFileA in URLDownloadToFileW geändert, dies scheint aber nicht zu funktionieren, da nichts heruntergeladen wird... Wie kann ich das mit "W" machen? :-[

daolix

versuch
Private Declare PtrSafe Function URLDownloadToFile Lib "urlmon" _
    Alias "URLDownloadToFileW" (ByVal pCaller As Long, ByVal szURL As longptr, _
    ByVal szFileName As longptr, ByVal dwReserved As Long, ByVal lpfnCB As Longptr) As Long


aufruf ist dann
URLDownloadToFile 0, strptr(psURL), strptr(sPath), 0, 0

markus888

@daolix,
nur so eine Frage am Rande.
Der erste Parameter ist ja auch ein Pointer.
Weißt du, ob bei einer 64 Bit Anwendung die fehlenden 4 Byte am Stack mit Null überschrieben werden, wenn nur 4 geschrieben werden,
oder bleibt der alte Inhalt stehen?
Falls ja, könnte das Beispiel möglicherweise zu einem Fehler führen.
LG Markus
10 Jahre Access

daolix

Oktober 27, 2020, 22:19:28 #8 Letzte Bearbeitung: Oktober 27, 2020, 22:54:26 von daolix
Stimmt ist ein Pointer. sollte aber kein Prob sein, solange der Wert 0 ist, ich denk mal schon das der Stack vor der Übergabe aufgeräumt ist. 

wenn ich jetzt bei
Private Declare PtrSafe Function URLDownloadToFile Lib "urlmon" _
    Alias "URLDownloadToFileW" (ByVal pCaller As Long, ByVal szURL As longptr, _
    ByVal szFileName As longptr, ByVal dwReserved As Long, ByVal lpfnCB As Integer) As Long

den letzte parameter der Deklaration ändere passiert unter 32bit ja jetzt auch nix aufregendes.

maddhin

Zitat von: daolix am Oktober 27, 2020, 14:20:53Private Declare PtrSafe Function URLDownloadToFile Lib "urlmon" _
    Alias "URLDownloadToFileW" (ByVal pCaller As Long, ByVal szURL As longptr, _
    ByVal szFileName As longptr, ByVal dwReserved As Long, ByVal lpfnCB As Longptr) As Long

URLDownloadToFile 0, strptr(psURL), strptr(sPath), 0, 0

Das hat jetzt funktioniert! Eine Datei (aus 845) wird immernoch nicht erkannt bzw. der FSO.FileExists-Vergleich schlägt fehl, aber ggf. liegt das an was anderem.

Noch eine Frage:

Bei dbWiki steht sowas:

Public Function DateiHerunterladen(Url As String, Pfad As String) As Long
   DoCmd.Hourglass True
   DateiHerunterladen = URLDownloadToFile(0, Url, Pfad, 0, 0)
   DoCmd.Hourglass False
End Function

[...]
   If DateiHerunterladen(strquellpfad, strzielpfad) = 0 Then
      MsgBox "Download erfolgreich"
   Else
      MsgBox "Download nicht erfolgreich"
   End If

Heißt dies, dass URLDownloadToFile nach erfolgreichem Herunterladen immer 0 zurückgibt? D.h. dann könnte ich mir die If-FSO.FileExists-Geschichte ja eigentlich sparen. Zumindest wenn es nur darum geht ein isDownloaded?-Häckchen zu setzen.

Lieben Dank schonmal für die exzellent und effiziente Hilfe bisher, das hat mir sehr geholfen!

markus888

Zitat von: maddhin am Oktober 28, 2020, 04:12:38Heißt dies, dass URLDownloadToFile nach erfolgreichem Herunterladen immer 0 zurückgibt?

Hasst du dir die Dokumentation zu der Funktion durchgelesen?
Da erfährst du alles zum Rückgabewert.
Oder meinst du jeder kennt jede Funktion auswendig ohne selbst nachsehen zu müssen?
10 Jahre Access

markus888

Zitat von: daolix am Oktober 27, 2020, 22:19:28ich denk mal schon das der Stack vor der Übergabe aufgeräumt ist.

Bei dem Punkt bin ich mir eben nicht sicher.
Weiß auch nicht wovon das abhängt - Betriebssystem, calling convention oder was anderes?
Vor dem Push steht ja irgendwas drinnen.
Die Frage ist, ob sicher immer die restlichen Bytes die auf die 8 Byte fehlen auf 0 gestellt werden,
da das ja Zeit kostet die man sich bei korrekter Übergabe sparen kann.
10 Jahre Access

maddhin

Zitat von: markus888 am Oktober 28, 2020, 09:30:22Hasst du dir die Dokumentation zu der Funktion durchgelesen?
Da erfährst du alles zum Rückgabewert.
Ja, ich habe die Doku sogar auf 3-4 unterschiedlichen Seiten gelesen, allerdings fehlt mir leider das nötige Hintergrundwissen, um das wirklich verstehen zu können.

Soweit ich es interpretieren kann, gibt die Funktion 0 zurück, solange nichts Außergewöhnliches/kein Fehler passiert ist. Soweit ich verstanden habe, kann dies auch ein Abbruch durch den Benutzer, etc. bedeuten. D.h. eine Prüfung auf den Rückgabewert um isDownloaded? zu setzen wäre nicht zuverlässig. D.h. die FSO.FileExist-Prüfung erscheint sinnvoll.

Zitat von: markus888 am Oktober 28, 2020, 09:30:22Oder meinst du jeder kennt jede Funktion auswendig ohne selbst nachsehen zu müssen?
Um Deine rhetorische Frage zu beantworten: Nein, ganz sicher nicht! Aber Du/ihr scheint Euch mit dieser Funktion recht gut auszukennen. Daher meine Frage an Euch Experten, wie ihr sowas löst. Ich habe dies sicherlich zu kurz formuliert und verstehe, dass man den Satz auch als der-ist-zu-faul-zum-Googlen interpretieren kann. My bad.

markus888

Oktober 28, 2020, 11:34:03 #13 Letzte Bearbeitung: Oktober 28, 2020, 11:57:08 von markus888
Zitat von: maddhin am Oktober 28, 2020, 10:14:50Aber Du/ihr scheint Euch mit dieser Funktion recht gut auszukennen.

Ich hab die Funktion noch nie verwendet.
Mit dem Programmieren ist es recht einfach.
Alles was man nicht versteht, muss man sich erarbeiten.
Da gibt es keine Abkürzung, sonst wird man nie in der Lage sein, eigenständig Code zu schreiben
sondern bleibst ewig ein Kopierer, der bei Problemen nicht weiter kommt.
Du kannst dann auch weder mit der Hilfe noch mit der Dokumentation was anfangen -
fürs Programmieren bleibst du also unbrauchbar, wenn du dich nicht systematisch weiterbildest.

Die Frage ist also wo die Reise hin soll.

Hab jetzt nachgesehen, was unter anderem in der Doku steht:

Zitat von: undefinedURLDownloadToFile returns S_OK even if the file cannot be created and the download is canceled. If the szFileName parameter contains a file path, ensure that the destination directory exists before calling URLDownloadToFile. For best control over the download and its progress, an IBindStatusCallback interface is recommended.

Das ist also ziemlich klar.
10 Jahre Access

daolix

Zitat von: markus888 am Oktober 28, 2020, 09:35:34Vor dem Push steht ja irgendwas drinnen.
Stimmt.

Zitat von: markus888 am Oktober 28, 2020, 09:35:34
Zitat von: daolix am Oktober 27, 2020, 22:19:28ich denk mal schon das der Stack vor der Übergabe aufgeräumt ist.

Bei dem Punkt bin ich mir eben nicht sicher.
Der Stack wird mit dem Push aufgeräumt, d.h. mit "push eineVariable" wird der Stack bereinigt, der Wert gesetzt, und der Stackptr korrigiert.

Selbstredend kannst du dir deine eigene Aufrufmethode schreiben bei der du meinst das diese schneller ist. Nur ob du damit einen messbaren effekt erreichtst glaub ich jetzt nicht.


Zitat von: markus888 am Oktober 28, 2020, 11:34:03Hab jetzt nachgesehen, was unter anderem in der Doku steht:

Zitat von: undefinedURLDownloadToFile returns S_OK even if the file cannot be created and the download is canceled. If the szFileName parameter contains a file path, ensure that the destination directory exists before calling URLDownloadToFile. For best control over the download and its progress, an IBindStatusCallback interface is recommended.

Das ist also ziemlich klar.

Hmm. wenn bei mir szFileName auf einen nicht existierenden Path zeigt gibt URLDownloadToFile bei mir ein e_abort zurück. ist aber winversionsabhängig, glaub ich.