none
What is tp_WebPartTypeId in WebParts table?

    Question

  • I am trying to look at a SharePoint content database and pull out a list of all WebPart usage.  If I have to limit myself to custom webparts (non-MS) that would be fine.  I have the ability to run code in conjunction with SQL queries.  The problem is a pesky little column in the WebParts table called tp_WebPartTypeId.  This is a Guid that is not referenced in any other column in the database that I can find.  I searched all uniqueidentifier columns and text based columns for this Guid using a cursor which built on the fly SQL statements.  I also used this same technique on the SP config database.  I have also searched the registry and all files on the SP box for the text representation of one of these Guids to no avail.  After hours of Googling and Db queries I still have no clue what this Guid relates to.

    What does the tp_WebPartTypeId of the WebParts table map to?  I need to somehow map this to the WebPart assembly and TypeName.

     

    Thanks,

    Vance

    Friday, September 21, 2007 1:13 PM

Answers

  •  

    Hi,

     

    Having the same need of info, I dug a bit and finally found the answer: The WebPartTypeID is not really a GUID, it is the concatenation of the full assembly name + '|' (pipe symbol) + full type name of the WebPart, converted to a byte array using Unicode encoding, and hashed using the MD5 provider. Result of MD5 is always 16 bytes, so it fits into a GUID.

     

    No more, no less.

     

    Hope this helps you,

     

    Tom

    Wednesday, October 03, 2007 9:54 AM
  • I have been trying for days to post a response to this but keep getting errors....

     

    Sorry for the brain freeze on the full assembly name.  I have not had much time to devote to this lately and had to rush through it.

     

    And converting the hashed bytes into an actual Guid does make them line up correctly.  Here is the final code to generate the correct tp_WebPartTypeId value:

     

    Code Block

    public static void Main()

    {

        Assembly assembly = Assembly.LoadFile(@"C:\...\SimpleWebPart.dll");

        string data = assembly.FullName;

        // Will use reflection to pull up WebPart subclasses

        // but for now just use this 1 test case

        data += "|SimpleWebPart.SimpleWebPart";

        Console.WriteLine("{0}", data);

     

        byte[] dataBytes = Encoding.Unicode.GetBytes(data);

        MD5 hashAlgo = new MD5CryptoServiceProvider();

        byte[] hash = hashAlgo.ComputeHash(dataBytes);

     

        Guid hashGuid = new Guid(hash);

        Console.WriteLine("Hash={0}", hashGuid.ToString() );

        Console.ReadLine();

    }

     

     

     

    Thanks again for your help, I really appreciate it.  And hopefully this will help others as well.

     

    Vance

     

    Tuesday, October 09, 2007 3:32 PM

All replies

  • I have been looking at the for days now without a solution!  Here is what I have discovered (and not discovered).

     

    The tp_WebPartTypeId is the same if you deploy the same webpart to different farms.  So, this Guid has to be something it gets from the WebPart assembly itself.  It is NOT the Guid given to the WebPart using the GuidAttribute in source code.  I built a test WebPart assigned a Guid to the class and dropped it on a page.  The tp_WebPartTypeId for that test part was totally different than what I had assigned.

     

    It has to be using reflection or COM to generate this Guid but I cannot determine out how.

     

    BTW, I am having trouble with parts that are NOT in the web gallery.  They have a DWP in the wpcatalog directory but are not actually in the web part gallery.

     

    Please help,

    Vance

    Thursday, September 27, 2007 1:15 PM
  •  

    Hi,

     

    Having the same need of info, I dug a bit and finally found the answer: The WebPartTypeID is not really a GUID, it is the concatenation of the full assembly name + '|' (pipe symbol) + full type name of the WebPart, converted to a byte array using Unicode encoding, and hashed using the MD5 provider. Result of MD5 is always 16 bytes, so it fits into a GUID.

     

    No more, no less.

     

    Hope this helps you,

     

    Tom

    Wednesday, October 03, 2007 9:54 AM
  • THANKS!  I really appreciate that information.  Were you able to use this to map back to an assembly and type?  I don't see how since a hash is a one way road.  Since this value is not stored in the SP Db (from what I can tell) I guess SP is building an internal map in memory of these hashes to assembly and type names.  It must run through all the assemblies in the web part gallery and the wpcatalog DWP files and search for WebPart subclasses.

     

    If MS would be kind enough to share their encryption key (salt, IV, etc..) then I could generate the same map and quickly run a report to show all the web part usage info.  However, since that will never happen I guess we will have to continue to use the SP object model to retrieve this information which is painfully SLOW.

     

    I can only guess that the reason they do this instead of actually storing useful information like the assembly and type name is for security purposes.  Even though they could still store the names and use the hash value to actually do the mapping and still have the security to prevent someone dropping in a hazardous assembly and type name.  But once they have the level of access to the Db that allows that then all bets are pretty much off anyway.

     

    Thanks again, now we can stop chasing a dead horse (mixed metaphor is intentional)

    Vance

    Wednesday, October 03, 2007 7:44 PM
  • I see no way to map back to an assembly. As long as you got them on the server, there's a way to "discover" them and map them back to the table. (Iterate through assemblies, though types with reflexion and find out available WebPart types). Now we got the algo to get the GUID, you can map those to the WebPartTypeID column.

     

    Though, since MD5 is a hashing algorithm (implying data loss), there is definitely no way back (except brute forcing a reverse MD5 that would leave you for each GUID with plenty assembly name/type name combinations to try).

    Hashing algorithms have no salt nor IV nor key; it is not an encryption method. No way back.

     

    The only hope for "salvation" is to go the opposite way: load assemblies and types first, generate the GUIDs and match them in the table.

     

    Hope this helps,

     

    Tom

    Thursday, October 04, 2007 4:23 PM
  • Yes, that helps me remember that MD5 is a non-keyed hash.  So, yes we should be able to generate the same hash as in the Db if we start with the same data.  That is VERY encouraging!

     

    Yes, I agree we should be able to reflect through all the assemblies and generate a valid map.  However, I have yet to generate the same webpart for a known test case.  Here is the code I am using to generate my hash:

     

    Code Block

    // Full assembly and type name concated w/|

    string data = "SimpleWebPart|SimpleWebPart.SimpleWebPart";

    byte[] dataBytes = Encoding.Unicode.GetBytes(data);

    MD5 hashAlgo = new MD5CryptoServiceProvider();

    byte[] hash = hashAlgo.ComputeHash(dataBytes);

    Console.Write("Hash=");

    for (int idx = 0; idx < hash.Length; ++idx)

    {

        Console.Write("{0}", hash[idx].ToString("x") );

    }

     

    This results in the bytes f1615b2d22ac3e9deb292fd7f2a45f for the hash but the bytes in the Db for that part are 736C4DDE-C461-B5DA-679D-57B70AAEEF2C.  Even accounting for possible byte re-ordering this is not even close.  I tried converting to all lowercase or all upper case but that does not match either.

     

    Vance

    Thursday, October 04, 2007 6:38 PM
  •  

    Well, I found a couple of things. Looking at your code block, the type name is a full name, alright.

    Though, the assembly name isn't qualified, that's maybe the "salt" that's lacking.

     

    With a similar attempt, I got a "data" of "SimpleWebPart, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null|SimpleWebPart.SimpleWebPart", a hash of "2c2098941d9dbbad8f07fdf5702200c1" and a Guid of "9498202C-9D1D-ADBB-8F07-FDF5702200C1" (your hash hasn't been yet converted to a GUID !)

     

    So if you correct the full assembly name with type.Assembly.FullName, you might (I hope so) get the correct GUID to lookup the WebParts table.

     

    Actually, I found out the algorithm by reversing the obfuscated SharePoint sources (which was quite a hard time, but makes me sure of the result).

    Thursday, October 04, 2007 8:48 PM
  • I have been trying for days to post a response to this but keep getting errors....

     

    Sorry for the brain freeze on the full assembly name.  I have not had much time to devote to this lately and had to rush through it.

     

    And converting the hashed bytes into an actual Guid does make them line up correctly.  Here is the final code to generate the correct tp_WebPartTypeId value:

     

    Code Block

    public static void Main()

    {

        Assembly assembly = Assembly.LoadFile(@"C:\...\SimpleWebPart.dll");

        string data = assembly.FullName;

        // Will use reflection to pull up WebPart subclasses

        // but for now just use this 1 test case

        data += "|SimpleWebPart.SimpleWebPart";

        Console.WriteLine("{0}", data);

     

        byte[] dataBytes = Encoding.Unicode.GetBytes(data);

        MD5 hashAlgo = new MD5CryptoServiceProvider();

        byte[] hash = hashAlgo.ComputeHash(dataBytes);

     

        Guid hashGuid = new Guid(hash);

        Console.WriteLine("Hash={0}", hashGuid.ToString() );

        Console.ReadLine();

    }

     

     

     

    Thanks again for your help, I really appreciate it.  And hopefully this will help others as well.

     

    Vance

     

    Tuesday, October 09, 2007 3:32 PM
  • Hello Vancek
    I have posted a simple windows form GUI project on codeplex, based on your method, giving you credits for your work.
    http://easywebparttypeidgen.codeplex.com/
    Thank you for sharing your solution.

    • Proposed as answer by myrocode Tuesday, July 13, 2010 12:18 PM
    Tuesday, July 13, 2010 11:03 AM