4

I think what I need is something the .net folks call "transparent dynamic proxy", but all the implementations I've seen this far (Castle DynamicProxy, Spring.NET AOP, etc) require me to do at least one of these:

  1. Declare intercepted method as virtual
  2. Wrap class and create instances of the wrapper instead of wrapped class
  3. Change inheritance or implement interfaces

Obviously, if both caller and callee are nonvirtual and from thirdy-party closed source libraries, which is the case, there is nothing I can do.

If C# were a dynamic language like Python I would do something like this:

foo = ThirdyPartyLibA.Foo()
def interceptor(self, *args, **kwargs):
    do_something_before(self, *args, **kwargs)
    result = ThirdyPartyLibB.Bar.intercepted(self, *args, **kwargs)
    do_something_after(self, result, *args, **kwargs)
    return result
foo.bar.intercepted = interceptor # bar is an instance of ThirdyPartyLibB.Bar
foo.do_its_job() # Foo.do_its_job calls Bar.intercepted

I need this to change a bad behavior of ThirdyPartyLibA.Foo while interacting with ThirdyPartyLibB.Bar. I know exactly what causes this behavior and exactly how to change Foo or Bar to fix this bug thanks to dissasemblers.

Some (very unlikely to work) ideas:

  • Disassemble ThirdyPartyLibA, make changes in code and generate a compatible assembly (unlikely to work because it's a strong-named assembly)
  • Edit binary to make Foo's buggy methods virtual and change whatever is necessary for it to remain a valid assembly so I can use dynamic proxies (very unlikely to work, also because of the same reason as the idea above)
  • Find a transparent dynamic proxy implementation that fits (I think there is none based on this forum thread: http://www.pcreview.co.uk/forums/overriding-non-virtual-methods-using-il-and-reflection-emit-t2605695.html)
  • Contact the company that created the library (they don't support the product anymore)
  • Stop using the library or use an alternative (impossible, since it's part of the runtime of a RAD IDE that we are tied to because there is a HUGE amount of code written using the IDE own language)
  • Control the calls to the problematic methods to avoid the bug (we already did this but it didn't solve the problem completely)

Do you have any other idea?

PS: Sorry for my bad english. Also, sorry for my Python. This code is here just to illustrate what I need, don't take it as a recipe because it's horrible.

JimmyPena
  • 8,694
  • 6
  • 43
  • 64
ygormutti
  • 358
  • 3
  • 17
  • You can try using C# 3.5 extension methods, the syntax looks similar to the one used in Phyton, they allow you to add functionality to sealed classes, e.g. create instance methods on classes you don't have the control (sealed, third party, etc). The only thing is: you cannot replace or overwrite a method that the class have already implemented. – Adrian Salazar Aug 01 '12 at 16:46
  • Extension methods can't solve this problem because, as you know, they cannot override methods and I cannot change the methods that lib A calls from lib B. – ygormutti Aug 02 '12 at 04:32

2 Answers2

3

Possible Solution 1:

Wrap the library and using a tool like ReSharper to find all usages of the library and replace with the wrapper class. You could also use that opportunity to clean up the presumably crappy interface of the third party library.

Possible Solution 2:

While TypeMock is generally used as a testing tool, it will allow you to mock everything. Because it injects itself as a code profiler, the stuff you can mock included private and static members of classes. As a bonus, any overridden methods do not need to be virtual, so you may be able to intercept the calls that way.

My recommendation

I would recommend you go with Solution 1. A wrapper is very easy to understand, and it'll give you a good opportunity to really improve the code. I'd even recommend that you wrap third party libraries as a general rule.

Josh Kodroff
  • 27,301
  • 27
  • 95
  • 148
  • +1 for everything except " I'd even recommend that you wrap third party libraries as a general rule" which mystifies me a little. – spender Aug 01 '12 at 16:29
  • I can't replace all usages of the library in the code with a wrapper, because is not my code that uses the library, but another thirdy party library. I use library A that uses library B in a wrong manner. I can wrap my uses of the library A, but this doesn't fix the buggy interactions between A and B. – ygormutti Aug 02 '12 at 04:23
  • The second solution seems promising. I will investigate it. – ygormutti Aug 02 '12 at 04:23
  • 1
    @spender Fair enough. Let me clarify: *Always* wrap a shitty 3rd party library. *Strongly consider* wrapping any other 3rd party library you think has even a slight chance of being changed in the course of the app. In the latter case, it's about not scattering dependencies on the library all over the code base. – Josh Kodroff Aug 02 '12 at 13:10
1

If Bar is static, you might be able to use Moles to detour the method call. Note that this is a pretty heavy-handed approach to fixing the bug and is really not recommended for production code, but it's an option if you're desperate.

Dan Bryant
  • 27,329
  • 4
  • 56
  • 102
  • Both Foo and Bar aren't static. :/ Your link seems to be very interesting though. I'll check it out. (yes, I'm desperate) – ygormutti Aug 01 '12 at 16:17
  • 1
    According to Mole's page at Microsoft Research site "The Fakes Framework [...] is the next generation of Moles & Stubs, and will eventually replace it." Unfortunately, both Moles and Fakes need the application to be hosted by some sort of profiler. I'm desperate, but I ain't that desperate yet. ;) – ygormutti Aug 15 '12 at 06:26