Unmanaged C++ regfree COM application: problems when launching from IDE with parent directory in path (VS2010) - visual-studio-2010

I have an application A and a DLL B, both unmanaged C++ built in VS2010. DLL B is a COM server. A uses both implicit C++ linkage to B for some methods and classes but it is also accessing coclasses implemented in B via COM. This all works fine when using registered COM, but when A is using B via registration free COM, a weird problem is occuring.
When I start A from within the IDE (either F5 or Ctrl+F5), then my application crashes when trying to cocreate a coclass implemented in B. I also noticed that when I try to debug this the Visual Studio Debugger sometimes simply ignores my commands to step into methods. The source code and what is actually executing seem to be somehow out of sync.
I then eventually found the cause of the problem: In application A's Debugging settings Command is set to $(TargetPath). $(TargetPath) in turn uses the project's output directory which was set to "$(ProjectDir)..\bin\". Changing the output directory to an absolute path that does not contain the ".." parent directory syntax, solved the problem, even though the directory pointed to was exactly the same.
Can anyone explain what is going on there? I have a vague idea that it has something to do with the fact that my application both loads B.dll at startup via implicit C++ linkage and also lists it as a dependent assembly in its manifest for registration free COM. Could this lead to B dll somehow being loaded twice? And why does this only happen when starting A from within the IDE? When launching A from the command line, using ".." in thee path works fine.

Related

Easiest way to remove dependency on certain ActiveX library in MFC project?

I am working on greatly simplifying an old MFC application, and I'd like to use that opportunity to remove a dependency on a certain third-party ActiveX library that has been causing headaches. I first simply tried to remove all references to these controls in the cpp code. It compiles fine, but when installed on a new computer, it crashes if the library .msm file is not included in the setup project.
The problem now is that I don't know where any remaining references to these objects in the project is, and therefore have no easy way of tracking them down and removing them. Simply using the Find-functionality in VS only returns results in cpp files which I've already cleaned up, not any control instances in f.ex. dialog resource files. Is there a way to search for all these objects in the project, or check for them compile-time so that I can use compile errors to see where any library objects remain?
First of all: CoCreateInstance, CoGetClassObject are the normal ways how COM Objects are created. Should be easy to set break points here or to set a hook to monitor what's going on.
Seams that you have a bad error handling in you software. Otherwise you application should show an error message if an object could not be created.
Try to debug.... use remote debugging, even a crash dump will lead you to the code location.
Easiest approach: In your development machine. Try to look into the output window and see if any of the old ActiveX DLLs gets loaded. Trigger down to the location were any of those objects is created.
You can do the same with remote debugging on the test machine.

How to handle legacy OCX files in a Visual Basic Project

I have inherited an old VBP project that is ostensibly usable in Visual Basic 6.
It has two .ocx files which, research seems to indicate, need to be registered with regsvr32. Loading VB6 onto a Windows XP installation seems to allow me to successfully register them both.
However, VB6 still chokes on them. When I load the project, two errors are logged:
Line 228: Class CvsInSightDisplayOcx.CvsInSightDisplay of control Camera1 was not a loaded control class.
Line 5841: Class Xtimer01.Xtimer of control Xtimer1 was not a loaded control class.
And, naturally, the project will not compile. What steps can I take to get these .ocx files to play well?
#Bob77's comment led me to a two part resolution.
1) One of the components needed to be formally installed. (Thankfully it was still available.)
2) Both needed to be manually removed from the .vdp file using Notepad, and added via the VB6 UI, through Project -> Components... and 'Browse...'

Developing Reg-Free COM application with VB6

I'm maintaining a VB6 application with many COM components (DLLs and OCXs). In order to streamline development and deployment I'd like to use reg-free com. The problem with development is that the application runs within the VB6.EXE instance. How can I trick VB6 to use my (unregistered) components? It is very important for me to not have to go through registering/unregistering components when switching between branches. Generating a .manifest file for VB6 is not out of the question but is there some other, more optimal way, to specify a .manifest file when launching VB6.EXE?
Note: The Activation Context API doesn't seem to help, even if used from within the development environment.
Solutions I've thought:
A utility application that activates a context from a manifest and launches VB6 as a child process (doesn't work; processes don't inherit activation context)
Injecting context activation into the VB6 process at startup (too complicated; must hack the executable to do this)
Hosting VB6 in my own process after activating the right context (can't even find out if this is possible)
Using a VB6 Add-In or other utility that runs within VB6 to activate a context (tried that but it doesn't seem to work)
Update Jan. 16
As suggested by wqw, I did some testing with a VB.exe.manifest. The VB6.exe.manifest worked, with some caveats:
The SxS dll specified in the manifest would not appear in the references window on projects that didn't actually reference the component
On projects that did reference the component it would be shown to reside in the directory according to the following order:
The pathname recorded in the project file (if the file was still present)
A pathname as if it resided in the same folder as the project (vbp)
If the file was not in any of these folders, the project would not compile (just running the code causes an internal compile in VB6) with the message "Can't find project or library".
Obviously, VB6 actualy scans the registry to find COM components and verifies, during compilation, that they exist where they say they exist. I'm not sure what that might mean if I actually want to use VB6.exe.manifest to redirect COM component instantiation. Perhaps having dummy component files at some predefined location might trick VB6 into believing that everything is as it should be, although an entirely different set of components got loaded for use.
Further update:
I did a test on that last assumption and it proved to be false. The component has to actually be there in order for the project to compile. It must even properly load (no dummy, zero-length files accepted!). Now I'm not even sure if the manifest works. That's a more time-consuming test (requires a component with two versions that produce different results, one with the project, and one for the manifest).
Our approach to this problem was to write a build assist program that registered and unregistered components, run the VB6 compiler, and would even rewrite project files with updated GUIDs when interfaces changed. You would hand it a VBG project group and it would do the rest.
I suppose we could also have added a mode that unregistered components when you switched branches.
Are you following the practice of using "compatibility" binaries? You shouldn't use the binary at your build location for compatibility references - you should commit a separate copy to version control and configure your project to consider that the "compatible" version - only change this file when you break interfaces.

"Declare" statement as yet an other way to circumvent the .dll hell?

To my astonishment I found VB6 code that uses Declare statements to define functions in a .dll that lives in the Program Folder without it being registered on Windows. This seems like a supersimple way to avoid the .dll hell without having to resort to using Side by side manifests. Can I read some more about this somewhere? Are there snags?
The Declare statement is used to do "just in time" binding to non-ActiveX DLLs. Until your program "touches" a Declared entrypoint no attempt is made to load the library.
It basically has nothing at all to do with the topic of DLL Hell.
Muddled thinking can even lead people to plop ActiveX DLLs "next to" the EXE which actually can result in DLL Hell because people who tend to do this also use poor techniques for installing and uninstalling applications.
Poorly designed application A deployment plops a commonly shared DLL or OCX next to the EXE.
Poorly designed application is run, the VB6 runtime can't find the classes in the registry, does a DLL Search using Windows heuristics, immediately locates the DLL next to the EXE and calls its self-registration entrypoint.
Innocent, properly designed applications B, C, D are later installed that use the same DLL/OCX and their installers find the library already registered.
Poorly designed application A is uninstalled, typically by simply deleteing its folder in Program Files.
Applications B, C, and D (and any future applications using the library) are now broken - due to orphaned component registration pointing to a non-existant library.
Moral of the story:
Never, never, never put DLLs "next to" your VB6 application on installation. If you have private DLLs that are not shared with other applications even then put them into a libs, etc. folder under the application folder. Possible exception might be non-COM DLLs, such as those you expect to use Declare with.
There is also a great deal of misunderstanding about manifests, of which there are multiple kinds. The ones you are probably thinking of are application and assembly manifests.
These can be used for selecting among different versions of a library installed Side by Side, or they can be used to isolate applications and assemblies which is the part that has bearing on DLL Hell.
Of course application manifests can be used to specify quite a few other things about how Windows should run the application.
Windows searches in a well-documented sequence of folders for LoadLibrary (which VB6 uses behind the scenes to resolve Declare declarations). Since the first location on the list of search folders is the app's own folder, your discovery makes perfect sense.
It doesn't resolve the "DLL hell" issue for the most part, though. It can't work for system DLLs, for instance, because Windows preloads most of them. Also, if a DLL is already loaded into memory, Windows may use that copy of the DLL (not sharing data, but code can be reused).
That's part of the reason that manifests were created; they allow an application to strictly define required versions of system DLLs in order to provide certain functionality. VB6's technique is old fashioned (just like VB6).

Loading multiple copies of a group of DLLs in the same process

Background
I'm maintaining a plugin for an application. I'm Using Visual C++ 2003.
The plugin is composed of several DLLs - there's the main DLL, that's the one that the application loads using LoadLibrary, and there are several utility DLLs that are used by the main DLL and by each other.
Dependencies generally look like this:
plugin.dll -> utilA.dll, utilB.dll
utilA.dll -> utilB.dll
utilB.dll -> utilA.dll, utilC.dll
You get the picture.
Some of the dependencies between the DLLs are load-time and some run-time.
All the DLL files are stored in the executable's directory (not a requirement, just how it works now).
The problem
There's a new requirement - running multiple instances of the plugin within the application.
The application runs each instance of a plugin in its own thread, i.e. each thread calls functions exported by plugin.dll. The plugin's code, however, is anything but thread-safe - lots of global variables etc..
Unfortunately, fixing the whole thing isn't currently an option, so I need a way to load multiple (at most 3) copies of the plugin's DLLs in the same process.
Option 1: The distinct names approach
Creating 3 copies of each DLL file, so that each file has a distinct name. e.g. plugin1.dll, plugin2.dll, plugin3.dll, utilA1.dll, utilA2.dll, utilA3.dll, utilB1.dll, etc.. The application will load plugin1.dll, plugin2.dll and plugin3.dll. The files will be in the executable's directory.
For each group of DLLs to know each other by name (so the inter-dependencies work), the names need to be known at compilation time - meaning the DLLs need to be compiled multiple times, only each time with different output file names.
Not very complicated, but I'd hate having 3 copies of the VS project files, and don't like having to compile the same files over and over.
Option 2: The side-by-side assemblies approach
Creating 3 copies of the DLL files, each group in its own directory, and defining each group as an assembly by putting an assembly manifest file in the directory, listing the plugin's DLLs.
Each DLL will have an application manifest pointing to the assembly, so that the loader finds the copies of the utility DLLs that reside in the same directory. The manifest needs to be embedded for it to be found when a DLL is loaded using LoadLibrary. I'll use mt.exe from a later VS version for the job, since VS2003 has no built-in manifest embedding support.
I've tried this approach with partial success - dependencies are found during load-time of the DLLs, but not when a DLL function is called that loads another DLL.
This seems to be the expected behavior according to this article - A DLL's activation context is only used at the DLL's load-time, and afterwards it's deactivated and the process's activation context is used.
Edit: Works with ISOLATION_AWARE_ENABLED as expected - runtime loading of DLLs uses the original activation context of the loading DLL.
Questions
Got any other options? Any quick & dirty solution will do. :-)
Will ISOLATION_AWARE_ENABLED even work with VS2003? Edit: It does.
Comments will be greatly appreciated.
Thanks!
ISOLATION_AWARE_ENABLED is implemented by the Windows SDK header files and thus probably wont worth with VS2003 at all. However, it is possible to download the latest Windows 7 SDK and use that with VS2003.
You don't need to use MT to link in manifests. Manifests can be embedded as resources in environments that dont have explicit knowledge.
Add the following to a dll's .rc file to embed a manifest. (With a recent enough platform sdk RT_MANIFEST should already be defined):
#define RT_MANIFEST 24
#define APP_MANIFEST 1
#define DLL_MANIFEST 2
DLL_MANIFEST RT_MANIFEST dllName.dll.embed.manifest

Resources