Click here to Skip to main content
14,975,411 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
Hi All I am new to C# and marshalling.
My objective is:
Creating an Interface in C# and implementing it in class.
One of method will take a structure [if not a good idea then pointer to structure]
For now I an try filling the structure with primitive datatype.

Generating a dll and tlb out of it.
Now, From C++ importing the tlb and use the method.

I succeeded till accessing the C# class and access one method but the one takes structure is throwing E_INVALIDARG exception.

here is my code:
From C#

C#
namespace ComTestCS
{
   [ComVisible(true)]
   [Guid("F9386F04-2F38-4DF5-9C9C-544DF9354AAC")]
    [InterfaceType (ComInterfaceType.InterfaceIsIDispatch)]
    public interface IShCom
    {
       int getHello(int x);       
       void getCSObj(out CSStObj ob);
    }

     [
       ComVisible(true),
       Guid("E2B721B2-5AEA-4AD7-BCC2-1DA51D0E9C79"),   
       ClassInterface(ClassInterfaceType.None),
       ComSourceInterfaces (typeof(IShCom))    ,
       ProgId("ShCom")
       
   ]

   public class ShCom : IShCom
    {
       public int getHello(int x)
       {
           System.IO.TextWriter textWriter = System.Console.Out;
           textWriter.WriteLine("getHello");//its printing
           return x+2;       
       }
      
       public void getCSObj(out CSStObj ob)//not working at all
       {
           System.IO.TextWriter textWriter = System.Console.Out;
           textWriter.WriteLine("getCSObj");
           ob = new CSStObj();
           ob.intData= 45;              
       }
    }

    [
    ComVisible(true),
    Guid("93BA11B9-A89B-4CB4-8ACE-1CDFC42FA978"),
     StructLayout(LayoutKind.Sequential,CharSet= CharSet.Ansi) 
         
    ]
     public struct CSStObj
     {
        //[MarshalAs (UnmanagedType.I4)]
         public int intData;       
     }


Now Accessing it from C++

C#
#import "ComTestCS.tlb"      named_guids
void main()
{

   try{

       CoInitialize(NULL);
       //regasm ComTestCS.dll /tlb: c:\bin\ComTestCS.dll /codebase
       ComTestCS::IShComPtr pShCom;
       ComTestCS::CSStObj *stObj = new ComTestCS::CSStObj();
       HRESULT hr= pShCom.CreateInstance(__uuidof(ComTestCS::ShCom));
       if(hr == S_OK)
       {
       long ret;
       //returning 10 as expected along with the console msg
       ret =pShCom->getHello(8L);
       pShCom->getCSObj(stObj);    //exception here   
   }
   }
    catch(_com_error& ce)
   {

      cout<<ce.Error()<<(wchar_t*)ce.Description();//E_INVALIDARG 
   }
  
   CoUninitialize();
  
}

Please help!
Thanks in advance
Posted
Updated 27-Apr-15 22:56pm
v3

If you are working with primitive data type you can use direct memory access via casting. It is better to create memory in the process/language where you used it and NOT transfer between c# and C++. If you want to use C# objects in C++ you need CLR project in C++.

Take a look at my article about an easy way to work with that issue.
   
Hi All thanks for the response ,
Actually I have to use non CLR C++ native code.

I got the answer from :
Here
see the Native Exe calling Managed Dll

Just twisted little bit for my need

C# code:

C#
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
namespace CSDll
{
    using System;

    [
    Guid("A2BDE14F-EC09-4E3C-9E39-A24B49352120")
    ]
    public struct CSStObj
    {
        //[MarshalAs (UnmanagedType.I4)]
        public int intData;
        [MarshalAs(UnmanagedType.BStr)]
        public string strData;
    }

    [Guid("D4660088-308E-49fb-AB1A-77224F3FF851")]
    public interface IMyManagedInterface
    {
        int factorial(int arg);
        void getCSObj(out CSStObj ob);
        void getCSObjs([MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_USERDEFINED)] out CSStObj[] ob);
      //  void getCSObjs(out List<csstobj> ob);
    }

    /// <summary>
    ///    Summary description for Class1.
    /// </summary>
    [Guid("46A951AC-C2D9-48e0-97BE-91F3C9E7B065")]
    public class Class1 : IMyManagedInterface
    {
        public Class1()
        {
        }

        public int factorial(int arg)
        {
            int result;

            if (arg == 1)
            {
                result = arg;
            }
            else
            {
                result = arg * factorial(arg - 1);
            }

            return result;
        }
        public void getCSObj(out CSStObj ob)
        {
            System.IO.TextWriter textWriter = System.Console.Out;
            textWriter.WriteLine("getCSObj");
            ob.intData = 7;
            ob.strData = "hiCPP";
        }

        public void getCSObjs([MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_USERDEFINED)] out CSStObj[] ob)
        {
            System.IO.TextWriter textWriter = System.Console.Out;
            textWriter.WriteLine("getCSObjs");
    

            ob =
           new CSStObj[]{
                new CSStObj() { intData = 1, strData = "1" },
                new CSStObj() { intData = 2, strData = "2" },
                new CSStObj() { intData = 3, strData = "3" }
            };
            
        }

    }
}</csstobj>




In Atl non clr project

C++
#include "stdafx.h"
#pragma once

#include "windows.h"
#include <stdio.h>
#import "C:\bin\CSDll.tlb"      named_guids 
//#import "CSDll.tlb" named_guids

int main(int argc, char* argv[])
{
   try{
    HRESULT hRes = S_OK;
    CoInitialize(NULL);
      
  
    CSDll::IMyManagedInterface *pManagedInterface = NULL;

    hRes = CoCreateInstance(CSDll::CLSID_Class1, NULL, CLSCTX_INPROC_SERVER, 
     CSDll::IID_IMyManagedInterface, reinterpret_cast<void**> (&pManagedInterface));

    if (S_OK == hRes)
    {
        long retVal =0;
        hRes = pManagedInterface->raw_factorial(4, &retVal);
        printf("The value returned by the dll is %ld\n",retVal);
		
		CSDll::CSStObj stObj;
      pManagedInterface->getCSObj(&stObj); 
	  printf("getCSObj sent is %ld %s\n",stObj.intData,stObj.strData);

     SAFEARRAY* saValues(NULL);
     pManagedInterface->getCSObjs(&saValues);
     ////////////////////////////////////////
     
      CSDll::CSStObj* pVals;
      HRESULT hr = SafeArrayAccessData(saValues, (void**)&pVals); // direct access to SA memory
      if (SUCCEEDED(hr))
      {
        long lowerBound, upperBound;  // get array bounds
        SafeArrayGetLBound(saValues, 1 , &lowerBound);
        SafeArrayGetUBound(saValues, 1, &upperBound);

        long cnt_elements = upperBound - lowerBound + 1; 
        for (int i = 0; i < cnt_elements; ++i)  // iterate through returned values
        {                              
          CSDll::CSStObj pVal = pVals[i];   
          printf("getCSObjs Array of Struct sent is %ld %s\n",pVal.intData,pVal.strData);
        }       
        SafeArrayUnaccessData(saValues);
     }

     
     
     SafeArrayDestroy(saValues);
     saValues = NULL; // set the pointer to NULL
     /////////////////////////////////////////
	  
	  
        pManagedInterface->Release();
    }


   } catch(_com_error& ce)
   {
      
      printf("Exception::%d||%s",ce.Error(),(wchar_t*)ce.Description());
   }

    CoUninitialize();
    return 0;
}


and I got access to String,Structure,Array of Structure etc all what I need in my next stage.
   
v3

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)




CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900