How can I check local OneDrive folder is in sync?
Asked Answered
L

2

7

I need to be able to check if the local OneDrive folder is in sync/up to date.

Can I check this by looking at any of the file/folder properties(in C# code) Without using any of One Drive APIs?

Latvian answered 20/12, 2016 at 11:25 Comment(4)
Are you trying to compare the folder to another to see if they're matching?Sweptback
It may be a file or a folder.Latvian
I just want to know if I can make out whether an item(file or folder) is in sync by looking at any of its properties. I am assuming since the icon of the item changes if it is in sync, may be there are some file properties that change as well.Latvian
Why not use a One Drive API?Iscariot
E
6

I was stuck with that and tought to check the main folder Icon.

EDITED

The point is to extract the synced folder icon and get the overlay CLSID.

You first need a class to extract the info you need :

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);
                }
        }
    }
}

The relevant info here is IconOverlayGuid.

Then compare :

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;
}

The Overlay Icons are stored in FileSyncShell.dll. Their CLSID are stored in the registry : HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers and HKLM\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers.

REGEDIT

  • OneDrive1 : Sync in error
  • OneDrive2 : Synced Shared
  • OneDrive3 : Syncing Shared
  • OneDrive4 : Synced
  • OneDrive5 : Syncing

Hope that helps !

(I spent way too much time on that one, but I learned a lot !)

Energetic answered 28/8, 2017 at 9:40 Comment(5)
Have you tested this? Does this actually realiably work?Squirearchy
@Squirearchy Yes, tested and now in productionEnergetic
Uhm, I used this to test my OneDrive folder and it is always return empty guid for icon (for both folder and files). May be OneDrive version does matter?Coenesthesia
This is used to extract the overlay icon guid. If you have an overlay icon on a folder, it should return a guid regardless of the software.Energetic
This wouldn't work for me. I get an exception - Retrieving the COM class factory for component with CLSID {BBACC218-34EA-4666-9D7A-C78F2274A524} failed due to the following error: 80040154 Class not registeredGallego
C
0

Probably comparing the LastModifiedDateTime.

...Drive.Root.ItemWithPath(file).Request().GetAsync()).FileSystemInfo.LastModifiedDateTime
Crepuscular answered 20/12, 2016 at 14:7 Comment(2)
Well...don't think this is gonna help.Latvian
My intention is to check local file/folder's properties to see if they are in sync with onedrive instead of being dependent on OneDrive APIs. Something must be changing in the file's properties when the icon changes by itself to indicate out of sync/syncing/in-sync status. Want to grab that locally.Latvian

© 2022 - 2024 — McMap. All rights reserved.