Link to home
Start Free TrialLog in
Avatar of payal1711
payal1711

asked on

string to CString conversion error

Hi,

       I have an C++ unmanaged dll that has a method signature like:

          void DoConversion(CString videoIn, CString videoOut)
       
      I am building a VC++.NET managed wrapper class  for this dll. It has the following method:

      public:
            [DllImport("anothertestproc.dll",
                  EntryPoint="?convertd@CanothertestprocApp@@QAEXQAD0@Z",
                  CallingConvention=CallingConvention::ThisCall)]
            static void convertd(CString, CString );

      };

      The final C#.Net call should look something like this:

                     convertd(string, string);

      I am very new to the unmanaged and managed wrappers and all this stuff. It gives me error when I try to pass string values to CString type.

      Does anyone know how to go about this whole thing?

      Thanks.
Avatar of Axter
Axter
Flag of United States of America image

Hi payal1711,
To pass a string value to CString use the string::c_str() method.


David Maisonave (Axter)
Cheers!
Example:

string foo1 = "Hello World";
CString foo2 = foo1.c_str();
To pass the value back to string, use the CString::operator LPCTSTR() function
Avatar of AlexFM
AlexFM

Is unmanaged Dll written in MFC, what version of MFC?
Avatar of payal1711

ASKER


  I have written the unmanaged Dll using Visual Studio 2005. I don't know what version it uses. None of the above things are working. Let me know if you find any answers. Thanks.

  Yes it is MFC in a shared dll.
>>None of the above things are working.

Please post the new code, and please state exactly how it's not working?

We can give you further assistence if you give us proper feedback.

    function in unmanaged dll:  
   void DoConversion (LPCTSTR, LPCTSTR);

    managed wrapper:
 [DllImport("anothertestproc.dll",
   EntryPoint = "?convertd@CanothertestprocApp@@QAEXPBD0@Z",
    CallingConvention = CallingConvention.ThisCall)]
        private static extern void convertd(string videoIn, string videoOut);

    managed call:
         convertd(string p2, string p3);

     Below is the error I get.


A call to PInvoke function 'ManagedDemo!ManagedDemo.Form1::convertd' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the calling convention and parameters of the PInvoke signature match the target unmanaged signature.

       I have checked the signature using dumpbin and copied it exactly the same way and also have updated the reference dll in the managed project.

      Do I have to specify an entry point in my Dll using "BOOL APIENTRY DllMain". I found this in one of the example projects found on internet.

      Thanks.
   
Why did you change the function signature?
That's not what I recommended.

I recommend you leave the function signature as it was, and change the calling code, to pass the right type.
//Example calling code

void foo()
{
  std::string data1 = "Hello World";
  std::string data2 = "Goodby World";
  DoConversion(data1.c_str(), data2.c_str());

The above code will work with the original function signature.

  ok, how do you write the same thing in C#.net. because my final call is from a c# form.
 I cannot do using namespace std;
>>  ok, how do you write the same thing in C#.net. because my final call is from a c# form.

What do you mean by you're final call?

Do you mean the calling function, or the DoConversion implementation.

If you need to cross over languages, then I recommend you use a simple type like "const char*".


   I mean the call from my c# code.The implementation is in c++.  And yes it is using cross over languages. How do I work with const char* then? Can you show me the calls in the functions?
>>Can you show me the calls in the functions?

I'm not that familiar with C#, so I can't post example code callin the DoConversion function.
I'm sure C# has an easy method to convert from String to const char* type.
C++ function should be declared as the following:
void DoConversion (const char*,  const char*);

You need to make this change on ALL parts of your code that declares or implements DoConversion.

Do a keyword search for "DoConversion" on your project on ALL types (*.*) of files (an not just *.cpp and *.c).

After making your changes, do a Rebuild-ALL.

  ok below is what I am doing now

      C++ function:
   void DoConversion(const char* videoIn, const char* videoOut)

      managed wrapper class call:

       [DllImport("anothertestproc.dll",
          EntryPoint = "?convertd@CanothertestprocApp@@QAEXPBD0@Z",
              CallingConvention = CallingConvention::ThisCall)]
        static void convertd(const char* videoIn, const char* videoOut);


        In my C# project, I add the managed wrapper class dll as a reference. But now it wants sbyte* as arguments. Below is the error I get:

     The best overloaded method match for 'testmanaged.ImageWrap.convertd(sbyte*, sbyte*)' has some invalid arguments

      This error reminds me that I had tried this before also. I came to know then that sbyte is for integer characters or something of that kind. How do I resolve this?

   Thanks.
         


>>This error reminds me that I had tried this before also. I came to know then that sbyte is for integer characters or something of
>>that kind. How do I resolve this?

I think you need to post this part of the quesion in the c# topic area, or post a link to this question in the C# topic area.
See the following reference:

  http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpcondefaultmarshalingforstrings.asp

Basically use CharSet.Ansi and let System.Interop take care of things for you:

    [DllImport("anothertestproc.dll",
          EntryPoint = "?convertd@CanothertestprocApp@@QAEXPBD0@Z",
            CallingConvention = CallingConvention.ThisCall, CharSet=CharSet.Ansi)]
     static extern void convertd(String videoIn, String videoOut);

And then calling it with:

   string videoIn = "video in";
   string videoOut = "video out";
   IntPtr pCppObj = myCppObjConstructor();  // whatever DLLImport you've got defined to return a pointer to an instance of your C++ object
   convertd(pCppObj, videoIn, videoOut);
   myCppObjDestructor(pCppObj);


If you want to manually marshal stuff yourself then look at System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi:

  http://msdn2.microsoft.com/en-us/library/system.runtime.interopservices.marshal.stringtohglobalansi.aspx

I think it would look something like:

   [DllImport("anothertestproc.dll",
      EntryPoint = "?convertd@CanothertestprocApp@@QAEXPBD0@Z",
      CallingConvention = CallingConvention.ThisCall)]
    unsafe extern static void convertd(char* videoIn, char* videoOut);

And then using it:

   string videoIn = "video in";
   string videoOut = "video out";

   IntPtr pvidIn = System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(videoIn);
   IntPtr pvidOut =  System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(videoOut);  
   IntPtr pCppObj = myCppObjConstructor();
   convertd(pCppObj, (char*) pvidIn, (char*) pvidOut);
   System.Runtime.InteropServices.Marshal.FreeHGlobal(pvidIn);
   System.Runtime.InteropServices.Marshal.FreeHGlobal(pvidOut);
   myCppObjDestructor(pCppObj);
Why do you use PInvoke in C++? You can do this directly from C#.
If you write C++ wrapper, you can call unmanaged functions directly. Add unmanaged library to libker settings, include it's h-file to the project, and make direct call.
Yeah... my post was all C# -- calling it directly from C# without any form of C++ wrapper besides a wrap to get an instance of your object.  Sorry... didn't make that clear.  And to further clarify, I have no idea why you'd want to actually marshal stuff yourself (in this case), but posted it because you seemed to be going down that path.  

And, finally, AlexFM would certainly know more than I would about how to do any/all of this.

  Thanks clockwatcher.
   I get the following error in the C# code posted below:

       PInvokeStackImbalance was detected. "A call to PInvoke function 'ManagedDemo!ManagedDemo.Form1::convertd' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the calling convention and parameters of the PInvoke signature match the target unmanaged signature."

      Here is what my C# code looks like now.

      public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            string p2 = "C:\\bla105_050a_008_cp_001.avi";
            string p3 = "C:\\New Folder 2\\newimage_0001.tga";

            convertd(p2, p3);
        }


        [DllImport("anothertestproc.dll",
            EntryPoint = "?convertd@CanothertestprocApp@@QAEXPBD0@Z",
            CallingConvention = CallingConvention.ThisCall,
            CharSet = CharSet.Ansi)]
        static extern void convertd(string videoIn, string videoOut);
    }

    What am I doing wrong here? I didn't understand your comment "a wrap to get an instance of your object" what object. I have eliminated the C++ wrapper class totally. There is only unmanged dll and then this C# code.

    Thanks.
You're using the ThisCall calling convention which implies that you want to call a method of a C++ unmanaged class or you want to pass a variable number of arguments.  If your not calling a class method (or a function that takes a variable number of arguments) then you shouldn't be using ThisCall.  If you are, then you need to pass the pointer to your unmanaged class as the first parameter (in the case that you're calling a method of a class).  In order to get a pointer to your C# unmanaged class, you'd have to have some sort of class factory standard cdecl type DLLImport that you called that returned a pointer to it.  You'd also want to have a destructor wrapper.

If you're just calling a standard function, you probably want to change your call type to Cdecl.
Sorry... meant to say:

  In order to get a pointer to your C++ unmanaged class

not

  In order to get a pointer to your C# unmanaged class

   I am calling a class method described below in my unmanaged class

BEGIN_MESSAGE_MAP(CanothertestprocApp, CWinApp)
END_MESSAGE_MAP()


// CanothertestprocApp construction

CanothertestprocApp::CanothertestprocApp()
{
      // TODO: add construction code here,
      // Place all significant initialization in InitInstance
}


// The one and only CanothertestprocApp object

CanothertestprocApp theApp;


// CanothertestprocApp initialization

BOOL CanothertestprocApp::InitInstance()
{
      CWinApp::InitInstance();

      return TRUE;
}
    void CanothertestprocApp::convertd(const char* videoIn, const char* videoOut)  
{
            DoConversion(videoIn, videoOut);  

}

   -----------and the signature is as below in the header file:

    class CanothertestprocApp : public CWinApp
{
public:
      CanothertestprocApp();

// Overrides
public:

      virtual BOOL InitInstance();

CPPWIN32DLL_API      void convertd(const char*, const char*);  



      DECLARE_MESSAGE_MAP()
};

   -------------------  

     and I gave you my C# code in the previous posting.

     So I am calling a class method from an unmanaged class but it does not have variable number of arguments. It always has two fixed arguments. So should I be using ThisCall at all? How do I pass pointer to my unmanaged class? Can you please show me how to in the code that I posted. That will be really helpful.

   Thanks.
 
Ignore the variable number of parameters stuff.  ThisCall is strictly for use with member functions.  That's what I thought and what made sense.  Tired and misread the documentation.

  I forgot to post the following macros in the unmanaged header file

#ifndef __AFXWIN_H__
      #error "include 'stdafx.h' before including this file for PCH"
#endif

#ifdef CPPWIN32DLL_EXPORTS
#define CPPWIN32DLL_API __declspec(dllexport)
#else
#define CPPWIN32DLL_API __declspec(dllimport)
#endif

     Thanks.
 
   
So it's a MFC DLL?  I'll have to take a look.  Or someone else will have to jump in.

I'm definitely not a MFC programmer.  I would expect that you'd want to expose your DoConversion call directly instead of wrapped in a method call of your MFC CWinApp subclass.  Something like:

__declspec(dllexport) void DoConversion(char*, char*);

extern "C" __declspec(dllexport) void DoConversion(char* vidIn, char* vidOut)
{
     AFX_MANAGE_STATE(AfxGetStaticModuleState( ));
     // .. do whateve DoConversion does here    

}

And get rid of all that junk within your CWinApp subclass. My guess is that CWinApp just encapsulates the standard DllMain call of a standard DLL.  But again I'm not a MFC programmer.

I don't have time right now to try to put together a test, but I expect that's how you expose functions from MFC dlls-- not as methods of your CWinApp object itself.
ASKER CERTIFIED SOLUTION
Avatar of clockwatcher
clockwatcher

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial

  It works!!!!

  Thanks so much clockwatcher. All the points go to you.

  Thanks Axter for your help!!

  This was my first MFC/unmanaged C++ project. I learned a lot from it. Thanks everybody for your time.

  -Payal