Getting missing component error in a VB6 application - vb6

I have a VB6 app that has a ton of 3rd party components. The app works well, but on exit (and only when running as a standalone EXE, e.g. not in the IDE), it pops up an error message:
I've seen errors like these before but typically it says which component is missing dependencies or is not registered properly.
I ran it through Process Monitor and got the following files that it cannot find:
And then it quits. I googled the file names that it cannot find and can't seem to find anything. It seems like its searching for a variation of MSComENU, MSComEN and MSCOENU dlls.
I checked and rechecked to make sure that all the the 3rd party components are there and they are - the application functions fine, it wouldn't if they weren't there.
It is worth noting that error occurs after the last line of VB6 code (in Form_Unload event) has fired. I know this because the last line is a message box that does appear.
Much, much later EDIT: I finally got back to dealing with the problem and figured it out by process of elimination (and it was a loooong process). In the end it had nothing to do with any MSCOMM*.dll entries. In fact, I do not know why they still show up in Process Monitor. The problem was much simpler.
I had several 3rd party controls on the main form. In an effort not to pollute the main form with a ton of event handling code, I delegated these controls to a new class, like so:
' declaration code in main form'
Private WithEvents moDelegateObject as clsDelegateObject
' still in the main form, after initialization'
Set moDelegateObject = new clsDelegateObject
With moDelegateObject
Set .ThirdPartyCtlHandler1 = me.ThirdPartyCtl1
Set .ThirdPartyCtlHandler2 = me.ThirdPartyCtl2
Set .ThirdPartyCtlHandler3 = me.ThirdPartyCtl3
end with
' declarations and properties inside of clsDelegateObject'
Private WithEvents moThirdPartyCtlHandler1 as ThirdPartyCtl
Private WithEvents moThirdPartyCtlHandler2 as ThirdPartyCtl
Private WithEvents moThirdPartyCtlHandler3 as ThirdPartyCtl
Public Event FooEvent() ' other various events as well '
Public Property Set ThirdPartyCtlHandler1(o as ThirdPartyCtl)
moThirdPartyCtlHandler1 = o
End Property
Public Property Get ThirdPartyCtlHandler1() as ThirdPartyCtl
ThirdPartyCtlHandler1 = moThirdPartyCtlHandler1
End Property
' ... Repeat for each handler ...'
What was missing was code to explicitly deallocate these objects prior to closing. This is something that Visual Basic typically does. So I added the following in the Form_QueryClose in the main form:
With moDelegateObject
Set .ThirdPartyCtlHandler1 = Nothing
Set .ThirdPartyCtlHandler2 = Nothing
Set .ThirdPartyCtlHandler3 = Nothing
End with
Set moDelegateObject = Nothing
Last line turned out to be superflous, but I threw it in there for completeness sake. I think it was a combination of delegating controls to a delegate class and receiving events from it in the Main form and using a good number of really obscure 3rd party controls which contributed to this problem. It is probable that the 3rd party control does not cleanly deallocates itself. Anyway, lesson learned.

This could be a DLL_PROCESS_DETACH or CoUninitialize problem. Raymond Chen's blog "The Old New Thing" has a couple of relevant articles:
When DLL_PROCESS_DETACH tells you that the process is exiting, your best bet is just to return without doing anything
Do you know when your destructors run? Part 1.
Quick overview of how processes exit on Windows XP
How you might be loading a DLL during DLL_PROCESS_DETACH without even realizing it
The thread that gets the DLL_PROCESS_DETACH notification is not necessarily the one that got the DLL_PROCESS_ATTACH notification
As you said these are 3rd party components. You could try to make smaller test cases until the problem disappears to pinpoint the buggy component. You could also try a native code debugger and analyze what code created the error message.
The simplest solution however to work around the issue is trying to force a specific load-order of all those components. In the Main() or startup form, try to use some functionality of each 3rd party component in a fixed order. If the bug still appears, change the order until the problem vanishes. That might work.

You could try using dependency walker on the list of references in the project file to see if any of those third party files are missing dependencies. If no dependencies are missing then try registering the files again using regsvr32. If any of the regsvr32 commands fail then you may have found the component with missing dependencies.

Related

Using isolated storage right after application launch seems to cause stability problems

I'm trying to add support for tracking application usage to a WP7 app (e.g., how many times has the app been run, including wakeups from tombstoning). I thought I was doing this in a pretty straight-forward way, but I'm frequently getting IsolatedStorageExceptions, and I'm kind of at a loss for debugging it at the moment.
Paraphrasing from my App.xaml.cs
private void HandleAppCounter() {
int i = 0;
settings.TryGetValue<int>("usage", out i);
i++;
settings["usage"] = i;
settings.Save();
}
I call this function when the app is launching or activating. The thing is, it works just fine. But the thing is, if I make another call to isolated storage soon after then that is what blows up. And furthermore, it seems to only blow up if the next call happens pretty soon after.. although I'm having trouble understanding that as well.
The next action performed is usually a user clicking an item in a list. If it happens immediately when it's displayed, I get a crash from isolated storage. If the user waits a few seconds after the app is launched and then clicks it, there is no crash.
If the above code in HandleAppCounter() is commented out, then the user can click either immediately or later and it never crashes.
Can anyone give me some tips on how to debug this? I'm kind of running into a wall here.
I would suggest stepping away from your custom implementation for a sec and try the same behaviour with the default IsolatedStorageSettings class, if it is still a problem then you should report it to Microsoft Connect site as a bug, otherwise there is some kind of an error in your settings class.
If you're using IsolatedStorageSettings you might want to make sure, that whenever you're querying/adding a key, the key is there, if not you must create it.
If you're using IsolatedStorage filesystem then make sure you are always closing the streams when you're reading/writing. If you don't you will get absurd exceptions.

Crash after the second RichEdit initialization in x64

According to Using Rich Edit Controls I use RichEdit in such way:
MyControl::OnCreate()
{
handle = LoadLibrary(_T("Riched20.dll"));
}
MyControl::OnDestroy()
{
FreeLibrary(handle);
}
It works fine for win32 but recently I’ve built x64 configuration and now my control fails after the page reload.
I’ve noticed that if do this:
MyControl::OnCreate()
{
handle = LoadLibrary(_T("Riched20.dll"));
FreeLibrary(handle);
handle = LoadLibrary(_T("Riched20.dll"));
}
everything works fine.
I don't wish to put this code into production, so is there any suggestions about better solution/workaround?
Since the reported fault module is Richedit20.dll_unloaded it means you are unloading the DLL while code from it is still in use.
For example, if you still have a richedit window open when you (completely) free the DLL, you can see crashes like that as soon as anything triggers a call to the control's window-proc. This is because the control's window-proc was inside the unloaded DLL code.
It should be safe to call LoadLibrary and FreeLibrary multiple times (so long as the calls balance out), so I doubt that is the problem. It may just be triggering the problem. Also, the problem was there in 32-bit builds; you just got lucky and never triggered it.
OnDestroy is the wrong place to call FreeLibrary. There are several window messages which get sent to a window after WM_DESTROY (e.g. WM_NCDESTROY).
Child windows also still exist when OnDestroy is called. If the richedits are children of your control (rather than the control itself) then moving the FreeLibrary into OnNcDestroy may save you. (Child windows are destroyed by the time WM_NCDESTROY is called.) I'd still say it's not a good place to free the library, however.
So you definitely want to move your FreeLibrary call. I would move both it and the LoadLibrary completely out of the control itself. It's not normal to have controls which load/free libraries whenever an instance of them is created. Instead, have some static init/uninit code somewhere which loads the libraries you need once and for all and frees them when the application is shutting down.
(If your app only rarely uses the control then it might make sense to load/free the library only when windows using the control are active. That situation is rare, though. Usually you're better off just leaving the DLL loaded.)

VB6 IE frame / WebBrowser causing NT.dll error

We have a legacy VB6 application which has worked just fine on Windows XP Professional SP 3 until just recently when we added an IE frame control so that we could display static local HTML files on a form. And, it works fine until I go to close the application. And, then it reports the following error message (consistently):
Faulting module ntdll.dll, version 5.1.2600.5755, stamp 49901d48
Here's the reference in the Visual Basic project file:
Object={EAB22AC0-30C1-11CF-A7EB-0000C05BAE0B}#1.1#0; ieframe.dll
And we use it by performing the following actions:
Development machine is running Win7 + Vb6 IDE.
Add a component reference to the "Microsoft Internet Controls" located at C:\Windows\SysWow64\ieframe.dll
Place a control on the form at design time.
Show that form modally by calling Form.Show vbModal The error happens when I use the default form instance frmMyForm.Show vbModal as well as when I use a local instance Dim MyForm as New frmMyFormMyForm.Show vbModal
Call WebBrowser.Navigate "staticPage.html"
When the user presses a button, the button click event returns the user choice and the form is disposed of.
Exit the application -- Here's where I get the error.
I've been looking all over the web, and haven't been able to find a whole lot of people still trying to use VB6 in this way. So, I'm wondering if someone might be able to help me on stackoverflow. Any help is much appreciated!
[Update] And, the plot thickens. I made a sample application with just that web component in order to make sure that it was causing the error. But, I didn’t experience the error when it closed like I was when exiting our existing/legacy vb6 application. I'll do a bit more investigating.
A follow up to this in case any runs into the same issue (the original poster and I were coworkers at the time)...
The application was using the VBCorLib library, and some of its string manipulation classes utilized direct memory access incorrectly. Read more at this VBCorLib forum post.
It turns out that the issue was that I was trying to delete the temporary file that the browser had loaded. It works now that I've moved that delete file code to the form unload event.

Referencing functions within VB6 User Controls

I'm having an issue referencing public procedures of User Controls that I've created within a VB6 project.
A simple example (exe), I have a form with a button and a user control:
Option Explicit
Private Sub Command1_Click()
UserControl1.updateMessage ("TIME NOW: " & DateTime.Time)
End Sub
The User Control code is as follows:
Option Explicit
Public Sub updateMessage(ByVal newMessage As String)
Label1.Caption = newMessage
End Sub
This exe compiles and works fine, and when I'm typing updateMessage in the Form, it appears in the intellisense list with the appropriate requirements. The issue I have is when I'm wanting to "go to the definition" of updateMessage, instead of going to the appropriate section of the code within the User Control, the message always returns with:
"Cannot jump to 'updateMessage' because it is in the library 'Unknown1' which is not currently referenced."
where the numbered suffix of "Unkown1" changes from time to time.
It seems that if there were no reference to this procedure, then it would not appear in the intellisense and the project shouldn't compile. When running this with MZTools (though the error appears regardless of this plug-in being installed), I can go into the updateMessage procedure, and use it to find all procedures calling this function, so the link between the two should exist (although I'm not sure if MZTools just finds using a text-matching pattern).
If anyone out there could shed some light on this matter, it would be very much appreciated, and save this poor VB6 developer a lot of hassle!
I have SP6 installed (build 9782) of VB6 and am running XP SP3 on an HP dx2400.
Yes, this is extremely annoying and I'm convinced it's a bug in VB6. I say this because, if you locate the updateMessage method in the object browser and double-click on it, you are taken to the definition. So, the compiler actually does know where the definition is, it just refuses to take you there with Shift+F2.

Subscribe to Vista Events in .NET (e.g. Window Opened)

I am trying to build my own little toolbox for Vista.
One of the features is a "window placeing tool" which places the windows at saved position. Another tool I could imagine are extensions to firefox or thunderbird...
For these tools to work, I need them to be able to capture "Events" in Vista.
To give you a concrete example:
Explorer Opened New Window
User started Firefox
Mouse moved
For the mouse case, there are some examples for C#.
I also know about the directory watcher, nice little helper.
Want I now need is the "new window opened event"
Any idea how to monitor this, without iterating the current window list every 5 seconds (I already know how to get Windows using the DLLImports, and getting Processes using managed code. But I have no Event when the explorer process opens a new windows)
Thanks for your help,
Chris
What you're talking about doing is not simple by any stretch.
You're going to need to register a hook, and you're going to have to build a callback procedure that gets called within another process's execution context -- this is not going to be .NET code (probably C instead), and will have to be in a DLL. That callback procedure will get called every time a certain class of events happens. It will examine the events it receives and filter out the ones you're interested, then send your application the notifications you want (probably via PostMessage). You'll then tap in to your application's main message loop to intercept those messages, and from there you can fire a .NET Event, or whatever you want.
Writing hook callbacks is tricky stuff because the code gets run within another process, not your own, and the memory management and concurrency issues take a bit of forethought. For that same reason, it's not going to be done in C#. Ideally, though, this callback code will be very small and very fast, since it's going to get called so often.
Also note that while perfectly "legal" in Win32, these system hooks have an immense amount of power and are commonly used by malware to change the way your system works. For that reason, you may run afoul of antivirus software if you attempt to do this sort of thing on a customer's computer.
Also note that the far-reaching effects of system hooks also means that simple programming mistakes can take down your whole system, which you will probably discover for yourself at some point while debugging; so save everything before you hit "run".
Good luck!
EDIT
Did a bit more search to see if there's any way to write the hook proc in C#, and came up with this:
How to set a Windows hook in Visual C# .NET
This is almost what you're looking for, but not quite. Hook procedures can either be global (which means that they run on every application) or thread (only runs within your application). The document states that:
Global hooks are not supported in the .NET Framework
Except for the
WH_KEYBOARD_LL low-level hook and the
WH_MOUSE_LL low-level hook, you cannot
implement global hooks in the
Microsoft .NET Framework. To install a
global hook, a hook must have a native
DLL export to inject itself in another
process that requires a valid,
consistent function to call into. This
behavior requires a DLL export. The
.NET Framework does not support DLL
exports. Managed code has no concept
of a consistent value for a function
pointer because these function
pointers are proxies that are built
dynamically.
Which means, again, to monitor things that go on outside your application's view, you need to set a global hook, which can't be written in .NET.
I have exactly the same issue as this, and I think I have a workable solution. Initially I looked into a similar option to the one mentioned by 'tylerl'. In my case however, instead of using 'SetWindowsHookEx', I attempted to use the similar function 'RegisterShellHookWindows'.
Unfortunately, this only succeeded in providing me with notifications of when a subset of windows are created/destroyed. The only windows which it provided notifications for are those shown on the taskbar.
Since I didn't fancy hacking into other processes, or writing the native code which would be required for SetWindowHookEx, I tried digging into the .NET automation APIs introduced in .NET 4.0, and I think this has the answer to your problem (at least as far as detecting when windows are opened / closed).
Here's a code snippet for using this API to detect windows being opened/closed:
using System.Windows.Automation;
private void StartMonitoringForWindowEvents()
{
Task.Factory.StartNew(() =>
{
AutomationEventHandler windowOpenedHandler = new AutomationEventHandler(OnWindowOpened);
System.Windows.Automation.Automation.AddAutomationEventHandler(
WindowPattern.WindowOpenedEvent, AutomationElement.RootElement,
TreeScope.Descendants, windowOpenedHandler);
});
}
private void OnWindowOpened(object source, AutomationEventArgs eventArgs)
{
try
{
AutomationElement sourceElement = (AutomationElement)source;
string message = string.Format(
"Automation.WindowOpened PID: {0}, Handle: {1}, Name:{2}",
sourceElement.Current.ProcessId,
sourceElement.Current.NativeWindowHandle,
sourceElement.Current.Name);
Debug.WriteLine(message);
// for each created window, register to watch for it being closed
RegisterForClosedWindowEvent(sourceElement);
}
catch
{
}
}
private void RegisterForClosedWindowEvent(AutomationElement element)
{
try
{
string elementName = element.Current.Name;
int processId = element.Current.ProcessId;
int nativeHandle = element.Current.NativeWindowHandle;
AutomationEventHandler windowClosedHandler = new AutomationEventHandler(
(ignoreSource, ignoreArgs) => OnWindowClosed(nativeHandle, processId, elementName));
System.Windows.Automation.Automation.AddAutomationEventHandler(
WindowPattern.WindowClosedEvent, element,
TreeScope.Element, windowClosedHandler);
}
catch
{
}
}
private void OnWindowClosed(int nativeHandle, int processId, string elementName)
{
string message = string.Format(
"Automation.WindowClosed PID: {0}, Handle: {1}, Name:{2}",
processId,
nativeHandle,
elementName);
Debug.WriteLine(message);
}
You will need to add a reference to the assemblies 'UIAutomationClient' and 'UIAutomationClientTypes'.
Here's a link to the MSDN documentation (you'll probably particularly want to take a look at the information on events):
http://msdn.microsoft.com/en-us/library/ms747327.aspx
Important implementation Notes:
1.) Notice that in the sample, I used a task factory to register for reception of the automation events. It's particularly important to avoid using the UI thread when registering for automation events or generally interacting with the automation APIs. Doing so can (and usually quickly does) result in a deadlock. Therefore, I use the task factory to ensure registration is done via the thread pool.
This also means, that the events will be received on the thread pool... So, if you need to perform any UI updates, you will have to marshal these across to the UI thread.
2.) You'll also note, that I capture any needed information on the element which may be closed, at the time of registration (using a closure). This is because, once the element is closed, we will no longer have access to this information - since the element has been destroyed.
Phil
The answer is not C# (or .Net) specific. You'll need to call SetWindowsHookEx( WH_CBT, ... ). This will allows to know when a window is created, destroyed, moved, sized, etc. You'll also need to get the relevant information from the window to identify if its one you need to do something about. Perhaps GetClassInfo, GetWindowLong, and GetWindowText.
The problem with the SetWindowsHookEx is that in order to get events from every window you need to have a separate win32 dll with the function in question exported. Although you might have success with the procedure outlined here.
To expand upon Joel Lucsy's answer, you need to use the Win32 API. However, there's a nice library, the Managed Windows API, that provides an object-oriented wrapper over common APIs. It may have what you need.

Resources