3F / DllExport

.NET DllExport with .NET Core support (aka 3F/DllExport aka DllExport.bat)
MIT License
961 stars 133 forks source link

Problem with parameters #166

Closed robigit closed 4 years ago

robigit commented 4 years ago

From a c++ program I want call some functions of an interface into a managed class library in c#. The call works but I got an exception when the caller exit from the function and it happens only if and when the called function has a parameter. I think this is related to the calling convention stdcall, but I can't figure out how to correctly configure the interfaces. May you give me an hint? Thank you

This is the calling c++ part

Interface

struct INavig : IUnknown
{
   virtual int  __stdcall  CreateNavig   ( int p ) = 0;
   virtual int  __stdcall  CreateNavig2  ( ) = 0;

This is the call

            INavig *prm;
            hPromo = LoadLibrary(L"Managed.dll");
            FARPROC proc = GetProcAddress(hPromo, "get_InstanceByName");
            typedef void(__stdcall* f)(void** I);
            ((f)proc)((void**)(&prm));
             if(prm!=NULL)
             {
                prm->CreateNavig(0);   // ERROR   <----
                prm->CreateNavig2();   // NO ERROR
              }
             FreeLibrary(hPromo);

This is how it is implemented in c #

DllExport

     public static class Exports
    {
        [DllExport (ExportName = "get_InstanceByName" ) ]
        public static void get_InstanceByName(  [MarshalAs(UnmanagedType.Interface)]out ItfNavig instance)
        {
                instance = new ImplItfNavig();
        }
    }

The Interface

    [Guid("546e25c7-59b2-4d58-ac72-442293da4753"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface ItfNavig
    {
        int CreateNavig(int p);
        int CreateNavig2();

and the implementation

    public class ImplItfNavig : ItfNavig
    {
        public int CreateNavig(int p)
        {
            return 1;
        }
        public int CreateNavig2()
        {
            return 2;
        }
3F commented 4 years ago

@robigit

I'm not sure what are you trying, but, I'm watching IUnknown interface in your src. Is there any reason why not to make it initially as COM visible component if you've plan to use this anyway? Or try this if you just need complex types etc.

I think this is related to the calling convention stdcall

In any case, our tool is configured for __cdecl by default [?]

robigit commented 4 years ago

Thank you for your answer. What I'd want to do i substitute an old c++ dll, that exports the ItfNavig I put in the example (not INavig, that was a typo cleaning the code) with a new c# managed dll. However I discovered that in c# there is an automatic signature conversion of the methods of the com interfaces. Infact they should always export an HRESULT. So if in c# you write

int CreateNavig(int p);

in c++ it should become

HRESULT CreateNavig (int *result, int p)

But this is not my case and the reason of the exception.

As I can't change the c++ interface, to solve it has been sufficient tell to c# to not change the signature, so in c# the method changes in

[PreserveSig]
int CreateNavig(int p);

And all worked fine.

I hope this can be useful to someone else

3F commented 4 years ago

All correct. We need to use PreserveSig to avoid mentioned transformation in COM interop.

Otherwise you can also try with some offset to the specific field like ~ prm->CreateNavig(0, &result)

Moreover, in past, I already voiced about hack with offsets for IUnknown https://github.com/3F/Conari/issues/8 when raw access to data https://github.com/3F/Conari/issues/2#issuecomment-238394036

That can be considered in the manner of Conari: https://github.com/3F/Examples/blob/master/DllExport/BasicExport/UnmanagedCppConsole/UnmanagedCppConsole.cpp#L59 and so on

I hope this can be useful to someone else

Yes! Thank you for the tips in our Q&A list.