Пример #1
0
        /// <summary>
        /// Gets file icon as <b>Bitmap</b>.
        /// Returns null if the icon is not cached and failed to get it, eg file does not exist.
        /// </summary>
        /// <param name="file">Any file or folder.</param>
        /// <param name="useExt">
        /// Get file type icon, depending on filename extension. Use this to avoid getting separate image object for each file of same type.
        /// This is ignored if filename extension is ".ico" or ".exe" or starts with ".exe," or ".dll,".
        /// </param>
        /// <param name="giFlags">Flags for <see cref="AIcon.GetFileIconImage"/>.</param>
        /// <param name="autoUpdate">
        /// If not null, the cached image will be auto-updated when changed. Then will be called this function. It can update the image in UI.
        /// How it works: If this function finds cached image, it sets timer that after ~50 ms loads that icon/image from file again and compares with the cached image. If different, updates the cache. Does it once, not periodically.
        /// Use only in UI threads. Does not work if this thread does not retrieve/dispatch posted messages.
        /// </param>
        /// <param name="auParam">Something to pass to the <i>autoUpdate</i> callback function.</param>
        /// <remarks>
        /// If the icon is in the memory cache, gets it from there.
        /// Else if it is in the file cache, gets it from there and adds to the memory cache.
        /// Else gets from file (uses <see cref="AIcon.GetFileIconImage"/> and adds to the file cache and to the memory cache.
        /// </remarks>
        public Bitmap GetImage(string file, bool useExt, IconGetFlags giFlags = 0, Action <Bitmap, object> autoUpdate = null, object auParam = null)
        {
            if (useExt)
            {
                var ext = APath.GetExtension(file);
                if (ext.Length == 0)
                {
                    if (AFile.ExistsAsDirectory(file))
                    {
                        ext = file;
                    }
                    else
                    {
                        ext = ".no-ext";
                    }
                }
                else
                {
                    //ext = ext.Lower();
                    if (ext.Eqi(".ico") || ext.Eqi(".exe") || ext.Starts(".exe,", true) || ext.Starts(".dll,", true))
                    {
                        ext = file;
                    }
                }
                file = ext;
            }
            else if (APath.IsFullPathExpandEnvVar(ref file))
            {
                file = APath.Normalize_(file, noExpandEV: true);
            }

            return(_GetImage(file, giFlags, null, autoUpdate, auParam, true));
        }
Пример #2
0
 /// <summary>
 /// Gets file path extension like ".txt" or URL protocol like "http".
 /// Returns null if path does not end with ".extension" and does not start with "protocol:"; also if starts with "shell:".
 /// </summary>
 /// <param name="path">File path or URL. Can be just extension like ".txt" or protocol like "http:".</param>
 /// <param name="isProtocol">Receives true if URL or protocol.</param>
 internal static string GetExtensionOrProtocol(string path, out bool isProtocol)
 {
     isProtocol = false;
     if (path.NE())
     {
         return(null);
     }
     if (!PathIsExtension(path))
     {
         int i = path.IndexOf(':');
         if (i > 1)
         {
             path = path.Remove(i);                         //protocol
             if (path == "shell")
             {
                 return(null);                                        //eg "shell:AppsFolder\Microsoft.WindowsCalculator_8wekyb3d8bbwe!App"
             }
             isProtocol = true;
         }
         else
         {
             path = APath.GetExtension(path);
             if (path.NE())
             {
                 return(null);
             }
         }
     }
     return(path);
 }
Пример #3
0
            //this is ~300 times slower than AFile.Move. SHFileOperation too. Use only for files or other shell items in virtual folders. Unfinished.
            public static void RenameFileOrDirectory(string path, string newName)
            {
                APerf.First();
                if (APath.IsInvalidFileName(newName))
                {
                    throw new ArgumentException("Invalid filename.", nameof(newName));
                }
                path = _PreparePath(path, nameof(path));

                APerf.Next();
                var si = _ShellItem(path, "*rename");

                APerf.Next();
                var fo = new Api.FileOperation() as Api.IFileOperation;

                APerf.Next();
                try {
                    fo.SetOperationFlags(4);             //FOF_SILENT. Without it shows a hidden dialog that becomes the active window.
                    AuException.ThrowIfFailed(fo.RenameItem(si, newName, null), "*rename");
                    APerf.Next();
                    AuException.ThrowIfFailed(fo.PerformOperations(), "*rename");
                    APerf.Next();
                }
                finally {
                    Api.ReleaseComObject(fo);
                    Api.ReleaseComObject(si);
                }
                APerf.NW();
            }
Пример #4
0
        /// <summary>
        /// Gets process executable file name (like "notepad.exe") or full path.
        /// Returns null if fails.
        /// </summary>
        /// <param name="processId">Process id.</param>
        /// <param name="fullPath">
        /// Get full path.
        /// Note: Fails to get full path if the process belongs to another user session, unless current process is running as administrator; also fails to get full path of some system processes.
        /// </param>
        /// <param name="noSlowAPI">When the fast API QueryFullProcessImageName fails, don't try to use another much slower API WTSEnumerateProcesses. Not used if <i>fullPath</i> is true.</param>
        /// <remarks>
        /// This function is much slower than getting window name or class name.
        /// </remarks>
        /// <seealso cref="AWnd.ProgramName"/>
        /// <seealso cref="AWnd.ProgramPath"/>
        /// <seealso cref="AWnd.ProcessId"/>
        public static string GetName(int processId, bool fullPath = false, bool noSlowAPI = false)
        {
            if (processId == 0)
            {
                return(null);
            }
            string R = null;

            //var t = ATime.PerfMicroseconds;
            //if(s_time != 0) AOutput.Write(t - s_time);
            //s_time = t;

            using var ph = Handle_.OpenProcess(processId);
            if (!ph.Is0)
            {
                //In non-admin process fails if the process is of another user session.
                //Also fails for some system processes: nvvsvc, nvxdsync, dwm. For dwm fails even in admin process.

                //getting native path is faster, but it gets like "\Device\HarddiskVolume5\Windows\System32\notepad.exe" and I don't know API to convert to normal
                if (_QueryFullProcessImageName(ph, !fullPath, out var s))
                {
                    R = s;
                    if (APath.IsPossiblyDos_(R))
                    {
                        if (fullPath || _QueryFullProcessImageName(ph, false, out s))
                        {
                            R = APath.ExpandDosPath_(s);
                            if (!fullPath)
                            {
                                R = _GetFileName(R);
                            }
                        }
                    }
                }
            }
            else if (!noSlowAPI && !fullPath)                //the slow way. Can get only names, not paths.
            {
                using (new _AllProcesses(out var p, out int n)) {
                    for (int i = 0; i < n; i++)
                    {
                        if (p[i].processID == processId)
                        {
                            R = p[i].GetName(cannotOpen: true);
                            break;
                        }
                    }
                }
            }

            return(R);

            //Would be good to cache process names here. But process id can be reused quickly. Use GetNameCached_ instead.
            //	tested: a process id is reused after creating ~100 processes (and waiting until exits). It takes ~2 s.
            //	The window finder is optimized to call this once for each process and not for each window.
        }
Пример #5
0
        static void _Print(object text, string cp, int cln, string cmn)
        {
            string s      = AOutput.ObjectToString_(text);
            string prefix = null; if (s.Starts("<>"))

            {
                prefix = "<>"; s = s.Substring(2);
            }

            AOutput.Write($"{prefix}Debug: {cmn} ({APath.GetFileName(cp)}:{cln}):  {s}");
        }
Пример #6
0
 /// <summary>
 /// Loads XML file in a safer way.
 /// Uses <see cref="XDocument.Load(XmlReader, LoadOptions)"/> and <see cref="AFile.WaitIfLocked"/>.
 /// </summary>
 /// <param name="file">File. Must be full path. Can contain environment variables etc, see <see cref="APath.ExpandEnvVar"/>.</param>
 /// <param name="options"></param>
 /// <exception cref="ArgumentException">Not full path.</exception>
 /// <exception cref="Exception">Exceptions of <see cref="XDocument.Load"/>.</exception>
 /// <remarks>
 /// Unlike <see cref="XDocument.Load(string, LoadOptions)"/>, does not replace <c>\r\n</c> with <c>\n</c>.
 /// </remarks>
 public static XDocument LoadDoc(string file, LoadOptions options = default)
 {
     file = APath.NormalizeForNET_(file);
     return(AFile.WaitIfLocked(() => {
         using var r = new XmlTextReader(file);
         if (0 == (options & LoadOptions.PreserveWhitespace))
         {
             r.WhitespaceHandling = WhitespaceHandling.Significant;
         }
         return XDocument.Load(r, options);
     }));
 }
Пример #7
0
 /// <summary>
 /// Loads XML file in a safer way.
 /// Uses <see cref="XElement.Load(XmlReader, LoadOptions)"/> and <see cref="AFile.WaitIfLocked"/>.
 /// </summary>
 /// <param name="file">File. Must be full path. Can contain environment variables etc, see <see cref="APath.ExpandEnvVar"/>.</param>
 /// <param name="options"></param>
 /// <exception cref="ArgumentException">Not full path.</exception>
 /// <exception cref="Exception">Exceptions of <see cref="XElement.Load"/>.</exception>
 /// <remarks>
 /// Unlike <see cref="XElement.Load(string, LoadOptions)"/>, does not replace <c>\r\n</c> with <c>\n</c>.
 /// </remarks>
 public static XElement LoadElem(string file, LoadOptions options = default)
 {
     file = APath.NormalizeForNET_(file);
     return(AFile.WaitIfLocked(() => {
         using var r = new XmlTextReader(file);                 //to preserve \r\n
         if (0 == (options & LoadOptions.PreserveWhitespace))
         {
             r.WhitespaceHandling = WhitespaceHandling.Significant;                                                                 //to save correctly formatted. Default of XElement.Load(string).
         }
         return XElement.Load(r, options);
     }));
     //tested: XElement.Load(string) uses XmlReader.Create. It replaces \r\n with \n and does not have an option to preserve \r\n.
 }
Пример #8
0
 /// <summary>
 /// Gets <see cref="FileId"/> of a file or directory.
 /// Returns false if fails. Supports <see cref="ALastError"/>.
 /// </summary>
 /// <param name="path">Full path. Supports environment variables (see <see cref="APath.ExpandEnvVar"/>).</param>
 /// <param name="fileId"></param>
 public static unsafe bool GetFileId(string path, out FileId fileId)
 {
     path        = APath.NormalizeMinimally_(path, false);
     fileId      = new FileId();
     using var h = Api.CreateFile(path, Api.FILE_READ_ATTRIBUTES, Api.FILE_SHARE_ALL, default, Api.OPEN_EXISTING, Api.FILE_FLAG_BACKUP_SEMANTICS);
     if (h.Is0)
     {
         return(false);
     }
     if (!Api.GetFileInformationByHandle(h, out var k))
     {
         return(false);
     }
     fileId.VolumeSerialNumber = (int)k.dwVolumeSerialNumber;
     fileId.FileIndex          = k.FileIndex;
     return(true);
 }
Пример #9
0
        //Used by Run and RunConsole.
        static string _NormalizeFile(bool runConsole, string file, out bool isFullPath, out bool isShellPath)
        {
            isShellPath = isFullPath = false;
            file        = APath.ExpandEnvVar(file);
            if (file.NE())
            {
                throw new ArgumentException();
            }
            if (runConsole || !(isShellPath = APath.IsShellPath_(file)))
            {
                if (isFullPath = APath.IsFullPath(file))
                {
                    var fl = runConsole ? PNFlags.DontExpandDosPath : PNFlags.DontExpandDosPath | PNFlags.DontPrefixLongPath;
                    file = APath.Normalize_(file, fl, true);

                    //ShellExecuteEx supports long path prefix for exe but not for documents.
                    //Process.Run supports long path prefix, except when the exe is .NET.
                    if (!runConsole)
                    {
                        file = APath.UnprefixLongPath(file);
                    }

                    if (ADisableFsRedirection.IsSystem64PathIn32BitProcess(file) && !AFile.ExistsAsAny(file))
                    {
                        file = ADisableFsRedirection.GetNonRedirectedSystemPath(file);
                    }
                }
                else if (!APath.IsUrl(file))
                {
                    //ShellExecuteEx searches everywhere except in app folder.
                    //Process.Run prefers current directory.
                    var s2 = AFile.SearchPath(file);
                    if (s2 != null)
                    {
                        file       = s2;
                        isFullPath = true;
                    }
                }
            }
            return(file);
        }
Пример #10
0
        /// <summary>
        /// Opens or creates a database file.
        /// </summary>
        /// <param name="file">
        /// Database file. Can be:
        /// - Full path. Supports environment variables etc, see <see cref="APath.ExpandEnvVar"/>
        /// - ":memory:" - create a private, temporary in-memory database.
        /// - "" - create a private, temporary on-disk database.
        /// - Starts with "file:" - see sqlite3_open_v2 documentation in SQLite website.
        /// </param>
        /// <param name="flags">sqlite3_open_v2 flags, documanted in SQLite website. Default: read-write, create file if does not exist (and parent directory).</param>
        /// <param name="sql">
        /// SQL to execute. For example, one or more ;-separated PRAGMA statements to configure the database connection. Or even "CREATE TABLE IF NOT EXISTS ...".
        /// This function also always executes "PRAGMA foreign_keys=ON;PRAGMA secure_delete=ON;".
        /// </param>
        /// <exception cref="ArgumentException">Not full path.</exception>
        /// <exception cref="SLException">Failed to open database or execute sql.</exception>
        /// <remarks>
        /// Calls sqlite3_open_v2.
        /// <note>If a variable of this class is used by multiple threads, use <c>lock(variable) {  }</c> where need.</note>
        /// </remarks>
        public ASqlite(string file, SLFlags flags = SLFlags.ReadWriteCreate, string sql = null)
        {
            bool isSpec = file != null && (file.Length == 0 || file == ":memory:" || file.Starts("file:"));

            if (!isSpec)
            {
                file = APath.Normalize(file);
                if (flags.Has(SLFlags.SQLITE_OPEN_CREATE) && !AFile.ExistsAsFile(file, true))
                {
                    AFile.CreateDirectoryFor(file);
                }
            }
            var r = SLApi.sqlite3_open_v2(AConvert.ToUtf8(file), ref _db, flags, null);

            if (r != 0)
            {
                Dispose();
                throw new SLException(r, "sqlite3_open " + file);
            }
            Execute("PRAGMA foreign_keys=ON;PRAGMA secure_delete=ON;" + sql);
        }
Пример #11
0
        public static void Dialog(object text, [CallerFilePath] string cp = null, [CallerLineNumber] int cln = 0, [CallerMemberName] string cmn = null)
        {
            string s = AOutput.ObjectToString_(text);

            ADialog.Show("Debug", s, flags: DFlags.ExpandDown, expandedText: $"{cmn} ({APath.GetFileName(cp)}:{cln})");
        }
Пример #12
0
        /// <summary>
        /// Runs/opens a program, document, directory (folder), URL, new email, Control Panel item etc.
        /// The returned <see cref="RResult"/> variable contains some process info - process id etc.
        /// </summary>
        /// <param name="file">
        /// Examples:
        /// - <c>@"C:\file.txt"</c>
        /// - <c>AFolders.Documents</c>
        /// - <c>AFolders.System + "notepad.exe"</c>
        /// - <c>@"%AFolders.System%\notepad.exe"</c>
        /// - <c>@"%TMP%\file.txt"</c>
        /// - <c>"notepad.exe"</c>
        /// - <c>@"..\folder\x.exe"</c>
        /// - <c>"http://a.b.c/d"</c>
        /// - <c>"file:///path"</c>
        /// - <c>"mailto:[email protected]"</c>
        /// - <c>":: ITEMIDLIST"</c>
        /// - <c>@"::{CLSID}"</c>
        /// - <c>@"shell:AppsFolder\Microsoft.WindowsCalculator_8wekyb3d8bbwe!App"</c>.
        /// </param>
        /// <param name="args">
        /// Command line arguments.
        /// This function expands environment variables if starts with <c>"%"</c> or <c>"\"%"</c>.
        /// </param>
        /// <param name="flags"></param>
        /// <param name="curDirEtc">
        /// Allows to specify more parameters: current directory, verb, etc.
        /// If string, it sets initial current directory for the new process. If "", gets it from <i>file</i>. More info: <see cref="ROptions.CurrentDirectory"/>.
        /// </param>
        /// <exception cref="ArgumentException">Used both ROptions.Verb and RFlags.Admin.</exception>
        /// <exception cref="AuException">Failed. For example, the file does not exist.</exception>
        /// <remarks>
        /// It works like when you double-click a file icon. It may start new process or not. For example it may just activate window if the program is already running.
        /// Uses API <msdn>ShellExecuteEx</msdn>.
        /// Similar to <see cref="Process.Start(string, string)"/>.
        ///
        /// The <i>file</i> parameter can be:
        /// - Full path of a file or directory. Examples: <c>@"C:\file.txt"</c>, <c>AFolders.Documents</c>, <c>AFolders.System + "notepad.exe"</c>, <c>@"%AFolders.System%\notepad.exe"</c>.
        /// - Filename of a file or directory, like <c>"notepad.exe"</c>. The function calls <see cref="AFile.SearchPath"/>.
        /// - Path relative to <see cref="AFolders.ThisApp"/>. Examples: <c>"x.exe"</c>, <c>@"subfolder\x.exe"</c>, <c>@".\subfolder\x.exe"</c>, <c>@"..\another folder\x.exe"</c>.
        /// - URL. Examples: <c>"http://a.b.c/d"</c>, <c>"file:///path"</c>.
        /// - Email, like <c>"mailto:[email protected]"</c>. Subject, body etc also can be specified, and Google knows how.
        /// - Shell object's ITEMIDLIST like <c>":: ITEMIDLIST"</c>. See <see cref="APidl.ToBase64String"/>, <see cref="AFolders.Virtual"/>. Can be used to open virtual folders and items like Control Panel.
        /// - Shell object's parsing name, like <c>@"::{CLSID}"</c>. See <see cref="APidl.ToShellString"/>, <see cref="AFolders.VirtualPidl"/>. Can be used to open virtual folders and items like Control Panel.
        /// - To run a Windows Store App, use <c>@"shell:AppsFolder\WinStoreAppId"</c> format. Examples: <c>@"shell:AppsFolder\Microsoft.WindowsCalculator_8wekyb3d8bbwe!App"</c>, <c>@"shell:AppsFolder\windows.immersivecontrolpanel_cw5n1h2txyewy!microsoft.windows.immersivecontrolpanel"</c>. To discover the string use <see cref="AWnd.More.GetWindowsStoreAppId"/> or Google.
        ///
        /// Supports environment variables, like <c>@"%TMP%\file.txt"</c>. See <see cref="APath.ExpandEnvVar"/>.
        /// </remarks>
        /// <seealso cref="AWnd.FindOrRun"/>
        /// <example>
        /// Run notepad and wait for its window.
        /// <code><![CDATA[
        /// AExec.Run("notepad.exe");
        /// AWnd w = AWnd.Wait(10, true, "*- Notepad", "Notepad");
        /// ]]></code>
        /// Run notepad or activate its window.
        /// <code><![CDATA[
        /// AWnd w = AWnd.FindOrRun("*- Notepad", run: () => AExec.Run("notepad.exe"));
        /// ]]></code>
        /// </example>
        public static RResult Run(string file, string args = null, RFlags flags = 0, ROptions curDirEtc = null)
        {
            //SHOULDDO: from UAC IL admin run as user by default. Add flag UacInherit.

            Api.SHELLEXECUTEINFO x = default;
            x.cbSize = Api.SizeOf(x);
            x.fMask  = Api.SEE_MASK_NOZONECHECKS | Api.SEE_MASK_NOASYNC | Api.SEE_MASK_NOCLOSEPROCESS | Api.SEE_MASK_CONNECTNETDRV | Api.SEE_MASK_UNICODE;
            x.nShow  = Api.SW_SHOWNORMAL;

            bool curDirFromFile = false;
            var  more           = curDirEtc;

            if (more != null)
            {
                x.lpVerb = more.Verb;
                var cd = more.CurrentDirectory; if (cd != null)
                {
                    if (cd.Length == 0)
                    {
                        curDirFromFile = true;
                    }
                    else
                    {
                        cd = APath.ExpandEnvVar(cd);
                    }
                }
                x.lpDirectory = cd;
                if (!more.OwnerWindow.IsEmpty)
                {
                    x.hwnd = more.OwnerWindow.Wnd.Window;
                }
                switch (more.WindowState)
                {
                case ProcessWindowStyle.Hidden: x.nShow = Api.SW_HIDE; break;

                case ProcessWindowStyle.Minimized: x.nShow = Api.SW_SHOWMINIMIZED; break;

                case ProcessWindowStyle.Maximized: x.nShow = Api.SW_SHOWMAXIMIZED; break;
                }
            }

            if (flags.Has(RFlags.Admin))
            {
                if (more?.Verb != null && !more.Verb.Eqi("runas"))
                {
                    throw new ArgumentException("Cannot use Verb with flag Admin");
                }
                x.lpVerb = "runas";
            }
            else if (x.lpVerb != null)
            {
                x.fMask |= Api.SEE_MASK_INVOKEIDLIST;                                     //makes slower. But verbs are rarely used.
            }
            if (0 == (flags & RFlags.ShowErrorUI))
            {
                x.fMask |= Api.SEE_MASK_FLAG_NO_UI;
            }
            if (0 == (flags & RFlags.WaitForExit))
            {
                x.fMask |= Api.SEE_MASK_NO_CONSOLE;
            }

            file = _NormalizeFile(false, file, out bool isFullPath, out bool isShellPath);
            APidl pidl = null;

            if (isShellPath)                   //":: Base64ITEMIDLIST" or "::{CLSID}..." (we convert it too because the API does not support many)
            {
                pidl = APidl.FromString(file); //does not throw
                if (pidl != null)
                {
                    x.lpIDList = pidl.UnsafePtr;
                    x.fMask   |= Api.SEE_MASK_INVOKEIDLIST;
                }
                else
                {
                    x.lpFile = file;
                }
            }
            else
            {
                x.lpFile = file;

                if (curDirFromFile && isFullPath)
                {
                    x.lpDirectory = APath.GetDirectoryPath(file);
                }
            }
            if (!args.NE())
            {
                x.lpParameters = APath.ExpandEnvVar(args);
            }

            AWnd.More.EnableActivate();

            if (!Api.ShellExecuteEx(ref x))
            {
                throw new AuException(0, $"*run '{file}'");
            }
            pidl?.Dispose();

            var         R           = new RResult();
            bool        waitForExit = 0 != (flags & RFlags.WaitForExit);
            bool        needHandle  = flags.Has(RFlags.NeedProcessHandle);
            WaitHandle_ ph          = null;

            if (!x.hProcess.Is0)
            {
                if (waitForExit || needHandle)
                {
                    ph = new WaitHandle_(x.hProcess, true);
                }
                if (!waitForExit)
                {
                    R.ProcessId = AProcess.ProcessIdFromHandle(x.hProcess);
                }
            }

            try {
                Api.AllowSetForegroundWindow(Api.ASFW_ANY);

                if (x.lpVerb != null && Thread.CurrentThread.GetApartmentState() == ApartmentState.STA)
                {
                    Thread.CurrentThread.Join(50);                     //need min 5-10 for file Properties. And not Sleep.
                }
                if (ph != null)
                {
                    if (waitForExit)
                    {
                        ph.WaitOne();
                        if (Api.GetExitCodeProcess(x.hProcess, out var ec))
                        {
                            R.ProcessExitCode = ec;
                        }
                    }
                    if (needHandle)
                    {
                        R.ProcessHandle = ph;
                    }
                }
            }
            finally {
                if (R.ProcessHandle == null)
                {
                    if (ph != null)
                    {
                        ph.Dispose();
                    }
                    else
                    {
                        x.hProcess.Dispose();
                    }
                }
            }

            return(R);

            //tested: works well in MTA thread.
            //rejected: in QM2, run also has a 'window' parameter. However it just makes limited, unclear etc, and therefore rarely used. Instead use AWnd.FindOrRun or Find/Run/Wait like in the examples.
            //rejected: in QM2, run also has 'autodelay'. Better don't add such hidden things. Let the script decide what to do.
        }