Hi guys. I'm just going to drop a few things into this conversation that may help some.
First of all, you are dealing with 'instance properties' here, which are different than 'custom iProperties', even though they may both seem to utilize the same system. A single BOMRow can represent any number of 'instances' (BOMRow.ComponentOccurrences) that exist within an assembly (not always just one 'instance'). Therefore, if a single BOMRow represents 6 instances of a part (for example), and only one of those instances has this instance property applied to it, then you would have to iterate through each of the instances in the collection which that BOMRow represents to find what you are looking for. Yes, a BOMRow object does have a property for instance properties (BOMRow.OccurrencePropertySets), but that is generally for 'applying' instance properties to all instances which that BOMRow represents, but not the greatest for retrieving existing instance properties when there are potentially different instances within that row's ComponentOccurrences collection that potentially have different properties, with potentially different values. If every instance has the same instance property assigned to it, with the same value, then you might as well just use the custom iProperties of the referenced document, unless that referenced document is ReadOnly for some reason. So, it may make more sense to simply iterate through all of the ComponentOccurrences, and use their ComponentOccurrence.OccurrencePropertySets property. But that is just my 2 cents, so you can take it or leave it.
The ProxyObject.NativeObject property does not always lead to the 'original', non-proxy object either. It can lead to a lower level proxy object, if that is what this proxy was based on. An example of this is when you have a part occurrence that is within a sub assembly occurrence, and that sub assembly occurrence is in the main assembly. A proxy object that exists within the main assembly for geometry of that part occurrence will be a proxy of a proxy (2 levels removed from the original), meaning that top level proxy object's 'NativeObject' will be the proxy of that geometry which exists within the context of the sub assembly, instead of within the context of the main assembly, but is still not the original geometry, which only exists within the context of the part document itself. Then that second level proxy's NativeObject will finally be the original geometry that exists within the context of the part document.
And yes, the first 4 PropertySet objects within every Inventor.Document will always be in the same Index order, because they will always pre-exist, and are standard. So, referring to them by their Index number is a stable way to do it, but perhaps not the most readable/understandable way to do so. For years I referred to them my their full Inventor API names myself, but have since (past couple of years) switched mostly to referring to them by their Index numbers, mainly because I now know it is safe to do so, is much faster/easier to do so, but often leave myself a 'comment' about which one it is accessing. But I likely got into that habit more due to a lot of the code I wrote was for others, who may live in other countries, and may speak other languages, in which case using the Index numbers is language independent.
Wesley Crihfield

(Not an Autodesk Employee)