C# 调用非托管 C++ 返回平方符号字符串

Posted

技术标签:

【中文标题】C# 调用非托管 C++ 返回平方符号字符串【英文标题】:C# call to unmanaged C++ returning string of squares symbols 【发布时间】:2009-10-29 14:59:41 【问题描述】:

我有一些 C# 代码调用非托管 C++ DLL。我正在调用的方法旨在接受一个字符串作为参考。为了处理这个问题,我传入了一个 StringBuilder,否则会出现 ***Exception。

这工作正常,但在某些调用中,从非托管代码返回的字符串是这样的混乱字符串:øŸE~.,Ê。我知道这一定与编码有关,但是我尝试了下面列出的几件事,但没有任何效果。这不是其他人编写的用于执行类似操作的 VB.Net 代码中的问题。

这是我尝试过的: 1. 我正在使用这个:[DllImport("dmphnx32.dll")],但是尝试了所有字符集选项都没有成功。

    尝试使用 Encoding.Default.GetBytes、Encoding.ASCII、Encoding.Unicode 等都没有成功。

我没有任何 C++ 经验,所以我可以真正使用帮助。

这是 DLLIMport 方法:

[DllImport("dmphnx32.dll")]
        public static extern int PhxQueryDataAttributes(int handle,
                                        StringBuilder lTableName,
                                        StringBuilder lColumnName,
                                        ref short lIteration,
                                        ref short type,
                                        ref short maxLen,
                                        ref short endorsement,
                                        StringBuilder endorsementId);

这是 C++ 代码:

short DMEXP PhxQueryDataAttributes(HWND handle,
                                   char *lTableName,
                                   char *lColumnName,
                                   short *lIteration,
                                   short *Type,
                                   short *MaxLen,
                                   short *Endorsement,
                                   char  *EndorsementID)


    handle = PhxInfo.HiddenHwnd;
    strcpy(lTableName, PhxInfo.TableName);
    strcpy(lColumnName, PhxInfo.ColumnName);


    *Type = PhxInfo.PhnxDataType;
    // max len
    *MaxLen = PhxInfo.MaxDataLen;
    *Endorsement = PhxInfo.Endorsement;
    strcpy(EndorsementID, PhxInfo.EndorsementID);
    // determine which table we need the iteration of
    *lIteration = PhxIterationArray[PhxInfo.sEffectiveTableID];

    return SUCCESS;

这是调用非托管代码的 C# 代码:

public int PhxQueryDataAttributes(int handle, ref string lTableName, ref string lColumnName, 
            ref short lIteration, ref short type, ref short maxLen, ref short endorsement, 
            ref string endorsementId)
        
            var sbTableName = new StringBuilder();
            var sbColName = new StringBuilder();
            var sbEndId = new StringBuilder();

            var ret = RatingProxy.PhxQueryDataAttributes(handle, sbTableName, sbColName,
            ref lIteration, ref type, ref maxLen, ref endorsement, sbEndId);


            lTableName = sbTableName.ToString();
            lColumnName = sbColName.ToString();
            endorsementId = sbEndId.ToString();
return ret;

谢谢, 科里

【问题讨论】:

您能否发布您尝试调用的 C++ 方法的声明? 如果您可以使用 C++ 代码(并且足够小可以发布),请发布。 另外发布调用代码的 sn-p 【参考方案1】:

好的,我明白你的问题了。你需要为StringBuilder分配一个容量,你不能只使用默认值。

【讨论】:

这看起来很有希望它的简单性,但不幸的是它没有工作。我仍然得到相同的奇怪编码值。【参考方案2】:

您可以尝试使用 IntPtr 和 Marshal 类来代替 StringBuilder。

所以,它看起来像这样:

[DllImport("dmphnx32.dll")]
    public static extern int PhxQueryDataAttributes(int handle,
                                    IntPtr lTableName,
                                    IntPtr  lColumnName,
                                    ref short lIteration,
                                    ref short type,
                                    ref short maxLen,
                                    ref short endorsement,
                                    IntPtr endorsementId);

调用代码是:

public int PhxQueryDataAttributes(int handle, ref string lTableName, ref string lColumnName, 
        ref short lIteration, ref short type, ref short maxLen, ref short endorsement, 
        ref string endorsementId)
    
        var sbTableName = Marshal.AllocHGlobal(1024);//Change these to the max length possible for each string.
        var sbColName = Marshal.AllocHGlobal(1024);
        var sbEndId = = Marshal.AllocHGlobal(1024);

        var ret = RatingProxy.PhxQueryDataAttributes(handle, sbTableName, sbColName,
        ref lIteration, ref type, ref maxLen, ref endorsement, sbEndId);


        lTableName = Marshal.PtrToStringAnsi(sbTableName);
        lColumnName = Marshal.PtrToStringAnsi(sbColName);
        endorsementId = Marshal.PtrToStringAnsi(sbEndId);
        return ret;

通过这种方式,您可以指定正确的编码(使用字符串生成器无法做到这一点,它假定它是 UTF 类型编码)。尝试一下,看看 Marshal.PtrToString... 中的哪一个有效,尽管 ANSI 应该有效,因为这是大多数 C++ 库使用的。

【讨论】:

我尝试了这种方法并尝试了不同类型的编码,但我仍然得到了我的编码字符串。我想知道是否发生了其他事情? 您是否注意到 C++ 版本使用 'handle' 作为 HWND,而您将其用作 int?据我所知,HWND 是一个 Int64(很长)。我唯一能想到的另一件事是,也许你有对齐问题。【参考方案3】:

在我尝试了前 2 个答案并得知它们没有帮助之后,我知道肯定还有其他问题。我在我的应用程序的其他地方发现了一个小错误,实际上我缺少非托管代码的初始化参数。这导致了我奇怪的编码字符串。

感谢您的帮助, 科里

【讨论】:

以上是关于C# 调用非托管 C++ 返回平方符号字符串的主要内容,如果未能解决你的问题,请参考以下文章

c# - 如何调用分配输出缓冲区以在c#中返回数据的非托管c++函数?

调用使用 C# 返回结构数组的非托管 dll 函数

让非托管 c++ 代码调用调用 c# 代码的托管 c++ 代码

未导出成员函数时,从 C# 调用 C++ 本机/非托管成员函数

我是不是需要在 C# 中使用来自非托管 C++ dll 的原始数据类型执行 MarshalAs?

从非托管 c++ 调用 C# 函数(通过托管包装器)