如何检查本地OneDrive文件夹是否同步?

8

我需要能够检查本地OneDrive文件夹是否同步/更新。

我能否通过查看任何文件/文件夹属性(在C#代码中)来检查此内容,而不使用任何OneDrive API?


你是想比较这个文件夹和另一个文件夹,看它们是否匹配吗? - David
它可能是一个文件或一个文件夹。 - monika pathak
1
我只想知道是否可以通过查看任何属性来判断一个项目(文件或文件夹)是否同步。我假设如果该项目的图标发生变化,则表示它已同步,可能还有一些文件属性也会发生变化。 - monika pathak
为什么不使用 OneDrive API? - R.D. Alkire
2个回答

5
我被卡住了,想检查主文件夹图标。 编辑 重点是提取同步文件夹图标并获取覆盖的CLSID。
首先需要一个类来提取所需信息:
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Win32;


namespace MyApp.Classes
{
    public class ExtractIconInfo
    {
        [Flags]
        private enum SHGFI : uint
        {
            SHGFI_ICON = 0x000000100,
            SHGFI_DISPLAYNAME = 0x000000200,
            SHGFI_TYPENAME = 0x000000400,
            SHGFI_ATTRIBUTES = 0x000000800,
            SHGFI_ICONLOCATION = 0x000001000,
            SHGFI_EXETYPE = 0x000002000,
            SHGFI_SYSICONINDEX = 0x000004000,
            SHGFI_LINKOVERLAY = 0x000008000,
            SHGFI_SELECTED = 0x000010000,
            SHGFI_ATTR_SPECIFIED = 0x000020000,
            SHGFI_LARGEICON = 0x000000000,
            SHGFI_SMALLICON = 0x000000001,
            SHGFI_OPENICON = 0x000000002,
            SHGFI_SHELLICONSIZE = 0x000000004,
            SHGFI_PIDL = 0x000000008,
            SHGFI_USEFILEATTRIBUTES = 0x000000010,
            SHGFI_ADDOVERLAYS = 0x000000020,
            SHGFI_OVERLAYINDEX = 0x000000040
        }

        [Flags]
        private enum FileAttributes : uint
        {
            Readonly = 0x00000001,
            Hidden = 0x00000002,
            System = 0x00000004,
            Directory = 0x00000010,
            Archive = 0x00000020,
            Device = 0x00000040,
            Normal = 0x00000080,
            Temporary = 0x00000100,
            SparseFile = 0x00000200,
            ReparsePoint = 0x00000400,
            Compressed = 0x00000800,
            Offline = 0x00001000,
            NotContentIndexed = 0x00002000,
            Encrypted = 0x00004000,
            Virtual = 0x00010000
        }

        [Flags]
        public enum ISIOI : uint
        {
            ISIOI_ICONFILE = 0x00000001,
            ISIOI_ICONINDEX = 0x00000002
        }

        private SortedDictionary<string, Guid> ShellIconOverlayIdentifiers;

        public string IconName { get; private set; }
        public Icon Icon { get; private set; }
        public int IconOverlayIndex { get; private set; }
        public Guid IconOverlayGuid { get; private set; }

        public ExtractIconInfo(FileInfo fi)
        {
            ExtractIcon(fi.FullName, false);
        }
        public ExtractIconInfo(DirectoryInfo di)
        {
            ExtractIcon(di.FullName, true);
        }
        public ExtractIconInfo(string path, bool isFolder)
        {
            ExtractIcon(path, isFolder);
        }

        [ComVisible(false)]
        [ComImport]
        [Guid("0C6C4200-C589-11D0-999A-00C04FD655E1")]
        [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        private interface IShellIconOverlayIdentifier
        {
            [PreserveSig]
            int IsMemberOf([MarshalAs(UnmanagedType.LPWStr)] string path, uint attributes);
            [PreserveSig]
            int GetOverlayInfo([MarshalAs(UnmanagedType.LPWStr)] string iconFileBuffer, int iconFileBufferSize, out int iconIndex, out uint flags);
            [PreserveSig]
            int GetPriority(out int priority);
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct SHFILEINFO
        {
            internal IntPtr hIcon;
            internal int iIcon;
            internal uint dwAttributes;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
            internal string szDisplayName;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
            internal string szTypeName;
        };

        [DllImport("shell32.dll")]
        private static extern IntPtr SHGetFileInfo(
            string pszPath,
            uint dwFileAttributes,
            ref SHFILEINFO psfi,
            uint cbSizeFileInfo,
            uint uFlags
        );

        [DllImport("shell32.dll")]
        private static extern int SHGetIconOverlayIndex(
            string pszIconPath,
            int iIconIndex
        );

        [DllImport("user32.dll")]
        public static extern int DestroyIcon(IntPtr hIcon);

        private void ExtractIcon(string path, bool isFolder)
        {
            SHFILEINFO shIconInfo = new SHFILEINFO();
            SHGetFileInfo(path, isFolder ? (uint)FileAttributes.Directory : (uint)FileAttributes.Normal, ref shIconInfo, (uint)Marshal.SizeOf(shIconInfo), (uint)SHGFI.SHGFI_ICON | (uint)SHGFI.SHGFI_LARGEICON | (uint)SHGFI.SHGFI_DISPLAYNAME | (uint)SHGFI.SHGFI_ADDOVERLAYS | (uint)SHGFI.SHGFI_OVERLAYINDEX);
            Icon = (Icon)Icon.FromHandle(shIconInfo.hIcon).Clone();
            IconName = shIconInfo.szDisplayName;
            IconOverlayIndex = (shIconInfo.iIcon >> 24) & 0xFF;
            GetOverlayIconGuid();

            DestroyIcon(shIconInfo.hIcon);
        }

        private void GetOverlayIconGuid()
        {
            IconOverlayGuid = Guid.Empty;
            GetRegistryShellIconOverlayIdentifiers();
            foreach (string key in ShellIconOverlayIdentifiers.Keys)
            {
                string AIconFileName = string.Empty;
                int AIconIndex = 0;
                Guid value = ShellIconOverlayIdentifiers[key];
                GetOverlayIconInfo(value, out AIconFileName, out AIconIndex);
                if (SHGetIconOverlayIndex(AIconFileName, AIconIndex) == IconOverlayIndex)
                    IconOverlayGuid = value;
            }
        }

        private void GetOverlayIconInfo(Guid CLSID, out string path, out int index)
        {
            try
            {
                path = new string(' ', 256);
                uint flags = 0;
                IShellIconOverlayIdentifier SHIOI = Activator.CreateInstance(Type.GetTypeFromCLSID(CLSID, true), true) as IShellIconOverlayIdentifier;
                SHIOI.GetOverlayInfo(path, path.Length, out index, out flags);
                Marshal.ReleaseComObject(SHIOI);

                if ((flags & (uint)ISIOI.ISIOI_ICONFILE) != 0)
                {
                    if ((flags & (uint)ISIOI.ISIOI_ICONINDEX) == 0) index = 0;
                }
                else
                {
                    path = string.Empty;
                    index = 0;
                }
                path = path.Substring(0, path.IndexOf('\0'));
            }
            catch
            {
                path = string.Empty;
                index = 0;
            }
        }

        private void GetRegistryShellIconOverlayIdentifiers()
        {
            ShellIconOverlayIdentifiers = new SortedDictionary<string, Guid>();

            using (RegistryKey key32 = RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, Environment.MachineName, RegistryView.Registry32).OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers"))
                foreach (string subKey in key32.GetSubKeyNames())
                {
                    Guid value = Guid.Parse((string)key32.OpenSubKey(subKey).GetValue(null));
                    if (!ShellIconOverlayIdentifiers.ContainsKey(subKey))
                        ShellIconOverlayIdentifiers.Add(subKey, value);
                }

            using (RegistryKey key64 = RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, Environment.MachineName, RegistryView.Registry64).OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers"))
                foreach (string subKey in key64.GetSubKeyNames())
                {
                    Guid value = Guid.Parse((string)key64.OpenSubKey(subKey).GetValue(null));
                    if (!ShellIconOverlayIdentifiers.ContainsKey(subKey))
                        ShellIconOverlayIdentifiers.Add(subKey, value);
                }
        }
    }
}

这里相关的信息是 IconOverlayGuid

然后进行比较:

public enum SyncStatus { Off, Unknown, Syncing, Synced, SharedSyncing, SharedSynced, Error }

private SyncStatus CheckIfSync(DirectoryInfo di)
{
    ExtractIconInfo IcoInfo = new ExtractIconInfo(di);
    if (IcoInfo != null)
    {
        if (IcoInfo.IconOverlayGuid == Guid.Empty) return SyncStatus.Off;
        else if (IcoInfo.IconOverlayGuid == new Guid("{BBACC218-34EA-4666-9D7A-C78F2274A524}")) return SyncStatus.Error;
        else if (IcoInfo.IconOverlayGuid == new Guid("{F241C880-6982-4CE5-8CF7-7085BA96DA5A}")) return SyncStatus.Synced;
        else if (IcoInfo.IconOverlayGuid == new Guid("{A0396A93-DC06-4AEF-BEE9-95FFCCAEF20E}")) return SyncStatus.Syncing;
        else if (IcoInfo.IconOverlayGuid == new Guid("{5AB7172C-9C11-405C-8DD5-AF20F3606282}")) return SyncStatus.SharedSynced;
        else if (IcoInfo.IconOverlayGuid == new Guid("{A78ED123-AB77-406B-9962-2A5D9D2F7F30}")) return SyncStatus.SharedSyncing;
        else return SyncStatus.Unknown;
    }
    else return SyncStatus.Unknown;
}

覆盖图标存储在FileSyncShell.dll中。 它们的CLSID存储在注册表中:HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiersHKLM\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers

REGEDIT

  • OneDrive1:同步出错
  • OneDrive2:已同步共享
  • OneDrive3:正在同步共享
  • OneDrive4:已同步
  • OneDrive5:正在同步

希望这能有所帮助!

(我花了太多时间在这上面,但我学到了很多!)


你测试过这个吗?这个确实可靠地工作吗? - user3700562
@user3700562 是的,已经测试并且现在正在生产中。 - DKH
1
嗯,我用这个来测试我的OneDrive文件夹,结果始终返回空的图标GUID(对于文件夹和文件都是如此)。也许OneDrive的版本有关系? - ndh103
1
这用于提取叠加图标 GUID。如果您在文件夹上有一个叠加图标,它应该返回一个 GUID,而不管软件是什么。 - DKH
这对我不起作用。我得到了一个异常 - 检索具有CLSID {BBACC218-34EA-4666-9D7A-C78F2274A524}的组件的COM类工厂失败,原因是以下错误:80040154类未注册。 - colinbashbash

0

可能是在比较LastModifiedDateTime。

...Drive.Root.ItemWithPath(file).Request().GetAsync()).FileSystemInfo.LastModifiedDateTime

嗯...我觉得这不会有帮助。 - monika pathak
2
我的意图是检查本地文件/文件夹的属性,以确定它们是否与OneDrive同步,而不是依赖于OneDrive API。当图标自动更改以指示不同步/同步/ 同步状态时,文件的属性肯定会发生变化。我想在本地获取这些信息。 - monika pathak

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接