File Coverage

blib/lib/Win32/FileOp.pm
Criterion Covered Total %
statement 4 6 66.6
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 6 8 75.0


line stmt bran cond sub pod time code
1             package Win32::FileOp;
2            
3 1     1   1249 use vars qw($VERSION);
  1         3  
  1         83  
4             $Win32::FileOp::VERSION = '0.16.02';
5            
6 1     1   1867 use Win32::API;
  0            
  0            
7             use File::Find;
8             use File::Path;
9             use File::DosGlob qw(glob);
10             use Cwd;
11             use strict;
12             use warnings;
13             no warnings 'uninitialized';
14             use Carp;
15            
16             use Data::Lazy;
17             use File::Spec;
18             sub Relative2Absolute {#inplace
19             foreach (@_) {
20             $_ = File::Spec->rel2abs($_)
21             }
22             }
23             sub RelativeToAbsolute {
24             if (@_ == 1) {
25             return File::Spec->rel2abs($_[0]);
26             }
27             my @list = @_;
28             foreach (@list) {
29             $_ = File::Spec->rel2abs($_)
30             }
31             return @list;
32             }
33            
34            
35             require Exporter;
36             @Win32::FileOp::ISA = qw(Exporter);
37            
38             $Win32::FileOp::BufferSize = 65534;
39            
40             my @FOF_flags = qw(
41             FOF_SILENT FOF_RENAMEONCOLLISION FOF_NOCONFIRMATION FOF_ALLOWUNDO
42             FOF_FILESONLY FOF_SIMPLEPROGRESS FOF_NOCONFIRMMKDIR FOF_NOERRORUI
43             FOF_NOCOPYSECURITYATTRIBS FOF_MULTIDESTFILES FOF_CREATEPROGRESSDLG
44             );
45            
46             my @OFN_flags = qw(
47             OFN_READONLY OFN_OVERWRITEPROMPT OFN_HIDEREADONLY OFN_NOCHANGEDIR OFN_SHOWHELP
48             OFN_ENABLEHOOK OFN_ENABLETEMPLATE OFN_ENABLETEMPLATEHANDLE OFN_NOVALIDATE
49             OFN_ALLOWMULTISELECT OFN_EXTENSIONDIFFERENT OFN_PATHMUSTEXIST OFN_FILEMUSTEXIST
50             OFN_CREATEPROMPT OFN_SHAREAWARE OFN_NOREADONLYRETURN OFN_NOTESTFILECREATE
51             OFN_NONETWORKBUTTON OFN_NOLONGNAMES OFN_EXPLORER OFN_NODEREFERENCELINKS
52             OFN_LONGNAMES OFN_SHAREFALLTHROUGH OFN_SHARENOWARN OFN_SHAREWARN
53             );
54            
55             my @BIF_flags = qw(
56             BIF_RETURNONLYFSDIRS BIF_DONTGOBELOWDOMAIN BIF_STATUSTEXT BIF_RETURNFSANCESTORS
57             BIF_BROWSEFORCOMPUTER BIF_BROWSEFORPRINTER BIF_BROWSEINCLUDEFILES
58             BIF_EDITBOX BIF_VALIDATE BIF_NEWDIALOGSTYLE BIF_USENEWUI BIF_BROWSEINCLUDEURLS
59             BIF_UAHINT BIF_NONEWFOLDERBUTTON BIF_NOTRANSLATETARGETS BIF_SHAREABLE
60             );
61            
62             my @CSIDL_flags = qw(
63             CSIDL_DESKTOP CSIDL_PROGRAMS CSIDL_CONTROLS CSIDL_PRINTERS CSIDL_PERSONAL
64             CSIDL_FAVORITES CSIDL_STARTUP CSIDL_RECENT CSIDL_SENDTO CSIDL_BITBUCKET
65             CSIDL_STARTMENU CSIDL_DESKTOPDIRECTORY CSIDL_DRIVES CSIDL_NETWORK CSIDL_NETHOOD
66             CSIDL_FONTS CSIDL_TEMPLATES CSIDL_COMMON_STARTMENU CSIDL_COMMON_PROGRAMS
67             CSIDL_COMMON_STARTUP CSIDL_COMMON_DESKTOPDIRECTORY CSIDL_APPDATA CSIDL_PRINTHOOD
68             );
69            
70             my @CONNECT_flags = qw(
71             CONNECT_UPDATE_PROFILE CONNECT_UPDATE_RECENT CONNECT_TEMPORARY CONNECT_INTERACTIVE
72             CONNECT_PROMPT CONNECT_NEED_DRIVE CONNECT_REFCOUNT CONNECT_REDIRECT CONNECT_LOCALDRIVE
73             CONNECT_CURRENT_MEDIA CONNECT_DEFERRED CONNECT_RESERVED
74             );
75            
76             my @SW_flags = qw(
77             SW_HIDE SW_MAXIMIZE SW_MINIMIZE SW_RESTORE SW_SHOW
78             SW_SHOWDEFAULT SW_SHOWMAXIMIZED SW_SHOWMINIMIZED
79             SW_SHOWMINNOACTIVE SW_SHOWNA SW_SHOWNOACTIVATE SW_SHOWNORMAL
80             );
81            
82             @Win32::FileOp::EXPORT = (
83             qw( Recycle RecycleConfirm RecycleConfirmEach RecycleEx
84             Delete DeleteConfirm DeleteConfirmEach DeleteEx
85             Copy CopyConfirm CopyConfirmEach CopyEx
86             Move MoveConfirm MoveConfirmEach MoveEx
87             MoveAtReboot DeleteAtReboot MoveFile MoveFileEx CopyFile
88             FillInDir UpdateDir
89             FindInPATH FindInPath Relative2Absolute RelativeToAbsolute
90             AddToRecentDocs EmptyRecentDocs
91             ReadINISectionKeys ReadINISections
92             WriteToINI WriteToWININI ReadINI ReadWININI DeleteFromINI DeleteFromWININI
93             OpenDialog SaveAsDialog BrowseForFolder
94             recycle
95             DesktopHandle GetDesktopHandle WindowHandle GetWindowHandle
96             Compress Uncompress UnCompress Compressed SetCompression GetCompression CompressedSize CompressDir UncompressDir UnCompressDir
97             Map Connect Unmap Disconnect Mapped
98             Subst Unsubst Substed SubstDev
99             GetLargeFileSize GetDiskFreeSpace ShellExecute ShellExecuteEx
100             ),
101             @FOF_flags,
102             @OFN_flags,
103             @BIF_flags,
104             @CSIDL_flags,
105             @SW_flags
106             );
107             # FOF_CONFIRMMOUSE FOF_WANTMAPPINGHANDLE
108            
109             *Win32::FileOp::EXPORT_OK = [@Win32::FileOp::EXPORT, @CONNECT_flags];
110            
111             %Win32::FileOp::EXPORT_TAGS = (
112             INI => [qw( ReadINISectionKeys ReadINISections WriteToINI WriteToWININI ReadINI ReadWININI DeleteFromINI DeleteFromWININI )],
113             DIALOGS => [qw( OpenDialog SaveAsDialog BrowseForFolder),
114             @OFN_flags, @BIF_flags, @CSIDL_flags],
115             _DIALOGS => [@OFN_flags, @BIF_flags, @CSIDL_flags],
116             HANDLES => [qw( DesktopHandle GetDesktopHandle WindowHandle GetWindowHandle )],
117             BASIC => [qw(
118             Delete DeleteConfirm DeleteConfirmEach DeleteEx
119             Copy CopyConfirm CopyConfirmEach CopyEx
120             Move MoveConfirm MoveConfirmEach MoveEx
121             MoveAtReboot DeleteAtReboot MoveFile MoveFileEx CopyFile
122             ),
123             @FOF_flags],
124             _BASIC => [@FOF_flags],
125             RECENT => [qw(AddToRecentDocs EmptyRecentDocs)],
126             DIRECTORY => [qw(UpdateDir FillInDir)],
127             COMPRESS => [qw(Compress Uncompress UnCompress Compressed SetCompression GetCompression CompressedSize CompressDir UncompressDir UnCompressDir)],
128             MAP => [qw(Map Connect Unmap Disconnect Mapped)],
129             _MAP => \@CONNECT_flags,
130             SUBST => [qw(Subst Unsubst Substed SubstDev)],
131             EXECUTE => ['ShellExecute ShellExecuteEx', @SW_flags],
132             _EXECUTE => \@SW_flags,
133             );
134            
135            
136             use vars qw($ReadOnly $DesktopHandle $fileop $ProgressTitle);
137             $Win32::FileOp::DesktopHandle = 0;
138             $Win32::FileOp::WindowHandle = 0;
139             sub Win32::FileOp::GetDesktopHandle;
140             sub Win32::FileOp::GetWindowHandle;
141             $Win32::FileOp::ProgressTitle = '';
142            
143             sub FO_MOVE () { 0x01 }
144             sub FO_COPY () { 0x02 }
145             sub FO_DELETE () { 0x03 }
146             sub FO_RENAME () { 0x04 }
147            
148             sub FOF_CREATEPROGRESSDLG () { 0x0000 } # default
149             sub FOF_MULTIDESTFILES () { 0x0001 } # more than one dest for files
150             #sub FOF_CONFIRMMOUSE () { 0x0002 } # not implemented
151             sub FOF_SILENT () { 0x0004 } # don't create progress/report
152             sub FOF_RENAMEONCOLLISION () { 0x0008 } # rename if coliding
153             sub FOF_NOCONFIRMATION () { 0x0010 } # Don't prompt the user.
154             #sub FOF_WANTMAPPINGHANDLE () { 0x0020 } # Fill in FILEOPSTRUCT.hNameMappings
155             sub FOF_ALLOWUNDO () { 0x0040 } # recycle bin instead of delete
156             sub FOF_FILESONLY () { 0x0080 } # on *.*, do only files
157             sub FOF_SIMPLEPROGRESS () { 0x0100 } # means don't show names of files
158             sub FOF_NOCONFIRMMKDIR () { 0x0200 } # don't confirm making needed dirs
159             sub FOF_NOERRORUI () { 0x0400 } # don't put up error UI
160             sub FOF_NOCOPYSECURITYATTRIBS () { 0x0800 } # dont copy file Security Attributes
161            
162             sub MOVEFILE_REPLACE_EXISTING () { 0x00000001 }
163             sub MOVEFILE_COPY_ALLOWED () { 0x00000002 }
164             sub MOVEFILE_DELAY_UNTIL_REBOOT () { 0x00000004 }
165            
166             sub OFN_READONLY () { 0x00000001}
167             sub OFN_OVERWRITEPROMPT () { 0x00000002}
168             sub OFN_HIDEREADONLY () { 0x00000004}
169             sub OFN_NOCHANGEDIR () { 0x00000008}
170             sub OFN_SHOWHELP () { 0x00000010}
171             sub OFN_ENABLEHOOK () { #0x00000020;
172             carp "OFN_ENABLEHOOK not implemented" }
173             sub OFN_ENABLETEMPLATE () { #0x00000040;
174             carp "OFN_ENABLEHOOK not implemented" }
175             sub OFN_ENABLETEMPLATEHANDLE () { #0x00000080;
176             carp "OFN_ENABLEHOOK not implemented" }
177             sub OFN_NOVALIDATE () { 0x00000100}
178             sub OFN_ALLOWMULTISELECT () { 0x00000200}
179             sub OFN_EXTENSIONDIFFERENT () { 0x00000400}
180             sub OFN_PATHMUSTEXIST () { 0x00000800}
181             sub OFN_FILEMUSTEXIST () { 0x00001000}
182             sub OFN_CREATEPROMPT () { 0x00002000}
183             sub OFN_SHAREAWARE () { 0x00004000}
184             sub OFN_NOREADONLYRETURN () { 0x00008000}
185             sub OFN_NOTESTFILECREATE () { 0x00010000}
186             sub OFN_NONETWORKBUTTON () { 0x00020000}
187             sub OFN_NOLONGNAMES () { 0x00040000} # // force no long names for 4.x modules
188             #if(WINVER >() { 0x0400)
189             sub OFN_EXPLORER () { 0x00080000} # // new look commdlg
190             sub OFN_NODEREFERENCELINKS () { 0x00100000}
191             sub OFN_LONGNAMES () { 0x00200000} # // force long names for 3.x modules
192            
193             sub OFN_SHAREFALLTHROUGH () { 2}
194             sub OFN_SHARENOWARN () { 1}
195             sub OFN_SHAREWARN () { 0}
196            
197            
198             sub BIF_RETURNONLYFSDIRS () { 0x0001 } #// For finding a folder to start document searching
199             sub BIF_DONTGOBELOWDOMAIN () { 0x0002 } #// For starting the Find Computer
200             sub BIF_STATUSTEXT () { 0x0004 } # Includes a status area in the dialog box.
201             # The callback function can set the status text
202             # by sending messages to the dialog box.
203            
204             sub BIF_EDITBOX () { 0x0010 } # Add an editbox to the dialog
205             sub BIF_VALIDATE () { 0x0020 } # insist on valid result (or CANCEL)
206            
207             sub BIF_NEWDIALOGSTYLE () { 0x0040 } # Use the new dialog layout with the ability to resize
208             # Caller needs to call OleInitialize() before using this API
209            
210             sub BIF_USENEWUI () { (BIF_NEWDIALOGSTYLE | BIF_EDITBOX) }
211            
212             sub BIF_BROWSEINCLUDEURLS () { 0x0080 } # Allow URLs to be displayed or entered. (Requires BIF_USENEWUI)
213             sub BIF_UAHINT () { 0x0100 } # Add a UA hint to the dialog, in place of the edit box. May not be combined with BIF_EDITBOX
214             sub BIF_NONEWFOLDERBUTTON () { 0x0200 } # Do not add the "New Folder" button to the dialog. Only applicable with BIF_NEWDIALOGSTYLE.
215             sub BIF_NOTRANSLATETARGETS () { 0x0400 } # don't traverse target as shortcut
216            
217             sub BIF_RETURNFSANCESTORS () { 0x0008 }
218             sub BIF_BROWSEFORCOMPUTER () { 0x1000 } # Browsing for Computers.
219             sub BIF_BROWSEFORPRINTER () { 0x2000 } # Browsing for Printers
220             sub BIF_BROWSEINCLUDEFILES () { 0x4000 } # Browsing for Everything
221             sub BIF_SHAREABLE () { 0x8000 } # sharable resources displayed (remote shares, requires BIF_USENEWUI)
222            
223             #BIF_BROWSEFORCOMPUTER Only returns computers. If the user selects
224             #anything other than a computer, the OK button is grayed.
225            
226             #BIF_BROWSEFORPRINTER Only returns printers. If the user selects
227             #anything other than a printer, the OK button is grayed.
228            
229             #BIF_DONTGOBELOWDOMAIN Does not include network folders below the
230             #domain level in the tree view control.
231            
232             #BIF_RETURNFSANCESTORS Only returns file system ancestors. If the user
233             #selects anything other than a file system ancestor, the OK button is
234             #grayed.
235            
236             #BIF_RETURNONLYFSDIRS Only returns file system directories. If the
237             #user selects folders that are not part of the file system, the OK button
238             #is grayed.
239            
240             #BIF_STATUSTEXT Includes a status area in the dialog box. The callback
241             #function can set the status text by sending messages to the dialog box.
242            
243             sub CSIDL_DESKTOP () { 0x0000 }
244             sub CSIDL_PROGRAMS () { 0x0002 }
245             sub CSIDL_CONTROLS () { 0x0003 }
246             sub CSIDL_PRINTERS () { 0x0004 }
247             sub CSIDL_PERSONAL () { 0x0005 }
248             sub CSIDL_FAVORITES () { 0x0006 }
249             sub CSIDL_STARTUP () { 0x0007 }
250             sub CSIDL_RECENT () { 0x0008 }
251             sub CSIDL_SENDTO () { 0x0009 }
252             sub CSIDL_BITBUCKET () { 0x000a }
253             sub CSIDL_STARTMENU () { 0x000b }
254             sub CSIDL_DESKTOPDIRECTORY () { 0x0010 }
255             sub CSIDL_DRIVES () { 0x0011 }
256             sub CSIDL_NETWORK () { 0x0012 }
257             sub CSIDL_NETHOOD () { 0x0013 }
258             sub CSIDL_FONTS () { 0x0014 }
259             sub CSIDL_TEMPLATES () { 0x0015 }
260             sub CSIDL_COMMON_STARTMENU () { 0x0016 }
261             sub CSIDL_COMMON_PROGRAMS () { 0x0017 }
262             sub CSIDL_COMMON_STARTUP () { 0x0018 }
263             sub CSIDL_COMMON_DESKTOPDIRECTORY () { 0x0019 }
264             sub CSIDL_APPDATA () { 0x001a }
265             sub CSIDL_PRINTHOOD () { 0x001b }
266            
267             #=rem
268             #sub FILE_SHARE_READ () { 0x00000001 }
269             #sub FILE_SHARE_WRITE () { 0x00000002 }
270             #sub FILE_SHARE_DELETE () { 0x00000004 }
271             #
272             #sub FILE_FLAG_WRITE_THROUGH () { 0x80000000 }
273             #sub FILE_FLAG_OVERLAPPED () { 0x40000000 }
274             #sub FILE_FLAG_NO_BUFFERING () { 0x20000000 }
275             #sub FILE_FLAG_RANDOM_ACCESS () { 0x10000000 }
276             #sub FILE_FLAG_SEQUENTIAL_SCAN () { 0x08000000 }
277             #sub FILE_FLAG_DELETE_ON_CLOSE () { 0x04000000 }
278             #sub FILE_FLAG_BACKUP_SEMANTICS () { 0x02000000 }
279             #sub FILE_FLAG_POSIX_SEMANTICS () { 0x01000000 }
280             #
281             #
282             #sub CREATE_NEW () { 1 }
283             #sub CREATE_ALWAYS () { 2 }
284             #sub OPEN_EXISTING () { 3 }
285             #sub OPEN_ALWAYS () { 4 }
286             #sub TRUNCATE_EXISTING () { 5 }
287             #=cut
288            
289             sub DDD_RAW_TARGET_PATH () { 0x00000001 }
290             sub DDD_REMOVE_DEFINITION () { 0x00000002 }
291             sub DDD_EXACT_MATCH_ON_REMOVE () { 0x00000004 }
292             sub DDD_NO_BROADCAST_SYSTEM () { 0x00000008 }
293            
294             sub CONNECT_UPDATE_PROFILE () {0x00000001}
295             sub CONNECT_UPDATE_RECENT () {0x00000002}
296             sub CONNECT_TEMPORARY () {0x00000004}
297             sub CONNECT_INTERACTIVE () {0x00000008}
298             sub CONNECT_PROMPT () {0x00000010}
299             sub CONNECT_NEED_DRIVE () {0x00000020}
300             sub CONNECT_REFCOUNT () {0x00000040}
301             sub CONNECT_REDIRECT () {0x00000080}
302             sub CONNECT_LOCALDRIVE () {0x00000100}
303             sub CONNECT_CURRENT_MEDIA () {0x00000200}
304             sub CONNECT_DEFERRED () {0x00000400}
305             sub CONNECT_RESERVED () {0xFF000000}
306            
307             sub SW_HIDE () { 0 }
308             sub SW_SHOWNORMAL () { 1 }
309             sub SW_NORMAL () { 1 }
310             sub SW_SHOWMINIMIZED () { 2 }
311             sub SW_SHOWMAXIMIZED () { 3 }
312             sub SW_MAXIMIZE () { 3 }
313             sub SW_SHOWNOACTIVATE () { 4 }
314             sub SW_SHOW () { 5 }
315             sub SW_MINIMIZE () { 6 }
316             sub SW_SHOWMINNOACTIVE () { 7 }
317             sub SW_SHOWNA () { 8 }
318             sub SW_RESTORE () { 9 }
319             sub SW_SHOWDEFAULT () { 10 }
320             sub SW_FORCEMINIMIZE () { 11 }
321             sub SW_MAX () { 11 }
322            
323             #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
324            
325             tie $Win32::FileOp::fileop, 'Data::Lazy', sub {
326             new Win32::API("shell32", "SHFileOperation", ['P'], 'I')
327             or
328             die "new Win32::API::SHFileOperation: $!\n"
329             }, &LAZY_READONLY;
330            
331             %Win32::FileOp::SHFileOperation_ret = (
332             0x71 => 'The source and destination files are the same file.',
333             0x72 => 'Multiple file paths were specified in the source buffer, but only one destination file path.',
334             0x73 => 'Rename operation was specified but the destination path is a different directory. Use the move operation instead.',
335             0x74 => 'The source is a root directory, which cannot be moved or renamed.',
336             0x75 => 'The operation was cancelled by the user, or silently cancelled if the appropriate flags were supplied to SHFileOperation.',
337             0x76 => 'The destination is a subtree of the source.',
338             0x78 => 'Security settings denied access to the source.',
339             0x79 => 'The source or destination path exceeded or would exceed MAX_PATH.',
340             0x7A => 'The operation involved multiple destination paths, which can fail in the case of a move operation.',
341             0x7C => 'The path in the source or destination or both was invalid.',
342             0x7D => 'The source and destination have the same parent folder.',
343             0x7E => 'The destination path is an existing file.',
344             0x80 => 'The destination path is an existing folder.',
345             0x81 => 'The name of the file exceeds MAX_PATH.',
346             0x82 => 'The destination is a read-only CD-ROM, possibly unformatted.',
347             0x83 => 'The destination is a read-only DVD, possibly unformatted.',
348             0x84 => 'The destination is a writable CD-ROM, possibly unformatted.',
349             0x85 => 'The file involved in the operation is too large for the destination media or file system.',
350             0x86 => 'The source is a read-only CD-ROM, possibly unformatted.',
351             0x87 => 'The source is a read-only DVD, possibly unformatted.',
352             0x88 => 'The source is a writable CD-ROM, possibly unformatted.',
353             0xB7 => 'MAX_PATH was exceeded during the operation.',
354             0x402 => 'An unknown error occurred. This is typically due to an invalid path in the source or destination. This error does not occur on Windows Vista and later.',
355             0x10000 => 'An unspecified error occurred on the destination.',
356             0x10074 => 'Destination is a root directory and cannot be renamed.',
357             0 => undef,
358             );
359            
360             tie $Win32::FileOp::copyfile, 'Data::Lazy', sub {
361             new Win32::API("KERNEL32", "CopyFile", [qw(P P I)], 'I')
362             or
363             die "new Win32::API::CopyFile: $!\n";
364             }, &LAZY_READONLY;
365            
366             tie $Win32::FileOp::movefileexDel, 'Data::Lazy', sub {
367             new Win32::API("KERNEL32", "MoveFileEx", ['P','L','N'], 'I')
368             or
369             die "new Win32::API::MoveFileEx for delete: $!\n";
370             }, &LAZY_READONLY;
371            
372             tie $Win32::FileOp::movefileex, 'Data::Lazy', sub {
373             new Win32::API("KERNEL32", "MoveFileEx", ['P','P','N'], 'I')
374             or
375             die "new Win32::API::MoveFileEx: $!\n";
376             }, &LAZY_READONLY;
377            
378             tie $Win32::FileOp::SHAddToRecentDocs, 'Data::Lazy', sub {
379             new Win32::API("shell32", "SHAddToRecentDocs", ['I','P'], 'I')
380             or
381             die "new Win32::API::SHAddToRecentDocs: $!\n";
382             }, &LAZY_READONLY;
383            
384             tie $Win32::FileOp::writeINI, 'Data::Lazy', sub {
385             new Win32::API("KERNEL32", "WritePrivateProfileString", [qw(P P P P)], 'I')
386             or
387             die "new Win32::API::WritePrivateProfileString: $!\n"
388             }, &LAZY_READONLY;
389            
390            
391             tie $Win32::FileOp::writeWININI, 'Data::Lazy', sub {
392             new Win32::API("KERNEL32", "WriteProfileString", [qw(P P P)], 'I')
393             or
394             die "new Win32::API::WriteProfileString: $!\n"
395             }, &LAZY_READONLY;
396            
397             tie $Win32::FileOp::deleteINI, 'Data::Lazy', sub {
398             new Win32::API("KERNEL32", "WritePrivateProfileString", [qw(P P L P)], 'I')
399             or
400             die "new Win32::API::WritePrivateProfileString for delete: $!\n"
401             }, &LAZY_READONLY;
402            
403             tie $Win32::FileOp::deleteWININI, 'Data::Lazy', sub {
404             new Win32::API("KERNEL32", "WriteProfileString", [qw(P P L)], 'I')
405             or
406             die "new Win32::API::WriteProfileString for delete: $!\n"
407             }, &LAZY_READONLY;
408            
409             tie $Win32::FileOp::readINI, 'Data::Lazy', sub {
410             new Win32::API("KERNEL32", "GetPrivateProfileString", [qw(P P P P N P)], 'N')
411             or
412             die "new Win32::API::GetPrivateProfileString: $!\n"
413             }, &LAZY_READONLY;
414            
415             tie $Win32::FileOp::readWININI, 'Data::Lazy', sub {
416             new Win32::API("KERNEL32", "GetProfileString", [qw(P P P P N)], 'N')
417             or
418             die "new Win32::API::GetProfileString: $!\n"
419             }, &LAZY_READONLY;
420            
421             tie $Win32::FileOp::GetOpenFileName, 'Data::Lazy', sub {
422             new Win32::API("comdlg32", "GetOpenFileName", ['P'], 'N')
423             or
424             die "new Win32::API::GetOpenFileName: $!\n"
425             }, &LAZY_READONLY;
426            
427             tie $Win32::FileOp::GetSaveFileName, 'Data::Lazy', sub {
428             new Win32::API("comdlg32", "GetSaveFileName", ['P'], 'N')
429             or
430             die "new Win32::API::GetSaveFileName: $!\n"
431             }, &LAZY_READONLY;
432            
433             tie $Win32::FileOp::CommDlgExtendedError, 'Data::Lazy', sub {
434             new Win32::API("comdlg32", "CommDlgExtendedError", [], 'N')
435             or
436             die "new Win32::API::CommDlgExtendedError: $!\n"
437             }, &LAZY_READONLY;
438            
439            
440             tie $Win32::FileOp::CreateFile, 'Data::Lazy', sub {
441             new Win32::API( "kernel32", "CreateFile", [qw(P N N P N N P)], 'N')
442             or
443             die "new Win32::API::CreateFile: $!\n"
444             }, &LAZY_READONLY;
445            
446             tie $Win32::FileOp::CloseHandle, 'Data::Lazy', sub {
447             new Win32::API( "kernel32", "CloseHandle", ['N'], 'N')
448             or
449             die "new Win32::API::CloseHandle: $!\n"
450             };
451            
452             tie $Win32::FileOp::GetFileSize, 'Data::Lazy', sub {
453             new Win32::API( "kernel32", "GetFileSize", ['N','P'], 'N')
454             or
455             die "new Win32::API::GetFileSize: $!\n"
456             };
457            
458             tie $Win32::FileOp::GetDiskFreeSpaceEx, 'Data::Lazy', sub {
459             new Win32::API( "kernel32", "GetDiskFreeSpaceEx", ['P','P','P','P'], 'N')
460             or
461             die "new Win32::API::GetDiskFreeSpaceEx: $!\n"
462             };
463            
464             tie $Win32::FileOp::DeviceIoControl, 'Data::Lazy', sub {
465             new Win32::API( "kernel32", "DeviceIoControl", ['N', 'N', 'P', 'N', 'P', 'N', 'P', 'P'], 'N')
466             or
467             die "new Win32::API::DeviceIoControl: $!\n"
468             }, &LAZY_READONLY;
469            
470             tie $Win32::FileOp::SHBrowseForFolder, 'Data::Lazy', sub {
471             new Win32::API("shell32", "SHBrowseForFolder", ['P'], 'N')
472             or
473             die "new Win32::API::SHBrowseForFolder: $!\n"
474             }, &LAZY_READONLY;
475            
476             tie $Win32::FileOp::SHGetPathFromIDList, 'Data::Lazy', sub {
477             new Win32::API("shell32", "SHGetPathFromIDList", ['N','P'], 'I')
478             or
479             die "new Win32::API::SHGetPathFromIDList: $!\n"
480             }, &LAZY_READONLY;
481            
482             tie $Win32::FileOp::SHGetSpecialFolderLocation, 'Data::Lazy', sub {
483             new Win32::API("shell32", "SHGetSpecialFolderLocation", ['N','I','P'], 'I')
484             or
485             die "new Win32::API::SHGetSpecialFolderLocation: $!\n"
486             }, &LAZY_READONLY;
487            
488             tie $Win32::FileOp::CoTaskMemFree, 'Data::Lazy', sub {
489             new Win32::API("Ole32", "CoTaskMemFree", ['P'], 'V')
490             or
491             die "new Win32::API::CoTaskMemFree: $!\n"
492             }, &LAZY_READONLY;
493            
494             tie $Win32::FileOp::GetFileVersionInfoSize, 'Data::Lazy', sub {
495             new Win32::API( "version", "GetFileVersionInfoSize", ['P', 'P'], 'N')
496             or
497             die "new Win32::API::GetFileVersionInfoSize: $!\n"
498             }, &LAZY_READONLY;
499            
500             tie $Win32::FileOp::GetFileVersionInfo, 'Data::Lazy', sub {
501             new Win32::API( "version", "GetFileVersionInfo", ['P', 'N', 'N', 'P'], 'N')
502             or
503             die "new Win32::API::GetFileVersionInfo: $!\n"
504             }, &LAZY_READONLY;
505            
506             tie $Win32::FileOp::GetCompressedFileSize, 'Data::Lazy', sub {
507             new Win32::API("kernel32", "GetCompressedFileSize", ['P','P'], 'L')
508             or
509             die "new Win32::API::GetCompressedFileSize: $!\n"
510             }, &LAZY_READONLY;
511            
512             tie $Win32::FileOp::VerQueryValue, 'Data::Lazy', sub {
513             new Win32::API( "version", "VerQueryValue", ['P', 'P', 'P', 'P'], 'N')
514             or
515             die "new Win32::API::VerQueryValue: $!\n"
516             }, &LAZY_READONLY;
517            
518             tie $Win32::FileOp::WNetAddConnection3, 'Data::Lazy', sub {
519             new Win32::API("mpr.dll", "WNetAddConnection3", ['L','P','P','P','L'], 'L')
520             or
521             die "new Win32::API::WNetAddConnection3: $!\n"
522             }, &LAZY_READONLY;
523            
524             tie $Win32::FileOp::WNetGetConnection, 'Data::Lazy', sub {
525             new Win32::API("mpr.dll", "WNetGetConnection", ['P','P','P'], 'L')
526             or
527             die "new Win32::API::WNetGetConnection: $!\n"
528             }, &LAZY_READONLY;
529            
530             tie $Win32::FileOp::WNetCancelConnection2, 'Data::Lazy', sub {
531             new Win32::API("mpr.dll", "WNetCancelConnection2", ['P','L','I'], 'L')
532             or
533             die "new Win32::API::WNetCancelConnection2: $!\n"
534             }, &LAZY_READONLY;
535            
536             tie $Win32::FileOp::GetLogicalDrives, 'Data::Lazy', sub {
537             new Win32::API("kernel32.dll", "GetLogicalDrives", [], 'N')
538             or
539             die "new Win32::API::GetLogicalDrives: $!\n"
540             }, &LAZY_READONLY;
541            
542            
543             tie $Win32::FileOp::QueryDosDevice, 'Data::Lazy', sub {
544             new Win32::API("kernel32.dll", "QueryDosDevice", ['P','P','L'], 'L')
545             or
546             die "new Win32::API::QueryDosDevice: $!\n"
547             }, &LAZY_READONLY;
548            
549             tie $Win32::FileOp::DefineDosDevice, 'Data::Lazy', sub {
550             new Win32::API("kernel32.dll", "DefineDosDevice", ['L','P','P'],'I')
551             or
552             die "new Win32::API::DefineDosDevice: $!\n"
553             }, &LAZY_READONLY;
554            
555             tie $Win32::FileOp::ShellExecute, 'Data::Lazy', sub {
556             new Win32::API("shell32", "ShellExecute", ['N','P','P','P','P','N'], 'I')
557             or
558             die "new Win32::API::ShellExecute: $!\n"
559             }, &LAZY_READONLY;
560            
561             Win32::API::Struct->typedef(
562             SHELLEXECUTEINFO => qw{
563             DWORD cbSize;
564             ULONG fMask;
565             HWND hwnd;
566             LPCTSTR lpVerb;
567             LPCTSTR lpFile;
568             LPCTSTR lpParameters;
569             LPCTSTR lpDirectory;
570             int nShow;
571             HINSTANCE hInstApp;
572             LPVOID lpIDList;
573             LPCTSTR lpClass;
574             HKEY hkeyClass;
575             DWORD dwHotKey;
576             HANDLE hIcon;
577             HANDLE hProcess;
578             }
579             );
580            
581             tie $Win32::FileOp::ShellExecuteEx, 'Data::Lazy', sub {
582             new Win32::API('shell32', 'BOOL ShellExecuteEx(SHELLEXECUTEINFO &shellex)')
583             or
584             die "new Win32::API::ShellExecuteEx: $!\n"
585             }, &LAZY_READONLY;
586            
587            
588             #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
589            
590             sub ShellExecute {
591             my ($operation, $file, $params, $dir, $show, $handle) = @_;
592             if (@_ == 1) { #ShellExecute( $file)
593             $file = $operation;
594             $operation = undef;
595             } elsif (ref $file) { #ShellExecute( $file, {options})
596             ($params, $file, $operation) = ($file, $operation, undef);
597             }
598             if (ref $params) {
599             $params = { map {lc($_) => $params->{$_}} keys %$params}; # lowercase the keys
600             $show = $params->{show};
601             $dir = $params->{dir};
602             $handle = $params->{handle};
603             $params = $params->{params};
604             }
605             if (defined $show) {
606             $show+=0;
607             } else {
608             $show = SW_SHOWDEFAULT;
609             }
610             $handle = Win32::FileOp::GetWindowHandle unless defined $handle;
611             return unless $handle;
612            
613             my $result;
614             if (ref($operation) eq 'ARRAY') {
615             foreach my $op (@$operation) {
616             $result = $Win32::FileOp::ShellExecute->Call( $handle, $op, $file, $params, $dir, $show);
617             last if $result != 31; # 31 = unknown operation
618             }
619             } else {
620             $result = $Win32::FileOp::ShellExecute->Call( $handle, $operation, $file, $params, $dir, $show);
621             }
622            
623             return $result > 32;
624             }
625            
626            
627             sub ShellExecuteEx {
628             my ($operation, $file, $params, $dir, $show, $handle, $expand, $unicode) = @_;
629            
630             if (@_ == 1) { #ShellExecuteEx( $file)
631             $file = $operation;
632             $operation = undef;
633             } elsif (ref $file) { #ShellExecuteEx( $file, {options})
634             ($params, $file, $operation) = ($file, $operation, undef);
635             }
636             if (ref $params) {
637             $params = { map {lc($_) => $params->{$_}} keys %$params}; # lowercase the keys
638             $show = $params->{show};
639             $dir = $params->{dir};
640             $handle = $params->{handle};
641             $expand = $params->{expand};
642             $unicode = $params->{unicode};
643             $params = $params->{params};
644             }
645             if (defined $show) {
646             $show+=0;
647             } else {
648             $show = SW_SHOWDEFAULT;
649             }
650             $handle = Win32::FileOp::GetWindowHandle unless defined $handle;
651            
652             my $mask = ($unicode) ? 0x4000 : 0;
653             $mask |= 0x200 if $expand || ! defined $expand;
654            
655             my $shellex = Win32::API::Struct->new('SHELLEXECUTEINFO');
656            
657             $shellex->{'cbSize'} = $shellex->sizeof();
658            
659             # I would like to offer a value of 0x100 for fMask, to wait for completion, but it does not work.
660             $shellex->{'fMask'} = $mask;
661             $shellex->{'hwnd'} = $handle;
662            
663             $shellex->{'lpVerb'} = (defined($operation) ? $operation."\0" : undef);
664             $shellex->{'lpFile'} = $file."\0";
665             $shellex->{'lpParameters'} = (defined($params) ? $params."\0" : undef);
666             $shellex->{'lpDirectory'} = (defined($dir) ? $dir."\0" : undef);
667             $shellex->{'nShow'} = $show;
668             $shellex->{'hInstApp'} = 0;
669            
670             return $Win32::FileOp::ShellExecuteEx->Call($shellex);
671             }
672            
673            
674             #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
675            
676             sub Recycle {
677             &DeleteEx (@_, FOF_ALLOWUNDO | FOF_NOCONFIRMATION | FOF_SILENT |
678             FOF_NOERRORUI);
679             }
680            
681             sub RecycleConfirm { &DeleteEx (@_, FOF_ALLOWUNDO); }
682            
683             sub RecycleEx { my $opt = pop; $opt |= FOF_ALLOWUNDO; &DeleteEx (@_, $opt); }
684            
685             sub Delete {
686             &DeleteEx (@_, FOF_NOCONFIRMATION | FOF_SILENT | FOF_NOERRORUI);
687             }
688            
689             sub DeleteConfirm { &DeleteEx (@_, FOF_CREATEPROGRESSDLG); }
690            
691             sub DeleteEx {
692             undef $Win32::FileOp::Error;
693             my $options = pop;
694             my ($opstruct, $filename);
695             my @files = map {if (/[*?]/) {glob($_)} elsif (-e $_) {$_} else {()}} @_; # since we change the names, make a copy of the list
696             return unless @files;
697            
698             # pass all files at once, join them by \0 and end by \0\0
699            
700             # fix to full paths
701             Relative2Absolute @files;
702            
703             $filename = join "\0", @files;
704             $filename .= "\0\0"; # double term the filename
705            
706             my $handle = Win32::FileOp::GetWindowHandle;
707            
708             # pack fileop structure (really more like lLppIilP)
709             # sizeof args = l4, L4, p4, p4, I4, i4, l4, P4 = 32 bytes
710             if ($Win32::FileOp::ProgressTitle and $options & FOF_SIMPLEPROGRESS) {
711             $Win32::FileOp::ProgressTitle .= "\0" unless $Win32::FileOp::ProgressTitle =~ /\0$/;
712             $opstruct = pack ('LLpLILC2p', $handle, FO_DELETE,
713             $filename, 0, $options, 0, 0,0, $Win32::FileOp::ProgressTitle);
714             } else {
715             $opstruct = pack ('LLpLILLL', $handle, FO_DELETE,
716             $filename, 0, $options, 0, 0, 0);
717             }
718             # call delete SHFileOperation with structure
719            
720             my $ret = $Win32::FileOp::fileop->Call($opstruct);
721             return 1 if $ret == 0;
722            
723             $Win32::FileOp::Error = (exists ($Win32::FileOp::SHFileOperation_ret{$ret}) ? $Win32::FileOp::SHFileOperation_ret{$ret} : "Unknown result code $ret");
724             return;
725             }
726            
727             #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
728            
729             sub RecycleConfirmEach { &_DeleteConfirmEach (@_, FOF_ALLOWUNDO); }
730            
731             sub DeleteConfirmEach { &_DeleteConfirmEach (@_, FOF_CREATEPROGRESSDLG); }
732            
733             sub _DeleteConfirmEach {
734             undef $Win32::FileOp::Error;
735             my $options = pop;
736            
737             return unless @_;
738            
739             my $res = 0;
740             my ($filename,$opstruct);
741             my $handle = Win32::FileOp::GetWindowHandle;
742            
743             while (defined($filename = shift)) {
744            
745             Relative2Absolute $filename;
746             $filename .= "\0\0"; # double term the filename
747             my $was = -e $filename;
748            
749             if ($Win32::FileOp::ProgressTitle and $options & FOF_SIMPLEPROGRESS) {
750             $Win32::FileOp::ProgressTitle .= "\0" unless $Win32::FileOp::ProgressTitle =~ /\0$/;
751             $opstruct = pack ('LLpLILC2p', $handle, FO_DELETE,
752             $filename, 0, $options, 0, 0,0, $Win32::FileOp::ProgressTitle);
753             } else {
754             $opstruct = pack ('LLpLILLL', $handle, FO_DELETE,
755             $filename, 0, $options, 0, 0, 0);
756             }
757            
758             my $ret = $Win32::FileOp::fileop->Call($opstruct);
759             if ($ret == 0) {
760             $res++ if ($was and !-e $filename);
761             } else {
762             $Win32::FileOp::Error = (exists ($Win32::FileOp::SHFileOperation_ret{$ret}) ? $Win32::FileOp::SHFileOperation_ret{$ret} : "Unknown result code $ret");
763             }
764             }
765             $res;
766             }
767            
768             #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
769            
770             sub Copy {
771             &_MoveOrCopyEx (@_, FOF_NOCONFIRMATION | FOF_NOCONFIRMMKDIR |
772             FOF_SILENT # | FOF_NOERRORUI
773             , FO_COPY);
774             }
775            
776             sub CopyConfirm { &_MoveOrCopyEx (@_, FOF_CREATEPROGRESSDLG, FO_COPY); }
777            
778             *CopyConfirmEach = \&CopyConfirm;
779            
780             sub CopyEx { &_MoveOrCopyEx (@_, FO_COPY); }
781            
782             sub Move {
783             &_MoveOrCopyEx (@_, FOF_NOCONFIRMATION | FOF_NOCONFIRMMKDIR | FOF_SILENT # | FOF_NOERRORUI
784             , FO_MOVE);
785             }
786            
787             sub MoveConfirm { &_MoveOrCopyEx (@_, FOF_CREATEPROGRESSDLG, FO_MOVE); }
788            
789             *MoveConfirmEach = \&MoveConfirm;
790            
791             sub MoveEx { &_MoveOrCopyEx (@_, FO_MOVE); }
792            
793             sub _MoveOrCopyEx {
794             undef $Win32::FileOp::Error;
795             my $func = pop;
796             my $options = pop;
797             my ($opstruct, $filename, $hash, $res, $from, $to);
798            
799             if (@_ % 2) { die "Wrong number of arguments to Win32::FileOp::CopyEx!\n" };
800            
801             my $handle = Win32::FileOp::GetWindowHandle;
802            
803             my $i = 0;
804             while (defined ($from = $_[$i++]) and defined ($to = $_[$i++])) {
805            
806             # fix to full paths
807            
808             if (UNIVERSAL::isa($from, "ARRAY")) {
809            
810             my @files = map {
811             my $s = $_;
812             Relative2Absolute $s;
813             $s;
814             } @$from;
815             $from = join "\0", @files;
816            
817             } else {
818            
819             Relative2Absolute $from;
820             $from =~ s#/#\\#g;
821            
822             # if to ends in slash, get filename from from
823            
824             if ($to =~ m{[\\/]$} and $to !~ /^\w:\\$/) {
825             my $tmp = $from;
826             $tmp =~ s#^.*[\\/](.*?)$#$1#;
827             $to .= $tmp;
828             }
829             $to .= '\\' if $to =~ /:$/;
830             }
831             $from .= "\0\0"; # double term the filename
832            
833             my $options = $options;
834             if (UNIVERSAL::isa($to, "ARRAY")) {
835             my $strto='';
836             foreach (@$to) {
837             $strto .= RelativeToAbsolute($_) . "\0";
838             }
839             $to = $strto;
840             $options |= FOF_MULTIDESTFILES;
841             } else {
842             Relative2Absolute($to);
843             }
844             $to .= "\0\0"; # double term the filename
845             $to =~ s#/#\\#g;
846            
847             if ($Win32::FileOp::ProgressTitle and $options & FOF_SIMPLEPROGRESS) {
848            
849             $Win32::FileOp::ProgressTitle .= "\0" unless $Win32::FileOp::ProgressTitle =~ /\0$/;
850             $opstruct = pack ('LLppILC2p', $handle, $func,
851             $from, $to, $options, 0, 0,0, $Win32::FileOp::ProgressTitle);
852            
853             } else {
854            
855             $opstruct = pack ('LLppILLL', $handle, $func,
856             $from, $to, $options, 0, 0, 0);
857            
858             }
859            
860             unless ($Win32::FileOp::fileop->Call($opstruct)) {
861             $res++;
862             } else {
863             return;
864             }
865             }
866             $res;
867             }
868            
869             #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
870            
871             sub MoveFile {
872             MoveFileEx(@_,MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED);
873             }
874            
875             sub MoveAtReboot {
876             if (Win32::IsWinNT) {
877             MoveFileEx(@_,MOVEFILE_REPLACE_EXISTING | MOVEFILE_DELAY_UNTIL_REBOOT);
878             } else {
879             undef $Win32::FileOp::Error;
880             my @a;
881             my $i=0;
882             while ($_[$i]) {
883             $a[$i+1]= Win32::GetShortPathName $_[$i];
884             ($a[$i]= $_[$i+1]) =~ s#^(.*)([/\\].*?)$#Win32::GetShortPathName($1).$2#e;
885             $i+=2;
886             }
887             Relative2Absolute(@a);
888             WriteToINI($ENV{WINDIR}.'\\wininit.ini','Rename',@a);
889             }
890             }
891            
892             sub CopyFile {
893             undef $Win32::FileOp::Error;
894             my ($from,$to);
895            
896             while (defined($from = shift) and defined($to = shift)) {
897             # Relative2Absolute($to,$from);
898             $to .= "\0";
899             $from .= "\0";
900             $Win32::FileOp::copyfile->Call($from,$to, 0);
901             }
902             }
903            
904            
905             sub DeleteAtReboot {
906             undef $Win32::FileOp::Error;
907             if (Win32::IsWinNT) {
908             my $file;
909             while (defined($file = shift)) {
910             Relative2Absolute($file);
911             $Win32::FileOp::movefileexDel->Call($file, 0, MOVEFILE_DELAY_UNTIL_REBOOT);
912             }
913             } else {
914             my @a;
915             foreach (@_) {
916             my $tmp=$_;
917             Relative2Absolute($tmp);
918             $tmp = Win32::GetShortPathName $tmp;
919             push @a, 'NUL', $tmp;
920             }
921             WriteToINI($ENV{WINDIR}.'\\wininit.ini','Rename',@a);
922             }
923             1;
924             }
925            
926             sub MoveFileEx {
927             undef $Win32::FileOp::Error;
928             my $options = pop;
929            
930             my ($from,$to);
931             while (defined($from = shift) and defined($to = shift)) {
932             Relative2Absolute($to,$from);
933             $to .= "\0";
934             $from .= "\0";
935             $Win32::FileOp::movefileex->Call($from,$to, $options);
936             }
937             }
938            
939             #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
940            
941             sub UpdateDir {
942             undef $Win32::FileOp::Error;
943             local ($Win32::FileOp::from,$Win32::FileOp::to,$Win32::FileOp::callback) = @_;
944             -d $Win32::FileOp::from or return;
945             -d $Win32::FileOp::to or File::Path::mkpath $Win32::FileOp::to, 0777 or return;
946             Relative2Absolute($Win32::FileOp::to);
947             my $olddir = cwd;
948             chdir $Win32::FileOp::from;
949             find(\&_UpdateDir, '.');
950             chdir $olddir;
951             }
952            
953             sub _UpdateDir {
954             undef $Win32::FileOp::Error;
955             my $fullto = "$Win32::FileOp::to\\$File::Find::dir\\$_";
956             $fullto =~ s#/#\\#g;
957             $fullto =~ s#\\\.\\#\\#;
958             if (-d $_) {
959             return if /^\.\.?$/ or -d $fullto;
960             mkdir $fullto, 0777;
961             } else {
962             my $age = -M($fullto);
963             if (! -e($fullto) or $age > -M($_)) {
964             if (! defined $Win32::FileOp::callback or &$Win32::FileOp::callback()) {
965             CopyFile $_, $fullto;
966             }
967             }
968             }
969             }
970            
971            
972             sub FillInDir {
973             undef $Win32::FileOp::Error;
974             local ($Win32::FileOp::from,$Win32::FileOp::to,$Win32::FileOp::callback) = @_;
975             -d $Win32::FileOp::from or return;
976             -d $Win32::FileOp::to or File::Path::mkpath $Win32::FileOp::to, 0777 or return;
977             Relative2Absolute($Win32::FileOp::to);
978             my $olddir = cwd;
979             chdir $Win32::FileOp::from;
980             find(\&_FillInDir, '.');
981             chdir $olddir;
982             }
983            
984             sub _FillInDir {
985             my $fullto = "$Win32::FileOp::to\\$File::Find::dir\\$_";
986             $fullto =~ s#/#\\#g;
987             $fullto =~ s#\\\.\\#\\#;
988             if (-d $_) {
989             return if /^\.\.?$/ or -d $fullto;
990             mkdir $fullto, 0777;
991             } else {
992             if (! -e($fullto)) {
993             if (! defined $Win32::FileOp::callback or &$Win32::FileOp::callback()) {
994             CopyFile $_, $fullto;
995             }
996             }
997             }
998             }
999            
1000             #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1001            
1002             sub AddToRecentDocs {
1003             undef $Win32::FileOp::Error;
1004            
1005             my $file;
1006             my $res=0;
1007             while (defined($file = shift)) {
1008             next unless -e $file;
1009             Relative2Absolute($file);
1010             $file .= "\0";
1011             $Win32::FileOp::SHAddToRecentDocs->Call(2,$file);
1012             $res++;
1013             }
1014             $res;
1015             }
1016            
1017             sub EmptyRecentDocs {
1018             undef $Win32::FileOp::Error;
1019             my $x = 0;
1020             $Win32::FileOp::SHAddToRecentDocs->Call(2,$x);
1021             }
1022            
1023             #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1024            
1025             sub WriteToINI {
1026             undef $Win32::FileOp::Error;
1027             my ($INI) = RelativeToAbsolute(shift());$INI .= "\0";
1028             my $section = shift;$section .= "\0";
1029             my ($name,$value);
1030             while (defined($name = shift) and defined($value = shift)) {
1031             $name .= "\0";$value .= "\0";
1032             $Win32::FileOp::writeINI->Call($section,$name,$value,$INI)
1033             or return;
1034             }
1035             1;
1036             }
1037            
1038             sub WriteToWININI {
1039             undef $Win32::FileOp::Error;
1040             my $section = shift;$section .= "\0";
1041             my ($name,$value);
1042             while (defined($name = shift) and defined($value = shift)) {
1043             $name .= "\0";$value .= "\0";
1044             $Win32::FileOp::writeWININI->Call($section,$name,$value)
1045             or return;
1046             }
1047             1;
1048             }
1049            
1050             sub DeleteFromINI {
1051             undef $Win32::FileOp::Error;
1052             my ($INI) = RelativeToAbsolute(shift());$INI .= "\0";
1053             my $section = shift;$section .= "\0";
1054             my $name;
1055             while (defined($name = shift)) {
1056             $name .= "\0";
1057             $Win32::FileOp::deleteINI->Call($section,$name,0,$INI)
1058             or return;
1059             }
1060             1;
1061             }
1062            
1063             sub DeleteFromWININI {
1064             undef $Win32::FileOp::Error;
1065             my $section = shift;$section .= "\0";
1066             my $name;
1067             while (defined($name = shift)) {
1068             $name .= "\0";
1069             $Win32::FileOp::deleteWININI->Call($section,$name,0)
1070             or return;
1071             }
1072             1;
1073             }
1074            
1075             sub ReadINI {
1076             undef $Win32::FileOp::Error;
1077             my ($INI) = RelativeToAbsolute(shift());$INI .= "\0";
1078             my $section = shift;$section .= "\0";
1079             my $name = shift;$name .= "\0";
1080             my $default = shift;$default .= "\0";
1081             my $value = _ReadINI($section,$name,$default,$INI);
1082            
1083             $value =~ s/\0.*$// or return;
1084             return $value;
1085             }
1086            
1087             # MTY hack : Michael Yamada
1088             sub ReadINISectionKeys {
1089             undef $Win32::FileOp::Error;
1090             my ($INI) = RelativeToAbsolute(shift());$INI='win.ini' unless $INI;$INI .= "\0";
1091             my $section = shift;$section .= "\0";
1092             my $name = 0; # pass null to API
1093             my $default = "\0";
1094             my @values;
1095            
1096             @values = split(/\0/,_ReadINI($section,$name,$default,$INI));
1097             @{$_[0]} = @values if (UNIVERSAL::isa($_[0], "ARRAY"));
1098             return wantarray() ? @values : (@values ? \@values : undef);
1099             }
1100             # END MTY Hack
1101            
1102             sub ReadINISections {
1103             undef $Win32::FileOp::Error;
1104             my ($INI) = RelativeToAbsolute(shift());$INI='win.ini' unless $INI;$INI .= "\0";
1105             my $section = 0; # pass null to API
1106             my $name = 0;
1107             my $default = "\0";
1108             my @values;
1109            
1110             @values = split(/\0/,_ReadINI($section,$name,$default,$INI));
1111             @{$_[0]} = @values if (UNIVERSAL::isa($_[0], "ARRAY"));
1112             return wantarray() ? @values : (@values ? \@values : undef);
1113             }
1114            
1115            
1116             sub ReadWININI {
1117             undef $Win32::FileOp::Error;
1118             my $section = shift;$section .= "\0";
1119             my $name = shift;$name .= "\0";
1120             my $default = shift;$default .= "\0";
1121             my $value = "\0" x 2048;
1122            
1123             $Win32::FileOp::readWININI->Call($section,$name,$default,$value,256)
1124             or return;
1125            
1126             $value =~ s/\0.*$// or return;
1127             return $value;
1128             }
1129            
1130             sub _ReadINI { # $section, $name, $default, $INI
1131             my $size = 10;#24;
1132             my $value = "\0" x $size; # large buffer to accomodate many keys
1133             my $retsize = $size-2;
1134             while ($size-$retsize <=2) {
1135             $size*=2;$value = "\0" x $size;
1136             $retsize = $Win32::FileOp::readINI->Call($_[0],$_[1],$_[2],$value,$size,$_[3])
1137             or return '';
1138             }
1139             return $value;
1140             }
1141            
1142             #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1143            
1144             sub OpenDialog {
1145             OpenOrSaveDialog($Win32::FileOp::GetOpenFileName,@_);
1146             }
1147            
1148             sub SaveAsDialog {
1149             OpenOrSaveDialog($Win32::FileOp::GetSaveFileName,@_);
1150             }
1151            
1152             sub OpenOrSaveDialog {
1153             undef $Win32::FileOp::Error;
1154             my $fun = shift;
1155             my $params;
1156             if (UNIVERSAL::isa($_[0], "HASH")) {
1157             $params = $_[0];
1158             $params->{filename} = $_[1] if defined $_[1];
1159             } else {
1160             if (@_ % 2) {
1161             my $filename = pop;
1162             $params = {@_};
1163             $params->{filename} = $filename;
1164             } else {
1165             $params = {@_};
1166             }
1167             }
1168             foreach (grep {s/^-//} keys %$params) {$params->{$_} = $params->{"-$_"};delete $params->{"-$_"}};
1169            
1170             $params->{handle} = 'self' unless exists $params->{handle};
1171             $params->{options} = 0 unless exists $params->{options};
1172            
1173            
1174             my $lpstrFilter = '';
1175             if (UNIVERSAL::isa($params->{filters}, "HASH")) {
1176             foreach (keys %{$params->{filters}}) {
1177             $lpstrFilter .= $_ . "\0" . $params->{filters}->{$_} . "\0";
1178             }
1179             } elsif (UNIVERSAL::isa($params->{filters}, "ARRAY")) {
1180             my ($title,$filter,$i);
1181             $i=0;$lpstrFilter='';
1182             while ($title = ${$params->{filters}}[$i++] and $filter = ${$params->{filters}}[$i++]) {
1183             $lpstrFilter .= $title . "\0" . $filter . "\0";
1184             }
1185             $params->{defaultfilter} = $title if $title && !$params->{defaultfilter};
1186             } elsif ($params->{filters}) {
1187             $lpstrFilter = $params->{filters};
1188             $lpstrFilter .= "\0\0" unless $lpstrFilter =~ /\0\0$/
1189             } else {
1190             $lpstrFilter = "\0\0";
1191             }
1192            
1193             local $^W = 0;
1194            
1195             my $nFilterIndex = $params->{defaultfilter};
1196             $nFilterIndex = 1 unless $nFilterIndex>0; # to be sure it's a reasonable number
1197            
1198             my $lpstrFile = $params->{filename}."\0".
1199             ($params->{options} & OFN_ALLOWMULTISELECT
1200             ? ' ' x ($Win32::FileOp::BufferSize - length $params->{filename})
1201             : ' ' x 256
1202             );
1203            
1204             my $lpstrFileTitle = "\0";
1205             my $lpstrInitialDir = $params->{dir} . "\0";
1206             my $lpstrTitle = $params->{title} . "\0";
1207             my $Flags = $params->{options};
1208             my $nFileExtension = "\0\0";
1209             my $lpstrDefExt = $params->{extension}."\0";
1210             my $lpTemplateName = "\0";
1211             my $Handle = $params->{handle};
1212             if ($Handle =~ /^self$/i) {$Handle = GetWindowHandle()};
1213            
1214             # my $struct = pack "LLLpLLLpLpLppLIIpLLp",
1215             my $struct = pack "LLLpLLLpLpLppLIppLLp",
1216             (
1217             76, #'lStructSize' # DWORD
1218             $Handle, #'hwndOwner' # HWND
1219             0, #'hInstance' # HINSTANCE
1220             $lpstrFilter, #'lpstrFilter' # LPCTSTR
1221             0,
1222             0,
1223             # $lpstrCustomFilter, #'lpstrCustomFilter' # LPTSTR
1224             # length $lpstrCustomFilter, #'nMaxCustFilter' # DWORD
1225             #I'm not able to make it work with CustomFilter
1226            
1227             $nFilterIndex, #'nFilterIndex' # DWORD
1228             $lpstrFile, #'lpstrFile' # LPTSTR
1229             length $lpstrFile, #'nMaxFile' # DWORD
1230             $lpstrFileTitle, #'lpstrFileTitle' # LPTSTR
1231             length $lpstrFileTitle, #'nMaxFileTitle' # DWORD
1232             $lpstrInitialDir, #'lpstrInitialDir' # LPCTSTR
1233             $lpstrTitle, #'lpstrTitle' # LPCTSTR
1234             $Flags, #'Flags' # DWORD
1235             0, #'nFileOffset' # WORD
1236             # 0, #'nFileExtension' # WORD
1237             $nFileExtension, #'nFileExtension' # WORD
1238             $lpstrDefExt, #'lpstrDefExt' # LPCTSTR
1239             0, #'lCustData' # DWORD
1240             0, #'lpfnHook' # LPOFNHOOKPROC
1241             $lpTemplateName #'lpTemplateName' # LPCTSTR
1242             );
1243            
1244             if ($fun->Call($struct)) {
1245             $Flags = unpack("L", substr $struct, 52, 4);
1246             $Win32::FileOp::SelectedFilter = unpack("L", substr $struct, 6*4, 4);
1247            
1248             $Win32::FileOp::ReadOnly = ($Flags & OFN_READONLY);
1249            
1250             if ($Flags & OFN_ALLOWMULTISELECT) {
1251             $lpstrFile =~ s/\0\0.*$//;
1252             my @result;
1253             if ($Flags & OFN_EXPLORER) {
1254             @result = split "\0", $lpstrFile;
1255             } else {
1256             @result = split " ", $lpstrFile;
1257             }
1258             my $dir = shift @result;
1259             $dir =~ s/\\$//; # only happens in root
1260             return $dir unless @result;
1261             return map {$dir . '\\' . $_} @result;
1262             } else {
1263             $lpstrFile =~ s/\0.*$//;
1264             return $lpstrFile;
1265             }
1266             # } else {
1267             # my $err = $Win32::FileOp::Error = $Win32::FileOp::CommDlgExtendedError->Call();
1268             # if ($err == 12291) {
1269             # print "Sh!t, the buffer was too small!\n";
1270             # $fun->Call($struct);
1271             # }
1272             }
1273             return;
1274             }
1275            
1276             #=======================
1277            
1278             sub BrowseForFolder {
1279             undef $Win32::FileOp::Error;
1280             my $lpszTitle = shift() || "\0";
1281             my $nFolder = shift();
1282             my $ulFlags= (shift() || 0) | 0x0000;
1283             my $hwndOwner = (defined $_[0] ? shift() : GetWindowHandle());
1284            
1285             my ($pidlRoot, $pszDisplayName, $lpfn, $lParam, $iImage, $pszPath)
1286             = ("\0"x260, "\0"x260, 0, 0, 0, "\0"x260 );
1287            
1288             $nFolder = CSIDL_DESKTOP() unless defined $nFolder;
1289            
1290             $Win32::FileOp::SHGetSpecialFolderLocation->Call($hwndOwner, $nFolder, $pidlRoot)
1291             and return;
1292            
1293             my $pidlRootUnpacked = hex unpack 'H*',(join'', reverse split//, $pidlRoot);
1294            
1295             my $browseinfo = pack 'LLppILLI',
1296             ($hwndOwner, $pidlRootUnpacked, $pszDisplayName, $lpszTitle,
1297             $ulFlags, $lpfn, $lParam, $iImage);
1298            
1299             my $bool = $Win32::FileOp::SHGetPathFromIDList->Call(
1300             $Win32::FileOp::SHBrowseForFolder->Call($browseinfo),
1301             $pszPath
1302             );
1303            
1304             $pszPath =~ s/\0.*$//s;
1305            
1306             $Win32::FileOp::CoTaskMemFree->Call($pidlRoot);
1307             $bool ? $pszPath : undef;
1308             }
1309            
1310             #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1311            
1312             sub FindInPATH {
1313             undef $Win32::FileOp::Error;
1314             my $file = shift;
1315             return $file if -e $file;
1316             foreach (split ';',$ENV{PATH}) {
1317             return $_.'/'.$file if -e $_.'/'.$file;
1318             }
1319             return;
1320             }
1321             *FindInPath = \&FindInPATH;
1322            
1323            
1324             #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1325            
1326             sub GetDesktopHandle {
1327             undef $Win32::FileOp::Error;
1328             my ($function, $handle);
1329            
1330             # if handle already saved, use that one
1331            
1332             return $Win32::FileOp::DesktopHandle if $Win32::FileOp::DesktopHandle != 0;
1333            
1334             # find GetDesktopWindow routine
1335            
1336             $function = new Win32::API("user32", "GetDesktopWindow", [], 'I') or
1337             die "new Win32::API::GetDesktopHandle: $!\n";
1338            
1339             # call it, get window handle back, save it and return it
1340            
1341             $Win32::FileOp::DesktopHandle = $function->Call();
1342            
1343             }
1344            
1345             sub GetWindowHandle {
1346             undef $Win32::FileOp::Error;
1347             if (! $Win32::FileOp::WindowHandle) {
1348             my $GetConsoleTitle = new Win32::API("kernel32", "GetConsoleTitle", ['P','N'],'N');
1349             my $SetConsoleTitle = new Win32::API("kernel32", "SetConsoleTitle", ['P'],'N');
1350             my $SleepEx = new Win32::API("kernel32", "SleepEx", ['N','I'],'V');
1351             my $FindWindow = new Win32::API("user32", "FindWindow", ['P','P'],'N');
1352            
1353             my $oldtitle = " " x 1024;
1354             $GetConsoleTitle->Call($oldtitle, 1024);
1355             my $newtitle = sprintf("PERL-%d-%d", Win32::GetTickCount(), $$);
1356             $SetConsoleTitle->Call($newtitle);
1357             $SleepEx->Call(40,1);
1358             $Win32::FileOp::WindowHandle = $FindWindow->Call(0, $newtitle);
1359             $SetConsoleTitle->Call($oldtitle);
1360             }
1361             return $Win32::FileOp::WindowHandle;
1362             }
1363            
1364             #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1365            
1366             sub SetCompression {
1367             undef $Win32::FileOp::Error;
1368             my $file;
1369             my $flag;
1370             if ($_[-1] eq ($_[-1]+0)) {
1371             $flag = pop
1372             } else {
1373             $flag = 1;
1374             }
1375             $_[0] = $_ unless @_;
1376             while (defined($file = shift)) {
1377            
1378             #print "\t$file\n";
1379            
1380             my $handle;
1381             $handle = $Win32::FileOp::CreateFile->Call($file, 0xc0000000, # FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES |
1382             7, 0, 3, 0x2000000, 0);
1383             # $handle = $Win32::FileOp::CreateFile->Call($file, FILE_FLAG_WRITE_THROUGH | FILE_FLAG_OVERLAPPED,
1384             # FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0,
1385             # OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
1386            
1387             if($handle != -1) {
1388             my $br = pack("L", 0);
1389             my $inbuffer = pack("S", $flag);
1390             my $comp = $Win32::FileOp::DeviceIoControl->Call(
1391             $handle, 639040, $inbuffer, 2, 0, 0, $br, 0,
1392             );
1393             if(!$comp) {
1394             $Win32::FileOp::Error = "DeviceIoControl failed: "
1395             . Win32::FormatMessage(Win32::GetLastError);
1396             return;
1397             }
1398             $Win32::FileOp::CloseHandle->Call($handle);
1399             next;
1400             } else {
1401             $Win32::FileOp::Error = "CreateFile failed: "
1402             . Win32::FormatMessage(Win32::GetLastError);
1403             return;
1404             }
1405             }
1406             return 1;
1407             }
1408            
1409             sub GetCompression {
1410             undef $Win32::FileOp::Error;
1411             my ($file) = @_;
1412             $file = $_ unless defined $file;
1413             my $permission = 0x0080; # FILE_READ_ATTRIBUTES
1414             my $handle = $Win32::FileOp::CreateFile->Call($file, $permission, 0, 0, 3, 0, 0);
1415             if($handle != -1) {
1416             my $br = pack("L", 0);
1417             my $outbuffer = pack("S", 0);
1418             my $comp = $Win32::FileOp::DeviceIoControl->Call(
1419             $handle, 589884, 0, 0, $outbuffer, 2, $br, 0,
1420             );
1421             if(!$comp) {
1422             $Win32::FileOp::Error = "DeviceIoControl failed: "
1423             . Win32::FormatMessage(Win32::GetLastError);
1424             return;
1425             }
1426             $Win32::FileOp::CloseHandle->Call($handle);
1427             return unpack("S", $outbuffer);
1428             } else {
1429             $Win32::FileOp::Error = "CreateFile failed: "
1430             . Win32::FormatMessage(Win32::GetLastError);
1431             return;
1432             }
1433             }
1434            
1435             sub Compress {SetCompression(@_,1)}
1436             sub Uncompress {SetCompression(@_,0)}
1437             *UnCompress = \&Uncompress;
1438             sub Compressed {&GetCompression}
1439            
1440             sub CompressedSize {
1441             my $file = $_[0];
1442             my $hsize = "\0" x 4;
1443             my $lsize = $Win32::FileOp::GetCompressedFileSize->Call( $file, $hsize);
1444             return $lsize + 0x10000*unpack('L',$hsize);
1445             }
1446            
1447             sub UncompressDir {
1448             undef $Win32::FileOp::Error;
1449             if (ref $_[-1] eq 'CODE') {
1450             my $fun = pop;
1451             find( sub{Uncompress if &$fun}, @_);
1452             } else {
1453             find( sub {Uncompress}, @_);
1454             }
1455             }
1456             *UnCompressDir = \&UncompressDir;
1457            
1458             sub CompressDir {
1459             undef $Win32::FileOp::Error;
1460             if (ref $_[-1] eq 'CODE') {
1461             my $fun = pop;
1462             find( sub{Compress if &$fun}, @_);
1463             } else {
1464             find( sub {Compress}, @_);
1465             }
1466             }
1467            
1468             #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1469            
1470             sub GetLargeFileSize {
1471             undef $Win32::FileOp::Error;
1472             my ($file) = @_;
1473             my $handle = $Win32::FileOp::CreateFile->Call($file, 0x0080, 0, 0, 3, 0, 0); # 0x0080 = FILE_READ_ATTRIBUTES
1474             if($handle != -1) {
1475             my $buff = "\0" x 4;
1476             my $size1 = $Win32::FileOp::GetFileSize->Call(
1477             $handle, $buff
1478             );
1479             $Win32::FileOp::CloseHandle->Call($handle);
1480             $size1 = $size1 & 0xFFFFFFFF;
1481             if (wantarray()) {
1482             return ($size1,unpack('L',$buff));
1483             } else {
1484             return unpack('L',$buff)*0xFFFFFFFF + $size1
1485             }
1486             } else {
1487             $Win32::FileOp::Error = "CreateFile failed: " . Win32::FormatMessage(Win32::GetLastError);
1488             return;
1489             }
1490             }
1491            
1492             sub GetDiskFreeSpace {
1493             undef $Win32::FileOp::Error;
1494             my ($file) = @_;
1495             $file .= '\\' if $file =~ /^\\\\/ and $file !~ /\\$/;
1496             $file .= ':' if $file =~ /^[a-zA-Z]$/;
1497             my ($freePerUser,$total, $free) = ("\x0" x 8) x 3;
1498            
1499             $Win32::FileOp::GetDiskFreeSpaceEx->Call($file, $freePerUser,$total, $free)
1500             or return;
1501            
1502             if (wantarray()) {
1503             my @res;
1504             for ($freePerUser,$total, $free) {
1505             my ($lo,$hi) = unpack('LL',$_);
1506             push @res, ($hi & 0xFFFFFFFF) * 0xFFFFFFFF + ($lo & 0xFFFFFFFF);
1507             }
1508             return @res;
1509             } else {
1510             my ($lo,$hi) = unpack('LL',$freePerUser);
1511             return ($hi & 0xFFFFFFFF) * 0xFFFFFFFF + ($lo & 0xFFFFFFFF);
1512             }
1513             }
1514            
1515             #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1516            
1517             sub FreeDriveLetters {
1518             undef $Win32::FileOp::Error;
1519             my (@dr, $i);
1520            
1521             my $bitmask = $Win32::FileOp::GetLogicalDrives->Call();
1522             for $i(0..25) {
1523             push (@dr, ('A'..'Z')[$i]) unless $bitmask & 2**$i;
1524             }
1525             @dr;
1526             }
1527            
1528            
1529             sub Map {
1530             undef $Win32::FileOp::Error;
1531             my $disk = $_[0] =~ m#^[\\/]# ? (FreeDriveLetters())[-1] : shift;
1532             if (!defined $disk or $disk eq '') {
1533             undef $disk;
1534             } else {
1535             $disk =~ s/^(\w)(:)?$/$1:/;
1536             $disk .= "\0";
1537             }
1538             my $type = 0; # RESOURCETYPE_ANY
1539             my $share = shift || croak('Ussage: Win32::FileOp::Map([$drive,]$share[,\%options])',"\n");
1540             $share =~ s{/}{\\}g;
1541             $share .= "\0";
1542            
1543             my $opt = shift || {};
1544             croak 'Ussage: Win32::FileOp::Map([$drive,]$share[,\%options])',"\n"
1545             unless (UNIVERSAL::isa($opt, "HASH"));
1546             my $username = 0;
1547             if (defined $opt->{user}) {
1548             $username = $opt->{user}."\0";
1549             # $username =~ s/(.)/\0$1/g if Win32::IsWinNT;
1550             }
1551             my $passwd = 0;
1552             if (defined $opt->{passwd} or defined $opt->{password} or defined $opt->{pwd}) {
1553             $passwd = ($opt->{passwd} || $opt->{password} || $opt->{pwd})."\0";
1554             # $passwd =~ s/(.)/\0$1/g if Win32::IsWinNT;
1555             }
1556             my $options = 0;
1557             $options += CONNECT_UPDATE_PROFILE if $opt->{persistent};
1558             $options += CONNECT_INTERACTIVE if $opt->{interactive};
1559             $options += CONNECT_PROMPT if $opt->{prompt};
1560             $options += CONNECT_REDIRECT if $opt->{redirect};
1561            
1562             $options += CONNECT_UPDATE_RECENT;
1563            
1564             my $struct = pack('LLLLppLL',0,$type,0,0,$disk,$share,0,0);
1565             my $res;
1566             my $handle = undef;
1567             if ($opt->{interactive}) {
1568             $handle = $opt->{interactive}+0;
1569             $handle = GetWindowHandle() || GetDesktopHandle();
1570             }
1571            
1572             if ($res = $Win32::FileOp::WNetAddConnection3->Call( $handle, $struct, $passwd, $username, $options)) {
1573             if (($res == 1202 or $res == 85) and ($opt->{overwrite} or $opt->{force_overwrite})) {
1574             Unmap($disk,{force => $opt->{force_overwrite}})
1575             or return;
1576             $Win32::FileOp::WNetAddConnection3->Call( $handle, $struct, $passwd, $username, $options)
1577             and return;
1578             } elsif ($res == 997) { # Overlapped I/O operation is in progress.
1579             return 1;
1580             } else {
1581             return;
1582             }
1583             }
1584             if (defined $disk and $disk) {$disk} else {1};
1585             }
1586            
1587             sub Connect {
1588             Map(undef,@_);
1589             }
1590            
1591             sub Disconnect {
1592             undef $Win32::FileOp::Error;
1593             croak 'Ussage: Win32::FileOp::Map([$drive,]$share[,\%options])',"\n"
1594             unless @_;
1595             my $disk = shift() . "\0";$disk =~ s/^(\w)\0$/$1:\0/;
1596             my $opt = shift() || {};
1597             croak 'Ussage: Win32::FileOp::Map([$drive,]$share[,\%options])',"\n"
1598             unless (UNIVERSAL::isa($opt, "HASH"));
1599             my $options = $opt->{persistent} ? 1 : 0;
1600             my $force = $opt->{force} ? 1 : 0;
1601            
1602             $Win32::FileOp::WNetCancelConnection2->Call($disk,$options,$force)
1603             and return;
1604             1;
1605             }
1606            
1607             sub Unmap {
1608             undef $Win32::FileOp::Error;
1609             if (UNIVERSAL::isa($_[1], "HASH")) {
1610             $_[1]->{persistent} = 1 unless exists $_[1]->{persistent};
1611             } else {
1612             $_[1] = {persistent => 1}
1613             }
1614             goto &Disconnect;
1615             }
1616            
1617             sub Mapped {
1618             undef $Win32::FileOp::Error;
1619             goto &_MappedAll unless (@_);
1620             my $disk = shift();
1621             if ($disk =~ m#^[\\/][\\/]#) {
1622             $disk =~ tr#/#\\#;
1623             $disk = uc $disk;
1624             my %drives = _MappedAll();
1625             my ($drive,$share);
1626             while (($drive,$share) = each %drives) {
1627             return uc($drive).':' if (uc($share) eq $disk);
1628             }
1629             return;
1630             } else {
1631             $disk =~ s/^(\w)$/$1:/;$disk.="\0";
1632             my $size = 1024;
1633             my $share = "\0" x $size;
1634            
1635             $size = pack('L',$size);
1636             $Win32::FileOp::WNetGetConnection->Call($disk,$share,$size)
1637             and return;
1638             $share =~ s/\0.*$//;
1639             return $share;
1640             }
1641             }
1642            
1643             sub _MappedAll {
1644             my %hash;
1645             my $share;
1646             foreach (('A'..'Z')) {
1647             $share = Mapped $_
1648             and
1649             $hash{$_}=$share;
1650             }
1651             return %hash;
1652             }
1653            
1654             sub Connected {
1655             # use WNetOpenEnum , WNetEnumResource and WNetCloseEnum
1656             }
1657            
1658             #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1659            
1660             sub Subst {
1661             my $drive = shift;
1662             return unless $drive =~ s/^(\w):?$/$1:\0/;
1663             my $path = shift();
1664             return unless -e $path;
1665             $path.="\0";
1666             $Win32::FileOp::DefineDosDevice->Call(0,$drive,$path);
1667             }
1668            
1669             sub SubstDev {
1670             my $drive = shift;
1671             return unless $drive =~ s/^(\w):?$/$1:\0/;
1672             my $path = shift();
1673             # return unless -e $path;
1674             $path = "\\Device\\$path" unless $path =~ /\\Device\\/i;
1675             $path.="\0";
1676             $Win32::FileOp::DefineDosDevice->Call(&DDD_RAW_TARGET_PATH,$drive,$path);
1677             }
1678            
1679             sub Unsubst {
1680             my $drive = shift;
1681             return unless $drive =~ s/^(\w):?$/$1:\0/;
1682             $Win32::FileOp::DefineDosDevice->Call(&DDD_REMOVE_DEFINITION,$drive,0);
1683             }
1684            
1685             sub Substed {
1686             my $drive = shift;
1687             if (defined $drive) {
1688             return unless $drive =~ s/^(\w):?$/$1:\0/;
1689             my $path = "\0" x 1024;
1690             my $device;
1691             $Win32::FileOp::QueryDosDevice->Call($drive,$path,1024)
1692             or return;
1693            
1694             $path =~ s/\0.*$//;
1695            
1696             $path =~ s/^\\\?\?\\UNC/\\/ and $device = 'UNC'
1697             or
1698             $path =~ s/\\Device\\(.*?)\\\w:/\\/ and $device = $1
1699             or
1700             $path =~ s/\\Device\\(.*)$// and $device = $1;
1701            
1702             return wantarray ? ($path,$device) : $path;
1703             } else {
1704             my ($drive,$path,%data);
1705             foreach $drive (('A'..'Z')) {
1706             $drive.=':';
1707             $path = Substed($drive);
1708             $data{$drive} = $path if defined $path;
1709             }
1710             return wantarray() ? %data : \%data;
1711             }
1712             }
1713            
1714            
1715             #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1716            
1717             package Win32::FileOp::Error;
1718             require Tie::Hash;
1719             @Win32::FileOp::Error::ISA=qw(Tie::Hash);
1720            
1721             sub TIEHASH {
1722             my $pkg = shift;
1723             my %hash = @_;
1724             my $self = \%hash;
1725             bless $self, $pkg;
1726             }
1727            
1728             sub FETCH { $_[0]->{$_[1]} || Win32::FormatMessage($_[1]) || "Unknown error ($_[1])" };
1729            
1730             package Win32::FileOp;
1731            
1732             tie %Win32::FileOp::ERRORS, 'Win32::FileOp::Error', (
1733             12291 => 'The buffer was too small!'
1734             );
1735            
1736             #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1737            
1738             1;
1739            
1740             __END__