Solved

Detect if DLL Reference is Valid on Client Machine

Posted on 2011-09-02
12
402 Views
Last Modified: 2012-05-12

When my exe is on a client machine, I want to see if a reference in the program is valid. Mine happens to be for SMO, but it could be anything. In my project, I've added the necessary dll references. My code is follows...

Imports Microsoft.SqlServer.Management.Smo

Private Function DoesSMOExist() As Boolean
    Try
        Dim oServer As New Server() ' See if SMO dlls are even present on the machine.
        Return True
    Catch ex As Exception
        Return False
    End Try
End Function

Open in new window


I want my Try-Catch to return true or false, based on if a new object to this particular reference can be created. Instead, I get an unhandled exception that pops up a message, which I definitely want to avoid showing the users. The text is as follows...

System.IO.FileNotFoundException: Could not load file or assembly Microsoft.SqlServer.Smo, Version 10.0.0.0...

Anyway, I don't need this to only work for this particular reference/object, but I want to know how to do it for *any* reference/object to determine if a client machine can handle it. I don't want to put the dlls on the client machine, nor do I want to search the registry (could be multiple places), nor do I want to see if a certain dll is in a certain location (could be multiple places).

I only want to detect if a given machine can reference (and create) a given DLL reference without popping up an ugly message.

Anyone know how to do it? Or can't it be done?
0
Comment
Question by:jjsather
  • 6
  • 4
  • 2
12 Comments
 
LVL 33

Expert Comment

by:Todd Gerbert
ID: 36474535
Yeah, you would get an exception if a client is missing a DLL before any of your code ever runs - I'm not sure there's anyway to trap that exception since it's thrown by the runtime before your code ever runs.

You could create a stub application (which does not reference anything else) to attempt to use System.Reflection to load a particular library, and if successful you know the necessary DLL is present and then use Diagnostics.Process to start your main (i.e. actual) application. Otherwise you can show a meaningful error message to the user.
0
 
LVL 33

Expert Comment

by:Todd Gerbert
ID: 36474544
>> I'm not sure there's anyway to trap that exception since it's thrown by the runtime before your code ever runs.

The phrase I'm not sure is key here, there could be a way I just don't know what it is. ;)
0
 

Author Comment

by:jjsather
ID: 36474569

For what it's worth, the exe pulls up fine and runs. However, the moment that function is called, I get the ugly MS message. (And maybe that's what you meant, but I just wanted to clarify.)

Thanks for the input, but we'd really like to keep it all internal to the app, if possible, and fairly clean. Though like I wondered above, maybe it's not possible. At the very least, I assumed there'd be a way to suppress that message, but maybe not.
0
 
LVL 33

Assisted Solution

by:Todd Gerbert
Todd Gerbert earned 300 total points
ID: 36474722
I was probably just wrong (or the behavior's changed in .Net 4)...anyway, you can still use something like System.Reflection.Assembly.ReflectionOnlyLoad() to attempt to load a given library in your application to determine whether the DLL is present or not.

Public Function CanLoadLibrary(ByVal libFullName As String) As Boolean
    Dim result As Boolean = True
    Try
        Dim asmbly As System.Reflection.Assembly = System.Reflection.Assembly.ReflectionOnlyLoad(libFullName)
        If IsNothing(asmbly) Then
            result = False
        End If
    Catch ex As Exception
        result = False
    End Try

    Return result
End Function

Open in new window

0
 
LVL 33

Assisted Solution

by:Todd Gerbert
Todd Gerbert earned 300 total points
ID: 36474731
...a global exception handler might catch that exception you initially referred to (which might not be a bad idea regardless, would give you a chance to pretty up the error message and do some logging).
0
 

Author Comment

by:jjsather
ID: 36475428

I tried the ReflectionOnlyLoad, which works if my libFullName is this value...

"Microsoft.SqlServer.Smo, Version=10.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91"

But my concern is Smo has different versions, so this will only catch the one version, right?

Otherwise, I like the idea of a global exception handler (assuming it can catch this problem.) But can I have it only catch this error, or will it handle every single error, thereby ignoring all the other Try-Catch statements in my code?

After googling it, I'm a bit confused. What's the cleanest way to implement a global exception handler?
0
What Is Threat Intelligence?

Threat intelligence is often discussed, but rarely understood. Starting with a precise definition, along with clear business goals, is essential.

 

Accepted Solution

by:
jjsather earned 0 total points
ID: 36475679

OK, I stumbled across something. On a whim, I tried the following, and it works...

Private Function DoesSMOExist() As Boolean
    Try
        DoesSMOExist2()
        Return True
    Catch ex As Exception
        Return False
    End Try
End Function

Private Sub DoesSMOExist2()
    Dim oServer As New Server() ' See if SMO dlls are even present on the machine.
End Sub

Open in new window


Amazingly (to me at least) this works, though I don't understand why since without a Try-Catch in DoesSMOExist2, it should work the same as if I put that one row in DoesSMOExist, right? Or am I missing something?
0
 
LVL 40

Assisted Solution

by:Jacques Bourgeois (James Burger)
Jacques Bourgeois (James Burger) earned 200 total points
ID: 36476192
There is a way to trap all the exceptions that are not handled by a Try... Catch, but I am not sure (another one) if it is in function at the time the framework checks the system for missing dlls.

In your startup module or class, prepare a method with the following declaration:
Private Sub UnhandledExceptionHandler(ByVal sender As Object, ByVal e As UnhandledExceptionEventArgs)
    MessageBox.Show("Your message")
End Sub

Open in new window

If you are running a multithread application or a Windows application (every window runs on a different thread), you will also have to add this one:
Private Sub ThreadExceptionHandler(ByVal sender As Object, ByVal e As Threading.ThreadExceptionEventArgs)
     MessageBox.Show("Your message")
End Sub

Open in new window

Then, as the first line(s) of your Main method, add the following to register the previous method(s) as an event:
AddHandler AppDomain.CurrentDomain.UnhandledException, AddressOf UnhandledExceptionHandler
AddHandler Application.ThreadException, AddressOf ThreadExceptionHandler

Open in new window

These events are called when an unhandled exception is triggered, or when a Catch retrow the exception. According to Microsoft, there are some exceptions that cannot be catched that way.

Hoping yours is not one of those...
0
 

Author Comment

by:jjsather
ID: 36478148

Thanks for the unhandled exception error. To learn it, I gave it a try, without worrying about multithreads. When running through VS, I get "Your message" and it puts the next line on the unhandled exception. But once I create an exe, I don't get the "Your message" and it instead crashes. Are unhandled exceptions only supposed to work in VS, but not in an exe? If so, then it's very limited in it's helpfulness.

So does anyone know why much tweaked code works above? I'm still scratching my head on it.
0
 
LVL 40

Assisted Solution

by:Jacques Bourgeois (James Burger)
Jacques Bourgeois (James Burger) earned 200 total points
ID: 36478304
No. Unhandled exceptions are the suggested way to deal with exceptions in all applications. They work in the compiled exe.

The code that runs in Visual Studio is not exactly the same as the final one. Instead of running app.exe, Visual Studio runs the app.vshost.exe that you see in the bin directory. According to Microsoft, this is to speed up a few things while debugging.

I never really knew what those "few things" were, but I think that you found one. I suspect that the check for missing dlls is not done to speed up the startup when in Visual Studio. The application starts even if the dll is missing. When your code tried to call something in the dll, then the UnhandledException event was triggered.

That means that in the real .exe, the check for the missing dlls is done before Main is even called, so there is no way to trap the problem and give a better message from the application itself.

I think that your best bet then is tgerbert solution of testing with ReflectionOnlyLoad.

There is your concern about the version number, but I would not put too much emphasis on that. If the application targets version 10, it will start only with version 10, unless a redirection has been set in one of the different configuration files (application, machine, entreprise).

You might try with System.Assembly.Load() instead of ReflectionOnlyLoad. As far as I understand, it follows the same path as the system does to verify if an assembly is present, and that leaves a little more latitude than ReflectionOnlyLoad that is not meant to load an assembly for execution and is thus very strict.

Alternative to all that. Distribute SMO with your application. I have searched a bit because I was curious. I do not thing you can redistribute it alone, but it is part of the SQL Server Feature pack (http://www.microsoft.com/download/en/details.aspx?id=26728). Look for the version compatible for your SQL Server installation, and then look for SharedManagementObjects.msi about 3/4 down the list.
0
 

Author Comment

by:jjsather
ID: 36478420

The point of is to see if SMO exists on the machine, so including the dlls would defeat the purpose.

You made me wonder if it was because I'd been using a debug exe on the client. So I ran a release exe on the test machine, but the app still loads, even though it has a references to dlls that don't exist on the machine. So while I hear what you're saying, the real life situation is different.

To make sure it wasn't SMO only, I  added a reference to an unrelated 3rd party control and created a new object of it in the code, and the same things occurs -- app loads fine, and only get an error when I try to create the object in code. And if I nest it in a separate function, I can trap it fine.

So even with the release exe, I appear to be fine to nest my call to create a New Object to a reference dll to see if it exists. It doesn't really make sense to me, but it works.

I'll gladly take any more input on this mystery and/or I'd be curious if someone can verify it with their own code.
0
 

Author Closing Comment

by:jjsather
ID: 36553530
It works, but not sure why.
0

Featured Post

What Is Threat Intelligence?

Threat intelligence is often discussed, but rarely understood. Starting with a precise definition, along with clear business goals, is essential.

Join & Write a Comment

Suggested Solutions

This article explains how to create and use a custom WaterMark textbox class.  The custom WaterMark textbox class allows you to set the WaterMark Background Color and WaterMark text at design time.   IMAGE OF WATERMARKS STEPS Create VB …
Parsing a CSV file is a task that we are confronted with regularly, and although there are a vast number of means to do this, as a newbie, the field can be confusing and the tools can seem complex. A simple solution to parsing a customized CSV fi…
This video demonstrates how to create an example email signature rule for a department in a company using CodeTwo Exchange Rules. The signature will be inserted beneath users' latest emails in conversations and will be displayed in users' Sent Items…
This video explains how to create simple products associated to Magento configurable product and offers fast way of their generation with Store Manager for Magento tool.

707 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

16 Experts available now in Live!

Get 1:1 Help Now