views:

274

answers:

5

I have a project which has a set of binary dependencies (assembly dlls for which I do no have the source code). At runtime those dependencies are required pre-installed on the machine and at compile time they are required in the source tree, e,g in a lib folder. As I'm also making source code available for this program I would like to enable a simple download and build experience for it. Unfortunately I cannot redistribute the dlls, and that complicates things, since VS wont link the project without access to the referenced dlls.

Is there anyway to enable this project to be built and linked in absence of the real referenced dlls?

Maybe theres a way to tell VS to link against an auto generated stub of the dll, so that it can rebuild without the original? Maybe there's a third party tool that will do this? Any clues or best practices at all in this area?

I realize the person must have access to the dlls to run the code, so it makes sense that he could add them to the build process, but I'm just trying to save them the pain of collecting all the dlls and placing them in the lib folder manually.

A: 

Well save them the pain of collecting all the assemblies and place them in the lib folder yourself. Then commit them along with the source code in the repository. This way people checking out the code from the repository will also get everything that is needed to compile the project.

Darin Dimitrov
The op said "Unfortunately I cannot redistribute the dlls"
bingle
A: 

A rather drastic possibility would be to use the separated interface pattern and programatically load the dlls at runtime.

bingle
That would be a lot of work. I don't think the benefits of streamlining compilation for people that download the source code pays for it.
David Reis
A: 

I'd rather have a compiletime dependancy fail my build than a runtime error that could take some time to track down.

Place a Readme.txt in your solution and spell out explicitly what the dependancies are, where to get them, and what to do with them.

pms1969
Most people will not change the code, so they will just download the binaries, which already do dependency validation and fail gracefully when they are not there. At compile time on the other hand, the actual dependency code to run is not needed, just the functions signatures, so I was just wandering if there was a way to inform the compiler of those so that doesn't need the actual dlls.As for the readme file, that's what I've been doing so far.
David Reis
+1  A: 

Maybe one of these ideas will help you to solve the problem:

  • Derive interfaces from all classes in the 3rd party dlls. Put these interfaces into an own project (same solution) and add a reference to this interface assembly. Also add an EventHandler to AppDomain.AssemblyResolve and try to find and load the assemblies at run-time. (Credits go to NG)
    • Depending on how big the dlls are and how often changes are made to the public part this can get a real pain.
  • Provide a readme.txt where you explain how to get the needed assemblies and where the user should put them relative to the project path. Normally VS is smart enough to remove the exclamation mark right after you put the assembly into the right spot, where the project references it from (maybe you have to press refresh in Solution Explorer) (Credits go to Paul)
    • Don't forget to add the readme.txt also to your solution by right clicking on your solution and select 'Add -> Existing Item'. In this case it will get a quite prominent place within visual studio Solution Explorer and the user can read it on a double click.
  • Create another project within your solution that is able to download all the needed dlls automatically and to put them into the right spot. Maybe it should check beforehand if these needed files are already there before it starts to download. Set the Project Dependencies of your original project, so that it will depend on this one. So it will always be built before your original one. Then start this download helper tool in the pre-build event of your original project and don't forget to exit your program with an int value. 0 means success, any other error and so also Visual Studio knows if your tool was successfull and stops the compile process before hanging on missing dlls.
    • Maybe it is nearly impossible to automatically download the files, cause you need a login to a webpage or some cookies, flash, captcha, etc. is used which can't be automated by a tool.
Oliver
For now I was going with the readme.txt file strategy. The interfaces strategy is basically doing manually what I wish could be automatic. Furthermore I would not like to complicate my program with this extra level of indirection.The third strategy sounds nice, but it's a hellalota work and the dlls and not publicly available on the web...
David Reis
While this is not at all the solution to the problem, I'm accepting it because it seems no such tool exists.
David Reis
@David: Maybe it is possible to get some automation into the creating of a wrapper by using Dynamic Proxy: http://www.castleproject.org/dynamicproxy/index.html I never used it, but maybe it can be used for the first suggestion.
Oliver
+1  A: 

To all the good advice above, agreed. That being said, maybe there is a valid scenario where the external DLL's are generally not needed? So here is what you do. You wrap and isolate them. (Its a higher level of abstraction than creating interfaces, so a bit easier to maintain).

In Visual Studio, if you do not recompile the specific VS Projects which reference the external DLL's then you can get away with compiling the rest of the VS Solution's Projects without having those DLL's handy. Thus if you somehow wrap the external DLL's with your own DLL's and then distribute those wrappers as binary only, the person sharing your source code will not need the external DLL's to compile the main solution.

Considerations: 1. Extra work to separate out the wrapper code into isolated Projects. 2. The other VS Projects must add references to your wrapper DLL's as "File System" references to a "LIB" folder, rather than "Project References". 3. The VS Solution configurations must disable compile for the wrapper DLL's. A new configuration should be added to explictly re-compile them, if needed. 4. The VS Project definition for each of the Wrapper DLL's should include a post-build event to copy them to the expected "LIB" folder location. 5. At runtime, the external DLL's must be present in the application's bin directory, or the machine's GAC, or otherwise explictly loaded. NOTE: If they are missing, it is only when they actually invoked at runtime that their absence will result in a runtime error. i.e. You do not need to have them if the code doesn't happen to call them in a general situation. 6. At runtime, you can catch errors loading the external DLL's and present a pretty error message to the user to say "In order to user this function, please install the following product: xyz". Which is better than displaying "AssemblyLoadException... please use FusionLogViewer... etc" 7. At application startup, you can test and detect missing DLL's and then disable specific functions which depend upon them.

For example: According to this pattern I could have an application which integrates with Microsoft CRM and SAP, but only for a specific function, i.e. Import/Export.
At design time, if the developer never neeeds to change the wrapper, they will be able to recompile without these external DLL's. At runtime, if the user never invokes this function, the application will never invoke the wrapper and thus the external DLL's are not needed.

Jennifer Zouak
That can definitely solve the problem, but the benefit it brings, people having an easier time compileing the source, does not pay the extra work. If the work was done by a tool maybe the scale would be tipped to the other side :-)
David Reis
I doubt there is any tool that can redesign your application to be loosely coupled to your dependencies. Let us know what you find :)
Jennifer Zouak
What redesign is there in stubing methods? For every called method in the app from assembly X, include a stub method with empty body in a new assembly Y, which can in turn replace X in the build process. Does not seem like rocket science to me ;-)
David Reis
Point is, you have a choice. If you wrap the methods as-is, you remain tightly coupled to the external DLL implementations. If you abstract it a bit, then you gain the 2nd benefit of being potentially able to swap external DLL's later. i.e Logging. Write your own logging api and then just map it to the provider you are using. If you change providers, your own api will still work just fine. Usually a program only uses a subset of the external function, so just wrap the ones you need.
Jennifer Zouak
Actually to answer your question, yes deciding to wrap external methods is in fact an act of Design.
Jennifer Zouak