-
Upcoming world star?
-
Note: this entry has moved.
I wasn't as shocked as Luke, mainly
because on a
google search for "Daniel" my weblog comes up as #587, which is pretty
far from his #5... But considering I "fight" against
Jack Daniel's,
Daniel Boone,
Daniel Stowe (If he's
Madelaine Stowe's husband, he's a really lucky man),
Daniel Lee "Manimals" (?!) and several senators and miscelanous
politicians, I think it's not bad anyways :o)
-
Avoid the default designer for your IComponent-based classes: how to get the code view always!
-
Note: this entry has moved.
How many times you create a WebService,
an Installer, or more
generally, any class that directly or indirectly implements IComponent,
and wish it doesn't open the default ComponentDesigner?
The simple way to achieve it is adding the following attribute to your class declaration:
[System.ComponentModel.DesignerCategory("Code")]
public class YouClass : YourComponentBasedClass
Now whenever you double-click the class in the solution explorer, the code editor will show up, instead of the
designer, all thanks to the DesignerCategoryAttribute.
-
WebService proxy bug?: "Web Service method name is not valid."
-
Note: this entry has moved.
Let's suppose you create a proxy for a webservice. Assume that you KNOW that only certain
(signed) code should use your proxy, then, it's only natural that you add a
StrongNameIdentityPermission to its declaration:
[StrongNameIdentityPermission(SecurityAction.LinkDemand,PublicKey="your_trusted_key")]
public class MyWebService : SoapHttpClientProtocol
When you try to call any method with such a proxy, you'll get a pretty strange
error:
[your_webmethod] Web Service method name is not valid.
It turns out to be that because of the permission, the internal classes that
reflect your type can't get the methods in the class. And when the method to
call is looked-up in an internal list and not found, that exception is thrown.
However, there's nothing wrong with the method name. The method that throws the
exception is SoapHttpClientProtocol.BeforeSerialize, which
performs the lookup against a private instance of the type SoapClientType.
The proxy initialization code involves the following steps before that failure:
-
Proxy constructor initializes a
SoapClientType: it passes the
current proxy type so that it is reflected to get the web methods.
-
SoapClientType constructor calls
LogicalMethodInfo.Create
passing all the proxy public methods, retrieved using
Type.GetMethods(BindingFlags.Instance | BindingFlags.Public).
This method checks for asynchronous methods and does some other trivial work.
-
SoapClientType constructor calls SoapReflector.ReflectMethod
with each LogicalMethodInfo constructed in the previous step.
I've tracked down the bug by making extensive use of reflection. I've executed
each of the methods above, which I identified as the hot-spots for the bug,
using trusted code (the assembly signed with the key required by the proxy) and
then with untrusted code, which will always be the case for
System.Web.Services.dll. Here are the results:
-
SoapClientType constructor: it works as expected, but its internal
list of methods available is empty when the constructor is run without the
required public key:
Type t = ReflexHelper.LoadPrivateType(
"System.Web.Services.Protocols.SoapClientType, System.Web.Services");
object sct = Activator.CreateInstance(
t, BindingFlags.Instance | BindingFlags.NonPublic, null,
new object[] { typeof(Messaging.MessagingWebService) }, null, null);
FieldInfo fi = t.GetField("methods",
BindingFlags.Instance | BindingFlags.NonPublic);
Hashtable methods = fi.GetValue(sct) as Hashtable;
Note that I access a private field called methods which contains
the list loaded by the constructor code.
I went deeper as the methods hastable I got was always empty when
I run the unsigned version. The following method is called inside this
constructor, and is the next hot-spot.
-
LogicalMethodInfo.Create: it correctly returns the list of methods
irrespective of the signature of the calling code. Therefore, the bug isn't
here:
MethodInfo[] mis = typeof(MyWebService).GetMethods(
BindingFlags.Instance | BindingFlags.Public);
LogicalMethodInfo[] li = LogicalMethodInfo.Create(mis, LogicalMethodTypes.Sync);
So I went further to the call on SoapReflector.
-
SoapReflector.ReflectMethod: I couldn't go any further than this
because the dependecies are too deep with other private and internal types.
However, this method IS the one to blame for the strange error. It fails to
reflect the methods on the type because of the StrongNameIdentityPermission,
but does not throw any exceptions, thus swallowing the fact that it's a
security permission that is failing, instead of the unrelated exception thrown
at the moment we actually perform the WebMethod call on the proxy.
Here's the code that proves it:
ArrayList list = new ArrayList();
t = ReflexHelper.LoadPrivateType(
"System.Web.Services.Protocols.SoapReflector, System.Web.Services");
MethodInfo reflect = t.GetMethod("ReflectMethod",
BindingFlags.Static | BindingFlags.NonPublic);
foreach (LogicalMethodInfo i in li)
{
//This always returns null if we're not signed with the key required
//(always the case for System.Web.Services.dll)
object m = reflect.Invoke(
null, BindingFlags.Static | BindingFlags.NonPublic, null,
new object[] {
i, true, new XmlReflectionImporter(),
new SoapReflectionImporter(), "default-namespace" },
null);
if (m != null)
list.Add(m);
}
Console.WriteLine(list.Count);
Signing this test console project with the public key required by the proxy
results in the correct list of webmethods being loaded into the list
object. The object returned by the ReflectMethod is checked in the
SoapClientType constructor, but only for non-null returns. If the returned
object is null, nothing is done and the internal list of available methods will silently
remain empty.
So, how do you protect the class now? Well, put your demand in the class constructor instead of the definition,
and nobody will be able to instantiate your proxy unless it's signed with the trusted key. What's more, it makes
the code more performant because it avoids the check for each member used (as is the case for the class-level demand).