This is the main class, you can bolt on a GUI or widget by implementing the bellow interface and passing it to the constructor.
1: /// <summary>
2: /// Copies a list of files or a directory tree to a destination
3: ///
4: /// Support for GUI is implamented by the ICopyFilesDiag interface
5: /// and passed to the class in the copy() method.
6: /// </summary>
7: public class CopyFiles
8: {
9:
10: // Variables
11: private List<String> files = new List<String>();
12: private List<String> newFilenames = new List<String>();
13: private List<ST_CopyFileDetails> filesCopied = new List<ST_CopyFileDetails>();
14: private Int32 totalFiles = 0;
15: private Int32 totalFilesCopied = 0;
16: private String destinationDir = "";
17: private String sourceDir = "";
18: private String currentFilename;
19: private Boolean cancel = false;
20: private IAsyncResult CopyResult;
21: private DEL_CopyFiles delCopy;
22: private ICopyFilesDiag digWindow;
23:
24: // Structurs
25: public struct ST_CopyFileDetails
26: {
27:
28: String OriginalURI;
29: String NewURI;
30:
31: // Constructor
32: public ST_CopyFileDetails(String FromURI, String ToURI)
33: {
34: OriginalURI = FromURI;
35: NewURI = ToURI;
36: }
37:
38: }
39:
40: // Enums
41: // These Enums are used for the windows CopyFileEx function
42: [Flags]
43: private enum CopyFileFlags : uint
44: {
45: COPY_FILE_FAIL_IF_EXISTS = 0x00000001,
46: COPY_FILE_RESTARTABLE = 0x00000002,
47: COPY_FILE_OPEN_SOURCE_FOR_WRITE = 0x00000004,
48: COPY_FILE_ALLOW_DECRYPTED_DESTINATION = 0x00000008
49: }
50: private enum CopyProgressResult : uint
51: {
52: PROGRESS_CONTINUE = 0,
53: PROGRESS_CANCEL = 1,
54: PROGRESS_STOP = 2,
55: PROGRESS_QUIET = 3
56: }
57: private enum CopyProgressCallbackReason : uint
58: {
59: CALLBACK_CHUNK_FINISHED = 0x00000000,
60: CALLBACK_STREAM_SWITCH = 0x00000001
61: }
62:
63: // Events
64: public event DEL_copyComplete EV_copyComplete;
65: public event DEL_copyCanceled EV_copyCanceled;
66:
67: // Delegates
68: private delegate CopyProgressResult CopyProgressRoutine(Int64 TotalFileSize, Int64 TotalBytesTransferred, Int64 StreamSize, Int64 StreamBytesTransferred, UInt32 dwStreamNumber, CopyProgressCallbackReason dwCallbackReason, IntPtr hSourceFile, IntPtr hDestinationFile, IntPtr lpData);
69: private delegate CopyProgressResult DEL_CopyProgressHandler(Int64 total, Int64 transferred, Int64 streamSize, Int64 StreamByteTrans, UInt32 dwStreamNumber, CopyProgressCallbackReason reason, IntPtr hSourceFile, IntPtr hDestinationFile, IntPtr lpData);
70: private delegate void DEL_CopyFiles();
71: private delegate void DEL_ShowDiag(ICopyFilesDiag diag);
72: private delegate void DEL_HideDiag(ICopyFilesDiag diag);
73: private delegate void DEL_CopyfilesCallback(IAsyncResult r);
74:
75: public delegate void DEL_cancelCopy();
76: public delegate void DEL_copyComplete();
77: public delegate void DEL_copyCanceled(List<ST_CopyFileDetails> filescopied);
78:
79: // Constructors
80: public CopyFiles(String source, String destination)
81: {
82: //As the directory tree might be large we work out the
83: //files in the threaded call Copyfiles()
84: sourceDir = source;
85: destinationDir = destination;
86: }
87: public CopyFiles(List<String> sourceFiles, String destination)
88: {
89:
90: //The sourceDir does not need to be set if the user is supplying a
91: //list of files.
92: //
93: //Example :
94: // Source Destination
95: // c:\Temp1\Test.txt c:\DestFolder\Test.txt
96: // c:\temp2\temp1\test1.txt c:\DestFolder\Test1.txt
97: // c:\temp3\blah\Test.txt c:\DestFolder\Test (2).txt
98: //
99: //This is worked out in CheckFilenames()
100: files = sourceFiles;
101: totalFiles = files.Count;
102: destinationDir = destination;
103: }
104:
105: // Kernal32 Calls
106: // Unsafe is need to show that we are using
107: // pointers which are classed as "unsafe" in .net
108: [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
109: [return: MarshalAs(UnmanagedType.Bool)]
110: static extern unsafe bool CopyFileEx(string lpExistingFileName, string lpNewFileName, CopyProgressRoutine lpProgressRoutine, IntPtr lpData, Boolean* pbCancel, CopyFileFlags dwCopyFlags);
111:
112: // Methods
113: private List<String> GetFiles(String sourceDir)
114: {
115:
116: // Variables
117: List<String> foundFiles = new List<String>();
118: String[] fileEntries;
119: String[] subdirEntries;
120:
121: //Add root files in this DIR to the list
122: fileEntries = System.IO.Directory.GetFiles(sourceDir);
123: foreach (String filename in fileEntries)
124: {
125: foundFiles.Add(filename);
126: }
127:
128: //Loop the DIR's in the current DIR
129: subdirEntries = System.IO.Directory.GetDirectories(sourceDir);
130: foreach (string subdir in subdirEntries)
131: {
132:
133: //Dont open Folder Redirects as this can end up in an infinate loop
134: if ((System.IO.File.GetAttributes(subdir) &
135: System.IO.FileAttributes.ReparsePoint) !=
136: System.IO.FileAttributes.ReparsePoint)
137: {
138: //Run recursivly to follow this DIR tree
139: //adding all the files along the way
140: foundFiles.AddRange(GetFiles(subdir));
141: }
142:
143: }
144:
145: return foundFiles;
146: }
147: private CopyProgressResult CopyProgressHandler(Int64 total, Int64 transferred, Int64 streamSize, Int64 StreamByteTrans, UInt32 dwStreamNumber, CopyProgressCallbackReason reason, IntPtr hSourceFile, IntPtr hDestinationFile, IntPtr lpData)
148: {
149: //Check to see if there is a dialog window to use
150: if (digWindow != null)
151: {
152: //Are we going to send the update on the correct thread?
153: if (digWindow.SynchronizationObject != null && digWindow.SynchronizationObject.InvokeRequired)
154: {
155: digWindow.SynchronizationObject.Invoke(new CopyProgressRoutine(CopyProgressHandler),
156: new Object[] { total, transferred, streamSize, StreamByteTrans, dwStreamNumber, reason, hSourceFile, hDestinationFile, lpData });
157: }
158: else
159: {
160: digWindow.update(totalFiles, totalFilesCopied, total, transferred, currentFilename);
161: }
162:
163: }
164: return CopyProgressResult.PROGRESS_CONTINUE;
165: }
166: private void ShowDiag(ICopyFilesDiag diag)
167: {
168: //Check to see if there is a dialog window to use
169: if (digWindow != null)
170: {
171: //Are we going to send the update on the correct thread?
172: if (digWindow.SynchronizationObject != null && digWindow.SynchronizationObject.InvokeRequired)
173: {
174: digWindow.SynchronizationObject.Invoke(new DEL_ShowDiag(ShowDiag),
175: new Object[] { diag });
176: }
177: else
178: {
179: diag.Show();
180: }
181: }
182: }
183: private void HideDiag(ICopyFilesDiag diag)
184: {
185: //Check to see if there is a dialog window to use
186: if (digWindow != null)
187: {
188: //Are we going to send the update on the correct thread?
189: if (digWindow.SynchronizationObject != null && digWindow.SynchronizationObject.InvokeRequired)
190: {
191: digWindow.SynchronizationObject.Invoke(new DEL_HideDiag(HideDiag),
192: new Object[] { diag });
193: }
194: else
195: {
196: diag.Hide();
197: cancel = false;
198: }
199: }
200: }
201: private void CancelCopy()
202: {
203: cancel = true;
204: OnCopyCanceled();
205: }
206: private void Copyfiles()
207: {
208:
209: Int32 index = 0;
210:
211: //Show the dialog box and hook into its cancel event if
212: //a dialog box has been given
213: if (digWindow != null)
214: {
215: digWindow.EN_cancelCopy += CancelCopy;
216: ShowDiag(digWindow);
217: }
218:
219: //If we have been a sourceDIR then find all the files to copy
220: if (sourceDir != "")
221: {
222: files = GetFiles(sourceDir);
223: }
224: else
225: {
226: CheckFilenames();
227: }
228: totalFiles = files.Count;
229:
230: //Loop each file and copy it.
231: foreach (String filename in files.ToArray())
232: {
233: String[] filepath;
234: String tempFilepath;
235: String tempDirPath = "";
236:
237: //If we have a source directory, strip that off the filename
238: if (sourceDir != "")
239: {
240: tempFilepath = filename;
241: tempFilepath = tempFilepath.Replace(sourceDir, "");
242: tempFilepath = System.IO.Path.Combine(destinationDir, tempFilepath);
243: }
244: //otherwise strip off all the folder path
245: else
246: {
247: tempFilepath = System.IO.Path.Combine(destinationDir, newFilenames[index]);
248: }
249:
250: //Save the new DIR path and check the DIR exsits,
251: //if it does not then create it so the files can copy
252: filepath = tempFilepath.Split('\\');
253: for (int i = 0; i < filepath.Length - 1; i++)
254: {
255: tempDirPath += filepath[i] + "\\";
256: }
257: if (!System.IO.Directory.Exists(tempDirPath))
258: {
259: System.IO.Directory.CreateDirectory(tempDirPath);
260: }
261:
262: //Have be been told to stop copying files
263: if (cancel)
264: {
265: break;
266: }
267:
268: //Set the file thats just about to get copied
269: currentFilename = filename;
270:
271: //Unsafe is need to show that we are using
272: //pointers which are classed as "unsafe" in .net
273: //
274: //CopyFileEx needs a pointer to the cancel boolean, it checks this
275: //constantly as the file copies, if it gets set to true it will stop
276: //
277: //Note :
278: // fixed is used to get the memory pointer of our local boolean.
279: // It is then saved in a pointer (declared like a normal type but
280: // with a * at the end)
281: //
282: // We can then pass this memory address to the Kernal32 call.
283: unsafe
284: {
285: fixed (Boolean* cancelp = &cancel)
286: {
287: CopyFileEx(filename, tempFilepath, new CopyProgressRoutine(this.CopyProgressHandler), IntPtr.Zero, cancelp, 0);
288: }
289: }
290: filesCopied.Add(new ST_CopyFileDetails(filename, tempFilepath));
291: totalFilesCopied += 1;
292: index += 1;
293:
294: }
295:
296: }
297: private void OnCopyComplete()
298: {
299: if (EV_copyComplete != null)
300: {
301: EV_copyComplete();
302: }
303: }
304: private void OnCopyCanceled()
305: {
306: if (EV_copyCanceled != null)
307: {
308: EV_copyCanceled(filesCopied);
309: }
310: }
311: private void CheckFilenames()
312: {
313: // Variables
314: String[] fileNames = new String[files.Count];
315: List<String> tempFileNameArr;
316: Int32 index = 0;
317: Int32 innerIndex = 0;
318: Int32 filenameIndex = 0;
319: Int32 filenameNumber = 0;
320:
321: //Load filenames into an array
322: foreach (String tempFileName in files)
323: {
324: fileNames[index] = System.IO.Path.GetFileName(tempFileName);
325: index += 1;
326: }
327:
328: //Loop each filename in the array
329: index = 0;
330: foreach (String originalFilename in fileNames)
331: {
332:
333: //See if this filename is repeated in the list
334: innerIndex = 0;
335: filenameNumber = 2;
336: foreach (String dupeFilename in fileNames)
337: {
338: //dont compair the same index!
339: if (innerIndex != index)
340: {
341:
342: if (originalFilename == dupeFilename)
343: {
344: //insert the duplicate number into the new filename e.g (2) and clear
345: //the current name.
346: tempFileNameArr = new List<String>(fileNames[innerIndex].Split('.'));
347: tempFileNameArr.Insert(tempFileNameArr.Count - 1, "[*REMOVEME*] (" + filenameNumber + ")");
348: fileNames[innerIndex] = "";
349:
350: //Rebuild the new filename
351: filenameIndex = 0;
352: foreach (String newFilename in tempFileNameArr)
353: {
354:
355: //put a dot before the file extension
356: if (filenameIndex == tempFileNameArr.Count - 1)
357: { fileNames[innerIndex] += "."; }
358:
359: //append the new filename
360: fileNames[innerIndex] += newFilename.Replace("[*REMOVEME*]", "");
361:
362: //only add a . if its not the injected portion e.g (2)
363: if ((filenameIndex < tempFileNameArr.Count - 3 && newFilename.StartsWith("[*REMOVEME*]") == false))
364: { fileNames[innerIndex] += "."; }
365:
366: filenameIndex += 1;
367: }
368:
369: //Trim any trailing .'s
370: fileNames[innerIndex].TrimEnd(new Char[] { '.' });
371: filenameNumber += 1;
372: }
373: }
374: innerIndex += 1;
375: }
376: index += 1;
377:
378: }
379:
380: //Update the list of new filenames.
381: newFilenames = new List<String>(fileNames);
382:
383: }
384:
385: //Copy the files
386: public void Copy()
387: {
388: Copyfiles();
389: }
390: public void CopyAsync(ICopyFilesDiag diag)
391: {
392: digWindow = diag;
393:
394: if (digWindow != null && digWindow.SynchronizationObject == null)
395: {
396: throw new Exception("Dialog window sent with no SynchronizationObject");
397: }
398:
399: delCopy = new DEL_CopyFiles(Copyfiles);
400: CopyResult = delCopy.BeginInvoke(CopyfilesCallback, null);
401: }
402:
403: // Async Callbacks
404: private void CopyfilesCallback(IAsyncResult r)
405: {
406: //Kill off the thread as its finished.
407: delCopy.EndInvoke(CopyResult);
408: HideDiag(digWindow);
409: OnCopyComplete();
410: }
411:
412: }
413:
Here is the interface that should be placed with the class.
1: //The interface for the Dialog the CopyFiles class uses.
2: public interface ICopyFilesDiag
3: {
4: //needed to sync the CopyClass update events with the dialog thread
5: System.ComponentModel.ISynchronizeInvoke SynchronizationObject { get; set; }
6:
7: //This event should fire when you want to cancel the copy
8: event CopyFiles.DEL_cancelCopy EN_cancelCopy;
9:
10: //This is how the CopyClass will send your dialog information about
11: //the transfer
12: void update(Int32 totalFiles, Int32 copiedFiles, Int64 totalBytes, Int64 copiedBytes, String currentFilename);
13: void Show();
14: void Hide();
15:
16: }
17:
Finally here is an implentation of the interface, this is a simple form that opens up and whows the prgress of the copy. You will need to add two lables, two progress bars and a button to get it to work.
1: public partial class DIA_CopyFiles : Form, ICopyFilesDiag
2: {
3:
4: // Properties
5: public System.ComponentModel.ISynchronizeInvoke SynchronizationObject { get; set; }
6:
7: // Constructors
8: public DIA_CopyFiles()
9: {
10: InitializeComponent();
11: }
12:
13: // Methods
14: public void update(Int32 totalFiles, Int32 copiedFiles, Int64 totalBytes, Int64 copiedBytes, String currentFilename)
15: {
16: Prog_TotalFiles.Maximum = totalFiles;
17: Prog_TotalFiles.Value = copiedFiles;
18: Prog_CurrentFile.Maximum = 100;
19: if (totalBytes != 0)
20: {
21: Prog_CurrentFile.Value = Convert.ToInt32((100f / (totalBytes / 1024f)) * (copiedBytes / 1024f));
22: }
23:
24: Lab_TotalFiles.Text = "Total files (" + copiedFiles + "/" + totalFiles + ")";
25: Lab_CurrentFile.Text = currentFilename;
26: }
27: private void But_Cancel_Click(object sender, EventArgs e)
28: {
29: RaiseCancel();
30: }
31: private void DIA_CopyFiles_Closed(object sender, System.EventArgs e)
32: {
33: RaiseCancel();
34: }
35: private void RaiseCancel()
36: {
37: if (EN_cancelCopy != null)
38: {
39: EN_cancelCopy();
40: }
41: }
42:
43: //Events
44: public event CopyFiles.DEL_cancelCopy EN_cancelCopy;
45:
46:
47: }
48:
|