【问题标题】:Passing a C# string to C++/CLI does not show the string value in the CPP program将 C# 字符串传递给 C++/CLI 不会在 CPP 程序中显示字符串值
【发布时间】:2013-03-16 03:06:03
【问题描述】:

我想通过 CLI/C++ 从 C++ 调用 C# 函数。

C#代码

private string _text = " ";

public void setText(string text)
{
    // _text = text;
    _text = "HI World";
}

理想情况下,setText 应该只有注释行。 _text = "HI World" 就是一个例子。

public string getText()
{
    return _text;
}

C++/CLI 代码

标题:

gcroot<Bridge> _managedObject;

virtual void setText(std::string text);
virtual std::string getText();

CPP 文件

std::string CStringBridge::getText()
{

// _managedObject = gcnew Bridge(); 返回 (marshal_as(_managedObject->getText())); }

void CStringBridge::setText(std::string text)
{

// _managedObject = gcnew Bridge(); _managedObject->setText(gcnew System::String(text.c_str())); }

IStringBridgeWrapper* IStringBridgeWrapper::CreateInstance(void)
{
return ((IStringBridgeWrapper *)new CStringBridge());
}

注意:当我使用以下代码时

virtual void setText(System::String^ text);
virtual System::String^ getText();

我收到以下错误 3395

*__declspec(dllexport) 不能应用于具有 __clrcall 调用约定的函数*

,所以我坚持使用 std::string

当我使用 C++/CLI 代码中的库并从我的 C++ 程序中调用时,应该打印“Hi World”;而是什么都没有打印出来

C++ 控制台应用程序

IStringBridgeWrapper *pBridge = IStringBridgeWrapper::CreateInstance();

pBridge->setText(std::string("I am here"));
pBridge->getText();

我认为字符串没有被正确传递。

任何解决它的想法都会受到赞赏。

编辑

我在 cmets 之后更新了代码,但没有任何显示。

gcroot 创建句柄,但不为其分配内存。但是由于 Bridge 没有分配内存,因此应用程序无法运行。我的代码在本文的同一行中 - http://www.codeproject.com/Articles/10020/Using-managed-code-in-an-unmanaged-application

【问题讨论】:

  • 为什么要在 CPP/CLI getText 函数中创建 Bridge 的新实例,如果我理解正确,那不会重置 C# 类中的 _text 对象吗?
  • 您正在 getText 和 setText 函数中创建新的 Bridge 实例。这当然行不通,因为您刚刚创建了对象,getText 将始终返回一个空字符串。您的包装器需要一个 Bridge 实例作为成员。查看您最喜欢的关于封装的 C++ 语言书籍。
  • 我也在想这可能是个问题。谢谢你指点。我有 gcroot _managedObject ;我应该用那个吗? (请参阅编辑后的帖子)您对我在 setText 中传递的字符串有何看法;我应该 gcnew 吗?
  • 如何在控制台上显示文本?您是否调试过代码并确认返回的字符串实际上是空的?
  • @RedSerpent 。我确实放置了一些 Console.Writeline 语句来检查流程,并且流程符合预期 - C++ 到 C++/CLI 到 C#。

标签: c# c++ c++-cli


【解决方案1】:

COM 是你的朋友:

在 C# 中创建一个接口

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;


namespace CsharpLibrary
{
   // Since the .NET Framework interface and coclass have to behave as 
   // COM objects, we have to give them guids.
   [Guid("56A868B1-0AD4-11CE-B03A-0020AF0BA770"),
    InterfaceType(ComInterfaceType.InterfaceIsDual)]
   public interface IStringHolder
   {
      String GetText();
      void SetText(String s);
   }
}

实现C#接口:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace CsharpLibrary
{
   [Guid("C6659361-1625-4746-931C-36014B146679")]
   public class MyStringHolder : IStringHolder
   {
      String _text;

      public String GetText()
      {
         return this._text;
      }

      public void SetText(String value)
      {
         _text = value;
      }

   }
}

从 C++ 创建和调用您的 C# 对象

#include <windows.h>
#include <stdio.h>

#pragma warning (disable: 4278)

// To use managed-code servers like the C# server, 
// we have to import the common language runtime:
#import <mscorlib.tlb> raw_interfaces_only


#pragma warning (disable: 4278)

// To use managed-code servers like the C# server, 
// we have to import the common language runtime:
#import <mscorlib.tlb> raw_interfaces_only

#import "..\CsharpLibrary\bin\Debug\CsharpLibrary.tlb" no_namespace named_guids





int main(int argc, char* argv[])
{
   HRESULT hr = S_OK;

   IStringHolder *pStringHolder = NULL;

   //
   // Initialize COM and create an instance of the InterfaceImplementation class:
   //
   CoInitialize(NULL);

   hr = CoCreateInstance(   __uuidof(MyStringHolder),
                           NULL,
                           CLSCTX_INPROC_SERVER,
                           __uuidof(IStringHolder),
                           reinterpret_cast<void**>(&pStringHolder));

   if(SUCCEEDED(hr))
   {
      _bstr_t sHelloWorld = SysAllocString( L"Hello, World" );

      hr = pStringHolder->SetText(sHelloWorld);

      SysFreeString(sHelloWorld);
   }


   //
   // Be a good citizen and clean up COM
   //
   CoUninitialize();

   return hr;
}

在 C# 方面,您必须生成类型库并通过构建后事件注册类:

生成类型库: "$(FrameworkSDKDir)bin\NETFX 4.0 Tools\tlbexp.exe" "$(TargetPath)" /out:"$(TargetDir)$(TargetName).tlb"

注册课程: C:\Windows\Microsoft.NET\Framework\v4.0.30319\RegAsm.exe "$(TargetPath)"

享受吧!

【讨论】:

    【解决方案2】:

    我想通过 CLI/C++ 从 C++ 调用 C# 函数。

    等等...你想从 C# 调用一个 C++ 函数,对吧?这就是 C++/CLI 的优点。包装 C++ 代码以在托管环境中访问。如果您真的想从 C++ 调用 C# 代码,您应该查看 COM 注册您的 C# 代码。如果您为此使用 C++/CLI,您的整个 C++ 程序将被拖入 .NET 世界,您可以从一开始就使用 C#。

    在 C++/CLI 中,ref (.NET) 类的整个公共类接口应该只包含托管类型。那将是 System::String^ 而不是 std::string

    【讨论】:

    • 不,我想从 C++ 调用 C# 函数。它也被称为反向 Pinvoke。我已经在我的问题中证明了一个链接,请看一下。 C++ 和 C# 中有一个现有的代码库,所以我必须使用 C++/CLI。我知道 String^ 部分 - 我已经解释了为什么我不使用它。正如其他 cmets 所指出的,我们可能需要 Bridge 类的引用;但我发现很难做到这一点。
    • “我已经证明”应该读作“我已经提供”。抱歉打错了。
    猜你喜欢
    • 1970-01-01
    • 2011-02-11
    • 1970-01-01
    • 1970-01-01
    • 2014-03-19
    • 2011-11-26
    • 1970-01-01
    • 2023-03-15
    • 1970-01-01
    相关资源
    最近更新 更多