Home All Groups Group Topic Archive Search About
Author
1 Dec 2006 9:45 PM
Larry Smith
Hi there,

When I run the following on my app's primary thread:

System.Type.GetType("System.Windows.Forms.Form")

It works as expected. If I then launch a thread via the "BackgroundWorker"
class immediately after the above call, and invoke the above again as soon
as the thread starts, it now returns null instead. Can anyone explain this.
Thanks.

Author
1 Dec 2006 9:57 PM
Tom Porterfield
Larry Smith wrote:
> Hi there,
>
> When I run the following on my app's primary thread:
>
> System.Type.GetType("System.Windows.Forms.Form")
>
> It works as expected. If I then launch a thread via the "BackgroundWorker"
> class immediately after the above call, and invoke the above again as soon
> as the thread starts, it now returns null instead. Can anyone explain
> this. Thanks.

Change the call to System.Type.GetType("System.Windows.Forms.Form", true);

This change will cause it to throw an exception if it can't determine the
type.  The details in the exception might give you an indication as to what
is going wrong.
--
Tom Porterfield
Author
1 Dec 2006 10:07 PM
Larry Smith
Show quote
"Tom Porterfield" <tppor***@mvps.org> wrote in message
news:u0VilOZFHHA.924@TK2MSFTNGP02.phx.gbl...
> Larry Smith wrote:
>> Hi there,
>>
>> When I run the following on my app's primary thread:
>>
>> System.Type.GetType("System.Windows.Forms.Form")
>>
>> It works as expected. If I then launch a thread via the
>> "BackgroundWorker"
>> class immediately after the above call, and invoke the above again as
>> soon
>> as the thread starts, it now returns null instead. Can anyone explain
>> this. Thanks.
>
> Change the call to System.Type.GetType("System.Windows.Forms.Form", true);
>
> This change will cause it to throw an exception if it can't determine the
> type.  The details in the exception might give you an indication as to
> what is going wrong.

Thanks very much. It's returning "System.TypeLoadException". Docs state that
the "... common language runtime cannot find the assembly or the type within
the assembly, or cannot load the type". I see no obvious reason for this
although I do have a form going on the primary thread (why should this
matter though). Can you or anyone else comment? Thanks.
Author
2 Dec 2006 9:25 AM
Michael Nemtsev
Hello Larry,

Have u tried to specify fully qualified name of your assembly?

BTW, have u loaded it before calling GetType?

>>> When I run the following on my app's primary thread:
>>> System.Type.GetType("System.Windows.Forms.Form")
>>>
>>> It works as expected. If I then launch a thread via the
>>> "BackgroundWorker"
>>> class immediately after the above call, and invoke the above again
>>> as
>>> soon
>>> as the thread starts, it now returns null instead. Can anyone
LS> Thanks very much. It's returning "System.TypeLoadException". Docs
LS> state that the "... common language runtime cannot find the assembly
LS> or the type within the assembly, or cannot load the type". I see no

---
WBR,
Michael  Nemtsev [C# MVP] :: blog: http://spaces.live.com/laflour

"At times one remains faithful to a cause only because its opponents do not
cease to be insipid." (c) Friedrich Nietzsche
Author
2 Dec 2006 3:45 PM
Larry Smith
> Hello Larry,
>
> Have u tried to specify fully qualified name of your assembly?

All I have to work with is the full namespace and class name. Again, passing
this to "GetType()" works on the main thread (returning the
assembly-qualified name) but not on a worker thread.

> BTW, have u loaded it before calling GetType?

I'm not sure what I really need to do. I have a form going on the main
thread so the assembly is obviously loaded, i.e., I can get the
fully-qualifed assembly name by passing "System.Windows.Forms.Form" to
"GetType()". Doing this on a worker thread then fails. Ok, based on what Jon
said I'm guessing that this works because the forms assembly is somehow
bound to the main thread but not to the worker thread. Is this right? In any
case, given only "System.Windows.Forms.Form" to work with or any other
native .NET type for that matter, how can you get the fully-qualifed
assembly name even if the assembly isn't loaded. You don't know which
assembly contains it and you only have this string to work with. Thanks for
your help.
Author
2 Dec 2006 6:25 PM
Michael Nemtsev
Hello Larry,

LS> I'm not sure what I really need to do. I have a form going on the
LS> main thread so the assembly is obviously loaded, i.e., I can get the
LS> fully-qualifed assembly name by passing "System.Windows.Forms.Form"
LS> to "GetType()". Doing this on a worker thread then fails. Ok, based
LS> on what Jon said I'm guessing that this works because the forms
LS> assembly is somehow bound to the main thread but not to the worker
LS> thread. Is this right?

Yep, it seems to be the reason of the problem

LS> In any case, given only
LS> "System.Windows.Forms.Form" to work with or any other native .NET
LS> type for that matter, how can you get the fully-qualifed assembly
LS> name even if the assembly isn't loaded. You don't know which
LS> assembly contains it and you only have this string to work with.

Try to load assembly it in your background thread before GetType call
You can end up with that you can't load assembly, then u need to use Reflector/ILDASM
to get the fully-qualifed name for loading
System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089

---
WBR,
Michael  Nemtsev [C# MVP] :: blog: http://spaces.live.com/laflour

"At times one remains faithful to a cause only because its opponents do not
cease to be insipid." (c) Friedrich Nietzsche
Author
1 Dec 2006 10:51 PM
Jon Skeet [C# MVP]
Larry Smith <no_spam@_nospam.com> wrote:
> When I run the following on my app's primary thread:
>
> System.Type.GetType("System.Windows.Forms.Form")
>
> It works as expected. If I then launch a thread via the "BackgroundWorker"
> class immediately after the above call, and invoke the above again as soon
> as the thread starts, it now returns null instead. Can anyone explain this.

I would expect the above call to return null in either thread,
personally - Type.GetType(string) only looks in the currently executing
assembly and mscorlib unless the assembly details are specified.

In the real code, are you doing this with a string variable instead of
the hard-coded type name? If you only ever want to find the above type,
just use typeof(System.Windows.Forms.Form).

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet   Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too
Author
2 Dec 2006 4:08 AM
Larry Smith
> I would expect the above call to return null in either thread,
> personally - Type.GetType(string) only looks in the currently executing
> assembly and mscorlib unless the assembly details are specified.

It does work on the main thread but fails on the worker thread as noted.

> In the real code, are you doing this with a string variable instead of
> the hard-coded type name? If you only ever want to find the above type,
> just use typeof(System.Windows.Forms.Form).

No. I'm actually reading the type on-the-fly from a file. I only have the
fully-qualfied path name to work with as a string
("FullNamespace.ClassName"). I need to generate the assembly-qualified name
from this however. It will always be a native .NET type. Can you steer me in
the right direction. Thanks very much.
Author
2 Dec 2006 7:52 AM
Jon Skeet [C# MVP]
Larry Smith <no_spam@_nospam.com> wrote:
> > I would expect the above call to return null in either thread,
> > personally - Type.GetType(string) only looks in the currently executing
> > assembly and mscorlib unless the assembly details are specified.
>
> It does work on the main thread but fails on the worker thread as noted.

That sounds very odd, and I can't reproduce it. Can you post a short
but complete program which demonstrates it working in the main thread?

> > In the real code, are you doing this with a string variable instead of
> > the hard-coded type name? If you only ever want to find the above type,
> > just use typeof(System.Windows.Forms.Form).
>
> No. I'm actually reading the type on-the-fly from a file. I only have the
> fully-qualfied path name to work with as a string
> ("FullNamespace.ClassName"). I need to generate the assembly-qualified name
> from this however. It will always be a native .NET type. Can you steer me in
> the right direction. Thanks very much.

Well, either the file needs to specify the assembly as well, or if you
have a list of assemblies to check for all types, you could just keep
Assembly references, and call Assembly.GetType on each of them until
you find a match.

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet   Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too
Author
2 Dec 2006 3:32 PM
Larry Smith
Show quote
"Jon Skeet [C# MVP]" <sk***@pobox.com> wrote in message
news:MPG.1fdb4134489a7b6f98d69e@msnews.microsoft.com...
> Larry Smith <no_spam@_nospam.com> wrote:
>> > I would expect the above call to return null in either thread,
>> > personally - Type.GetType(string) only looks in the currently executing
>> > assembly and mscorlib unless the assembly details are specified.
>>
>> It does work on the main thread but fails on the worker thread as noted.
>
> That sounds very odd, and I can't reproduce it. Can you post a short
> but complete program which demonstrates it working in the main thread?
>
>> > In the real code, are you doing this with a string variable instead of
>> > the hard-coded type name? If you only ever want to find the above type,
>> > just use typeof(System.Windows.Forms.Form).
>>
>> No. I'm actually reading the type on-the-fly from a file. I only have the
>> fully-qualfied path name to work with as a string
>> ("FullNamespace.ClassName"). I need to generate the assembly-qualified
>> name
>> from this however. It will always be a native .NET type. Can you steer me
>> in
>> the right direction. Thanks very much.
>
> Well, either the file needs to specify the assembly as well, or if you
> have a list of assemblies to check for all types, you could just keep
> Assembly references, and call Assembly.GetType on each of them until
> you find a match.

Actually, the "file" I'm reading is source code. I'm reading ".cs" files in
a loaded solution (using an AddIn), extracting the name of a class in the
file (including its namespace), and it's this class whose assembly-qualified
name I want to find (its base class actually). The IDE itself is able to do
this without issue (long story) so presumably I should be able to do it as
well (assuming it's not relying on proprietary functions). I'll see if I can
condense this to a simple working example and post it as soon as I can
(perhaps a day or two). Thanks for your help ...
Author
2 Dec 2006 5:55 PM
Jon Skeet [C# MVP]
Larry Smith <no_spam@_nospam.com> wrote:
> > Well, either the file needs to specify the assembly as well, or if you
> > have a list of assemblies to check for all types, you could just keep
> > Assembly references, and call Assembly.GetType on each of them until
> > you find a match.
>
> Actually, the "file" I'm reading is source code. I'm reading ".cs" files in
> a loaded solution (using an AddIn), extracting the name of a class in the
> file (including its namespace), and it's this class whose assembly-qualified
> name I want to find (its base class actually). The IDE itself is able to do
> this without issue (long story) so presumably I should be able to do it as
> well (assuming it's not relying on proprietary functions). I'll see if I can
> condense this to a simple working example and post it as soon as I can
> (perhaps a day or two). Thanks for your help ...

The IDE has a list of referenced assemblies, so can easily ask each of
those assemblies whether or not it contains a type of the given name.

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet   Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too
Author
3 Dec 2006 6:40 PM
Larry Smith
> The IDE has a list of referenced assemblies, so can easily ask each of
> those assemblies whether or not it contains a type of the given name.

Ok, I created a separate test app out-of-the-box and you're right, a call to
"GetType("System.Windows.Forms.Form") does return null on the main thread as
well. In my own app however, it does work on the main thread as discussed.
I'm not entirely sure why without a wider investigation but obviously the
assembly is loaded in my own app (and already bound to the main thread) but
not in the test app (though both apps are running a form at the time of
call). In any case, none of this really matters since I'll be dealing with
arbitrary types anyway (not just forms) and have to proceed as if the
assembly is not loaded. That being the case, do you know what the general
steps are for doing this. Since I'm just reading ".cs" files in a project,
looking for the "Type" of classes defined in these files (their base classes
that is), I'm assuming that for each base class I'm interested in, I can
just iterate all referenced assemblies in the project looking for the class'
type (e.g., "System.Windows.Forms.Form" for instance). By "referenced
assemblies", I mean those assemblies listed under the "References" folder
seen under all projects in Solution Explorer (since all base classes in all
".cs" files must be referenced there presumably - is this correct?). If this
is in fact correct (I believe so), do you know which functions I now need to
be looking at to accomplish this. I already know how to get the list of
assemblies seen under the "References" folder of each project but which
functions do I then call in order to look up a string in the form
"<NameSpace>.<ClassName>" (in order to return its assembly-qualified name).
Author
3 Dec 2006 9:11 PM
Jon Skeet [C# MVP]
Larry Smith <no_spam@_nospam.com> wrote:

<snip>

> I'm assuming that for each base class I'm interested in, I can
> just iterate all referenced assemblies in the project looking for the class'
> type (e.g., "System.Windows.Forms.Form" for instance). By "referenced
> assemblies", I mean those assemblies listed under the "References" folder
> seen under all projects in Solution Explorer (since all base classes in all
> ".cs" files must be referenced there presumably - is this correct?). If this
> is in fact correct (I believe so), do you know which functions I now need to
> be looking at to accomplish this. I already know how to get the list of
> assemblies seen under the "References" folder of each project but which
> functions do I then call in order to look up a string in the form
> "<NameSpace>.<ClassName>" (in order to return its assembly-qualified name).

Well, rather than using the assembly-qualified name, you could load the
assemblies (once) and then call Assembly.GetType on each of the
assemblies.

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet   Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too
Author
4 Dec 2006 2:20 PM
Larry Smith
> Well, rather than using the assembly-qualified name, you could load the
> assemblies (once) and then call Assembly.GetType on each of the
> assemblies.

That definitely looks promising (probably what I'm after). Thanks again for
your help.
Author
4 Dec 2006 11:47 AM
Günter Prossliner
Hello Larry!

>> The IDE has a list of referenced assemblies, so can easily ask each
>> of those assemblies whether or not it contains a type of the given
>> name.

What do you meen by "the IDE" in this context? The IDE doen't matter here.


> ... but obviously the assembly is loaded in my own app (and
> already bound to the main thread)

An Assembly is never bound to a Thread, only to an AppDomain.

Are you absolutly sure that it isn't something in you code that causes the
error? If you are not able to reproduce this, it will be hard (likely
impossible) for anybody (including MS Support Team) to find out what may be
wrong.

> and have to proceed as if the assembly is not
> loaded.

You can easly get any Type-Instance of a Type in a (as far) not loaded
Assembly by specifing the Assembly-Qualified Name.

If the Assembly has not been loaded, it will be loaded by this operation,
and the class (and depend classes) will be initialized.

Of cause the Assembly must be reachable in any way (eigther through GAC
installation or through being within the search-path). It doesn't matter if
the assembly is reference or not. It must be just there.


> That being the case, do you know what the general steps are
> for doing this. Since I'm just reading ".cs" files in a project,

You source .cs files??? This sounds like a compilation time "feature".

> looking for the "Type" of classes defined in these files (their base
> classes that is), I'm assuming that for each base class I'm
> interested in, I can just iterate all referenced assemblies in the
> project looking for the class' type (e.g.,
> "System.Windows.Forms.Form" for instance). By "referenced
> assemblies", I mean those assemblies listed under the "References"
> folder seen under all projects in Solution Explorer

Forget about this IDE things. At runtime there is no IDE.

An example:
You can set a reference to anything you like within your IDE. The referenced
Assemblies will only be included as references within the Assemblies
Manifest if they are _actually used_ by your code.

You can get a List of referenced Assemblies by calling the Assemblies
"GetReferencedAssemblies()" method. But it will only return "AssemblyName"
objects. If you need the "Assembly" Instance you have to go through
AppDomain.GetAssemblies() or through Assembly.Load().


> (since all base
> classes in all ".cs" files must be referenced there presumably - is
> this correct?).

Yes.

> I already know how to get the list of assemblies seen under the
> "References" folder of each project but which functions do I then
> call in order to look up a string in the form
> "<NameSpace>.<ClassName>" (in order to return its assembly-qualified
> name).

From the CLR's point of view the Namespace is just on part of the Assembly's
name (there is no "Namespace" property of the Assembly). What would be
possible (but I won't recomment this) is to get all Types of an Assembly by
it's .GetTypes() method and to lookup the Namespace.ClassName Type within
that list.


Do not use this that way. Just use Assembly Qualified Names and all will
work "automaticly". Do do not have to care about when an Assembly will be
loaded or something like that.

Type t = Type.GetType("System.Windows.Forms.Form, System.Windows.Forms,
Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");


If you do not what to specify this "long and complex" Assembly Qualified
Name (it is realy not very user-friendly ;-)), setup a kind of default
(fallback) logic. If you are dealing with Types from strings there will be a
configuration file (or other form of persistent storage) which holds this
strings.

You can implement a simple "Namespace->Assembly Parameters" mapping:

e.g.

<anyTag>

    <assemblyMappings>

        <assemblyMapping
            namespace="System.Windows.Forms"
            version="2.0"
            culture="neutral"
            publicKeyToken="b77a5...." />

        <assemblyMapping
            namespace="...."
            ...
        />

    </assemblyMappings>

</anyTag>

* If a full assembly qualified name is entered, you don't have to do
anything
* If the name is only "Namespace.Class", you have to lookup within the
"assemblyMappings" to get the other parameters
* If no corresponding "assemblyMapping" is found, throw an Exception
(configuration error).

Now you have the Assembly Qualified Name, which can be passed to
Type.GetType(...).

By using the Architecture, you can avoid all unnessersary Reflection stuff,
looping through Assemblies, References and Types.




GP
Author
4 Dec 2006 3:55 PM
Larry Smith
Thanks for your detailed feedback (appreciated).

> What do you meen by "the IDE" in this context? The IDE doen't matter here.

It does in this case. I'm working on an AddIn which runs in the IDE :)

> An Assembly is never bound to a Thread, only to an AppDomain.

I was really speaking figuratively. That is, the call to "GetType()" returns
the correct value when invoked from my app's main thread but not a worker
thread. Since a form is running on the main thread at the time I invoke
"GetType()", and I'm passing "System.Windows.Forms.Form" to this (and the
function succeeds), it seems likely that some type of affinity must exist
between the loaded (forms) assembly and the main thread. Otherwise, why does
the same call fail on a worker thread.

> Are you absolutly sure that it isn't something in you code that causes the
> error? If you are not able to reproduce this, it will be hard (likely
> impossible) for anybody (including MS Support Team) to find out what may
> be wrong.

The behaviour is consistent (reproducible) in my own app. If I call
"GetType()" on my app's primary thread and it succeeds, and then I
immediately lauch my thread on the very next line and call "GetType()" again
(as soon as the thread starts), why should it now fail? I don't see what I
could be doing that would cause this.

> You can easly get any Type-Instance of a Type in a (as far) not loaded
> Assembly by specifing the Assembly-Qualified Name

I don't have the Assembly-Qualified name which is the issue. That's what I'm
trying to find given only a namespace and class name.

> You source .cs files??? This sounds like a compilation time "feature".

Exactly.

> Forget about this IDE things. At runtime there is no IDE.

I'm writing an AddIn which runs in the IDE.

>
> An example:
> You can set a reference to anything you like within your IDE. The
> referenced Assemblies will only be included as references within the
> Assemblies Manifest if they are _actually used_ by your code.
>
> You can get a List of referenced Assemblies by calling the Assemblies
> "GetReferencedAssemblies()" method. But it will only return "AssemblyName"
> objects. If you need the "Assembly" Instance you have to go through
> AppDomain.GetAssemblies() or through Assembly.Load().

This AddIn is targetting a project in someone else's solution (reading the
source files of each project in the solution). I have to target the
referenced assemblies in each project that the AddIn is processing. The .NET
extensibility API allows you to read the "References" folder for any project
in Solution Explorer. From this I can get the referenced assemblies, load
them one-by-one and invoke "Assembly.GetType()" as Jon suggested in his last
post. I think that may solve the problem.

> From the CLR's point of view the Namespace is just on part of the
> Assembly's name (there is no "Namespace" property of the Assembly). What
> would be possible (but I won't recomment this) is to get all Types of an
> Assembly by it's .GetTypes() method and to lookup the Namespace.ClassName
> Type within that list.

I was going to try that but I think "Assembly.GetType()" will do what I need
instead (again, as suggested by Jon). Otherwise, what other choice do I have
but to manually search all types returned by "GetTypes()".

> Do not use this that way. Just use Assembly Qualified Names and all will
> work "automaticly". Do do not have to care about when an Assembly will be
> loaded or something like that.

While I appreciate your recommendation (detailed in the remainder of your
post), it won't work if I understand you correctly. You likely didn't
appreciate that nature of my app however (since I really haven't spelled out
much detail). The app will be running in the IDE as a VS AddIn. It has to
read ".cs" files in the currently loaded solution, identify classes in these
files (whose types I don't know ahead of time), and then retrieve the
assembly-qualified name of these classes (given only the namespace and class
name read from the ".cs" file). I think the method for doing this is now
sinking in. As I read each class in a given ".cs" file (its base class
actually), I can then iterate the project's references and load each
assembly one-by-one, invoking "Assembly.GetType()" until it returns
non-null. Would you agree this is the corret approach (it now appears so).

Thanks again for your help (any further comments welcome).
Author
4 Dec 2006 5:41 PM
Günter Prossliner
Hi Larry!

>> What do you meen by "the IDE" in this context? The IDE doen't matter
>> here.
>
> It does in this case. I'm working on an AddIn which runs in the IDE :)

This makes most of my posting obsolete ;-) Sorry for that!

Ist the problem reproducable by an mini-AddIn?



GP
Author
4 Dec 2006 5:58 PM
Larry Smith
"Günter Prossliner" <g.prossliner/gmx/at> wrote in message
news:%2366tvt8FHHA.1188@TK2MSFTNGP06.phx.gbl...
> Hi Larry!
>
>>> What do you meen by "the IDE" in this context? The IDE doen't matter
>>> here.
>>
>> It does in this case. I'm working on an AddIn which runs in the IDE :)
>
> This makes most of my posting obsolete ;-) Sorry for that!
>
> Ist the problem reproducable by an mini-AddIn?

I haven't tried it that way, only in a standalone (test) app where it fails
on the primary thread as mentioned in an earlier post. Why it works in my
AddIn is anymone's guess at this point. It probably doesn't matter as much
anymore however since it really needs to be done the way I mentioned in my
last post (AFAIK at this point). I almost certainly will investigate this
further at some later point however. I need to ensure it's not symptomatic
of some other other problem that could come back to bite me. Anyway, thanks
again for your help (very much appreciated).
Author
5 Dec 2006 11:10 PM
Ben Voigt
Show quote
"Larry Smith" <no_spam@_nospam.com> wrote in message
news:%23a0FG38FHHA.1252@TK2MSFTNGP02.phx.gbl...
>
> "Günter Prossliner" <g.prossliner/gmx/at> wrote in message
> news:%2366tvt8FHHA.1188@TK2MSFTNGP06.phx.gbl...
>> Hi Larry!
>>
>>>> What do you meen by "the IDE" in this context? The IDE doen't matter
>>>> here.
>>>
>>> It does in this case. I'm working on an AddIn which runs in the IDE :)
>>
>> This makes most of my posting obsolete ;-) Sorry for that!
>>
>> Ist the problem reproducable by an mini-AddIn?
>
> I haven't tried it that way, only in a standalone (test) app where it
> fails on the primary thread as mentioned in an earlier post. Why it works
> in my AddIn is anymone's guess at this point. It probably doesn't matter
> as much anymore however since it really needs to be done the way I
> mentioned in my last post (AFAIK at this point). I almost certainly will
> investigate this further at some later point however. I need to ensure
> it's not symptomatic of some other other problem that could come back to
> bite me. Anyway, thanks again for your help (very much appreciated).

I think you have the right way of doing it now, I would just additionally
suggest that you cache the result of the lookup (whether success or failure)
to avoid searching for the same name over and over.

Also, are you handling "using" directives?  Remember there are two kinds of
using directive, one which adds a namespace to the search scope, and one
which introduces an alias for a single type.
Author
5 Jan 2007 9:35 PM
cody
>> Ist the problem reproducable by an mini-AddIn?
>
> I haven't tried it that way, only in a standalone (test) app where it fails
> on the primary thread as mentioned in an earlier post. Why it works in my
> AddIn is anymone's guess at this point. It probably doesn't matter as much
> anymore however since it really needs to be done the way I mentioned in my
> last post (AFAIK at this point). I almost certainly will investigate this
> further at some later point however. I need to ensure it's not symptomatic
> of some other other problem that could come back to bite me. Anyway, thanks
> again for your help (very much appreciated).

Does you test app have a reference to the System.Windows.Forms.dll?

AddThis Social Bookmark Button