How do I get TFS teambuild to build c#>VB6>c# application (ComReference prob?) - msbuild

I'm trying to get TFS team-build to reliably build a WPF C# app. This app relies on a VB6Lib.dll which we maintain, this VB6Lib.dll itself relies on other C# libs that we also maintain.
I've set up a build definition to build (in order):
VbDependencies.sln (all libs in this have com interop set, thus the VB6 can find their TLBs)
buildVB6Lib.proj (an msbuild file which calls "VB6.exe /make /d" to make the VBLib.dll on the build server, as part of this script I've been copying the VB6Lib.dll output to C:\tmp)
MainApp.sln (in my workspace, I've added a reference to C:\tmp\VB6Lib.dll)
Does this sound ok
?
On my dev laptop I usually build the VB6, copy its output to \tmp and then regsvr32 it there before adding a reference to it in my C# solution. It's this step that I'm not convinced my build def is doing.
Also, is there a way to get more useful output from the VB6 build, currently I get "Compile Error in File 'xxx.bas' Can' find project of library", but not which actual library it can't find.

You are correct in that the critical point in your build process on the development system lies in registering the COM object. However, one does not in general want to register the COM object on the build server, as this can cause all kinds of versioning issues and silent failures when the wrong COM object is registered or the registration fails.
The proper way to accomplish this is to generate an interop assembly manually and reference that instead of the COM object. This is accomplished with the tlbimp utility, for instance:
tlbimp ..\Libraries\VBLib.dll /out:..\Libraries\Interop.VBLib.dll
Run that command on your development system, then remove the reference to VB6Lib.dll and add a reference to Interop.VBLib.dll. You can then add the tlbimp command as a prebuild event in the referencing project so that the interop assembly is always build from the correct version, and you will never again need to have your COM object registered on the build system.

Related

Several short questions about COM .net assemblies, regasm, dll, tlb and guids

All question are related to a .net project dll in .net framework 2.0 that exposes itself as COM.
1) If we don't specify any GUIDs in the source code (typelib, classes, interfaces) who is generating the GUIDs? The compiler or regasm?
2) The GUIDs values exists in the dll, in the tlb or in both files?
3) Any developer with the same source code would generate the very same GUIDs independently on the machine where she builds or run regasm?
4) If I run regasm passing existing dll and tlb files, what happens If the dll and the tlb doesn't match? Regasm regenerate the tlb file with uptodate elements and GUIDs? Or it registers the TypeLib with the current tlb file?
5) What is the point of running regasm with dll and tlb parameters set?
Tlb file is part of what you deploy or it is best practice to only deploy the dll and let regasm generate the tlb on the fly?
6) And last question, is tlb really required? What is the point of having a tlb file? Is not all the information already in the registry? What extra info it provides?
7) When unregistering with regasm, what we need to provide? The dll? The Tlb? Both? What happens if dll (or tlb) doesn't match with existing reg entries? If already registered with tlb option but I run regasm unregister with dll only it would delete the TypeLyb entry too?
8) Regarding bitness, regasm will always generate entries under SysWow64 too? The regasm under Framework64 do the same as the one under Framework?
A type library is the exact equivalent of .NET metadata. It is most of all useful to the client programmer, it makes the compiler and the IDE smart about your library. Providing auto-completion and syntax checking so the odds of a mismatch between his code and yours are minimal. The registration step is necessary so your files can be found back. The type library is normally embedded as a resource in the DLL itself, like .NET metadata, but the .NET build model does not make that easy to do. The client compiler uses the type library info to generate the appropriate COM calls. Guids are a big deal because that is what the client compiler needs to use, identifier names play no role. There is a way to use "late binding" using names, the exact equivalent of Reflection in .NET, but that does not involve a type library.
who is generating the GUIDs?
The CLR does. Every .NET interface or class has one, regardless if it is [ComVisible(true)]. Exposed also through the Type.GUID property. If you didn't use the [Guid] attribute on the type then it runs an algorithm to generate the Guid that uses the type declaration as input. Or in other words, if you make any changes to the type then you can be sure that the Guid will have a different value. Which is the basic reason you should never use the [Guid] attribute, unless you have to create an exact drop-in replacement and cannot recompile client code. The TLBID comes from the AssemblyInfo.cs file that was auto-generated when you created the project.
in the dll, in the tlb or in both files?
It only exists in the DLL when you used the [Guid] attribute, but normally it is generated at runtime as explained above. It is always present in the type library, that's how the client compiler knows to create an object of your class and use its interface(s).
would generate the very same GUIDs
Yes, only the type declaration plays a role.
If I run regasm passing existing dll and tlb files
Regasm can only create a type library, as requested with its /tlb option, it cannot take an existing one. It otherwise does the exact same thing as Tlbexp.exe does, use Reflection to enumerate the types in the assembly to find the [ComVisible(true)] ones and generate the matching type library declaration. The extra thing it does is write the registry key for the type library to HKLM/Software/Classes/Typelib. So the client IDE can find it back.
What is the point of running regasm with dll and tlb parameters set?
No real idea with "dll parameter" might mean. As noted above, use /tlb to generate the type library. Whether or not you deploy the type library depends on its usage, if you don't also provide the client code then you should always deploy it so the client programmer can use it. Other usage of the type library is the subject of this post. If you're not sure how the client programmer is going to use your code then always deploy.
Is not all the information already in the registry?
What's in the registry is limited, only enough info to find the type library file back. The description of your interfaces, their method signatures, guids and the CLSID that the factory function needs is in the type library.
When unregistering with regasm, what we need to provide?
Exact same thing as registering it, you only add /unregister. You must also provide /tlb if you used it previously so the TypeLib registry key can be deleted. It can be pretty important to automate this while you are busy developing and testing the library, since the guids are normally auto-generated you can produce a lot of garbage in the registry. As well as ugly head-scratching when you forget to run Regasm. Project > Properties > Build tab, "Register for COM interop" checkbox. But with the downside that you have to run VS elevated so it can write to the registry.
regasm will always generate entries under SysWow64 too?
SysWow64 plays no role, do always avoid deploying to c:\windows. But yes, bitness does matter, the registry is structured so a 64-bit app cannot accidentally create an object in a 32-bit library and die on an ugly exception. And the other way around. A 32-bit client app will read registry keys from HKLM/Software/WOW6432Node, you only get your registry keys there is you used the 32-bit version of Regasm. Notable perhaps is that it is usually fine to run both flavors of Regasm, given that C# code can run on any platform.

Getting out of DLL Hell with Microsoft.VC90.CRT?

I've built a inproc com server dll which I can package as 1 file or many via the build utility py2exe. When I allow all the dependencies to remain external, I have no issues, but bundling as 1 file produces problems.
When the dll is utilized (either registering it or instantiating a com object from it), it immediately loads MSVCR90.DLL from the path c:\windows\winsxs\x86_microsoft.vc90.crt_1fc8b3b9a1e18e3b_9.0.30729.6871_none_50944e7cbcb706e5\MSVCR90.DLL no matter what I do, I can't change that. There is no information that I can find (using Dependency Walker) to indicate what is causing that to load. It just happens magically...
Then, later on it loads that dll again via an explicit call to LoadLibraryA("MSVCR90.dll") (part of some py2exe black box?), but this time it does not look into the winsxs manifests / directory. Instead it looks to the system path and/or will respect a dll redirection. That's when the problem occurs. If I set the system path to start with c:\windows\winsxs\x86_microsoft.vc90.crt...\ it will load the exact same dll and be happy - but if ANY other file is utilized - inclusive of a copy of the EXACT same dll - but at a different path - then the whole thing blows up. It can't handle using two different files.
How can I fix this? Ideally, I've love to make the initial magic loading of the dll draw upon a private assembly, but no matter what I do with manifests or .dll.local etc it will not respect that until this second dll loading takes place.
Note that with the non-bundled dll (external dependencies) it always uses the winsxs MSVCR90.DLL.
I can "fix" my failure to use the dll by forcing the system path to load the winsxs copy, but that is pretty useless for a deployable com server!
The reason is that you DLL has a manifest that tells the module loader to search also in the SxS storage.
You have several choices
Build your DLL using static linkage. Not using any of the MFC-DLLs (see project settings)
Don't use a side by side manifest for the DLL and still use the MFC DLLs. But beware you have to ship those DLL with your DLL in the local path (see DLL search sequence docs)
Use a later build of VS. Later versions of VS don't use the SxS storage any more and there are no manifests for those DLLs any more.
For the 2. see this article in code project. There is an update for VS-2008 [here].
2
Build your DLL

Reference VB.NET DLL in Kofax Document Validation Script

We are working on a validation script for Kofax Capture 9.0 / 10.0 in VB.NET 3.5.
We know how to create a script using the Admin Module, and how to get it operational.
The problem is that we need to reference a dll, located on a remote machine. (GAC is no option) This dll holds abstract classes we need in each validation script.
Even when putting the dlls locally (copy local), the Validation Module (index.exe) immediately throws the "cannot find reference" exception, even though the project compiled perfectly.
I guess the basic question comes down to: where do we put the dlls, in order for the Validation Module to find them?
The simple answer is to put the dll in the same folder as the application because this is one of the places which .NET will probe when trying to find it. The Validation module is run from the Capture bin directory which will be something like "C:\Program Files (x86)\Kofax\CaptureSS\ServLib\Bin\". This would need to be done on each client using Validation.
If you have a more complicated scenario, you could look implementing the AppDomain.AssemblyResolve Event and using Assembly.LoadFile to get the assembly from a custom location, but the using the bin path is less complicated.
If you end up having further trouble, you can troubleshoot by using the Assembly Binding Log Viewer (Fuslogvw.exe) which can tell you more details about why the assembly failed to load and where .NET tried to search for it. Assembly loading can fail for reasons other than just the path.
For more detail on how .NET loads assemblies, see the following:
How the Runtime Locates Assemblies
Locating the Assembly through Codebases or Probing
We found a solution: add all library files as "links" to the project. (Add --> Existing File --> small arrow next to "Add" --> Add as Link)
This ensures the files are compiled when you build the project. The Kofax Validation Module can now find the files, whereas when referencing the file, it could not. Why it could not, remains a mystery...

VB.Net project shows errors on clean in VS2010

my issue is very similar to:
http://www.dotnetmonster.com/Uwe/Forum.aspx/dotnet-vb/54944/VB-Net-project-throwing-errors-when-executing-Clean-Solution
Typical errors: Unable to load referenced library X
Type IWshRuntimeLibrary.WshShell is not defined.
Interface System.IDisposable is not implemented by this class.
Namespace of type specified in the Imports Z does not contain any public members or cannot be found. Make sure the imported element name does not use any aliases.
This must have to do with a VB.net project configuration. Currently there is a setting "Treat all warnings as errors". I would like to keep that setting, but also resolve these annoying "errors".
I've experienced a similar problem and fixed it by running the following command in the VS command line tool
devenv /resetskippkgs
I'ev had problems executing Clean's via MSBuild in VS2008 when projects were registered for COM interop.
The problem is that the "Clean" target auto-generated for a solution cleans projects in the same order in which they were built.
This causes problems for projects registered for COM when MSBuild attempts to load and then unregister the assembly from COM during a "Clean".The issue occurs if the assembly has a dependency on another project because the dependency will be deleted first and therefore loading the assembly will fail and MSBuild will be unable to unregister it.
A workaround is to change the solution .cache file (which contains the targets auto-generated by MSBuild during "Build") and reverse the order of the "Clean" target. I hacked-together a quick-fix custom build task to do this as a post-build step.
No idea if this was fixed in VS2010 or indeed if this matches your problem since you don't give many specifics in your question ;)

What exactly is the "Multi-threaded Debug DLL" Runtime Library option doing in VS 2008?

I have a solution in VS 2008 that creates a DLL. I then use that DLL in another application. If I go in to the DLL projects property pages and change the following configuration for a DEBUG build then the built dll no long provides the desired functionality. If I change it back and rebuild the DLL, then the DLL does provide the correct functionality:
Property Pages => Configuration Properties => C/C++ => Code Generation => Runtime Library
If set to "Multi-threaded Debug DLL (/MDd)"
then everything works as it should. I get the correct functionality from the DLL
If set to "Multi-threaded DLL (/MD)" then the DLL does not function properly...no runtime errors or anything, it just doesn't work (The DLL is supposed to plot some lines on a map but does not in this mode).
So the question is, why does using the /MDd flag result in correction functionality of the underlying code, while /MD results in incorrect functionality?
A little background...somebody else developed the DLL in C++ and I am using this DLL in a VB.net application.
All DLL's/debug code generation must match across everything that uses them. There may be another referenced library or object or dll or some code in there that is built using the wrong options; or specific options for an individual element that override the global project options.
The only way of figuring it out is to meticulously check all of the options for each file, checking the included and referenced libraries (.lib and .dll) and object files. Check the linker options too.
The reason why it doesn't work is probably because the debug version adds extra guard blocks around memory to allow detection of errors.
I had similar problems. My application which "used" a 3rd party DLL crashed when its runtime library was set to "Multi-threaded DLL (/MD)", but worked when its runtime library was set to "Multi-threaded Debug DLL (/MDd)".
It has something to do with passing std::strings and std::lists across the DLL interface.
Our guess was the low level definition of these types was somehow different in the two runtime libraries.
We solved our related problems using this rule...
The DLL and the DLL user must be build using the exact same runtime library.
The main difference between the two options is in the libraries that your code will be linked at later. for the debug version for example this will include LIBCMTD.LIB and a few others. if your library is going to be built as debug the you should always link with MDd. failing to do so will result in lots of unresolved external linker errors at best. and sometimes the code compiles normally but crashes at runtime. if this happens in vb.net then a catch can easily hide the error. I guess you should make sure you build setting is correct. for more detailed information check this.