NULL reference exception while reading user sessio

2019-04-29 15:59发布

I have implemented the code for reading the active sessions using the reference Reading All Users Session and Get a list of all active sessions in ASP.NET.

Private List<String> getOnlineUsers()
{
    List<String> activeSessions = new List<String>();
    object obj = typeof(HttpRuntime).GetProperty("CacheInternal", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null, null);
    object[] obj2 = (object[])obj.GetType().GetField("_caches", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(obj);
    for (int i = 0; i < obj2.Length; i++)
    {
        Hashtable c2 = (Hashtable)obj2[i].GetType().GetField("_entries", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(obj2[i]);
        foreach (DictionaryEntry entry in c2)
        {
            object o1 = entry.Value.GetType().GetProperty("Value", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(entry.Value, null);
            if (o1.GetType().ToString() == "System.Web.SessionState.InProcSessionState")
            {
                SessionStateItemCollection sess = (SessionStateItemCollection)o1.GetType().GetField("_sessionItems", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(o1);
                if (sess != null)
                {
                    if (sess["loggedInUserId"] != null)
                    {
                        activeSessions.Add(sess["loggedInUserId"].ToString());
                    }
                }
            }
        }
    }
    return activeSessions;
}

It is working fine in local system (in windows XP and Windows 7). While I hosted the application in Windows server 2003 (IIS version 6), it gives an NULL object reference error in the line

object[] obj2 = (object[])obj.GetType().GetField("_caches", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(obj);

Is this anything related to the permission issue or trust level settings related to IIS? Please let know anyone came across such an issue. Any help is highly appreciable.

5条回答
孤傲高冷的网名
2楼-- · 2019-04-29 16:17

I've tried Paully's solution, which didn't compile in some points and lead to runtime errors in others. Anyway, inspired on his suggestion (thanks a lot! My vote goes for that), I came to my own, which compiles and gets me the expected data.

Also, I'm returning a IEnumerable and I'm using "yield return", which makes it more performatic for big lists (kind of lazy loading of data). Here it goes:

public static System.Collections.Generic.IEnumerable<SessionStateItemCollection> GetAllUserSessions()
{
    List<Hashtable> hTables = new List<Hashtable>();
    object obj = typeof(HttpRuntime).GetProperty("CacheInternal", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null, null);
    dynamic fieldInfo = obj.GetType().GetField("_caches", BindingFlags.NonPublic | BindingFlags.Instance);

    //If server uses "_caches" to store session info
    if (fieldInfo != null)
    {
        object[] _caches = (object[])fieldInfo.GetValue(obj);
        for (int i = 0; i <= _caches.Length - 1; i++)
        {
            Hashtable hTable = (Hashtable)_caches[i].GetType().GetField("_entries", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(_caches[i]);
            hTables.Add(hTable);
        }
    }
    //If server uses "_cachesRefs" to store session info
    else
    {
        fieldInfo = obj.GetType().GetField("_cachesRefs", BindingFlags.NonPublic | BindingFlags.Instance);
        object[] cacheRefs = fieldInfo.GetValue(obj);
        for (int i = 0; i <= cacheRefs.Length - 1; i++)
        {
            var target = cacheRefs[i].GetType().GetProperty("Target").GetValue(cacheRefs[i], null);
            Hashtable hTable = (Hashtable)target.GetType().GetField("_entries", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(target);
            hTables.Add(hTable);
        }
    }

    foreach (Hashtable hTable in hTables)
    {
        foreach (DictionaryEntry entry in hTable)
        {
            object o1 = entry.Value.GetType().GetProperty("Value", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(entry.Value, null);
            if (o1.GetType().ToString() == "System.Web.SessionState.InProcSessionState")
            {
                SessionStateItemCollection sess = (SessionStateItemCollection)o1.GetType().GetField("_sessionItems", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(o1);
                if (sess != null)
                     yield return sess;
            }
        }
    }
}
查看更多
狗以群分
3楼-- · 2019-04-29 16:17

Exactly for your business case there is Application state variable in asp.net. It is similar to session state, but visible for all users request.

查看更多
劫难
4楼-- · 2019-04-29 16:28

I know this is an old thread, but this may save someone some time. Another thing to check is that obj is of type System.Web.Caching.CacheMultiple. I had this same problem and it was a platform-specific issue as Marc Gravell suggested. It turned out that on the Windows 2003 server, obj was type System.Web.Caching.CacheSingle and there was a null reference exception when trying to get the value for "_caches".

If that is the case, you can still get a list of active sessions with (Hashtable)obj.GetType().GetField("_entries", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(obj);

查看更多
Fickle 薄情
5楼-- · 2019-04-29 16:28

Try using this, _cachesRefs if _caches is NULL. The function below will return all user sessions collections for all multiple versions of Windows and including Windows Server.

It works.

public List<SessionStateItemCollection> GetAllUserSessions() {

List<Hashtable> hTables = new List<Hashtable>();

PropertyInfo propInfo = typeof(HttpRuntime).GetProperty("CacheInternal", BindingFlags.NonPublic | BindingFlags.Static);

object CacheInternal = propInfo.GetValue(null, null);

dynamic fieldInfo = CacheInternal.GetType().GetField("_caches", BindingFlags.NonPublic | BindingFlags.Instance);

if (fieldInfo != null) {
    object[] _caches = (object[])fieldInfo.GetValue(CacheInternal);
    for (int i = 0; i <= _caches.Length - 1; i++) {
        Hashtable hTable = (Hashtable)_caches(i).GetType().GetField("_entries", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(_caches(i));
        hTables.Add(hTable);
    }
} else {
    fieldInfo = CacheInternal.GetType().GetField("_cachesRefs", BindingFlags.NonPublic | BindingFlags.Instance);
    dynamic cacheRefs = fieldInfo.GetValue(CacheInternal);
    foreach (void cacheRef_loopVariable in cacheRefs) {
        cacheRef = cacheRef_loopVariable;
        dynamic target = cacheRef.Target;
        fieldInfo = target.GetType().GetField("_entries", BindingFlags.NonPublic | BindingFlags.Instance);
        Hashtable hTable = fieldInfo.GetValue(target);
        hTables.Add(hTable);
    }
}

List<SessionStateItemCollection> sessionlist = new List<SessionStateItemCollection>();

foreach (void hTable_loopVariable in hTables) {
    hTable = hTable_loopVariable;
    foreach (DictionaryEntry entry in hTable) {
        object value = entry.Value.GetType().GetProperty("Value", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(entry.Value, null);
        if (value.GetType().ToString() == "System.Web.SessionState.InProcSessionState") {
            SessionStateItemCollection sCollection = (SessionStateItemCollection)value.GetType().GetField("_sessionItems", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(value);
            if (sCollection != null)
                sessionlist.Add(sCollection);
        }
    }
}

return sessionlist;

}

查看更多
We Are One
6楼-- · 2019-04-29 16:31

It sounds like a different version (or update) of .NET is running on 2003 than you have on XP / Win7, although it could also just be a platform-specific difference. If it was permissions / trust, you would have seen an exception. Instead, it seems more likely that simply: _caches does not exist on whatever version is on the 2003 machine. If you use reflection to access private state: you should entirely expect it to explode between versions / updates / platforms / at-whim / etc.

To investigate:

  • check whether obj is null
  • check whether obj.GetType().GetField("_caches", BindingFlags.NonPublic | BindingFlags.Instance) is null

(either of those things could cause this exception on the line you cite)

查看更多
登录 后发表回答