尝试将托管代码中的字符串转换回C ++

时间:2015-07-27 18:40:29

标签: c# c++ xamarin marshalling

(对不起,如果以前曾经问过,但我见过的大多数例子都是从托管传递数据 - >原生,而不是相反)。

简短问题:如何从本机C ++代码中获取托管世界中的字符串?

长问题+背景: 我正在使用一些以前能够获取和设置名称/值字符串对的旧C ++代码(以大大简化设计)。我希望将这个名称/值对机制移动到我们应用程序的其余部分的C#托管代码中,所以我在C ++世界中放入函数回调,调用托管代码进行获取和设置。 C ++函数指针类型如下:

typedef int (GetConfigParamCallback)(const char* paramName, char* value);
typedef GetConfigParamCallback* LPGetConfigParamCallback;

typedef int (SetConfigParamCallback)(const char* paramName, const char* value);
typedef SetConfigParamCallback* LPSetConfigParamCallback;

正如你所看到的,棘手的是get,我想为调用者提供内存以填充。这将是托管代码向前发展。

这些回调由C#世界中的代表表示如下:

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate int Native_GetConfigParamCallBackMethodDelegate(
  string paramName, StringBuilder paramValue);

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate int Native_SetConfigParamCallBackMethodDelegate(
  string paramName, string paramValue);

然后我在获取值的托管代码中的GetConfig包装函数看起来像这样(并将正确的值放入paramValue中,如我的调试所示):

static int GetConfigParamCallBackWrapper(
  string paramName, 
  System.Text.StringBuilder paramValue)
{
  string valueTemp = // Fetch the string value here

  if (valueTemp == null)
  {
    return 0;
  }

  paramValue.Append(valueTemp);
  return 1;
}

因此,当托管C#启动时,它会在本机环境中设置这些回调函数。然后,我有本机代码运行一系列相当于获取和设置这些字符串的单元测试方法。现在,在桌面上这可以正常工作,但是当我尝试在iOS上使用Xamarin构建的应用程序运行它时,该字符串作为垃圾AFAICT返回到本地世界。

我也尝试过使用IntPtr手动编组,也没有运气。

2 个答案:

答案 0 :(得分:0)

您可以将字符串编组为BSTR:

<强> C ++:

typedef int (GetConfigParamCallback)(const char* paramName, BSTR* value);
typedef GetConfigParamCallback* LPGetConfigParamCallback;

// Usage:
LPGetConfigParamCallback pCallback = // ...
CComBSTR value;
int result = (*pCallback)("...", &value);

<强> C#:

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate int Native_GetConfigParamCallBackMethodDelegate(
    string paramName,
    [MarshalAs(UnmanagedType.BStr)] ref string paramValue
    );

static int GetConfigParamCallBackWrapper(
    string paramName, 
    ref string paramValue
    )
{
    string valueTemp = // Fetch the string value here

    if (valueTemp == null)
    {
        return 0;
    }

    paramValue = valueTemp;
    return 1;
}

答案 1 :(得分:0)

所以我能够找到一个稍微不那么漂亮但有效的解决方案,这要归功于这篇优秀的博文:http://randomencoding.tumblr.com/post/48564128118/returning-a-string-from-a-c-callback-to-c

我使用了IntPtr并进行了手动Marshaling:

static int GetConfigParamCallBackWrapper(
    string paramName, 
    IntPtr paramValue)
{

   string valueTemp = // Acquire valueTemp here. 

    IntPtr sPtr = Marshal.StringToHGlobalAnsi(valueTemp);
    try
    {
        // Create a byte array to receive the bytes of the unmanaged string
        var sBytes = new byte[valueTemp.Length + 1];
        // Copy the the bytes in the unmanaged string into the byte array
        Marshal.Copy(sPtr, sBytes, 0, valueTemp.Length);
        // Copy the bytes from the byte array into the buffer passed into this callback
        Marshal.Copy(sBytes, 0, paramValue, sBytes.Length);
        // Free the unmanaged string
    }
    finally 
    {
        Marshal.FreeHGlobal(sPtr);
    }