Jeśli pobieramy pliki z Internetu, to przeglądarka Internetowa zapisuje dodatkowy atrybut, że plik został pobrany z Internetu (dodatkowo gdy rozpakowujemy archiwum pobrane z Internetu to niektóre archiwizatory np. WinRAR, TotalCommander wszystkie rozpakowane pliki oznaczają jako pobrane z Internetu; aczkolwiek 7-zip nie dodaje tego atrybutu do rozpakowywanych plików). Mało tego takie oznaczenie stosuje też program Outlook gdy zapisujemy załącznik na dysku. Tutaj taka ciekawostka: ten atrybut jest zapisywany tylko na dyskach sformatowanych NTFS. Jeśli zapisujesz plik na nośniku sformatowanym w systemie FAT, FAT32, exFAT (np. pendrive są formatowane w tym systemie plików)
Czym to się objawia? Otóż jeśli pobieramy plik np. szablon.docx z Internetu to dodatkowo jest tworzony ADS (Alternate Data Streams) w pewnym uproszczeniu można powiedzieć że to jest to taki jakby dodatkowy mały plik o nazwie szablon.docx:Zone.Identifier. Normalnie go nie widać, eksplorator plików go nie widzi. Również jeśli w wierszu poleceń wpiszemy komendę dir to nie zostanie on wyświetlony.
Ale jeśli wpiszemy dir /r to taki „plik” zostanie wyświetlony
Dodatkowo wpisując polecenie more < szablon.docx:Zone.Identifier możemy zobaczyć jego zawartość (ponieważ plik o nazwie szablon.docx:Zone.Identifier tak naprawdę nie jest fizycznie plikiem a dodatkowym strumieniem danych zapisanym na dysku to wiele DOSowych poleceń np. type, copy nie radzi sobie z obsługą takich „plików”).
Pewnie wielokrotnie się spotkałeś z tym, że takie pliki pobrane z Internetu należało odblokować (bo np. w nowszych wersjach pakietu Office domyślnie była wyłączana obsługa makr dla plików pobranych z Internetu, z kolei zwykłe pliki docx otwierają się w widoku chronionym).
Sam pewnego razu padłem ofiarą tego rozwiązania. Otóż napisałem dla klienta proste makro generujące listę obecności. To makro pobierało dane z bazy danych (plik accdb) następnie otwierało plik szablon.docx i w odpowiednim miejscu w szablonie dodawało w tabeli kolejne wiersze z nazwiskami uczestników szkolenia.
Wszystko było fajnie do czasu aż nie wysłałem na maila poprawionego szablonu listy obecności. Klient zapisał załącznik (a wraz z nim zapisał się ten strumień danych informujących, że plik pochodzi z Internetu).
Podczas odpalenia mojego makra wyskoczył błąd: Application-defined or object-defined error.
Jak pozbyć się widoku chronionego? Otóż najprościej jest jeszcze przed otworzeniem pliku) sprawdzić czy istnieje ten alternatywny strumień danych (czyli „plik” o takiej samej nazwie jak nasz główny plik ale z doklejoną w nazwie końcówką :Zone.Identifier i go usunąć).
Oczywiście standardowa w VBA instrukcja Kill nie radzi sobie z takimi „plikami”. Również obiekt FSO nie potrafi ich usunąć (ale potrafi sprawdzić czy istnieje plik o takiej nazwie). Tutaj musiałem skorzystać z Windowsowej funkcji API DeleteFile, która potrafi usuwać takie „pliki”.
Poniżej przedstawiam listing przykładowego makra (mocno uproszczone, bo chcę przedstawić koncepcję mojego rozwiązania a nie profesjonalne makro do generowania list obecności), które otwiera szablon z listą obecności i w miejscu gdzie jest „znacznik” [Imie_nazwisko] wstawia tekst Jan Kowalski.
Uruchom procedurę TestListyObecnosci pamiętając aby podmienić odpowiednio ścieżkę do otwieranego pliku.
Option Explicit
#If VBA7 Then
Public Declare PtrSafe Function DeleteFile Lib "kernel32" Alias "DeleteFileA" (ByVal lpFileName As String) As Long
#Else
public Declare Function DeleteFile Lib "kernel32" Alias "DeleteFileA" (ByVal lpFileName As String) As Long
#End If
Public Function FileExists(fname) As Boolean
Dim FSO As Object
Set FSO = CreateObject("Scripting.FileSystemObject")
FileExists = FSO.FileExists(fname)
End Function
Sub GenerujListeObecnosci(Plik As String)
Dim appWord As Object
Dim docWord As Object
Dim a As Long
Dim tbl As Object
Dim newRow As Object
Dim Licznik As Long
On Error GoTo Err
If FileExists(Plik + ":Zone.Identifier") Then
DeleteFile (Plik + ":Zone.Identifier")
MsgBox "Usunąłem atrybut wymuszający otwieranie pliku w widoku chronionym", vbInformation
End If
Set appWord = CreateObject("Word.Application")
Set docWord = appWord.Documents.Add(Plik)
'Bardzo uproszczona funkcja wstawiania
With docWord.Content.Find
.Text = "[Imie_nazwisko]"
.Replacement.Text = "Jan Kowlaski"
.Execute Replace:=2
End With
appWord.Visible = True
appWord.Activate
Set docWord = Nothing
Set appWord = Nothing
MsgBox "Wygenerowałem listę obecności", vbInformation
Err2:
Exit Sub
Err:
MsgBox Err.Description, vbExclamation, "GenerujListeObecnosci"
Resume Err2
End Sub
Sub TestListyObecnosci()
'Tu wstaw odpowiednią ścieżkę do pliku
GenerujListeObecnosci ("v:\szablony\szablon.docx")
End Sub
W skrócie w procedurze GenerujListeObecnosci sprawdzamy czy istnieje zapisany alternatywny strumień danych (czyli „plik” z końcówką :Zone.Identifier) i jeśli istnieje to go kasujemy a dopiero później otwieramy szablon i na jego bazie generujemy listę obecności.
Ciekawy artykuł. Mam dwa pytania:
I. Jak sprawdzić ile takich ukrytych 'plików’ mam na dysku?
II. Czy da się te wszystkie 'pliki’ hurtowo usunąć?
Kiedyś trafiłem na taki oto programik: AlternateStreamView v1.58 https://www.nirsoft.net/utils/alternate_data_streams.html Ten program wyszykuje we wskazanej lokalizacji (np. cały dysk wraz z podfolderami) wszystkie takie „pliki” i ma możlwość np. hurtowego ich usunięcia