Registration free (sxs) COM DirectShow filter - c#

There are questions asking on how to get Registration free COM working, and this is not one of those. I have a DirectShow video source filter (catagory 860BB310-5D01-11d0-BD3B-00A0C911CE86) implemented in .Net with the help of an edited version of the code available here: Pure .Net DirectShow Filters by Maxim Kartavenkov.
I need to get ffmpeg to recognize my .Net DirectShow filter as a video source using Registration Free COM (Side by Side / sxs). Built into the .Net framework is support for COM component servers, so theoretically as long as the manifests are correct, ffmpeg should detect the filters.
Here is a snippet of the relevant sections of my manifest files currently.
<!-- FFMPEG MANIFEST -->
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<assemblyIdentity name="ffmpeg.exe" version="1.0.0.0" type="win32" processorArchitecture="*"/>
<dependency>
<dependentAssembly asmv2:codebase="DShowVideoFilter.manifest">
<assemblyIdentity name="DShowVideoFilter" version="1.0.0.0" publicKeyToken="26A05D7C90FBA3E8"/>
</dependentAssembly>
</dependency>
</assembly>
<!-- DIRECTSHOW FILTER MANIFEST -->
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity name="DShowVideoFilter" version="1.0.0.0" publicKeyToken="26A05D7C90FBA3E8" />
<clrClass
clsid="{65722BE6-3449-4628-ABD3-74B6864F9739}"
progid="DShowVideoFilter.VideoCaptureFilter"
threadingModel="Both"
runtimeVersion="v2.0.50727"/>
<file name="DShowVideoFilter.dll">
</file>
<file name="DShowVideoFilter.tlb">
<typelib
tlbid="{B618E67B-64C8-48E9-9F94-F13214B76808}"
version="1.0"
helpdir=""
flags="hasdiskimage"/>
</file>
</assembly>
So, I get no errors when running ffmpeg (like you would if there was a manifest error) - and I am confident that everything that is configured correctly (related to traditional sxs com loading), the problem I think (unconfirmed) is that ffmpeg loads DShow filters via DirectShow's intelligent connect system, which requires the filter and pins to be registered. Here are some documents that talk about how filters need to be registered that I've found:
#1 Registering DirectShow Filters
#2 Implementing DllRegisterServer
Now, in Maxim Kartavenkov's DShow base classes, he takes care of #2 automatically. Here is a significantly shortened version of the method that registers the filters implementing BaseFilter.
[ComRegisterFunction]
public static void RegisterFunction(Type _type)
{
AMovieSetup _setup = (AMovieSetup)Attribute.GetCustomAttribute(_type, typeof(AMovieSetup));
BaseFilter _filter = (BaseFilter)Activator.CreateInstance(_type);
string _name = _filter.Name;
DsGuid _category = new DsGuid(_setup.Category);
IFilterMapper2 _mapper2 = (IFilterMapper2)new FilterMapper2();
RegFilter2 _reg2 = new RegFilter2();
_reg2.dwVersion = (int)_setup.Version;
_reg2.dwMerit = _setup.FilterMerit;
_reg2.rgPins = IntPtr.Zero;
_reg2.cPins = 0;
IntPtr _register = Marshal.AllocCoTaskMem(Marshal.SizeOf(_reg2));
Marshal.StructureToPtr(_reg2, _register, true);
hr = _mapper2.RegisterFilter(_type.GUID, _name, IntPtr.Zero, _category, _instance, _register);
Marshal.FreeCoTaskMem(_register);
}
That is the method (particularly mapper2.RegisterFilter) that allows ffmpeg to find the DShow filter when it is registered traditionally (with RegAsm) into the registry, which creates registry keys for the filter and pins as described by #2 link.
tldr;
So the question is, how to emulate the function of RegisterFilter or the intelligent connect registry entries this within a manifest file as to allow the sxs context to find my DirectShow filter when ffmpeg searches for it.

This is almost one of those questions asking on how to get registration-free COM working.
As you correctly say, it's not a problem with getting it to work at the most basic level. However, the fact that the manifest doesn't generate SxS loading errors only means it's a valid manifest XML. To know if it's semantically correct, such as finding missing dependencies or typos in names, CLSIDs and/or versions, test with CLSIDFromProgID followed by CoCreateInstance natively, or Type.GetTypeFromCLSID/Type.GetTypeFromProgID followed by Activator.CreateInstance in .NET.
Unfortunately for cases like this, registration-free COM is only applicable for base COM functionality, like typical class, interface proxy/stub and type library registration, with a tiny bit of OLE (see miscStatus attributes). For categories or subkeys not used by COM itself, registration is necessary.
Why? Because no one else followed lead, not even COM+.

Related

Xamarin Android Library Binding - Class does not contain a definition

Xamarin Play Core (the package for reviews & tasks needed to launch reviews) split for v2.0.0 into individual packages, so I'm trying to create a Xamarin Android bindings library for the review and tasks. I successfully got tasks working but I am getting this error for the review nuget when it should just work according to their docs. It might be a simple fix, I thought I just had to add these lines in the metadata file but that didn't fix it:
<attr path="/api/package[#name='com.google.android.play.core.review']/class[#name='IReviewManager']" name="extends">Java.Lang.Object</attr>
<attr path="/api/package[#name='com.google.android.play.core.review.testing']/class[#name='FakeReviewManager']" name="extends">Java.Lang.Object</attr>
Here's the error I get:
...PlayCoreUpdateTest/PlayCoreUpdateTest.Android/InAppReviewService.cs(35,35): Error CS1061: 'FakeReviewManager' does not contain a definition for 'LaunchReviewFlow' and no accessible extension method 'LaunchReviewFlow' accepting a first argument of type 'FakeReviewManager' could be found (are you missing a using directive or an assembly reference?) (CS1061) (PlayCoreUpdateTest.Android)
I know the magic happens in the generated api.xml file, so here's a code dump of it. In there I do see the FakeReviewManager, but I don't see the RequestReviewFlow, when it should be there
It's still a work in progress but those are the only remaining issues, here's the GitHub code.
I watched Jonathan Dick's video on creating Xamarin library bindings and tried the Microsoft FAQ options here too
I know my functions in InAppReviewService.cs are correct because that's what the official docs tell us to use, and it's what we were using before for v1.10.
Update: I did notice there's an api.xml.class-parse file beside api.xml, that does contain the missing method LaunchReviewFlow inside the FakeReviewManager class. I'm trying to understand why it didn't show up in api.xml. Here's a dump for that one. I did notice that in the api.xml.class-parse file, there are these lines
return="com.google.android.play.core.tasks.Task<java.lang.Void>"
jni-return="Lcom/google/android/play/core/tasks/Task<Ljava/lang/Void;>;"
...
jni-signature="(Landroid/app/Activity;Lcom/google/android/play/core/review/ReviewInfo;)Lcom/google/android/play/core/tasks/Task;"
which that the v2.0 AAB file points to a play core tasks library even though it recommends pointing to the GMS version of Tasks
To counter that, I tried to add variations of these lines, but none of that helped:
<add-node path="/api/package[#name='com.google.android.play.core.review.testing']/class[#name='FakeReviewManager']">
<method abstract="false" deprecated="not deprecated" final="false" name="launchReviewFlow" jni-signature="(Landroid/app/Activity;Lcom/google/android/play/core/review/ReviewInfo;)Lcom/google/android/gms/tasks/Task;" bridge="false" native="false" return="com.google.android.gms.tasks.Task<java.lang.Void>" jni-return="Lcom/google/android/gms/tasks/Task<Ljava/lang/Void;>;" static="false" synchronized="false" synthetic="false" visibility="public" return-not-null="true">
<parameter name="p0" type="android.app.Activity" jni-type="Landroid/app/Activity;" not-null="true" />
<parameter name="reviewInfo" type="com.google.android.play.core.review.ReviewInfo" jni-type="Lcom/google/android/play/core/review/ReviewInfo;" not-null="true" />
</method>
<method abstract="false" deprecated="not deprecated" final="false" name="requestReviewFlow" jni-signature="()Lcom/google/android/gms/tasks/Task;" bridge="false" native="false" return="com.google.android.gms.tasks.Task<com.google.android.play.core.review.ReviewInfo>" jni-return="Lcom/google/android/gms/tasks/Task<Lcom/google/android/play/core/review/ReviewInfo;>;" static="false" synchronized="false" synthetic="false" visibility="public" return-not-null="true" />
</add-node>

WIX cannot write value to registry if key contains space

I need to enable IE feature for WebBrowser control. To emulate IE11, I need to write a value to registry key
HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION
This manipulation need to be done during installation.
Here is my code in WIX script:
<?define var.IEFeatureEmulationKey = "Software\Microsoft\Internet Explorer\FeatureControl\FEATURE_BROWSER_EMULATION" ?>
....
<Fragment>
<DirectoryRef Id="TARGETDIR">
<Component Id="registryValues" Guid="{some-guid}" >
<RegistryKey Root="HKCU" Key="$(var.IEFeatureEmulationKey)" Action="create">
<RegistryValue Name="MyApp.EXE" Value="11000" Type="integer" Action="write"/>
</RegistryKey>
</Component>
</Fragment>
This code work only if $(var.IEFeatureEmulationKey) contains no spaces. But I need to write a value to this specific key.
Please help, how tell WiX to write value to registry even in registry key contains spaces.
UPD:
Added appropriate issue in WiX repository
This Blogpost uses some special syntax I have not seen before either. It states that variable can be defined this way, even if it contains a white space character like so:
<!–?define var.IEFeatureEmulationKey = "Software\Microsoft\Internet Explorer\FeatureControl\FEATURE_BROWSER_EMULATION" ?>
The !- let aside, it looks quite similar to what you got. However, your value contains more than one whitespaces. I'm not sure if thats the value you truly want or if it was just for pointing the whitespace out clearly...
Still cannot overcome the issue, however I want to show a workaround that helped me.
I used custom action that allowed in WiX. First of all I added custom .NET assembly with following method within it
[CustomAction]
public static ActionResult SetRegistryItems(Session session)
{
session.Log("Begin SetRegistryItems");
try
{
// this private method actually does manipulation with registry
SetRegistry();
}
catch (Exception e)
{
session.Log(e.ToString());
}
return ActionResult.Success;
}
Then this method should be referenced in WiX config file (.wxs)
<Fragment>
...
<CustomAction Id='SetRegistryItems' BinaryKey='<NameOfTheAssemblyWithoutExtension>' DllEntry='SetRegistryItems' Execute='immediate'/>
...
<InstallExecuteSequence>
...
<Custom Action="SetRegistryItems" Before="LaunchConditions"/>
...
</InstallExecuteSequence>
...
</Fragment>

AppDomain.CurrentDomain.SetupInformation.PrivateBinPath is null

When I start my application that only has one AppDomain, AppDomain.CurrentDomain.SetupInformation.PrivateBinPath is null. Even though I have probing paths set in MyApp.exe.config as shown below.
I would have expeceted that AppDomain.CurrentDomain.SetupInformation.PrivateBinPath contains the string "Dir1;Dir2;Dir3".
How can I access the probing path as configured in the MyApp.exe.config?
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<appSettings>
<add key="Foo" value="Bar" />
</appSettings>
<startup>
<!-- supportedRuntime version="v1.1.4322" / -->
</startup>
<runtime>
<gcConcurrent enabled="true" />
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<publisherPolicy apply="yes" />
<!-- Please add your subdirectories to the probing path! -->
<probing privatePath="Dir1;Dir2;Dir3" />
</assemblyBinding>
</runtime>
<system.windows.forms jitDebugging="true" />
</configuration>
Update
As Hans Passant pointed out the comment below, SetupInformation.PrivateBinPath is not set for the primary appdomain. So the above doesn't work. What would be your suggestion to simulate the way fusion searches for assemblies in the probing path or at least take <probing privatePath="" /> from the current application configuration into account? The best thing I can come up with is to read <probing privatePath="" /> from App.config manually when the current domain is the primary appdomain (AppDomain.CurrentDomain.IsDefaultAppDomain() is true). Is there a better way?
Update 2
Here some additional background information what this is needed for: This problem occured in AppDomainAssemblyTypeScanner.GetAssemblyDirectories() of the Nancy framework.
Nancy autodiscovers and loads 3rd party modules and other "plugins". By default this is supposed to be done same way as normally linked assemblies would be loaded (i.e. as fusion would do it) by looking through the probing paths. Assemblies are loaded using Assembly.Load (as opposed to Assembly.LoadFrom) so as I understand it, all the dependent assemblies of the loaded assemblies must be reachable in the probing path of the application/appdomain too.
How can I access the probing path as configured in the MyApp.exe.config
To remain compatible what fusion will do, you can read the config file in effect to get the current probing paths:
private static string GetProbingPath()
{
var configFile = XElement.Load(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile);
var probingElement = (
from runtime
in configFile.Descendants("runtime")
from assemblyBinding
in runtime.Elements(XName.Get("assemblyBinding", "urn:schemas-microsoft-com:asm.v1"))
from probing
in assemblyBinding.Elements(XName.Get("probing", "urn:schemas-microsoft-com:asm.v1"))
select probing)
.FirstOrDefault();
return probingElement?.Attribute("privatePath").Value;
}
Supposing the config file sample in your question it returns:
"Dir1;Dir2;Dir3"
Not an answer, did not fit as a comment
As said above, default appdomain doesn't use AppDomainSetup for path probing config.
Instead of this the probing path is read from .appconfig file and is not exposed into managed code.
* self-hosted app on the full clr can override the behavior with custom ICustomAppDomainManager (or IHostAssemblyManager) but that's out of scope of the question.
So there're only three approaches possible:
First, you can call Nancy.Bootstrapper.AppDomainAssemblyTypeScanner.LoadAssemblies(somedir, "*.dll") by yourself
Second, you can wrap nancy host into secondary appdomain with custom private bin path set.
Third: wait for https://github.com/NancyFx/Nancy/pull/1846 and use custom IResourceAssemblyProvider.
In any case you'll need the list of assembly' directories. If you do not want to store the copy as <appSettings> value you'll had to parse the appconfig file by yourself.
I've always found that the easiest thing to do is intercept the AppDomain.AssemblyResolve event. Then you can load whatever assembly you want from wherever you want and return it. You can still store your settings in the appConfig...You could even probing path section if you particularly want to use it. One thing to note is that assemblies loaded using Assembly.Load don't end up in the same load context as assemblies loaded under the default load context (https://msdn.microsoft.com/en-us/library/dd153782(v=vs.110).aspx). This has the effect of changing how type and assembly resolution occurs for subsequent resolutions (after the initial call to Assembly.Load). Accordingly, you may want to intercept AppDomain.TypeResolve as well as AssemblyResolve...and you'll want to cache the Assemblies you load from AssemblyResolve...otherwise subsequent resolutions MAY actually load the same assembly again (depending on how exactly you call Assembly.Load)
If this is a problem of assemblies not loading, one method I've found to work effectively is to use the AppDomain.AssemblyResolve event which is fired whenever the appdomain fails to load an assembly...
Working with AppDomain.AssemblyResolve event
e.g.
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(LoadManually);
private Assembly LoadManually(object sender, ResolveEventArgs args)
{
....
return Assembly.LoadFrom(whereEverYouLike);
}
Inspired by g.pickardou's solution, I have created function without necessity to reference System.Xml.Linq:
private static string GetProbingPath()
{
var xmlDoc = new XmlDocument();
xmlDoc.Load(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile);
var privatePathAttribute = xmlDoc.SelectSingleNode("/*[name()='configuration']/*[name()='runtime']/*[name()='assemblyBinding']/*[name()='probing']/#privatePath");
return (privatePathAttribute as XmlAttribute)?.Value;
}

Events raised in .net code is does not seem to occur in COM code when deployed with Side by side manifests

Here is a really simple .net <-> COM interop example using events.
This example works just fine as long as i either use regasm or the register for com interop option in Visual studio build options for the .net library.
But I need to deploy using registration free interop enabled side-by-side manifests.
The application runs just fine in side-by-side mode, it's just that the events seems to disappear. I suspect it's some thread marshalling issue, but I can't seem to find the correct solution.
This is of course an attempt to replicate an issue I have with a slightly more complicated interop integration. There is one difference between the issues I'm having here compared to the real issues:
Both solutions fail to properly sink events raised in the .net code while running on reg-free deployment, and both solutions works as expected when the .net dlls are registered in the registry.
However: on the "real" project I get a runtime error when it fails from System.Reflection.Target. On this simplified example it just fails silently.
I'm thoroughly stuck on this one, so any and all suggestions and solutions will be very much welcomed.
I've put the complete code on github if anyone needs to play around with it before answering: https://github.com/Vidarls/InteropEventTest
The .net part
using System.Runtime.InteropServices;
using System.Threading.Tasks;
namespace InteropEventTest
{
[Guid("E1BC643E-0CCF-4A91-8499-71BC48CAC01D")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComVisible(true)]
public interface ITheEvents
{
void OnHappened(string theMessage);
}
[Guid("77F1EEBA-A952-4995-9384-7228F6182C32")]
[ComVisible(true)]
public interface IInteropConnection
{
void DoEvent(string theMessage);
}
[Guid("2EE25BBD-1849-4CA8-8369-D65BF47886A5")]
[ClassInterface(ClassInterfaceType.None)]
[ComSourceInterfaces(typeof(ITheEvents))]
[ComVisible(true)]
public class InteropConnection : IInteropConnection
{
[ComVisible(false)]
public delegate void Happened(string theMessage);
public event Happened OnHappened;
public void DoEvent(string theMessage)
{
if (OnHappened != null)
{
Task.Factory.StartNew(() => OnHappened(theMessage));
}
}
}
}
The COM (VB6) part
Private WithEvents tester As InteropEventTest.InteropConnection
Private Sub Command1_Click()
Call tester.DoEvent(Text1.Text)
End Sub
Private Sub Form_Load()
Set tester = New InteropConnection
End Sub
Private Sub tester_OnHappened(ByVal theMessage As String)
Text2.Text = theMessage
End Sub
I currently have the following files / folder structure for deploy:
Root
|-> [D] Interop.Event.Tester
|-> Interop.Event.Tester.manifest
|-> [D] InteropEventTest
|-> InteropEventTest.dll
|-> InteropEventTest.manifest
|-> InteropEventTest.tlb
|-> tester.exe
|-> tester.exe.manifest
Content of manifest files:
Interop.Event.Test.manifest
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity name="Interop.Event.Tester" version="1.0.0.0" type="win32" processorArchitecture="x86"/>
</assembly>
InteropEventTest.manifest
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity name="InteropEventTest" version="1.0.0.0" type="win32"/>
<clrClass
name="InteropEventTest.InteropConnection"
clsid="{2EE25BBD-1849-4CA8-8369-D65BF47886A5}"
progid="InteropEventTest.InteropConnection"
runtimeVersion="v4.0.30319"
threadingModel="Both"/>
<file name="InteropEventTest.tlb">
<typelib
tlbid="{5CD6C635-503F-4103-93B0-3EBEFB91E500}"
version="1.0"
helpdir=""
flags="hasdiskimage"/>
</file>
<comInterfaceExternalProxyStub
name="ITheEvents"
iid="{E1BC643E-0CCF-4A91-8499-71BC48CAC01D}"
proxyStubClsid32="{00020424-0000-0000-C000-000000000046}"
baseInterface="{00000000-0000-0000-C000-000000000046}"
tlbid="{5CD6C635-503F-4103-93B0-3EBEFB91E500}" />
</assembly>
tester.exe.manifest
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity name="tester.exe" version="1.0.0.0" type="win32" processorArchitecture="x86"/>
<dependency>
<dependentAssembly>
<assemblyIdentity name="InteropEventTest" version="1.0.0.0" type="win32"/>
</dependentAssembly>
</dependency>
<dependency>
<dependentAssembly>
<assemblyIdentity name="Interop.Event.Tester" version="1.0.0.0" type="win32" processorArchitecture="x86"/>
</dependentAssembly>
</dependency>
</assembly>
After a long time (and several failed attempts) It turned out I could make this work by making one tiny change:
Make the VB6 code compile to P-Code instead of native code.
I'm pretty sure this somehow affects how marshalling between threads is handles, but I've been unable to find any information confirming that theory.
At least it works...
Or Not! (24. October 2013)
It turned out that in real life compiling to P-Code was not enough. In another implementation of this pattern we ended up with the event just disappearing into nowhere, with no exceptions (we thought) and no traces.
So more investigation was due:
1. The real issue
Wrapping the event triggering in a try-catch clause revealed that there was in fact an exception being thrown, it just never surfaced anywhere
if (OnHappened != null)
{
try
{
OnHappened(theMessage));
}
catch (Exception e)
{
Messagebox.Show(e.GetType().Name + " : " + e.message)
}
}
The exception was a TargetException (the object does not match the target type). Some research revealed that this was most probably a threading issue (as I had suspected earlier.)
2. The solution
Most of the stuff written about this seemed to solve it by using an Invoke method. It turned out that most other people trying to solve this was building winforms application, and thus had a handy Ìnvoke(Delegate) method available on all forms and controls.
As Winforms also does quite a bit of COM interop behind the scenes (according to now forgotten articles on the google result list) The invoke method is used to ensure that a method call is executed on the thread that created the given component and thus ensure that it happens on the message-pumped UI thread.
I figured this could be relevant for my case aswell, so I cheated.
I made my interop class inherit from the winforms control
public class InteropConnection : Control, IInteropConnection
Now I wrapped my call in the Invoke method
if (OnHappened != null)
{
try
{
Invoke(OnHappened, theMessage);
}
catch (Exception e)
{
Messagebox.Show(e.GetType().Name + " : " + e.message)
}
}
Now I got an exception because the Control had no WindowHandle assigned.
As it turned out the Control class has a handy CreateHandle() method that can be called and solves this particular issue. (I do not know what possible consequences this has, as the documentation does not recommend calling this method directly.
Now all seems to be working all the time, though I would not be surprised if something new jumps up and bites me now...
I have run into the same issue. COM can marshal the event/call to the correct thread but it needs to have a proxy-stub. These are added to the registry if you use the /tlb option with regasm, and the equivalent in the manifest file are the elements typelib and comInterfaceExternalProxyStub. The VB6 executable can be compiled to a native binary.
For more info see my SO topic: Regfree COM event fails from other thread
We had the same problem.
In our case we have to change the proxyStubClsid32 to {00020420-0000-0000-C000-000000000046}.
Attention: There is a change in one digit!
More information here: http://www.mazecomputer.com/sxs/help/proxy.htm

Registering generic types and services with Castle Windsor IoC

Hello again stackoverflowians,
I thought it was about time that I learnt how to use a DI framework. I've heard a lot of good things about Castle Windsor so I decided to go with that. Now there are PLENTY of tutorials out there on how to use it, however, I cannot find much useful information about what to do when Generics get involved. Here is my issue.
I have a BaseDAO
namespace Utilities.DataAccess
{
public class BaseDAO<T> : IBaseDAO<T>
{
public BaseDAO(IConnectionProvider _connectionProvider)
{
// Stuff
}
}
}
Im a little bit new to generics in this context and I have seen some tutorials which have a 'BaseDAO' with no generic declaration and simply the interface it implements with the generics on it. I have used the above way of doing things on many previous projects (without IoC) and its worked fine for me...anyways, onwards to the App.config !
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section
name="castle"
type="Castle.Windsor.Configuration.AppDomain.CastleSectionHandler, Castle.Windsor"></section>
</configSections>
<castle>
<components>
<component
id="BaseDAO"
service="Utilities.DataAccess.Interfaces.IBaseDAO`1, Utilities.DataAccess"
type="Utilities.DataAccess.BaseDAO`1, Utilities.DataAccess" />
<component
id="NHibernateConnection"
service="Utilities.DataAccess.ConnectionProviders.IConnectionProvider, Finchtils"
type="Utilities.DataAccess.ConnectionProviders.NHibernateConnection" />
<component
id="XMLConnection"
service="Utilities.DataAccess.ConnectionProviders.IConnectionProvider, Finchtils"
type="Utilities.DataAccess.ConnectionProviders.XMLConnection, Utilities" />
</components>
</castle>
</configuration>
Now as some of you may of figured by now, this is a utility library. I intend to use this assembly for each project I create so that I don't have to write the same data access code which remains the same across all solutions. The implications of such of course is that I cannot tell castle exactly what type parameter I will pass to the BaseDAO, in one project it might be a Customer object, another entirely different. I have read on other forums that this is entirely possible as when you request the object from the container you can specify the type then like;
BaseDAO<Customer> baseDao = container.Resolve<BaseDAO<Customer>>();
Although it is against my design efforts, I have tried to use the following notation in the App.config
<component
id="BaseDAO"
service="Utilities.DataAccess.Interfaces.IBaseDAO`1[[Utilities.DataInterface.IEntity]], Finchtills.DataAccess"
type="Utilities.DataAccess.BaseDAO`1[[Utilities.DataInterface.IEntity]], Finchtils.DataAccess" />
However, this has not worked either, in any case I get the following error:
Utilities.Testing.DataAccess.Unit.Testing_BaseDAO (TestFixtureSetUp):
System.Exception : The type name Utilities.DataAccess.BaseDAO`1, Utilities.DataAccess could not be located.
----> System.IO.FileNotFoundException : Could not load file or assembly 'Utilities.DataAccess' or one of its dependencies. The system cannot find the file specified.
Reading this error, I think it could be one of two things:
I am missing something from the config file to do with the generics of the types and services.
I have named something incorrectly I.E an assembly name.
I have treated the assembly name as the project that item is contained within, in other words, at no point have i used <solution name>.<project name>.<item folder>.<item name> but merely started at the project level...I assume that any config option would know what solution it is being called from.
Thank you for any help you may be able to give on this subject.
The assembly name can be found in Visual Studio thus:
In the solution explorer, double-click the properties node
Open the Application tab
Assembly name is near the top right corner
Or, if you're compiling at the command line, you use the /out argument.
Also, you need to specify the assembly for the type arguments (inside the square brackets). So, assuming all your types are in the DataAccess assembly, and that the assembly is called (for brevity's sake) "DataAccess":
<component
id="BaseDAO"
service="Utilities.DataAccess.Interfaces.IBaseDAO`1[[Utilities.DataInterface.IEntity, DataAccess]], DataAccess"
type="Utilities.DataAccess.BaseDAO`1[[Utilities.DataInterface.IEntity, DataAccess]], DataAccess" />
But I agree with other commenters that it's better to do the registrations in code. You don't have to use the verbose type syntax, for one, and you get compiler checking of your types. There are some disadvantages, however: it's harder to tell if you have unused types because the registration call counts as using the type.

Categories