草庐IT

c# - 从 C# 到 C++ 编码(marshal)数据类型的类型转换问题

coder 2024-02-22 原文

我目前正在开发一个 C# (.NET Framework 4.7.2) 应用程序,它使用非托管 C++ 库中的一些业务逻辑。我尝试在 C# 和 C++ 之间来回传递数据(互操作)。 我可能不会使用 C++/CLI,我的项目中不允许使用公共(public)语言运行时。

它适用于 int。不幸的是,一旦我尝试发送另一种数据类型,我就会收到转换错误,例如float 4.2f 变为 1string "fourtytwo"变为 -1529101360

我的 C# 代码如下所示:

// works fine, creates an instance of TestClass    
var test = TestProxy.Wrapper_Create("test"); 

// int, works fine, a = 42
var a = TestProxy.TryInt(test, 42);

// float, problem, b = 1
var b = TestProxy.TryFloat(test, 4.2f);

// string, problem, c = -159101360
var c = TestProxy.TryString(test, "fourtytwo");

我的 C# Interop Proxy 类调用 native (非托管)C++ 代码如下所示:

public static class TestProxy
    {
        private const string coreDLL = "test.core.dll";

        [DllImport(coreDLL, CallingConvention = CallingConvention.Cdecl)]
        public static extern IntPtr Wrapper_Create(string name);

        [DllImport(coreDLL, EntryPoint = "?TryInt@TestClass@@XXX@X", CallingConvention = CallingConvention.ThisCall)]
        public static extern int TryInt(IntPtr instance, int n);

        [DllImport(coreDLL, EntryPoint = "?TryFloat@TestClass@@XXX@X", CallingConvention = CallingConvention.ThisCall)]
        public static extern int TryFloat(IntPtr instance, float n);

        [DllImport(coreDLL, EntryPoint = "?TryString@TestClass@@XXX@X", CallingConvention = CallingConvention.ThisCall)]
        public static extern int TryString(IntPtr instance, string n);

    }

我的原生(非托管)C++ 看起来像这样: 头文件:

#ifdef TESTCORE_EXPORTS
#define TESTCORE_API __declspec(dllexport)
#endif

#pragma once

extern "C"
{
    class TESTCORE_API TestClass
    {
    private:
        char* name;
    public:
        TestClass(char*);
        int TryInt(int);
        float TryFloat(float);
        char* TryString(char*);
    };

    TESTCORE_API TestClass* Wrapper_Create(char* name);
}

实现文件:

#include "stdafx.h"

#include "TESTCore.h"

TestClass::TestClass(char* n) 
{
    name = n;
}

int TestClass::TryInt(int n) 
{
    return n; // works fine
}

float TestClass::TryFloat(float n)
{
    return n; // something goes wrong here
}

char* TestClass::TryString(char* n)
{
    return n; // something goes wrong here
}

extern "C"
{
    TESTCORE_API TestClass * Wrapper_Create(char* name)
    {
        return new TestClass(name);
    }

    TESTCORE_API int TryInt(TestClass * instance, int n) 
    {
        if (instance != NULL) 
        {
            return instance->TryInt(n);
        }
    }

    TESTCORE_API float TryFloat(TestClass * instance, float n) 
    {
        if (instance != NULL) 
        {
            return instance->TryFloat(n);
        }
    }

    TESTCORE_API char* TryString(TestClass * instance, char* n) 
    {
        if (instance != NULL)
        {
            return instance->TryString(n); 
        }
    }
}

您知道如何正确地将 float 、字符串从 C# 编码到 C++ 并返回吗?

谢谢!

最佳答案

C++ 没有标准的 ABI。跨 DLL 使用 C++ 类很少是个好主意,即使双方使用相同的语言也是如此。

有更好的方法。

  1. 替换你的 __thiscall具有全局函数的类方法,cdecl 或 stdcall 随你喜欢(但请注意 C# 和 C++ 有不同的默认值,如果你什么都不做,C++ 将使用 cdecl,C# 将作为 stdcall 导入)。您可以在第一个参数(C# 中的 IntPtr)中传递类的“this”指针,就像您现在所做的那样。另外,如果您要写 extern "C"或者使用模块定义文件,它们将具有人类可读的名称。

  2. 如果您需要对象,请使用 COM。声明一个继承自 IUnknown 的接口(interface),用 C++ 实现它(我通常使用 ATL),并导出一个全局函数来创建该对象的一个​​实例(ATL 中有 2 行,CComObject<T>::CreateInstance 后面跟着 AddRef)。无需注册,类型库,你只需要实现 IUnknown(但是 see this 如果你想从多个线程使用它们)

更新:字符串确实更难。申请[MarshalAs(UnmanagedType.LPTStr)]到论点。申请[return: MarshalAs(UnmanagedType.LPTStr)]到功能。指定 PreserveSig=true在你的 DllImport 中。最后,修改 C++ 代码以返回字符串的拷贝,即调用 strlen然后CoTaskMemAlloc (不要忘记 '\0' )然后 strcpy .

处理字符串更简单的方法是这样的:

HRESULT TryString( TestClass *instance, BSTR i, BSTR *o )

至少有CComBSTR_bstr_t处理内存管理的内置类。

关于c# - 从 C# 到 C++ 编码(marshal)数据类型的类型转换问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54091053/

有关c# - 从 C# 到 C++ 编码(marshal)数据类型的类型转换问题的更多相关文章

  1. ruby-on-rails - 在 Rails 中将文件大小字符串转换为等效千字节 - 2

    我的目标是转换表单输入,例如“100兆字节”或“1GB”,并将其转换为我可以存储在数据库中的文件大小(以千字节为单位)。目前,我有这个:defquota_convert@regex=/([0-9]+)(.*)s/@sizes=%w{kilobytemegabytegigabyte}m=self.quota.match(@regex)if@sizes.include?m[2]eval("self.quota=#{m[1]}.#{m[2]}")endend这有效,但前提是输入是倍数(“gigabytes”,而不是“gigabyte”)并且由于使用了eval看起来疯狂不安全。所以,功能正常,

  2. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

    我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

  3. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  4. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  5. ruby - 将数组的内容转换为 int - 2

    我需要读入一个包含数字列表的文件。此代码读取文件并将其放入二维数组中。现在我需要获取数组中所有数字的平均值,但我需要将数组的内容更改为int。有什么想法可以将to_i方法放在哪里吗?ClassTerraindefinitializefile_name@input=IO.readlines(file_name)#readinfile@size=@input[0].to_i@land=[@size]x=1whilex 最佳答案 只需将数组映射为整数:@land边注如果你想得到一条线的平均值,你可以这样做:values=@input[x]

  6. ruby - 将散列转换为嵌套散列 - 2

    这道题是thisquestion的逆题.给定一个散列,每个键都有一个数组,例如{[:a,:b,:c]=>1,[:a,:b,:d]=>2,[:a,:e]=>3,[:f]=>4,}将其转换为嵌套哈希的最佳方法是什么{:a=>{:b=>{:c=>1,:d=>2},:e=>3,},:f=>4,} 最佳答案 这是一个迭代的解决方案,递归的解决方案留给读者作为练习:defconvert(h={})ret={}h.eachdo|k,v|node=retk[0..-2].each{|x|node[x]||={};node=node[x]}node[

  7. ruby - 用逗号、双引号和编码解析 csv - 2

    我正在使用ruby​​1.9解析以下带有MacRoman字符的csv文件#encoding:ISO-8859-1#csv_parse.csvName,main-dialogue"Marceu","Giveittohimóhe,hiswife."我做了以下解析。require'csv'input_string=File.read("../csv_parse.rb").force_encoding("ISO-8859-1").encode("UTF-8")#=>"Name,main-dialogue\r\n\"Marceu\",\"Giveittohim\x97he,hiswife.\"\

  8. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  9. ruby - Infinity 和 NaN 的类型是什么? - 2

    我可以得到Infinity和NaNn=9.0/0#=>Infinityn.class#=>Floatm=0/0.0#=>NaNm.class#=>Float但是当我想直接访问Infinity或NaN时:Infinity#=>uninitializedconstantInfinity(NameError)NaN#=>uninitializedconstantNaN(NameError)什么是Infinity和NaN?它们是对象、关键字还是其他东西? 最佳答案 您看到打印为Infinity和NaN的只是Float类的两个特殊实例的字符串

  10. ruby - 检查方法参数的类型 - 2

    我不确定传递给方法的对象的类型是否正确。我可能会将一个字符串传递给一个只能处理整数的函数。某种运行时保证怎么样?我看不到比以下更好的选择:defsomeFixNumMangler(input)raise"wrongtype:integerrequired"unlessinput.class==FixNumother_stuffend有更好的选择吗? 最佳答案 使用Kernel#Integer在使用之前转换输入的方法。当无法以任何合理的方式将输入转换为整数时,它将引发ArgumentError。defmy_method(number)

随机推荐