C# DLL with non .NET clients, getting ESP errors

D

dev

I have rewritten a COM DLL into C#, with a number of interface
functions, and a number of events that can be fired back to the
client(s). I can call it from a VB (6) client without problems.
However, when I try to use a C++ client (still visual studio 6) that
worked with the old DLL, any call to an interface function gets the
error "The value of ESP was not properly saved across a function
call."

A couple of example functions would be :
(C#)
int DialDigits([MarshalAs(UnmanagedType.BStr)]string
bsUserId,
[MarshalAs(UnmanagedType.BStr)]string szDigits);
int QueryStatusString([MarshalAs(UnmanagedType.BStr)] ref
string pbsStatus);
(C++)
m_pInterface->DialDigits(_bstr_t("1001").copy(),
_bstr_t("01234567890").copy());
BSTR bsError;
if ( m_pTapi->QueryStatusString( &bsError ) ==
S_OK ) ....
Both of these function calls get the same error.

The VB client doesn't seem to care whether the C# parameters are
marshalled or not, they just works, but I added the marshalling as
hinted by some other web site, but that didn't help in this C++ case.
Is there anything wrong with either my C#' interface definition, or
is
something special required in order to call a .NET COM dll from
unmanaged code ?

I can post the full interface if necessary, but it is a big lengthy.
I actually converted the IDL to C# by hand, but when it worked from
VB, I assumed I'd got it all right, but now I'm not so sure. Can
anyone help ?

I'm wondering if the issue is anything to do with the return type of
the
functions. In the old C++ DLL they were defined as STDMETHODIMP
(i.e. returning an HRESULT). What type should they therefore be in
C# ?

- Tim
 
D

dev

Thanks for the quick reply. I don't really want to rewrite/recreate my
interface if I don't have to,
as its mostly done, and seems to be working well with a VB client. I
am still hoping that
it just needs a minor 'tweak' to make it work with a C++ client. I
will see if I can create
a simple example to post, as you suggested.

In the meantime, does anyone how to generate C# code from a tlb file
(using tlbimp or another
tool)? Some people have mentioned it, but so far all I've managed to
generate is another DLL.

- Tim
 
A

Arne Vajhøj

In the meantime, does anyone how to generate C# code from a tlb file
(using tlbimp or another
tool)? Some people have mentioned it, but so far all I've managed to
generate is another DLL.

That is what you need.

:)

The generated DLL is a .NET assembly which you just
add a ref to similar to any other .NET assembly.

And you can then you the types in the .NET assembly
in your C# code.

Arne
 
A

Arne Vajhøj

That is what you need.

:)

The generated DLL is a .NET assembly which you just
add a ref to similar to any other .NET assembly.

And you can then you the types in the .NET assembly
in your C# code.

Example.

VBS usage:

Set test = CreateObject("Test.CoTest")
WScript.Echo CStr(test.Add(123, 456))
WScript.Echo test.Concat("ABC", "XYZ")

C# usage with ref til DLL fra TLB import:

CoTest cotest = new CoTestClass();
ITest test = (ITest)cotest;
Console.WriteLine(test.Add(123, 456));
Console.WriteLine(test.Concat("ABC", "XYZ"));

C# usage without TLB import but instead using the C# 4.0 dynamic keyword:

dynamic test =
Activator.CreateInstance(Type.GetTypeFromProgID("Test.CoTest"));
Console.WriteLine(test.Add(123, 456));
Console.WriteLine(test.Concat("ABC", "XYZ"));

The examples are not completely equivalent as the C# via TLB import
does not go through IDispatch as the other two does.

Arne
 
A

Arne Vajhøj

I have rewritten a COM DLL into C#, with a number of interface
functions, and a number of events that can be fired back to the
client(s). I can call it from a VB (6) client without problems.
However, when I try to use a C++ client (still visual studio 6) that
worked with the old DLL, any call to an interface function gets the
error "The value of ESP was not properly saved across a function
call."

A couple of example functions would be :
(C#)
int DialDigits([MarshalAs(UnmanagedType.BStr)]string
bsUserId,
[MarshalAs(UnmanagedType.BStr)]string szDigits);
int QueryStatusString([MarshalAs(UnmanagedType.BStr)] ref
string pbsStatus);
(C++)
m_pInterface->DialDigits(_bstr_t("1001").copy(),
_bstr_t("01234567890").copy());
BSTR bsError;
if ( m_pTapi->QueryStatusString(&bsError ) ==
S_OK ) ....
Both of these function calls get the same error.

The VB client doesn't seem to care whether the C# parameters are
marshalled or not, they just works, but I added the marshalling as
hinted by some other web site, but that didn't help in this C++ case.
Is there anything wrong with either my C#' interface definition, or
is
something special required in order to call a .NET COM dll from
unmanaged code ?

I can post the full interface if necessary, but it is a big lengthy.
I actually converted the IDL to C# by hand, but when it worked from
VB, I assumed I'd got it all right, but now I'm not so sure. Can
anyone help ?

I'm wondering if the issue is anything to do with the return type of
the
functions. In the old C++ DLL they were defined as STDMETHODIMP
(i.e. returning an HRESULT). What type should they therefore be in
C# ?

There is way too much going on behind the scene for .NET-COM
interop for manual conversion to be a good conversion.

TLB import or using dynamic are the ways to go in C#.

(dynamic requires C# 4.0 or higher)

See examples in other post.

Arne
 
D

dev

I have rewritten a COM DLL into C#, with a number of interface
functions, and a number of events that can be fired back to the
client(s). I can call it from a VB (6) client without problems.
However, when I try to use a C++ client (still visual studio 6) that
worked with the old DLL, any call to an interface function gets the
error "The value of ESP was not properly saved across a function
call."
A couple of example functions would be :
(C#)
         int DialDigits([MarshalAs(UnmanagedType.BStr)]string
bsUserId,
[MarshalAs(UnmanagedType.BStr)]string szDigits);
         int QueryStatusString([MarshalAs(UnmanagedType.BStr)] ref
string pbsStatus);
(C++)
                 m_pInterface->DialDigits(_bstr_t("1001").copy(),
_bstr_t("01234567890").copy());
                 BSTR bsError;
                 if ( m_pTapi->QueryStatusString(&bsError ) ==
S_OK ) ....
Both of these function calls get the same error.
The VB client doesn't seem to care whether the C# parameters are
marshalled or not, they just works, but I added the marshalling as
hinted by some other web site, but that didn't help in this C++ case.
Is there anything wrong with either my C#' interface definition, or
is
something special required in order to call a .NET COM dll from
unmanaged code ?
I can post the full interface if necessary, but it is a big lengthy.
I actually converted the IDL to C# by hand, but when it worked from
VB, I assumed I'd got it all right, but now I'm not so sure.  Can
anyone help ?
I'm wondering if the issue is anything to do with the return type of
the
functions. In the old C++ DLL they were defined as STDMETHODIMP
(i.e. returning an HRESULT). What type should they therefore be in
C# ?

There is way too much going on behind the scene for .NET-COM
interop for manual conversion to be a good conversion.

TLB import or using dynamic are the ways to go in C#.

(dynamic requires C# 4.0 or higher)

See examples in other post.

Arne

Thanks for all the replies.

However, just to be clear, the C# here is the server, not a
replacement client.

How would a tlb import work in that situation ?

- Tim
 
A

Arne Vajhøj

I have rewritten a COM DLL into C#, with a number of interface
functions, and a number of events that can be fired back to the
client(s). I can call it from a VB (6) client without problems.
However, when I try to use a C++ client (still visual studio 6) that
worked with the old DLL, any call to an interface function gets the
error "The value of ESP was not properly saved across a function
call."
A couple of example functions would be :
(C#)
int DialDigits([MarshalAs(UnmanagedType.BStr)]string
bsUserId,
[MarshalAs(UnmanagedType.BStr)]string szDigits);
int QueryStatusString([MarshalAs(UnmanagedType.BStr)] ref
string pbsStatus);
(C++)
m_pInterface->DialDigits(_bstr_t("1001").copy(),
_bstr_t("01234567890").copy());
BSTR bsError;
if ( m_pTapi->QueryStatusString(&bsError ) ==
S_OK ) ....
Both of these function calls get the same error.
The VB client doesn't seem to care whether the C# parameters are
marshalled or not, they just works, but I added the marshalling as
hinted by some other web site, but that didn't help in this C++ case.
Is there anything wrong with either my C#' interface definition, or
is
something special required in order to call a .NET COM dll from
unmanaged code ?
I can post the full interface if necessary, but it is a big lengthy.
I actually converted the IDL to C# by hand, but when it worked from
VB, I assumed I'd got it all right, but now I'm not so sure. Can
anyone help ?
I'm wondering if the issue is anything to do with the return type of
the
functions. In the old C++ DLL they were defined as STDMETHODIMP
(i.e. returning an HRESULT). What type should they therefore be in
C# ?

There is way too much going on behind the scene for .NET-COM
interop for manual conversion to be a good conversion.

TLB import or using dynamic are the ways to go in C#.

(dynamic requires C# 4.0 or higher)

See examples in other post.
However, just to be clear, the C# here is the server, not a
replacement client.

How would a tlb import work in that situation ?

Ah.

That is a complete different scenario.

Sorry for misunderstanding completely.

For server you can either:
a) write both interfaces and classes from scratch and try to add
all the necessary attributes.
b) tlbimp and just write the classes from scratch implementing the
generated interfaces

Again I will consider solution #a for unsuitable for more complex
cases. But if you go this route then you can use tlbexp to generate
a new tlb and use a tool to compare the content of old and the new tlb.

But option #b should be the way to go.

You may find the book "COM and .NET Interoperability"
by Andrew Troelsen very useful.

Chapter 10 covers #a above and chapter 12 covers #b above.

The book used to be available for download.

Arne
 
A

Arne Vajhøj

On 3/6/2012 10:18 AM, dev wrote:
I have rewritten a COM DLL into C#, with a number of interface
functions, and a number of events that can be fired back to the
client(s). I can call it from a VB (6) client without problems.
However, when I try to use a C++ client (still visual studio 6) that
worked with the old DLL, any call to an interface function gets the
error "The value of ESP was not properly saved across a function
call."

A couple of example functions would be :
(C#)
int DialDigits([MarshalAs(UnmanagedType.BStr)]string
bsUserId,
[MarshalAs(UnmanagedType.BStr)]string szDigits);
int QueryStatusString([MarshalAs(UnmanagedType.BStr)] ref
string pbsStatus);
(C++)
m_pInterface->DialDigits(_bstr_t("1001").copy(),
_bstr_t("01234567890").copy());
BSTR bsError;
if ( m_pTapi->QueryStatusString(&bsError ) ==
S_OK ) ....
Both of these function calls get the same error.

The VB client doesn't seem to care whether the C# parameters are
marshalled or not, they just works, but I added the marshalling as
hinted by some other web site, but that didn't help in this C++ case.
Is there anything wrong with either my C#' interface definition, or
is
something special required in order to call a .NET COM dll from
unmanaged code ?

I can post the full interface if necessary, but it is a big lengthy.
I actually converted the IDL to C# by hand, but when it worked from
VB, I assumed I'd got it all right, but now I'm not so sure. Can
anyone help ?

I'm wondering if the issue is anything to do with the return type of
the
functions. In the old C++ DLL they were defined as STDMETHODIMP
(i.e. returning an HRESULT). What type should they therefore be in
C# ?

There is way too much going on behind the scene for .NET-COM
interop for manual conversion to be a good conversion.

TLB import or using dynamic are the ways to go in C#.

(dynamic requires C# 4.0 or higher)

See examples in other post.
However, just to be clear, the C# here is the server, not a
replacement client.

How would a tlb import work in that situation ?

Ah.

That is a complete different scenario.

Sorry for misunderstanding completely.

For server you can either:
a) write both interfaces and classes from scratch and try to add
all the necessary attributes.
b) tlbimp and just write the classes from scratch implementing the
generated interfaces

Again I will consider solution #a for unsuitable for more complex
cases. But if you go this route then you can use tlbexp to generate
a new tlb and use a tool to compare the content of old and the new tlb.

But option #b should be the way to go.

You may find the book "COM and .NET Interoperability"
by Andrew Troelsen very useful.

Chapter 10 covers #a above and chapter 12 covers #b above.

The book used to be available for download.

Hey. It still is.

http://www.theserverside.net/tt/articles/showarticle.tss?id=ComAndDotNetInterop_Book

Arne
 
D

dev

Okay, the solution turned out to be fairly simple ...... after all
that.

It was that the #import needed to have a "no_namespace" option on it,
so that the definition matched that of the original COM server.

After a bit of fiddling with marshalling and types, I'm now calling my
COM
server functions correctly.

However, I'm still wasn't getting callbacks arriving, and I think I'll
post that
as a separate thread.

Many thanks to all those who responded.

- Tim
 

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