Karen’s Replicator 2 Command Line Interface

This is the beginning of the command line interface for Karen’s Replicator 2. I hard coded it to copy the contents of one directory to another, including sub directories.

Here’s the Code

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
using System.Threading;



namespace Replicator2_CLI
{
    internal class Program
    {
        static void Main(string[] args)
        {

            CopyAll(
                new DirectoryInfo(@"J:\home\Desktop\Folder Test"),
                new DirectoryInfo(@"B:\test\Folder Test")
            );


        }

        /**
         * https://learn.microsoft.com/en-us/dotnet/api/system.io.directoryinfo?view=net-7.0
         */
        public static void CopyAll(DirectoryInfo source, DirectoryInfo target)
        {
            if (source.FullName.ToLower() == target.FullName.ToLower())
            {
                return;
            }

            // Check if the target directory exists, if not, create it.
            if (Directory.Exists(target.FullName) == false)
            {
                Directory.CreateDirectory(target.FullName);
            }

            // Copy each file into it's new directory.
            foreach (FileInfo fi in source.GetFiles())
            {
                Console.WriteLine(@"Copying {0}\{1}", target.FullName, fi.Name);

                //(jmw) fi.CopyTo(Path.Combine(target.ToString(), fi.Name), true);

                FileRoutines.CopyFile(fi, new FileInfo(Path.Combine(target.FullName, fi.Name)) );


            }

            // Copy each subdirectory using recursion.
            foreach (DirectoryInfo diSourceSubDir in source.GetDirectories())
            {
                DirectoryInfo nextTargetSubDir =
                    target.CreateSubdirectory(diSourceSubDir.Name);
                CopyAll(diSourceSubDir, nextTargetSubDir);
            }
        }

        static void CopyFile( string source, string destination )
        {
            Console.WriteLine( source + " --> " + destination );
            FileRoutines.CopyFile(new FileInfo(source), new FileInfo(destination));
        }

    }


    /**
    * https://learn.microsoft.com/en-us/archive/msdn-magazine/2005/february/net-matters-file-copy-progress-custom-thread-pools
    */
    public sealed class FileRoutines
    {
        public static void CopyFile(FileInfo source, FileInfo destination)
        {
            CopyFile(source, destination, CopyFileOptions.None);
        }
        public static void CopyFile(FileInfo source, FileInfo destination, CopyFileOptions options)
        {
            CopyFile(source, destination, options, null);
        }
        public static void CopyFile(FileInfo source, FileInfo destination, CopyFileOptions options, CopyFileCallback callback)
        {
            CopyFile(source, destination, options, callback, null);
        }
        public static void CopyFile(FileInfo source, FileInfo destination, CopyFileOptions options, CopyFileCallback callback, object state)
        {
            if (source == null)
                throw new ArgumentNullException("source");
            if (destination == null)
                throw new ArgumentNullException("destination");
            if ((options & ~CopyFileOptions.All) != 0)
                throw new ArgumentOutOfRangeException("options");
            new FileIOPermission(FileIOPermissionAccess.Read, source.FullName).Demand();
            new FileIOPermission(FileIOPermissionAccess.Write, destination.FullName).Demand();
            CopyProgressRoutine cpr = callback == null ? null : new CopyProgressRoutine(new CopyProgressData(source, destination, callback, state).CallbackHandler);
            bool cancel = false;
            if (!CopyFileEx(source.FullName, destination.FullName, cpr, IntPtr.Zero, ref cancel, (int)options))
            {
                throw new IOException(new Win32Exception().Message);
            }
        }
        private class CopyProgressData
        {
            private FileInfo _source = null; private FileInfo _destination = null; private CopyFileCallback _callback = null; private object _state = null; public CopyProgressData(FileInfo source, FileInfo destination, CopyFileCallback callback, object state)
            {
                _source = source;
                _destination = destination;
                _callback = callback;
                _state = state;
            }
            public int CallbackHandler(long totalFileSize, long totalBytesTransferred, long streamSize, long streamBytesTransferred, int streamNumber, int callbackReason, IntPtr sourceFile, IntPtr destinationFile, IntPtr data)
            {
                return (int)_callback(_source, _destination, _state, totalFileSize, totalBytesTransferred);
            }
        }
        private delegate int CopyProgressRoutine(long totalFileSize, long TotalBytesTransferred, long streamSize, long streamBytesTransferred, int streamNumber, int callbackReason, IntPtr sourceFile, IntPtr destinationFile, IntPtr data); [SuppressUnmanagedCodeSecurity][DllImport("Kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern bool CopyFileEx(string lpExistingFileName, string lpNewFileName, CopyProgressRoutine lpProgressRoutine, IntPtr lpData, ref bool pbCancel, int dwCopyFlags);
    }
    public delegate CopyFileCallbackAction CopyFileCallback(FileInfo source, FileInfo destination, object state, long totalFileSize, long totalBytesTransferred); public enum CopyFileCallbackAction
    {
        Continue = 0, Cancel = 1, Stop = 2, Quiet = 3
    }
    [Flags]
    public enum CopyFileOptions
    {
        None = 0x0, FailIfDestinationExists = 0x1, Restartable = 0x2, AllowDecryptedDestination = 0x8, All = FailIfDestinationExists | Restartable | AllowDecryptedDestination
    }

    public sealed class CustomThreadPool : IDisposable
    {
        private Semaphore _workWaiting;
        private Queue<WaitQueueItem> _queue;
        private List<Thread> _threads;
        public CustomThreadPool(int numThreads)
        {
            if (numThreads <= 0)
                throw new ArgumentOutOfRangeException("numThreads");
            _threads = new List<Thread>(numThreads);
            _queue = new Queue<WaitQueueItem>();
            _workWaiting = new Semaphore(0, int.MaxValue);
            for (int i = 0; i < numThreads; i++)
            {
                Thread t = new Thread(Run);
                t.IsBackground = true;
                _threads.Add(t);
                t.Start();
            }
        }
        public void Dispose()
        {
            if (_threads != null)
            {
                _threads.ForEach(delegate (Thread t)
                {
                    t.Interrupt();
                });
                _threads = null;
            }
        }
        public void QueueUserWorkItem(WaitCallback callback, object state)
        {
            if (_threads == null)
                throw new ObjectDisposedException(GetType().Name);
            if (callback == null)
                throw new ArgumentNullException("callback");
            WaitQueueItem item = new WaitQueueItem();
            item.Callback = callback;
            item.State = state;
            item.Context = ExecutionContext.Capture();
            lock (_queue)
                _queue.Enqueue(item);
            _workWaiting.Release();
        }
        private void Run()
        {
            try
            {
                while (true)
                {
                    _workWaiting.WaitOne();
                    WaitQueueItem item;
                    lock (_queue)
                        item = _queue.Dequeue();
                    ExecutionContext.Run(item.Context, new ContextCallback(item.Callback), item.State);
                }
            }
            catch (ThreadInterruptedException) { }
        }
        private class WaitQueueItem
        {
            public WaitCallback Callback; public object State; public ExecutionContext Context;
        }
    }

}

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Scroll to Top