System.Reflection in a Windows Mobile Project - c#

Before I start, Let me clear up any confusion before anyone suggests or asks the question..
This Question is related to "Windows Mobile 6 Professional", NOT Windows Phone 7 and it can't be ported to windows phone 7 as it has to go on to an older device.
Now, with that out of the way...
I'm currently trying to port a C# library that I have the source for to run on a windows mobile 6 device, Iv'e coded these things it seems like for ever, but there's one thing Iv'e never had to deal with until now and that's reflection.
Now everyone know that the .NET compact framework does have some limitations, and it appears that lack of support for a lot of the methods and properties in the 'System.Reflection' namespace is one of them.
The actual desktop version of the library is set to target .NET V2.0, and I have devices that are running .NET 3.5 SP1 so for the most part Iv'e had very little problem in getting things to work, a cannot however seem to find a sensible to get the following 2 chunks of code working:
var a = AppDomain.CurrentDomain**.GetAssemblies**();
foreach (var assembly in a)
{
if (assembly is System.Reflection**.Emit.**AssemblyBuilder) continue;
if (assembly**.GetType().**FullName == "System.Reflection.Emit.InternalAssemblyBuilder") continue;
if (assembly**.GlobalAssemblyCache** && assembly**.CodeBase** == Assembly.GetExecutingAssembly()**.CodeBase**) continue;
foreach (var t in GetLoadableTypes(assembly))
{
if (t.IsInterface) continue;
if (t.IsAbstract) continue;
if (t.IsNotPublic) continue;
if (!typeof(IGeometryServices).IsAssignableFrom(t)) continue;
var constuctors = t.GetConstructors();
foreach (var constructorInfo in constuctors)
{
if (constructorInfo.IsPublic && constructorInfo.GetParameters().Length == 0)
return (IGeometryServices)Activator.CreateInstance(t);
}
}
}
And
catch (**ReflectionTypeLoadException** ex)
{
var types = ex**.Types**;
IList<Type> list = new List<Type>(types**.Length**);
foreach (var t in types)
if (t != null && t**.IsPublic**)
list.Add(t);
return list;
}
Specifically, those items in bold in the above code are the methods and properties that don't appear to be present in the compact framework, and after spending quite a chunk of time with intellisense and the object browser, Iv'e not found anything that returns (or makes available) the same types.
My question then is as follows:
Does anyone have any experience of using reflection in the Compact .NET framework, and can suggest how this code can be made to work as expected, or am I going to have to start writing custom stubs and functionality to replace the missing methods.
I know there is some reflection capabilities on the framework, so I'm sure there must be an equivalent way of achieving it.
Just on a final note, for anyone who may recognise the code. YES it is from the .NET topology suite, and yes it is that library I'm trying to build a WM6 version of, so if you know of anyone that has already done it please do put a comment on to that effect, and I'll go take a look at the easier path :-)
======================================================================
Update after posting
It appears 'Bold' text doesn't work in code snippets, so those methods / properties in the above code that are surrounded by ** are the parts supposed to be in bold.

So after studying some older builds and an experimental Silverlight build (Which apparently has a lot of the same limitations as Windows Mobile / CE)
I figured out how to make the magic work.
The first part was to fill the array with 'Assembly' structures representing the assemblies to search. Originally it was:
var a = AppDomain.CurrentDomain.GetAssemblies();
but since GetAssemblies doesn't exist in WM then the quickest way is to use the following:
var a = new[] {Assembly.GetCallingAssembly(), Assembly.GetExecutingAssembly()};
Now in my case this meant that I got two identical assemblies, but if I was calling an Assembly from my main exe which in turn used GeoAPI then 2 different assemblies would be shown here.
The next challenge was the "stripping out" of assemblies we didn't need to check:
if (assembly is System.Reflection.Emit.AssemblyBuilder) continue;
if (assembly.GetType().FullName == System.Reflection.Emit.InternalAssemblyBuilder") continue;
if (assembly.GlobalAssemblyCache && assembly.CodeBase == Assembly.GetExecutingAssembly().CodeBase) continue;
The first and second instances never crop up in WM6 so it's safe to just comment them out. The second one does work, but since you'll never see any assemblies with the given check name (Due to it being missing) on WM6 then again, you should be safe to comment it out. I didn't comment it out, but it also never got triggered as true.
The final part (at least as far as the original puzzle went anyway) was this:
foreach (var t in GetLoadableTypes(assembly))
{
if (t.IsInterface) continue;
if (t.IsAbstract) continue;
if (t.IsNotPublic) continue;
if (!typeof(IGeometryServices).IsAssignableFrom(t)) continue;
In my original attempt 'isInterface' & 'isNotPublic' where missing, however once I managed to fix up the contents of the 'var a' variable above with the data type it expected, everything started to work ok with nothing missing.
The final question is? Did this solve everything? Well not quite....
It turns out that the whole purpose of the 'ReflectInstance' method in GeoAPI was to find a user defined 'GeometryFactory' using an 'IGeometryServices' interface.
Since I was only reflecting over the assembly I was calling from (var a above) then the 'NetTopologySuite' (Where the geometry factory is defined) was not added to the select list (Obviously CurrentDomain.GetAssemblies includes this)
The net result was I still ended up hitting the exception at the end as no Assemblies of the correct type could be located.
It turns out however, that in the 'Geometry' constructor in GeoAPI has an overload that allows you to pass in a GeometryFactory, when you do this it completely ignores the ReflectInstance method and just uses what it's told.
or to put it another way , I never had to do any of this in the first place, I could have just set the method to return 'null' and passed in the geometry factory I wanted to use.
Anyway, if anyone is interested I now have a working copy of:
GeoAPI.NET
NetTopologySuite
Wintellect.PowerCollections
Built and working fine under Windows Mobile 6 and Windows CE with .NET CF 3.5.

Related

3ds Max .NET SDK and creating reference maker

I have .Net DLL for Max with ui, and I want to react to parameter changes of some nodes in the viewport. The easiest solution that came up to me, was to create ReferenceMaker plugin and set reference for node I want to watch. According to the documentation it should be
public class ReferenceListener : Autodesk.Max.Plugins.ReferenceMaker{ ... }
But when I create new instance of this class and try to set reference, it crashes on "Object reference not set to an instance of an object."
When I try to debug it, I see that all baseclass attributes are null, so it seams ReferenceMaker plugin instance was not created in Max.
Finally I found MaxSharp source code here but using resulting dll let me to the same result and frankly the implementation is quiet the same as I had before. Trying to attach ReferenceListener to ReferenceTarget allways crashes beacause of nulls in base class.
So I really don't know how to solve this, but maybe someone tried to create something like this and succedded? For now I'm thinking about writing those parameter changes callbacks to maxscript, and call .net form it, but it feels hacky..
I'm using Max 2014 (and MaxSharp is for 2013) but I did not found any differences mentioned in documentation and any help would be appreciated.
Thank you
UPDATE
So, I narrowed it down to really strange problem. I've created C++/CLI plugin, made ReferenceMaker class in C++ SDK and did .net wrapper to call the plugin form C#, but it still wasn't working with same symptoms.
It seems that wrong pointer address is stored inside Autodesk.Max wrapper objects, so this is the reason why it is failing. I did a comparison of pointer returned from .Net DLL and from C++ SDK, and they are always different by 64. And it is always like that.
C++/CLI code
IINode^ al = Autodesk::Max::GlobalInterface::Instance->COREInterface->GetSelNode(0);
IReferenceTarget^ ak = (IReferenceTarget^)al;
ReferenceTarget* nativeTarget = (ReferenceTarget*)ak->Handle.ToPointer();
m_notifyListener->Test(nativeTarget);
C++ Max SDK code
void NotifyListener::Test(RefTargetHandle managedPointer)
{
Interface* ip = GetCOREInterface();
RefTargetHandle nativePointer = ip->GetSelNode(0);
intptr_t P1 = (intptr_t)managedPointer;
intptr_t P2 = (intptr_t)nativePointer ;
}
and than resulting variables are eg.
P1 = 1490452112
P2 = 1490452048
P2 is always smaller than P1 by 64.
I would understand if those pointers were totally different, but this slight shift is really strange to me.
Does anyone have any idea what is happening there? This is something I really don't get.
I need to test if the same behaviour is in Max 2013 or 2015 as I'm using 2014. I saw on some other forums that other people are complaining sample .net plugins are not working in 2014, so maybe this is the reason?
Thank you for any advice.

Library requires reference to System.Windows.Forms

I'm having trouble finding information on this topic, possibly because I'm not sure how to phrase the question. Hopefully the braintrust here can help or at least advise. This situation might just be me being retentive but it's bugging me so I thought I'd ask for help on how to get around it.
I have a C# library filled with utility classes used in other assemblies. All of my extensions reside in this library and it comes in quite handy. Any of my other libraries or executables that need to use those classes must naturally reference the library.
But one of the extensions I have in there is an extension on the Control class to handle cross thread control updates in a less convoluted fashion. As a consequence the utility library must reference System.Windows.Forms.
The problem being that any library or executable that references the utilities library must now have a reference to System.Windows.Forms as well or I get a a build error for the missing reference. While this is not a big deal, it seems sort of stupid to have assemblies that have nothing to do with controls or forms having to reference System.Windows.Forms just because the utilities library does especially since most of them aren't actually using the InvokeAsRequired() extension I wrote.
I thought about moving the InvokeAsRequired() extension into it's own library, which would eliminate the System.Windows.forms problem as only assemblies that needed to use the InvokeAsRequired() extension would already have a reference to SWF.... but then I'd have a library with only one thing in it which will probably bother me more.
Is there a way around this requirement beyond separating out the 'offending' method and creating a nearly empty library? Maybe a compile setting or something?
It should be noted that the 'offending method' is actually used across multiple projects that have UI. A lot of the UI updates I do are as a result of events coming in and trying to update windows form controls from another thread causes various UI thread problems. Hence the method handling the Invoke when needed. (Though personally I think that whole InvokeRequired pattern should be wrapped up into the control itself rather than having something external do the thread alignment in the first place).
If it's just one function, then package it up as a source code file into a NuGet package and then add the NuGet package to your projects. Then, this code will be easily deployable to new projects (as well as easily updateable), but you don't need to create a separate assembly. Just compile it into your application.
You would then store your NuGet package in a local NuGet repository, or get a myget account, or even just store it somewhere on your network. Worst case, you can check it into your version control, but I would just check in the "project" that you build the nuget package from, so you can rebuild the package if need be.
Who knows, at some point, you may add more utility functions that require windows forms, and at that point you could justify a separate assembly.
It's easy: you have to move the offending code out. For now it might be a little concern, but in the end it might be a blast you did it now instead of at the moment you are forced to.
Even if it is just one method (for now), just move the method into another assembly. I didn't say a new one, it can be in the assembly that uses it if only one, or all others that need that moment derive from it.
You can solve your problem by switching the utility library code from the early-binding pattern to the late-binding pattern when it comes to types declared in the System.Windows.Forms namespace.
This article shows how to do it the short way: Stack Overflow: C#.NET - Type.GetType(“System.Windows.Forms.Form”) returns null
And this code snippet shows how the monoresgen tool from the Mono Project (open source ECMA CLI, C# and .NET implementation) solves the System.Windows.Forms dependency problem.
public const string AssemblySystem_Windows_Forms = "System.Windows.Forms, Version=" + FxVersion + ", Culture=neutral, PublicKeyToken=b77a5c561934e089";
// ...
static Assembly swf;
static Type resxr;
static Type resxw;
/*
* We load the ResX format stuff on demand, since the classes are in
* System.Windows.Forms (!!!) and we can't depend on that assembly in mono, yet.
*/
static void LoadResX () {
if (swf != null)
return;
try {
swf = Assembly.Load(Consts.AssemblySystem_Windows_Forms);
resxr = swf.GetType("System.Resources.ResXResourceReader");
resxw = swf.GetType("System.Resources.ResXResourceWriter");
} catch (Exception e) {
throw new Exception ("Cannot load support for ResX format: " + e.Message);
}
}
// ...
static IResourceReader GetReader (Stream stream, string name, bool useSourcePath) {
string format = Path.GetExtension (name);
switch (format.ToLower (System.Globalization.CultureInfo.InvariantCulture)) {
// ...
case ".resx":
LoadResX ();
IResourceReader reader = (IResourceReader) Activator.CreateInstance (
resxr, new object[] {stream});
if (useSourcePath) { // only possible on 2.0 profile, or higher
PropertyInfo p = reader.GetType ().GetProperty ("BasePath",
BindingFlags.Public | BindingFlags.Instance);
if (p != null && p.CanWrite) {
p.SetValue (reader, Path.GetDirectoryName (name), null);
}
}
return reader;
// ...
}
}
Snippet source: https://github.com/mono/mono/blob/mono-3.10.0/mcs/tools/resgen/monoresgen.cs#L30

How to find which resource cultures are available in a ResourceManager?

I have a situation where I would like to present a list of the "available languages" for my application (which, incidentally, is an ASP .NET MVC 3 application if that makes any odds). I thought that I could automatically get this list somehow since it should just be the resx files that are included in the build (I don't need to support English UK, German Austria or anything, just English or German) and I came up with a scheme that I will present below (implemented as a singleton since it is a bit of an intensive approach).
The problem is that on some machines it returns "Arabic" even though I have no such resource and on mine (since I installed VS 2012) it returns all of them (this makes more sense to me than returning just the two real cultures plus Arabic but it seems that the ResourceManager just wasn't designed to let me get at this information so I probably should not complain). Here is the scheme...
(I have a Strings.resx and a Strings.de.resx file)
IEnumerable<CultureInfo> cultures =
CultureInfo.GetCultures(CultureTypes.NeutralCultures)
.Where(c =>
{
// Exclude the invariant culture and then load up
// an arbitrary string so the resource manager
// loads a resource set, then get the set for the
// current culture specifically and it is, sometimes
// (I thought always but I was wrong) null if no
// set exists
if (c.LCID == CultureInfo.InvariantCulture.LCID)
return false;
var rm = Strings.ResourceManager;
rm.GetString("HELLO", c);
return rm.GetResourceSet(c, false, false) != null;
});
So then I thought, well, I could do this based on whether the language-specific directory exists like so:
var neutralCulture = new[]
{
CultureInfo
.CreateSpecificCulture(((NeutralResourcesLanguageAttribute)
Assembly
.GetExecutingAssembly()
.GetCustomAttributes(
typeof (NeutralResourcesLanguageAttribute),
false)[0])
.CultureName)
};
IEnumerable<CultureInfo> cultures =
CultureInfo.GetCultures(CultureTypes.NeutralCultures)
.Where(c => Directory.Exists(c.TwoLetterISOLanguageName))
.Union(neutralCulture);
This "works" (in so much as it returns English and German) but I think it is not a very stable approach being as it is prone to random problems like someone creating a folder and throwing it all out of whack. I can probably alleviate these issues with some more judicious checks (the where clause is crying out for more sophistication) but and here is the question (finally)...
Right now I am thinking of just going with a config file and keeping it totally simple since I do not really like where I have got to but is there a better way to do this (or: can it be done automatically in a safe way)?
I like your second approach for automatic detection. I would add though that you should only do this once (on application start or as part of a static constructor) and make it static instead of computing it every time you request the supported culture info.
I think the config approach would work as well, though it's not really automatic. My only thought for that case is that if you are localizing for a language, it's not something that's going to sneak into your application under the radar. That being said, adding a config value at that point seems like an easy thing to do (or to forget to do).
I'm not aware of anything built into the .NET Framework to give you this information with a method call.
I found no valid and reliable way of doing this.
In each of my assemblies I write a code like this that references the supported cultures. That's all we can do. You have to remember to update this array when you add a localization.
internal static class ThisAssembly
{
static readonly string[] SupportedCultures = new string[] { "en-US", "de-DE", };
}
I also tried to:
enumerate the loaded sattelite assemblies: there is no method for that;
get the value of ResourceManager._resourceSets: the dictionary of loaded sets gives correct and invalid values.
But to no avail.

Converting to C++ from C#

I have done pretty much all my programming using C# and very much a newbie to C++. However now I have to convert to C++ and is finding it a bit difficult. For example, I wrote a pretty simple program using C# to acquire a RegistryKey, then using a recursive function I iterate through my registry key to find a specific key and then get the values I want. No problem, I can write that program in 10 minutes using C#. Here is the code.
My primary function. It gets Bluetooth Registry Key and then call the recursive function.
private static void CheckOpenComPorts()
{
RegistryKey blueToothPorts = Registry.LocalMachine.OpenSubKey(#"SYSTEM\CurrentControlSet\Enum\Bluetooth");
List<string> foundPorts = new List<string>();
AddFoundPortsToList(blueToothPorts, ref foundPorts);
//Rest of the program; not relevant here.
}
Recursive Function. Iterates the passed Key to find out necessary values.
private static void AddFoundPortsToList(RegistryKey regKey, ref List<string> ports)
{
try
{
string[] subKeys = regKey.GetSubKeyNames();
if (subKeys != null)
{
foreach (string subKey in subKeys)
{
AddFoundPortsToList(regKey.OpenSubKey(subKey), ref ports);
}
}
if (regKey.Name.EndsWith("Device Parameters"))
{
string str = System.Convert.ToString(regKey.GetValue("PortName"));
if (String.IsNullOrEmpty(str) == false)
{
ports.Add(str);
}
}
}
catch (System.Security.SecurityException ex)
{
;
}
}
The above code works fine, but when I tried to convert it to C++, I'm pretty lost.
Note : I'm using a Win32 Console C++ Program.
I figured out that I can do something like the following to get the Bluetooth Registry Key.
RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Enum\\Bluetooth", 0, KEY_READ, &hKey)
But after that, I'm pretty lost about the recursive function. Specially, how do I get the available subkeys of the passed registry key when I do NOT know the subkey names?. Or in short, what is the equivalent behavior of RegistryKey.GetSubKeyNames() in C++?
As I am only beginning this thing a code sample with some explanations would be great.
Enumerate the subkeys of a key - RegEnumKeyEx
Enumerate the values of a key - RegEnumValue
You get all subkeys by calling RegEnumKeyEx in a loop until it returns ERROR_NO_MORE_ITEMS.
In the exact same way, you get all values by calling RegEnumValue in a loop until it returns ERROR_NO_MORE_ITEMS.
I'm assuming you want to transition from .NET to native C++ programming. (i.e. no CLI and no .NET framework, which you could still use if you enabled managed C++ compilation).
If you spent a ton of time in C# land, you are probably very used to a ton of very convenient classes for just about everything imaginable and all you have to do is hit "." and let the Intellisense list the methods. Well.... you can forget all those conveniences :)
There is no such (at least not complete) framework in C++ so often you have to turn to Win32 API. MSDN Library is YOUR FRIEND. If you want to get good at C++, learn how to read it and learn how to look things up (not just by name, but learn where different categories are). In this case, if you search for the function you found, you will find a whole set of functions that work on registry. So now, looking at other methods in the same category in MSDN library, you can find RegEnumKeyEx. (hint: make sure to switch MSDN library UI to classical view, that makes it much easier to navigate between topics. I don't know what MS was thinking with their "new" look and feel)
As you start using Win32 API you will realize what a pain it is, especially coming from C#. But you don't have to use it directly (or at least not every time). You can use other libraries, for example ATL provides you with CRegKey class which makes working with registry much simpler. If you can't find the class, do what C++ does best, write your own class. Constantly working directly with windows functions will make your code very, very long and a pain to read.
Another 2 libraries worth knowing as you get into C++ are STL (a must) and Boost (strong should). Boost especially has a lot of OS abstraction so you don't have to go directly to windows DLLs every time.
If you search for the MSDN help on RegOpenKeyEx and then go up in the contents you'll find all the related methods: Registry methods
You probably want to use RegEnumKeyEx to enumerate subkeys.

How to treat compressed folders as files using ShellObject from WindowsAPICodePack?

I am trying to implement a filesystem browser using the WindowsAPICodePack for C# (.Net 4), and it works pretty well, except that the ShellObject system treats zip files as folders, whereas I'd prefer they be files. Is there some way I can force it to work this way? The low-level interop it does is beyond me.
As far as I can tell, internally it asks if the item is a Folder or a Filesystem element. It then uses this (and some type checks) to figure out what it actually is. Is it safe to force it to treat it as a file if it's Compressed? Or do I have to do something else?
Ok, well first, I saw that there was a flag in ShellNativeMethods.SFGAO called SFGAO_COMPRESSED. This doesn't seem to actually appear ever though, maybe it was deprecated?
Failing that, I eventually just cheated and did the following in ShellObjectFactory.cs:
Below:
// Is this item a Folder?
bool isFolder = (sfgao & ShellNativeMethods.SFGAO.SFGAO_FOLDER) != 0;
I added:
// Is this a compressed Folder?
bool isCompressedFolder = (itemType == ".zip");
And then I replaced
else if (isFolder)
with
else if (isFolder && !isCompressedFolder)
This is a total hack, but it seems to work, so unless someone has a better idea I'm sticking with this. Hopefully it'll help someone else out in the future, posts on the WindowsAPICodePack seem pretty rare.

Categories

Resources