Solved

string to CString conversion error

Posted on 2006-06-27
28
2,911 Views
Last Modified: 2007-12-19
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.
0
Comment
Question by:payal1711
  • 10
  • 9
  • 7
  • +1
28 Comments
 
LVL 30

Expert Comment

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


David Maisonave (Axter)
Cheers!
0
 
LVL 30

Expert Comment

by:Axter
Comment Utility
Example:

string foo1 = "Hello World";
CString foo2 = foo1.c_str();
0
 
LVL 30

Expert Comment

by:Axter
Comment Utility
To pass the value back to string, use the CString::operator LPCTSTR() function
0
 
LVL 48

Expert Comment

by:AlexFM
Comment Utility
Is unmanaged Dll written in MFC, what version of MFC?
0
 
LVL 1

Author Comment

by:payal1711
Comment Utility

  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.
0
 
LVL 1

Author Comment

by:payal1711
Comment Utility

  Yes it is MFC in a shared dll.
0
 
LVL 30

Expert Comment

by:Axter
Comment Utility
>>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.
0
 
LVL 1

Author Comment

by:payal1711
Comment Utility

    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.
   
0
 
LVL 30

Expert Comment

by:Axter
Comment Utility
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.
0
 
LVL 30

Expert Comment

by:Axter
Comment Utility
//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.
0
 
LVL 1

Author Comment

by:payal1711
Comment Utility

  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;
0
 
LVL 30

Expert Comment

by:Axter
Comment Utility
>>  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*".
0
 
LVL 1

Author Comment

by:payal1711
Comment Utility


   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?
0
 
LVL 30

Expert Comment

by:Axter
Comment Utility
>>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.
0
IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

 
LVL 1

Author Comment

by:payal1711
Comment Utility

  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.
         


0
 
LVL 30

Expert Comment

by:Axter
Comment Utility
>>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.
0
 
LVL 25

Expert Comment

by:clockwatcher
Comment Utility
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);
0
 
LVL 48

Expert Comment

by:AlexFM
Comment Utility
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.
0
 
LVL 25

Expert Comment

by:clockwatcher
Comment Utility
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.
0
 
LVL 1

Author Comment

by:payal1711
Comment Utility

  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.
0
 
LVL 25

Expert Comment

by:clockwatcher
Comment Utility
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.
0
 
LVL 25

Expert Comment

by:clockwatcher
Comment Utility
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
0
 
LVL 1

Author Comment

by:payal1711
Comment Utility

   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.
 
0
 
LVL 25

Expert Comment

by:clockwatcher
Comment Utility
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.
0
 
LVL 1

Author Comment

by:payal1711
Comment Utility

  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.
 
   
0
 
LVL 25

Expert Comment

by:clockwatcher
Comment Utility
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.
0
 
LVL 25

Accepted Solution

by:
clockwatcher earned 200 total points
Comment Utility
Ok... had a chance to look at it and that is the way MFC dlls work and probably is what you're after.  Here's a quick example of the whole process:  creating a MFC dll, exposing a function and using it in C#.  

Create your MFC dll using the wizard.  Add a header file.  Here's a sample:

sample.h
----------
#include "stdafx.h"
#include "afxinet.h"

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


Add your implementation:

sample.cpp
-------------
#include "stdafx.h"
#include "sample.h"

extern "C" __declspec(dllexport) void DoConversion(const char* url, const char* filename)
{
     
      AFX_MANAGE_STATE(AfxGetStaticModuleState( ));
   
     CFile* outFile = new CFile(filename, CFile::modeCreate | CFile::modeWrite);
     CInternetSession* session = new CInternetSession();
     CStdioFile* inFile;

     try {
          inFile = session->OpenURL(url);
     }
     catch (CInternetException* err) {
          TCHAR message[4096];
          err->GetErrorMessage(message, 4096);
          err->Delete();
     }

     BYTE buffer[4096];
     DWORD bytesRead = 0;
     try {

          while (bytesRead = inFile->Read(buffer, 4096))
          {
               outFile->Write(buffer, bytesRead);
          }
     }
     catch (CFileException* err)
     {
          TCHAR message[4096];
          err->GetErrorMessage(message, 4096);
          err->Delete();
     }

     outFile->Close();
     inFile->Close();
     session->Close();

     delete inFile;
     delete outFile;
     delete session;

}

Build your DLL.


Your C# should look like this (assumes you've placed your DLL somewhere it can be found-- e.g., the directory your C# executable is in might be a good idea):

using System;
using System.Runtime.InteropServices;

namespace Q_21901100
{
     class Class1
     {

          [DllImport("SampleMFCDll.dll",
                EntryPoint = "DoConversion",
                CallingConvention = CallingConvention.Cdecl,
                CharSet=CharSet.Ansi)]
           extern static void DoConversion(String url, String outputFilePath);

          [STAThread]
          static void Main(string[] args)
          {
               string url = "http://www.google.com";
               string outputPath = "google.txt";

               DoConversion(url, outputPath);
          }
     }
}

And excuse the MFC code sample (if it can be done better or easier)-- I'm not a MFC programmer and just threw it together as an example.
0
 
LVL 1

Author Comment

by:payal1711
Comment Utility

  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
   
0

Featured Post

Free Trending Threat Insights Every Day

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

Join & Write a Comment

The following diagram presents a diamond class hierarchy: As depicted, diamond inheritance denotes when two classes (e.g., CDerived1 and CDerived2), separately extending a common base class (e.g., CBase), are sub classed simultaneously by a fourt…
In Easy String Encryption Using CryptoAPI in C++ (http://www.experts-exchange.com/viewArticle.jsp?aid=1193) I described how to encrypt text and recommended that the encrypted text be stored as a series of hexadecimal digits -- because cyphertext may…
Sending a Secure fax is easy with eFax Corporate (http://www.enterprise.efax.com). First, Just open a new email message.  In the To field, type your recipient's fax number @efaxsend.com. You can even send a secure international fax — just include t…
This video explains how to create simple products associated to Magento configurable product and offers fast way of their generation with Store Manager for Magento tool.

771 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

16 Experts available now in Live!

Get 1:1 Help Now