Reflection gives different results

J

John Rivers

Hi

I am calling the same static method but getting different results

if I use Assembly.LoadFile() and then reflection to call the method it
returns 0 (wrong result)

if instead I add a reference to the same DLL and call the method it
returns 12 (correct result)

Can anybody give me some ideas as to what might be different between
the two approaches

I have used FileMon and RegMon to confirm that the file and registry
accesses are identical between the two

Is there some Security, Evidence or Environment differences between
the two approaches I need to resolve?
 
J

John Rivers

here is the code:

reflection version: (no errors but returns 0 for count)

int count;
string[] engines;

Assembly assConnDlg = Assembly.LoadFile(pathConnDlg);
const string assNamePersonalization =
@"Microsoft.SqlServer.Management.UI.ConnectionDlg.Personalization";
const string typeNameSqlServerType =
@"Microsoft.SqlServer.Management.UI.ConnectionDlg.SqlServerType";
Type typePersonalization = assConnDlg.GetType
(assNamePersonalization);
Guid guidServerType = (Guid)assConnDlg.GetType
(typeNameSqlServerType).GetField("ServerType").GetValue(null);
int count;
string[] engines;
count = (int)typePersonalization.InvokeMember
("GetCountByServerType", System.Reflection.BindingFlags.InvokeMethod,
null, null, new object[] { guidServerType });
engines = new string[count];
typePersonalization.InvokeMember("GetStringsByServerType",
System.Reflection.BindingFlags.InvokeMethod, null, null, new object[]
{ guidServerType, engines, count });

non reflection version: (works ok returns 12 for count and populates
engines correctly)

count = Personalization.GetCountByServerType
(SqlServerType.ServerType);
engines = new string[count];
Personalization.GetStringsByServerType(SqlServerType.ServerType,
engines, count);
 
P

Peter Duniho

John said:
Hi

I am calling the same static method but getting different results

Same static method with different results means different inputs.
Assuming it's really the same static method, and the static method has
no other inputs than those you pass to it (a safe bet), you must be
passing different inputs.
if I use Assembly.LoadFile() and then reflection to call the method it
returns 0 (wrong result)

if instead I add a reference to the same DLL and call the method it
returns 12 (correct result)

Can anybody give me some ideas as to what might be different between
the two approaches

You are in the best position to judge that. Step through the code in
the debugger, checking each and every value returned by reflection, and
see where it's different from the values you pass in the non-reflection
case.
I have used FileMon and RegMon to confirm that the file and registry
accesses are identical between the two

Is there some Security, Evidence or Environment differences between
the two approaches I need to resolve?

It is almost guaranteed to be a bug in your code. You are either not
calling the same code when you think you are, or you are passing
different values to the same code when you think you aren't.

If you are 100% sure it's not a bug in your code and still need help
figuring it, you should post a concise-but-complete code example that
reliably demonstrates the problem. Then someone else can actually
reproduce the issue on their own computer and help you figure it out.

Pete
 
J

Jesse Houwing

* John Rivers wrote, On 10-11-2009 0:42:
here is the code:

reflection version: (no errors but returns 0 for count)

int count;
string[] engines;

Assembly assConnDlg = Assembly.LoadFile(pathConnDlg);
const string assNamePersonalization =
@"Microsoft.SqlServer.Management.UI.ConnectionDlg.Personalization";
const string typeNameSqlServerType =
@"Microsoft.SqlServer.Management.UI.ConnectionDlg.SqlServerType";
Type typePersonalization = assConnDlg.GetType
(assNamePersonalization);
Guid guidServerType = (Guid)assConnDlg.GetType
(typeNameSqlServerType).GetField("ServerType").GetValue(null);
int count;
string[] engines;
count = (int)typePersonalization.InvokeMember
("GetCountByServerType", System.Reflection.BindingFlags.InvokeMethod,
null, null, new object[] { guidServerType });
engines = new string[count];
typePersonalization.InvokeMember("GetStringsByServerType",
System.Reflection.BindingFlags.InvokeMethod, null, null, new object[]
{ guidServerType, engines, count });

In my experience it helps to pass BindingFLags.Instance |
BindingFlags.Public as well for instance based methods. And
BindingFlags.Static | BindingFlags.Public for static methods.
non reflection version: (works ok returns 12 for count and populates
engines correctly)

count = Personalization.GetCountByServerType
(SqlServerType.ServerType);
engines = new string[count];
Personalization.GetStringsByServerType(SqlServerType.ServerType,
engines, count);

Whay I don't understand is why you're using reflection for these method
calls. These are wellknown assemblies, so you should just be able to use
their respective interfaces directly. Or am I missing something?
 
J

John Rivers

Whay I don't understand is why you're using reflection for these method
calls. These are wellknown assemblies, so you should just be able to use
their respective interfaces directly. Or am I missing something?

These are my reasons:

- There is no guarantee that SQL Server Client or Server tools are
installed on the target machine, this application does not require
them - just uses them to get the MRU list of database engines if they
are there
- These assemblies are private to SSMS therefore not in the GAC so I
have to locate them manually (through the registry)
- There may be any combination of diffferent versions of SQL Server on
the target machine - I need to load different assemblies from
different locations based on versions installed

In other words if I release an application into the wild with a early
bound reference to ConnectionDlg it could stop the whole application
from running

So I am using Assembly.LoadFile() to load the DLL only *if* it exists
in the default location - now I am forced to use reflection to invoke
methods etc.

(as far as I know anyway)
 
J

John Rivers

In my experience it helps to pass BindingFlags.Instance | BindingFlags.Public as well for instance based methods.
And BindingFlags.Static | BindingFlags.Public for static methods.

I tried that also:

BindingFlags bindFlags = BindingFlags.Public | BindingFlags.Static |
BindingFlags.InvokeMethod;
count = (int)typePersonalization.InvokeMember("GetCountByServerType",
bindFlags, null, null, new object[] { guidServerType });

But same result :-(

I am certain there is some difference between an early bound assembly
and a late bound assembly that is causing this
 
P

Patrice

Hi,
count = (int)typePersonalization.InvokeMember("GetCountByServerType",
bindFlags, null, null, new object[] { guidServerType });

Have you tried to print out guidServerType to see if this is the same value
in both cases ?
 
J

John Rivers

Have you tried to print out guidServerType to see if this is the same value
in both cases ?

Yes - the GUID is correct - I even tested it with a GUID literal I
copied from the working code

That is why I am certain the reason is related to Assembly.LoadFile()
or Assembly.LoadFrom() (I have tried both)

When I look at ConnectionDlg.dll in Reflector it has many assembly
references - I guess it is possible there is a silent failure
due to failing assembly reference ...
 
P

Patrice

When I look at ConnectionDlg.dll in Reflector it has many assembly
references - I guess it is possible there is a silent failure
due to failing assembly reference ...

Rather than reusing a private DLL that comes with the IDE, my personal
preference would be to use
http://msdn.microsoft.com/en-us/lib...o.smoapplication.enumavailablesqlservers.aspx
to enumerate servers and would handle my own MRU...

Also this part is redistributable so you could have the same user experience
regardless of what is installed on the particular PC you are running on (and
you don't need any more to use reflection).

Do you specifically want to reuse SSMS MRU list or do you want just to have
a way to enumerate servers ?
 
P

Peter Duniho

John said:
[...]
I am certain there is some difference between an early bound assembly
and a late bound assembly that is causing this

Well, of course there is. But the question is, does that difference
exist in your code, or is in inherent in using reflection versus not
using reflection? My money's on the former. Perhaps you're simply not
using the right reflection code. Or maybe you've failed to set up some
environmental condition that would normally exist for the "early-bound"
case. In any case, done properly reflection will have the precise
effect as calling a compiled-in target (not counting the performance
overhead, of course).

But, until you post a concise-but-complete code example that reliably
reproduces the issue, it won't be possible for anyone else to know for sure.

Pete
 
J

John Rivers

Rather than reusing a private DLL that comes with the IDE, my personal
preference would be to usehttp://msdn.microsoft.com/en-us/library/microsoft.sqlserver.managemen...
to enumerate servers and would handle my own MRU...

Do you specifically want to reuse SSMS MRU list or do you want just to have
a way to enumerate servers ?

It is a nice to have feature - like in SQL Compare - it is the MRU
that is mostly likely to contain relevant engines for a connection
I want users to connect to a server using SSMS and then switch to my
tool and have an easy connection workflow (ie: double click)

My complete list of engines will be:

- mru
- local instances (from registry)
- registered servers (from smo)
- local lan servers (from smo - updated if user requests or in
background)

I think this puzzle is worth solving as the solution may be generally
applicable to Assembly.LoadFile()
 
J

John Rivers

But, until you post a concise-but-complete code example that reliably
reproduces the issue, it won't be possible for anyone else to know for sure.

The second post in this thread contains the reflection and non-
reflection code

this is the path for ConnectionDlg.dll in SQL Server 2005

%ProgramFiles%\Microsoft SQL Server\90\Tools\Binn\VSShell\Common7\IDE
\ConnectionDlg.dll

I also tried this but it is never raised:

assConnDlg.ModuleResolve += new ModuleResolveEventHandler
(assConnDlg_ModuleResolve);
 
J

John Rivers

The second post in this thread contains the reflection and non-
reflection code  [...]

You may find these links helpful:http://www.yoda.arachsys.com/csharp...s.com/csharp/incomplete.htmlhttp://sscce.org/

I understand the point you are trying to make

but this code *will* copy and paste into a method body

and the only thing that can't be copied and pasted - eg. a file
reference to an assembly I have given the typical path for

the only thing I missed out was the line:
using Microsoft.SqlServer.Management.UI.ConnectionDlg;

which is implied anyway in the string constant
"Microsoft.SqlServer.Management.UI.ConnectionDlg.Personalization"
and has to be commented out for the reflection approach anyway

anybody who couldn't copy and paste that and get it to compile doesn't
understand the code and won't be any help anyway!

so good point but it doesn't apply to this post
 
P

Peter Duniho

John said:
I understand the point you are trying to make

I don't think so.
[...]
anybody who couldn't copy and paste that and get it to compile doesn't
understand the code and won't be any help anyway! [...]

Feel free to believe that if you like. It's only yourself who will be
affected negatively.

Pete
 
J

John Rivers

How do you propose I solve the problem of specifying the correct
project assembly references?

The only convenient solution is to upload two separate Visual Studio
projects
(which Google Groups does not support)

but if I do that:

- they may be using a different version of Visual Studio
- they may be using a different IDE altogether
- as I am using file references they will likely be wrong for them
anyway

I posted the exact correct amount of code - no more and no less

If they want to compile it - paste it into a method body, setup the
reference to *their* ConnectionDlg.dll
and hit F5

Frankly anybody who is not capable of that would find my post
unintelligible anyway
 
P

Peter Duniho

John said:
How do you propose I solve the problem of specifying the correct
project assembly references?

There are two possibilities:

-- The assemblies are readily available, already installed, on each
reader's computer

-- The assemblies are not readily available, never mind already
installed, on each reader's computer

If the latter, then obviously the code you've posted so far is insufficient.

If the former, then obviously you need to include as part of your
concise-but-complete code example a clear explanation for how to include
those assemblies as part of your program (e.g. their exact name so they
can be used as a reference for the project, as well as loaded directly
via reflection).

It's quite simple. But neither answer has any relevance at all until
there's a proper _code_ example to compile/use with said assemblies. As
yet, there isn't one.
The only convenient solution is to upload two separate Visual Studio
projects
(which Google Groups does not support)

I really don't see why it's not convenient to simply name the
assemblies. How does uploading a binary file help?

Frankly, this business of "assembly references" is a complete red
herring. If _all_ that was missing from your code example was assembly
references, perhaps that would be worth discussing. But your code
example is missing much more.
but if I do that:

- they may be using a different version of Visual Studio
- they may be using a different IDE altogether
- as I am using file references they will likely be wrong for them
anyway

I posted the exact correct amount of code - no more and no less

No. You posted MUCH less than the "exact correct amount of code".
If they want to compile it - paste it into a method body, setup the
reference to *their* ConnectionDlg.dll
and hit F5

Frankly anybody who is not capable of that would find my post
unintelligible anyway

Same answer to that particular false assumption still applies.

Pete
 
F

Family Tree Mike

John said:
If they want to compile it - paste it into a method body, setup the
reference to *their* ConnectionDlg.dll
and hit F5

Is this intended only to work with SQL 2005? The SQL 2008
ConnectionDlg.dll does not appear to have the class that you are trying
to instantiate
(Microsoft.SqlServer.Management.UI.ConnectionDlg.Personalization).
Maybe I am missing something as I haven't tried to use this dll before.
 
J

John Rivers

Is this intended only to work with SQL 2005?  The SQL 2008
ConnectionDlg.dll does not appear to have the class that you are trying
to instantiate
(Microsoft.SqlServer.Management.UI.ConnectionDlg.Personalization).

Yes - I only have SQL 2005 currently
 
J

John Rivers

There are two possibilities: etc.

Peter

please let me know what is missing - that is what I am not clear about

the only thing that I can see is missing is boilerplate code
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top