Click here to Skip to main content
15,891,529 members
Please Sign up or sign in to vote.
4.50/5 (2 votes)
See more:
I need to modify my unmanaged win32 c++ dll so that it can call & return a string pointer from a managed c# dll.
I am using Smalltalk language to make a call to win32 dlls and I am trying to code a C# dll that will return a simple string (or rather a pointer to the string) to my c++ dll and then pass it back to my Smalltalk program. [note this is a very old version of smalltalk so direct Managed calls have not worked for it & that is why I have created a c++ win32 dll].
Thanks to help from the Visual C++ developer center board, I managed to get the c++ program working to return the string pointer. Now I need to update the C++ program to replace..
str("Very large string equalling 177 characters in length 123242424265416246514624162465421654211111111116666666");
DWORD buf_size = *lpnSize;

With a line to call the following on the managed DLL...
CallManagedCode
Can somebody give me the code that would work for this? I have read many confusing articles mentioning Marshalling, Unicode, assembly registration, type library exporter.
I realize this may not be simple, but could somebody give me steps & code that I would need to follow to get this working? This will save me weeks of work!! NOTE: I am not a c++ developer but i have c# experience & a lot of Smalltalk experience but I do not really understand memory is managed/accessed. This c++ dll works on the Smalltalk end so I really would not like to modify the return type if possible in the c++ program.

// dllmain.cpp : Defines the entry point for the DLL application.
#include "stdafx.h"
#include <afx.h>
#include <afxcoll.h>
 
_declspec(dllexport)  bool TestString(double a, double b);
 
BOOL APIENTRY TestCPP( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                                        )
{
        switch (ul_reason_for_call)
        {
        case DLL_PROCESS_ATTACH:
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        case DLL_PROCESS_DETACH:
                break;
        }
        return TRUE;
}
        extern "C" __declspec(dllexport)bool TestString(LPTSTR lpBuffer, LPDWORD lpnSize) {
 
           CString str("Very large string equalling 177 characters in length 123242424265416246514624162465421654211111111116666666");
           DWORD buf_size = *lpnSize;
 
           *lpnSize = str.GetLength() + 1;
 
           if (buf_size < *lpnSize) {
 
             // Buffer too small
             return false;
 
           }
 
           lstrcpy(lpBuffer, str);
 
           return true;
}
 
using System;
 
namespace Testcsharpstring
{
    public class Testcsharpstring
    {
        public String CallManagedCode()
        {/* Test web service call with no input parms */
 
            String result;
            result = "Very large string equalling 177 characters in length 123242424265416246514624162465421654211111111116666666";
            return result;
        }
    }
}</afxcoll.h></afx.h>

Thanks in advance,
John
Posted
Updated 19-Apr-11 16:00pm
v2
Comments
Andrew Rissing 19-Apr-11 22:01pm    
It might help you find an answer if you try to make the question (including code) as readable and straight forward as possible. I've tried to help by adding pre tags, but there is still much to be desired for someone to invest wading through this question to find what you need.
Sergey Alexandrovich Kryukov 19-Apr-11 22:56pm    
You need to show your [DllImport] static extern bool Teststring(...) part of code.
--SA
ukjohnct 20-Apr-11 7:09am    
Point taken. In my defense this is the first time I have posted and I am completely new to c++ so it may be diffult for me to define the question. All I can say is that I have a smalltalk program calling a c++ program and it must retain the following signature
bool TestString(LPTSTR lpBuffer, LPDWORD lpnSize)
Can you give me an example of how I would create a simple c# program (and code its call from c++) that could correctly return a value into the lpBuffer of the c++ program.

Thanks John

To get a string from native code from managed code, the best way is to use a StringBuilder:

In native code:
C++
extern "C" __declspec(dllexport) void FillString(char* myString, int length)
{
    //check that length is enough for your string
    ...
    //simple test
    strcpy_s(myString, length, "hello");
}


In C# code:
C#
[DllImport("YouNativeDLL")]
static extern void FillString(StringBuilder myString, int length);
public Test()
{
    //reserve storage
    StringBuilder str = new StringBuilder(100);
    //get my string from native code
    FillString(str, str.Capacity);
    string myString = str.ToString();
    ...
}


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

OK try using a callback and Marshal class then:

In native code:
C++
//declare the callback prototype
typedef BOOL (__stdcall *MY_CALLBACK)(char* str, int length);
//cette fonction appelle la callback
extern "C" __declspec(dllexport) void SetCallback(MY_CALLBACK callback)
{
    //you can store the callback for a later use
    //or do a simple test:
    char buffer[1000] = { 0 };
    BOOL result = callback(buffer, 1000);
    if (result)
	MessageBox(NULL, "Callback returned true", buffer, MB_OK);
    else
	MessageBox(NULL, "Callback returned false", "", MB_OK);
}


In C# code:
C#
//declare the callback prototype
delegate bool CALLBACK(IntPtr myString, int length);
//native function to set the callback
[DllImport("Native")]
static extern void SetCallback(CALLBACK callback);
//the callback
static bool Callback(IntPtr myString, int length)
{
    //a test string to copy
    string str = "hello";
    //make sure the buffer is big enough
    if (str.Length + 1 > length)
        return false;
    //convert the managed string into a unmanaged ANSI string
    IntPtr ptr = Marshal.StringToHGlobalAnsi(str);
    //get the bytes of the unmanaged string
    byte[] bytes = new byte[str.Length + 1];
    Marshal.Copy(ptr, bytes, 0, str.Length);
    //copy these bytes into myString
    Marshal.Copy(bytes, 0, myString, bytes.Length);
    //free the unmanaged memory
    Marshal.FreeHGlobal(ptr);
    return true;
}
void SomeFunction()
{
    //set the callback somewhere in your code
    SetCallback(Callback);
}
 
Share this answer
 
v3
Comments
ukjohnct 20-Apr-11 7:01am    
Thanks for your response but it looks like you show the c# pgm calling the c++. I need it the other way around. I need the c++ program to call the c# program.
Olivier Levrey 20-Apr-11 8:26am    
I updated my answer.
Nish Nishant 22-Apr-11 10:06am    
Voted 5, good detailed response.
Olivier Levrey 22-Apr-11 10:09am    
Thanks
[no name] 23-Apr-11 6:08am    
5++ for good effort
I think you will have to write a managed C++ class in order to be able to communicate with a .Net assembly. I don't have experience of how to do that, but once you have, that class can understand a System.String^ and convert it into a CString/char*. I believe it is easy to interoperate between managed and unmanaged C++ so you should be able to bolt the managed part onto your current DLL.
 
Share this answer
 
Comments
ukjohnct 20-Apr-11 8:20am    
Unfortunately that is not an option as my Smalltalk program will not work with Managed c++
apaka 23-Apr-11 3:40am    
Take a look on All in one code framework on codeplex , as I remember they have samples of caling managed dll in their inoerop\fusion section.

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