【问题标题】:C# DllImport troubleC# DllImport 麻烦
【发布时间】:2011-05-24 12:33:40
【问题描述】:

我的问题有点笼统,所以我不是在寻找确切的答案,但可能一些研究方向会对我有所帮助...

在我的工作场所,我主要使用 C# 进行编程。 我们有这家与我们合作的第 3 方公司,它为我们提供了我们需要使用的 Native C++ dll。 由于我需要的 C++ 方法没有以易于从 C# 引用的方式公开,因此我将 dll 包装在另一个 Native C++ Dll 中。

所以现在我有 2 个 Native C++ dll,一个包裹另一个。

我创建了一个小型 C# 控制台应用程序,它调用我在 C++ 中创建的方法。 我的方法签名如下所示:

[DllImport("HashMethodWrapper.dll")]
[return: MarshalAs(UnmanagedType.LPStr)]
private static extern string CreateHash(
            string input,
            [MarshalAs(UnmanagedType.LPStr)]StringBuilder output);

在我的控制台应用程序中,一切正常,我总是收到我期望的字符串。

但是当我将它移动到我创建的 Web 服务或 Web 应用程序时(因为这是我真正需要它的地方),我看到我收到的字符串是垃圾,甚至不一致。似乎我只是得到了一些对丢失的记忆的引用或类似的东西,但这只是我的猜测......

我不知道为什么会这样,因为在我的控制台应用程序中一切正常。

有没有人有方向可以帮助我???...

提前致谢, 吉利布

编辑: 我认为这可能与一些未固定的对象有关,所以我尝试在固定语句中调用该方法,例如:

unsafe public static string CreateHashWrap(string pass)
{
    String bb;
    StringBuilder outPass = new StringBuilder();
    fixed (char* resultStr = CreateHash(pass, outPass))
    {
        bb = new String(resultStr);
    }
    return bb;
}

...但这仍然没有为我做。这是固定对象的正确方法吗?

第二次编辑: C++ 中的方法签名如下所示:

extern "C" __declspec(dllexport) char *CreateRsaHash(char *inputPass, char *hashPass);

第三次编辑: 我将方法的签名更改为

extern "C" __declspec(dllexport) bool CreateRsaHash(char *inputPass, char *hashPass);

我要查找的返回值放在*hashPass参数中。

现在,我创建了一个简单的控制台应用程序来测试它。当在我的主类中插入 DllImport 并直接调用该方法时,一切正常,但是当我移动 DllImport 并将该方法包装在另一个类中并从控制台的“Main”方法中调用该类时,我得到了 StackOverflow 异常!

有人知道为什么会这样吗??

【问题讨论】:

  • 你能告诉我们函数头是如何在 C/C++ 中声明的吗?这可能会有所帮助:)
  • 杀死API作者,有很明显的内存泄漏发生...
  • 为什么???你能解释一下为什么吗,为什么从签名上看这么清楚??希望我们仍然可以修复它!
  • 返回指向任何非常量“对象”的指针是一个确定的信号...方法 sig 可能是“错误的”,但从它的外观来看,它无论如何都不会返回一个常量值......也许确实如此,我不知道,但它只是用斧头刺伤了我的眼睛......
  • @gillyb:如果可能的话,但我不知道如何,因为我可以推断出每次调用的返回值都会改变。最好的方法是传递预先分配的缓冲区(又名具有容量的 StringBuilder)以及指定每个传递的缓冲区的大小。否则你可以使用固定的字节/字符[]。

标签: c# asp.net pinvoke unmanaged


【解决方案1】:

在将 specifying the capacity of the StringBuilder 传递给您的互操作方法之前尝试一下。

【讨论】:

    【解决方案2】:

    从稀疏的信息中很难知道,但如果我不得不猜测,我会说你需要确保你固定输出对象。另外我可能会将输出参数更改为其他类型,StringBuilder 坦率地工作似乎很奇怪。

    我知道如果你分配一个对象,它会得到一个指针,但这并不意味着它不会移动。因此,如果您尝试将指向托管对象的指针传递到非托管环境中,您需要确保告诉 GC“固定”内存,以免它从您下方移出。

    这是我所说的固定的一个非常粗略的版本:

    string input = "...";
    StringBuilder output = new StringBuilder();
    var handle = System.Runtime.InteropServices.GCHandle.Alloc(output, GCHandleType.Pinned);
    try
    {
        CreateHash(input, output);
    }
    finally
    {
        handle.Free();
    }
    

    【讨论】:

    • 嗯,关于 StringBuilder,我在某处读到,如果值发生变化,我需要一个 stringbuilder,因为 'String' 是不可变的,在这种情况下不会完成这项工作。这是真的吗?
    • 关于固定对象,我认为这可能是问题所在,所以我尝试做一些固定。我会把它添加到我的问题中,你能告诉我我所做的和你用 GCHandle.Alloc 展示给我的有什么区别吗??
    • 两件事:您已固定 char* 但未固定 StringBuilder。另外,为什么你有 StringBuilder,它被称为“输出”,也是一个返回值?似乎您完全忽略了 StringBuilder 而只是返回返回值。
    • 是的,实际上,我不使用 StringBuilder 参数作为输出,我目前正在更改方法签名。我从该方法得到的真正输出是返回值。
    • @justin.m.chase:您的最后陈述是个人意见,不一定是最佳实践。我喜欢以相同的方式突出显示我的所有类型,所以选择大写版本,让所有关键字以不同的方式突出显示。
    【解决方案3】:

    我会考虑在 C# 共享程序集/dll 而不是 c++ dll 中变形,然后尝试让您的控制台应用程序与 dll 一起使用。无论如何,以这种方式包装外部依赖项是一种很好的做法。
    否则,一些传统问题是 32 位与 64 位,即共享库的加载路径。它真的只是一个字符串还是更复杂的东西?

    【讨论】:

      【解决方案4】:

      我找到了解决问题的方法,现在我觉得有点(如果不是真的!)愚蠢... :-|

      我使用 C++ 中的LoadLibrary() 方法从其他本机 dll 中动态调用方法。问题是我没有给该方法任何路径,而只是 dll 文件名。在 .net 中,它会在当前文件夹中进行搜索,但在本机代码中似乎无法以这种方式工作。

      我的编程实践中更大的问题显然是我没有完全涵盖我的原生 C++ dll 中的错误处理!

      我在这个页面上收到的所有回答都不是白费的......

      一旦我发现我的目录路径有问题,我就会遇到关于尝试访问损坏的内存等的不同异常。然后我需要创建固定对象,并为我的 StringBuilder 对象声明一个大小。

      感谢大家的帮助!!

      :)

      【讨论】:

        猜你喜欢
        • 2011-01-16
        • 1970-01-01
        • 1970-01-01
        • 2018-09-14
        • 1970-01-01
        • 1970-01-01
        • 2012-01-31
        • 2017-02-23
        • 2013-12-01
        相关资源
        最近更新 更多