Trouble converting C# class into COM

E

Eric

hi,

I want to convert a C# class into COM, so that I can use the class in C++.
The codes compile and link well. But when I run the program, I got a
exception.
Any comment is welcome. Thanks in advance.

Eric


----------------------------------------------------------------------
in C#, compile into a COM :

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Collections;

namespace SonicBase
{
public delegate int dProc(byte[] buf, int offset, int size);
public class MyClass : IMyClass
{

private dProc myProc = null;

bool StartServer(dProc dlgt)
{
myProc = dlgt;

byte[] buf = new byte[100];
int ret = myProc(buf, 0, 100);

return (ret > 0);
}

bool SonicBase.IMyClass.StartServer(dProc dlgt)
{
return false;
}

}

public interface IMyClass
{
bool StartServer(dProc dlgt);
}
}

--------------------------------------------------------------------------
in C++, use the COM :

// To use managed-code servers like the C# server,
// we have to import the common language runtime:
#import "mscorlib.tlb" raw_interfaces_only

#import "EricBase.tlb" no_namespace named_guids

extern "C"
int ProcData(char* buf, int offset, int size)
{
return 0;
}

void CMainFrame::OnTest()
{
CoInitialize(NULL);

IMyClass* icp = NULL;
HRESULT hr = CoCreateInstance(CLSID_MyClass,
NULL, CLSCTX_INPROC_SERVER,
IID_IMyClass, reinterpret_cast<void**>(&icp));


DWORD count=0;
AllocConsole();
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
WriteConsole(hOut, "a\n", 2, &count, NULL);

if(FAILED(hr))
{
AfxMessageBox("fail create");
return;
}

try
{
_dProc* dlgt = (_dProc*)ProcData;
icp->StartServer(dlgt);
}
catch(_com_error e)
{
CString strInfo = e.ErrorMessage();
}

WriteConsole(hOut, "b\n", 2, &count, NULL);

CoUninitialize();
}
 
W

Willy Denoyette [MVP]

Hmm... this is tricky stuff.
You can't pass a C function ptr and use it directly as a delegate pointer,
you have to pass it as a long (C/C++) and marshal this value as a
FuctionPtr.
Here's what you should do.

1. change you C# code..

public interface IMyClass
{
bool StartServer(dProc dlgt);
}
into:

public interface IMyClass
{
bool StartServer([MarshalAs(UnmanagedType.FunctionPtr)]dProc dlgt);
}

2. Your C++ code:

icp->StartServer(dlgt);
into:
// reinterpret_cast - You know what you are doing, don't you?
icp->StartServer(reinterpret_cast<long>(dlgt));

Why do you implement an explicit interface for the method, there is no need
to do this just declare your interface methods as public. Or am I missing
something?

Willy.
 
E

Eric

Hi, Willy,

After modifying the code, I got another exception saying that "invalid
agument", I guessed there would be some mismatch between delegate dProc and
functionPtr ProcData, but I don't know what's wrong.

I looked into the MSDN, it gaves only the samples using explicit
interface. Would you please give me a sample without an explicit interface?
Using interface to export a C# class into COM, is troublesome. It would be a
great pleasure not using the explicit interface here.

Thanks,
Eric


Willy Denoyette said:
Hmm... this is tricky stuff.
You can't pass a C function ptr and use it directly as a delegate pointer,
you have to pass it as a long (C/C++) and marshal this value as a
FuctionPtr.
Here's what you should do.

1. change you C# code..

public interface IMyClass
{
bool StartServer(dProc dlgt);
}
into:

public interface IMyClass
{
bool StartServer([MarshalAs(UnmanagedType.FunctionPtr)]dProc dlgt);
}

2. Your C++ code:

icp->StartServer(dlgt);
into:
// reinterpret_cast - You know what you are doing, don't you?
icp->StartServer(reinterpret_cast<long>(dlgt));

Why do you implement an explicit interface for the method, there is no need
to do this just declare your interface methods as public. Or am I missing
something?

Willy.


Eric said:
hi,

I want to convert a C# class into COM, so that I can use the class in C++.
The codes compile and link well. But when I run the program, I got a
exception.
Any comment is welcome. Thanks in advance.

Eric


----------------------------------------------------------------------
in C#, compile into a COM :

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Collections;

namespace SonicBase
{
public delegate int dProc(byte[] buf, int offset, int size);
public class MyClass : IMyClass
{

private dProc myProc = null;

bool StartServer(dProc dlgt)
{
myProc = dlgt;

byte[] buf = new byte[100];
int ret = myProc(buf, 0, 100);

return (ret > 0);
}

bool SonicBase.IMyClass.StartServer(dProc dlgt)
{
return false;
}

}

public interface IMyClass
{
bool StartServer(dProc dlgt);
}
}

--------------------------------------------------------------------------
in C++, use the COM :

// To use managed-code servers like the C# server,
// we have to import the common language runtime:
#import "mscorlib.tlb" raw_interfaces_only

#import "EricBase.tlb" no_namespace named_guids

extern "C"
int ProcData(char* buf, int offset, int size)
{
return 0;
}

void CMainFrame::OnTest()
{
CoInitialize(NULL);

IMyClass* icp = NULL;
HRESULT hr = CoCreateInstance(CLSID_MyClass,
NULL, CLSCTX_INPROC_SERVER,
IID_IMyClass, reinterpret_cast<void**>(&icp));


DWORD count=0;
AllocConsole();
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
WriteConsole(hOut, "a\n", 2, &count, NULL);

if(FAILED(hr))
{
AfxMessageBox("fail create");
return;
}

try
{
_dProc* dlgt = (_dProc*)ProcData;
icp->StartServer(dlgt);
}
catch(_com_error e)
{
CString strInfo = e.ErrorMessage();
}

WriteConsole(hOut, "b\n", 2, &count, NULL);

CoUninitialize();
}
 
E

Eric

I read the articles from the link, but i can't get what i need.

thank you any way.


Howard Swope said:
Check out the information at:
http://msdn.microsoft.com/library/d.../cpconexposingnetframeworkcomponentstocom.asp

Also checkout the interop news group:
microsoft.public.dotnet.framework.interop


Eric said:
hi,

I want to convert a C# class into COM, so that I can use the class in C++.
The codes compile and link well. But when I run the program, I got a
exception.
Any comment is welcome. Thanks in advance.

Eric


----------------------------------------------------------------------
in C#, compile into a COM :

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Collections;

namespace SonicBase
{
public delegate int dProc(byte[] buf, int offset, int size);
public class MyClass : IMyClass
{

private dProc myProc = null;

bool StartServer(dProc dlgt)
{
myProc = dlgt;

byte[] buf = new byte[100];
int ret = myProc(buf, 0, 100);

return (ret > 0);
}

bool SonicBase.IMyClass.StartServer(dProc dlgt)
{
return false;
}

}

public interface IMyClass
{
bool StartServer(dProc dlgt);
}
}

--------------------------------------------------------------------------
in C++, use the COM :

// To use managed-code servers like the C# server,
// we have to import the common language runtime:
#import "mscorlib.tlb" raw_interfaces_only

#import "EricBase.tlb" no_namespace named_guids

extern "C"
int ProcData(char* buf, int offset, int size)
{
return 0;
}

void CMainFrame::OnTest()
{
CoInitialize(NULL);

IMyClass* icp = NULL;
HRESULT hr = CoCreateInstance(CLSID_MyClass,
NULL, CLSCTX_INPROC_SERVER,
IID_IMyClass, reinterpret_cast<void**>(&icp));


DWORD count=0;
AllocConsole();
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
WriteConsole(hOut, "a\n", 2, &count, NULL);

if(FAILED(hr))
{
AfxMessageBox("fail create");
return;
}

try
{
_dProc* dlgt = (_dProc*)ProcData;
icp->StartServer(dlgt);
}
catch(_com_error e)
{
CString strInfo = e.ErrorMessage();
}

WriteConsole(hOut, "b\n", 2, &count, NULL);

CoUninitialize();
}
 
E

Eric

I knows how to export C# class into COM without an explicit interface now.
like following:

namespace EricBase
{
[ClassInterface(ClassInterfaceType.AutoDual)] //strange syntax, but it's
the key
[Guid("64074EFB-17F6-4eb8-BC35-116A4FC950DB")]
public class Test
{
public void FuncA()
{
System.Console.WriteLine("in the COM");
}
}
}


the exception says "invalid agument" is still not solved.




Eric said:
Hi, Willy,

After modifying the code, I got another exception saying that "invalid
agument", I guessed there would be some mismatch between delegate dProc and
functionPtr ProcData, but I don't know what's wrong.

I looked into the MSDN, it gaves only the samples using explicit
interface. Would you please give me a sample without an explicit interface?
Using interface to export a C# class into COM, is troublesome. It would be a
great pleasure not using the explicit interface here.

Thanks,
Eric


Willy Denoyette said:
Hmm... this is tricky stuff.
You can't pass a C function ptr and use it directly as a delegate pointer,
you have to pass it as a long (C/C++) and marshal this value as a
FuctionPtr.
Here's what you should do.

1. change you C# code..

public interface IMyClass
{
bool StartServer(dProc dlgt);
}
into:

public interface IMyClass
{
bool StartServer([MarshalAs(UnmanagedType.FunctionPtr)]dProc dlgt);
}

2. Your C++ code:

icp->StartServer(dlgt);
into:
// reinterpret_cast - You know what you are doing, don't you?
icp->StartServer(reinterpret_cast<long>(dlgt));

Why do you implement an explicit interface for the method, there is no need
to do this just declare your interface methods as public. Or am I missing
something?

Willy.


Eric said:
hi,

I want to convert a C# class into COM, so that I can use the class in C++.
The codes compile and link well. But when I run the program, I got a
exception.
Any comment is welcome. Thanks in advance.

Eric


----------------------------------------------------------------------
in C#, compile into a COM :

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Collections;

namespace SonicBase
{
public delegate int dProc(byte[] buf, int offset, int size);
public class MyClass : IMyClass
{

private dProc myProc = null;

bool StartServer(dProc dlgt)
{
myProc = dlgt;

byte[] buf = new byte[100];
int ret = myProc(buf, 0, 100);

return (ret > 0);
}

bool SonicBase.IMyClass.StartServer(dProc dlgt)
{
return false;
}

}

public interface IMyClass
{
bool StartServer(dProc dlgt);
}
}

--------------------------------------------------------------------------
in C++, use the COM :

// To use managed-code servers like the C# server,
// we have to import the common language runtime:
#import "mscorlib.tlb" raw_interfaces_only

#import "EricBase.tlb" no_namespace named_guids

extern "C"
int ProcData(char* buf, int offset, int size)
{
return 0;
}

void CMainFrame::OnTest()
{
CoInitialize(NULL);

IMyClass* icp = NULL;
HRESULT hr = CoCreateInstance(CLSID_MyClass,
NULL, CLSCTX_INPROC_SERVER,
IID_IMyClass, reinterpret_cast<void**>(&icp));


DWORD count=0;
AllocConsole();
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
WriteConsole(hOut, "a\n", 2, &count, NULL);

if(FAILED(hr))
{
AfxMessageBox("fail create");
return;
}

try
{
_dProc* dlgt = (_dProc*)ProcData;
icp->StartServer(dlgt);
}
catch(_com_error e)
{
CString strInfo = e.ErrorMessage();
}

WriteConsole(hOut, "b\n", 2, &count, NULL);

CoUninitialize();
}
 
W

Willy Denoyette [MVP]

Eric said:
I knows how to export C# class into COM without an explicit interface now.
like following:

namespace EricBase
{
[ClassInterface(ClassInterfaceType.AutoDual)] //strange syntax, but
it's
the key
[Guid("64074EFB-17F6-4eb8-BC35-116A4FC950DB")]
public class Test
{
public void FuncA()
{
System.Console.WriteLine("in the COM");
}
}
}


the exception says "invalid agument" is still not solved.

No, your problem was that your interface method was not public. The reason
it 'works' now has nothing to do with the ClassInterface attribute.
Not that it's much better to indicate the types and UUID's of your COM
interfaces explicitely.

Not sure where/when the exception is thrown on you. Still I'm not clear on
what you are trying here, basically you are using COM interop in one sense
(cpp -> cs) and PInvoke interop in the other (cs -> cpp), I hope you have
some real reason to do this.
But, next is a sample based on your original posting, maybe this will give
you some clue.

1. CS COM - server:
// server.cs
using System;
using System.Runtime.InteropServices;
namespace Whatever
{

public delegate int dProc(byte[] buf, int offset, int size);
// class interface (coclass)
[ClassInterface(ClassInterfaceType.None)]
[Guid("fa7e5cdd-766f-4541-9b9b-aee0cdde5c74")]
[ProgId("Test.MyClass")]
public class MyClass :IMyClass
{
private dProc myProc = null;

public bool StartServer(dProc dlgt)
{
int ret = 0;
myProc = dlgt;
byte[] buf = new byte[] {65, 66, 67, 68, 69, 70, 00}; // drop some bytes
in the buffer
ret = myProc(buf, 0, buf.Length);
return (ret > 0);
}
}
// interface
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
[Guid("29853bbf-1838-49fe-8a70-eb262f24ee56")]
public interface IMyClass
{
bool StartServer([MarshalAs(UnmanagedType.FunctionPtr)]dProc dlgt);
}
}

2. CPP file - client:
///////////////////////////////////
// C++ program
///////////////////////////////////
// cl /EHsc inprocclient.cpp

#define _WIN32_WINNT 0x0501
#include <objbase.h>
#include <iostream>
#import "server.tlb" no_namespace named_guids

// Callback function
extern "C"
int __stdcall ProcData(char* buf, int offset, int size)
{
std::cout << buf << "\toffset: " << offset << "\tsize: " << size <<
std::endl;
return 0;
}

int main ()
{
::CoInitialize(NULL);
// Create an instance of the COM object using smart ptr
IMyClassPtr pPtr(__uuidof(MyClass));
_dProc* dlgt = (_dProc*)ProcData;
//pPtr->Dummy();
// Call method passing a function pointer as a long
// the interop layer must marshal this long to a valid function pointer
used as delegate pointer
// See CS file
pPtr->StartServer(reinterpret_cast<long>(dlgt));
// uninitialize COM
::CoUninitialize();
}

To build from the commandline, issue following commands (Ignore warning in
3):

regasm /unregister /tlb server.dll
csc /t:library server.cs
regasm /codebase /tlb server.dll
cl /EHsc inprocclient.cpp



run the client.

Willy.
 
E

Eric

Thank you for your warmhearted reply.
I tried your code, an exception was thrown out at
"pPtr->StartServer(reinterpret_cast<long>(dlgt));" saying "invalid agument"
I stepped into the code, found that "raw_StartServer(dlgt, &_result);"
returns "E_INVALIDARG" and cause _com_issue_errorex() to throw out an
exception. Why raw_StartServer() returns "E_INVALIDARG" here ?





Willy Denoyette said:
Eric said:
I knows how to export C# class into COM without an explicit interface now.
like following:

namespace EricBase
{
[ClassInterface(ClassInterfaceType.AutoDual)] //strange syntax, but
it's
the key
[Guid("64074EFB-17F6-4eb8-BC35-116A4FC950DB")]
public class Test
{
public void FuncA()
{
System.Console.WriteLine("in the COM");
}
}
}


the exception says "invalid agument" is still not solved.

No, your problem was that your interface method was not public. The reason
it 'works' now has nothing to do with the ClassInterface attribute.
Not that it's much better to indicate the types and UUID's of your COM
interfaces explicitely.

Not sure where/when the exception is thrown on you. Still I'm not clear on
what you are trying here, basically you are using COM interop in one sense
(cpp -> cs) and PInvoke interop in the other (cs -> cpp), I hope you have
some real reason to do this.
But, next is a sample based on your original posting, maybe this will give
you some clue.

1. CS COM - server:
// server.cs
using System;
using System.Runtime.InteropServices;
namespace Whatever
{

public delegate int dProc(byte[] buf, int offset, int size);
// class interface (coclass)
[ClassInterface(ClassInterfaceType.None)]
[Guid("fa7e5cdd-766f-4541-9b9b-aee0cdde5c74")]
[ProgId("Test.MyClass")]
public class MyClass :IMyClass
{
private dProc myProc = null;

public bool StartServer(dProc dlgt)
{
int ret = 0;
myProc = dlgt;
byte[] buf = new byte[] {65, 66, 67, 68, 69, 70, 00}; // drop some bytes
in the buffer
ret = myProc(buf, 0, buf.Length);
return (ret > 0);
}
}
// interface
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
[Guid("29853bbf-1838-49fe-8a70-eb262f24ee56")]
public interface IMyClass
{
bool StartServer([MarshalAs(UnmanagedType.FunctionPtr)]dProc dlgt);
}
}

2. CPP file - client:
///////////////////////////////////
// C++ program
///////////////////////////////////
// cl /EHsc inprocclient.cpp

#define _WIN32_WINNT 0x0501
#include <objbase.h>
#include <iostream>
#import "server.tlb" no_namespace named_guids

// Callback function
extern "C"
int __stdcall ProcData(char* buf, int offset, int size)
{
std::cout << buf << "\toffset: " << offset << "\tsize: " << size <<
std::endl;
return 0;
}

int main ()
{
::CoInitialize(NULL);
// Create an instance of the COM object using smart ptr
IMyClassPtr pPtr(__uuidof(MyClass));
_dProc* dlgt = (_dProc*)ProcData;
//pPtr->Dummy();
// Call method passing a function pointer as a long
// the interop layer must marshal this long to a valid function pointer
used as delegate pointer
// See CS file
pPtr->StartServer(reinterpret_cast<long>(dlgt));
// uninitialize COM
::CoUninitialize();
}

To build from the commandline, issue following commands (Ignore warning in
3):

regasm /unregister /tlb server.dll
csc /t:library server.cs
regasm /codebase /tlb server.dll
cl /EHsc inprocclient.cpp



run the client.

Willy.
 
W

Willy Denoyette [MVP]

Eric said:
Thank you for your warmhearted reply.
I tried your code, an exception was thrown out at
"pPtr->StartServer(reinterpret_cast<long>(dlgt));" saying "invalid
agument"
I stepped into the code, found that "raw_StartServer(dlgt, &_result);"
returns "E_INVALIDARG" and cause _com_issue_errorex() to throw out an
exception. Why raw_StartServer() returns "E_INVALIDARG" here ?

The same code dumps :

ABCDEF offset: 0 size: 7

to the console when run on Framework v1.1.4322 (v1.1 SP1).
I'm using the latest C++ compiler ( Version 14.00.40904) to compile the cpp,
not sure what you are using. Did you inspect the tli and tlh files?
Are you sure you registered the tlb (regasm /tlb ...)?

Willy.
 
E

Eric

hi Willy,

I do the work with VirsualStudio2003, same version as you.

Look into the following link:
http://www.dotnetinterop.com/features/default.aspx?q=Whidbey

It says: in dotNET v2.0 "The runtime now supports marshaling of function
pointers to delegates. The reverse - delegate to function pointer - has been
possible since version one. "

Isn't it means we can't marshal function pointers to delegates currently now
?

Am I misunderstand something ?

Regards,
Eric
 
W

Willy Denoyette [MVP]

No, marshaling function pointers has nothing to do with this.
You problem is related to an argument that is flagged as invalid by the
interop layer (CCW).

If you say "same version as you", I think you are mistaken, I'm not using VS
at all.

I'm using C++ v14.00.40904 to compile the C++ file from the command line,
this is NOT the version you have!.
I'm using Csharp compiler 7.10.6001.4 from the command line.
I'm using regasm.exe v 1.1.4322.573 from the commandline, using /tlb and
/codebase options.
I'm running this code on CLR v1.1 SP1 (version 1.1.4222) without a problem
(note SP1!!).
So basically the only difference is the C++ compiler, but IMO this is a non
issue as it compiles to native code only.

Willy.
 
E

Eric

Thank you for you explanation. but I'm still puzzled whether we can
marshaling function pointers or not? I cares about this.

As it for me, every thing worked well until i tried to marshal a function
pointer to delegate.

regards,
eric
 

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