nicoriff / ORMi

A Light-ORM for accesing WMI
MIT License
209 stars 28 forks source link

System.InvalidCastException: Object must implement IConvertible #17

Closed ewisted closed 4 years ago

ewisted commented 4 years ago

I ran across this exception when trying to query for a WMI object with other WMI objects as properties. The specific instance is Win32_UserProfile.

As you can see in the docs, it has properties of Win32_FolderRedirectionHealth. My first thought was to treat it like you would serializing an object from JSON, where you have the properties as follows-

[WMIClass(Name = "Win32_UserProfile", Namespace = "root\\CimV2")]
    public class UserProfile
    {
        public string SID { get; set; }
        public string LocalPath { get; set; }
        public bool Loaded { get; set; }
        [WMIProperty("refCount")]
        public uint RefCount { get; set; }
        public bool Special { get; set; }
        public bool RoamingConfigured { get; set; }
        public string RoamingPath { get; set; }
        public bool RoamingPreference { get; set; }
        public uint Status { get; set; }
        public DateTime LastUseTime { get; set; }
        public DateTime LastDownloadTime { get; set; }
        public DateTime LastUploadTime { get; set; }
        public byte HealthStatus { get; set; }
        public DateTime LastAttemptedProfileDownloadTime { get; set; }
        public DateTime LastAttemptedProfileUploadTime { get; set; }
        public DateTime LastBackgroundRegistryUploadTime { get; set; }
        public FolderRedirectionHealth AppDataRoaming { get; set; }
        public FolderRedirectionHealth Desktop { get; set; }
        public FolderRedirectionHealth StartMenu { get; set; }
        public FolderRedirectionHealth Documents { get; set; }
        public FolderRedirectionHealth Pictures { get; set; }
        public FolderRedirectionHealth Music { get; set; }
        public FolderRedirectionHealth Videos { get; set; }
        public FolderRedirectionHealth Favorites { get; set; }
        public FolderRedirectionHealth Contacts { get; set; }
        public FolderRedirectionHealth Downloads { get; set; }
        public FolderRedirectionHealth Links { get; set; }
        public FolderRedirectionHealth Searches { get; set; }
        public FolderRedirectionHealth SavedGames { get; set; }
    }

And for the properties model, it would look like this-

[WMIClass(Name = "Win32_FolderRedirectionHealth", Namespace = "root\\CimV2")]
    public class FolderRedirectionHealth
    {
        public string OfflineFileNameFolderGUID { get; set; }
        public bool Redirected { get; set; }
        public bool OfflineAccessEnabled { get; set; }
        public DateTime LastSuccessfulSyncTime { get; set; }
        public DateTime LastSyncTime { get; set; }
        public byte LastSyncStatus { get; set; }
        public byte HealthStatus { get; set; }
    }

Using this data model and passing UserProfile as a type parameter to QueryAsync results in the following exception-

System.InvalidCastException: Object must implement IConvertible.
   at System.Convert.ChangeType(Object value, Type conversionType, IFormatProvider provider)
   at System.Convert.ChangeType(Object value, Type conversionType)
   at ORMi.Helpers.TypeHelper._SetPropertyValue(ManagementBaseObject mo, PropertyInfo p, Object o)
   at ORMi.Helpers.TypeHelper.LoadObject(ManagementObject mo, Type t)
   at ORMi.WMIHelper.QueryFirstOrDefault[T](String query)
   at ORMi.WMIHelper.<>c__DisplayClass28_0`1.<QueryFirstOrDefaultAsync>b__0()
   at System.Threading.Tasks.Task`1.InnerInvoke()
   at System.Threading.Tasks.Task.Execute()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at ORMi.WMIHelper.<QueryFirstOrDefaultAsync>d__28`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at servicedesk_tools.Services.Implementations.RemoteSessionService.<IsUserLoggedIn>d__1.MoveNext() in C:\Users\EricWi\Source\Repos\servicedesk-tools\servicedesk-tools\Services\Implementations\RemoteSessionService.cs:line 103
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at servicedesk_tools.UnitTests.RemoteSessionServiceIntegrationTests.<IsUserLoggedInSingleServerTest>d__3.MoveNext()

I've already forked the repo and fixed this with a little recursion, but I was wondering if this is a known issue or a design choice that this functionality was left out? Or is there a better way built in to handle this that I don't know about?

nicoriff commented 4 years ago

Hi @ewisted great catch!. It isn't because of any particular reasons that WMI objects weren't taken into accout for conversion. Just it was a functionality that wasn't implemented. You can send the PR so I can build a ner version. Thanks!

ewisted commented 4 years ago

Hey @nicoriff thanks for the quick reply. I've submitted the fix in Added testing and logic for recursively adding WMI objects as properties #18. Let me know if any changes need to be made.

Love the project btw. I was running into issues trying to run WMI queries asynchronously on multiple threads, but it works flawlessly with ORMi.

nicoriff commented 4 years ago

Thanks!. Just merged the PR you sent me. I'm glad you find ORMi useful and you are welcome to contribute whenever you want.