COM Interop Rebuild/Clean not calling registration/unregistration issues - c#

I am writing a library add-in that uses COM Interop and I am having problems during the clean and rebuild tasks. Visual Studio 2019 is setup to run a external program to debug and runs the .dll registration after the build using regasm to write registry values for the external program. I am using the "Register for COM interop" option.
This is causing problems during the clean/rebuild tasks. If I run a clean/rebuild on the solution I receive Access to the <dllPath> path is denied but if I run a clean on each individual library or run a clean twice (first time gives errors, second time works) things seem to work but leaves the msbuild and VBCSCompiler services hung up after shutting down VS.
However if I unregister the library via the command line using C:\Windows\Microsoft.NET\Framework64\v4.0.30319\regasm /unregister <dllPath> before building the project, VS throws an error saying that the registry key doesn't exist, VS thinks it needs to unregister the library but the clean works as expected and removes the files in bin/Debug.
To run the registration I have:
<PropertyGroup>
<FrameworkSDKPath>
$(registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework#InstallRoot)
</FrameworkSDKPath>
</PropertyGroup>
<PostBuildEvent>
"$(FrameworkSDKPath)v4.0.30319\regasm" /codebase "$(TargetPath)"
</PostBuildEvent>
If I don't have the post build event it only registers the .dll in the GC but doesn't call the methods decorated with [ComRegisterFunction] and the external program doesn't recognize it. There are a couple of problems besides not deleting the files, if the decorated [ComUnregisterFunction] is not called (VS doesn't call this when cleaning/rebuilding) it will leave artifacts within the external programs registry entries, but it clears the GC entry.
Is there a way to get all this to sync up or do I need to revert to a total manual build process to take care of the registration? Is there an option to tell VS to call the register/unregister functions during the build/clean? Am I missing a step or a configuration setup?

As Simon pointed out, I didn't need to have the "Register for COM interop" option enabled since I was calling RegAsm.exe in the build, disabling this option allowed the files to be updated without any problems.

Related

Unpredictable System.DllNotFoundException

I downloaded a package from SourceForge, PlanEph, which has 64 and 32 bit DLLs for C#. I got the 32 bit included C# demo to work by putting the DLL in my bin/Debug directory (I'm using Visual Studio 2015 Community) and adding the DLL as a reference.
Then I tried to make my own version of the demo in a separate solution, and got the System.DllNotFoundException. Various experimentation lead me to believe I can't have two identical namespace names anywhere in my Visual Studio installation, so I erased everything and started over.
I made a directory C\GJAbin, put the DLL in it, and added it to the system Path variable. I also put a helloWorld type program in that dir and executed it from the command line to verify the directory really was in the path. Then I recreated the demo solution, added the DLL as a resource, and built the solution "successfully". Then I ran it and got the System.DllNotFoundException.
So I can't understand why the DLL is being found when compiling but not at run time.
Go to project settings, go to "publish" tab and on the top most button (labeled something like "application files"). Chose "Show all files" checkbox if you don't see your DLL. Set the DLL's publish status to "Include" (NOT "Include (Auto)"!!) and publish it again.
Now the DLL should be inside the publish folder.
So I can't understand why the DLL is being found when compiling but not at run time.
Locating the assembly at compile time is done differently (by MSBuild) than at runtime (by the CLR).
At compile time, MSBuild has specific search paths that it knows about, or in most cases like this, there will be something in your project file telling MSBuild where to look for the file. Usually the <Reference> has a <HintPath>.
At runtime, the CLR will attempt to find the assembly from its own set of well-known paths. It will look in your app's config file (if applicable), then in the Global Assembly Cache (GAC), then in your app's root directory. Much more detail on this is available here.
You can tell MSBuild to copy the reference to your build output directory (usually the same as your app root directory when running). In VS, you can do this by selecting the reference and looking at the Properties tool window (or press F4 by default). Set the CopyLocal state to True. In the project file, this will add a <Private>True</Private> on the <Reference>.
You can also add the assembly to the GAC using the gacutil tool, but this does make it harder if you want to share your app with others. Usually it's preferable to keep a copy in your app root directory.
If it's still not working, you can also see the log for how the runtime is trying to find this assembly. The fuslogvw.exe tool in the Windows SDK (you can run it from the VS command prompt and it will be on the %PATH%) allows you to enable logging for assembly loads. You do need to be able to run this as an administrator to configure the setting.
As you can see in the screenshot, you can either log the results in the exception (so that you can see it while debugging), or you can log it to a file on disk (so you can see it whenenver).
The problem turned out to be an unfortunate interaction among the way the author chose names and the way Visual Studio displays information and error messages. The author created a c# dll Astronomy.PlanEph32.dll containing a namespace PlanEph32, which which was really just a wrapper for the c dll PlanEph32.dll. So all the error messages about not being able to load PlanEph32.dll were referring to not finding the c dll; the c# dll was being found just fine.

vb6 Is keeping an old reference locked

I created a wrapper class to load CLR4(.net) into CLR2(vb6). It works, sort of.
I compile my program in .net(4.0) and run a batch file to create a .tlb. I refrence the .tlb from my vb6 program and everything is awesome.
When I change the wrapper and redo the above process, vb6 will not see the new .tlb, it still uses the old one.
...Anything?
Try to un-register the old tlb before deploying the new one. Open a cmd shell and type "regsvr32 /u mytlb.tlb", then deploy the new, and run regsvr32 again without the /u switch.
see https://technet.microsoft.com/en-us/library/bb490985.aspx for additional information.
We have a similiar situation here at work ( a vb6 app hosting .net assemblies).
You want to run REGASM /unregister *.dll. The *.tlb is a product of running REGASM /TLB.
If you have your .net project is registered for COM interop, it should do this for you at ever build.

MSBuild assembly lock

I have a custom MSBuild task(resides in assembly A) to build a custom project type(let's call it 'TestAppContent'). 'A' references another assembly 'B' that is currently under development.
To test 'B', I use a test program, TestApp. TestApp depends on TestAppContent getting build using our custom task.
The problem is that after the task is loaded, 'B' assembly is locked by MSBuild or VisualStudio process as the assembly that contains the task('A') has a reference to it.
As I can't simply 'unload' an assembly and using separated AppDomain doesn't work, how can I stop this lock?
I know that Microsoft XNA can do this as you can supply custom assemblies to the build process and they are released after it so you can rebuilt those custom assemblies.
The only way is to use an AppDomain and activate the Shadow Copy on it. I don't think you can activate shadow copy on current AppDomain, but you can try (see question here)
Or you can manually copy the dll elsewhere and load it (programmatically) so that the original dll will not be loaded and will remain unlocked. But you can't load the same dll twice... so you'll need a separate AppDomain if you want to unload and load a new version (or you restart your program)
EDIT:
You can also use AppDomain.CurrentDomain.AssemblyResolve to intercept when you program try to load a dll. There you can copy it elsewhere and load this copy.
I think it won't help you that much since you're trying to achieve it automatically but one manual way to achieve this i found out was using sysinternals ProcessExplorer to kill the specific process using the dll. But killing doesn't seem to harm VS2013, which makes this is a workaround without the need to restart MSVS.
In this case my Visual Studio process instantiated an MSBuild instance which previously created the DesignLibrary assembly - now being locked, which the project SomeLibrary uses right within the build only. At this point it is not possible to rebuild the DesignLibrary assembly without killing the MSBuild task holding the lock.
I would like to have the MSBuild.exe checking during the build if it tries to rebuild a file created by itself, or maybe visual studio intervening this messy situation. Just another thought: Maybe it might be possible to inspect the process tree and kill the process automatically, like i did manually. A very hacky workaround ;)
One way I usually solve this is by having two solutions. The first builds the Tasks and then I hook it up to launch devenv.exe to load the second .sln when I press F5.
The second solution consumes the task built in the first causing it to be loaded into the second devenv.exe process. When you stop debugging the second Visual Studio will close and the assembly with the build task will be released.

Trying to debug VS2010 C# code that uses IBM WMQ (amqmdnet.dll)

I'm using IMB WMQ library to connect to a queue manager.
This is a code I use for a long time, it was created by a friend in VS2005 and I updated to be used in VS2010. It works fine when I use integrated with LoadRunner tool to run some performance scripts. However when I try to debug it returns the following error:
{"Unable to load DLL '**amqxcs2.dll**': The handle is invalid. (Exception from HRESULT: 0x80070006 (E_HANDLE))"}
This is something related to debugging external DLL, but I didn't find out how to resolve.
I used the following command to register the dll into GAC:
gacutil /I "C:\Program Files\IBM\WebSphere MQ\bin\amqmdnet.dll"
When I tried to add amqxcs2.dll it returns an error:
gacutil /I "C:\Program Files\IBM\WebSphere MQ\bin\amqxcs2.dll"
Failure adding assembly to the cache: The module was expected
to contain an assembly manifest.
I have VS2010 Ultimate running on a Windows Server 2003.
When running the code inside Loadrunner I'm able to put and get messages from the queues, but I'm not able to debug.
There's a similar post here, but it doesn't have a solution.
Why do you think "make sure the library is either where the application needs it to be" is not a solution?
You need to either properly install whatever product your are using on the machine you plan to use it or cheat your way through by copying necessary files to correct places by hand (and figuring "correct places" by hand too. SysInternals tools may be helpful to track what files an application tries to use).
This may be a COM error. The IBM assembly is probably a native dll and so you cannot register it in the GAC, which is for .NET dlls only. Native COM dlls can be registered with Regsvr32. This error can also occur if a native dll has a dependency on another dll that is missing. Also see Understanding Dependencies of a Visual C++ Application which gives information about the Dependency Walker tool that can be used to figure out the depndencies for a native dll.

Referenced DLL Cannot Be Found When Running Application

I have a weird situation with some code that I inherited at work. Their application is a multi-project solution, with several of the solutions being (code) pieces of the MS Enterprise Library (not sure which version).
They also have an existing C++ (unmanaged) application which has a bunch of DLLs. One of these DLLs is built in a separate solution, both in 64-bit and 32-bit flavours.
The main application has a reference to this DLL, and calls a couple of static functions (I can see intellisense, even). I can compile and build the main application EXEs, but when I run it, I get an exception that this DLL from the unmanaged code (lets call it CPlusPlusCode.dll cannot be found:
FileNotFound Exception was unhandled: Could not load file or assembly 'CPlusPlusCode.dll' or one of its dependencies. The specified module could not be found.
I'm quite stumped, because I can compile the code, see intellisense for the imported classes, and dig into the DLL in the object browser. I even made sure there's a copy in the \bin\Debug folder (although I don't see why that would make a difference). This is for a Windows Forms application.
Also, if it matters, I had some build issues related to x86 vs. x64 for different projects; I think (hope?) that this is not related to that, but I solved that by using the Configuration Manager to build everything as x64.
Check the GAC, and if necessary you might need to add it or register the DLL there.
I had this problem with a project, it all works ok from Visual Studio and most of my times running the project locally on my machine. But because of the unmanaged code I needed specifically allow the project to be executed with correct permission levels.
So have a look in the manifest file, that enough permissions etc exist.

Categories