通过 DllImport 调用非托管函数时堆损坏

Posted

技术标签:

【中文标题】通过 DllImport 调用非托管函数时堆损坏【英文标题】:Corrupted heap when calling unmanaged function via DllImport 【发布时间】:2014-08-06 15:19:16 【问题描述】:

我正在使用 C# 应用程序中用 C/C++ 编写的非托管 dll。我有兴趣使用 dll 中的以下函数:

    static void StorePath(const std::string& path, wchar_t *out_path,
     int *out_path_length)
      wcslcpy(out_path, c_str_w(path), *out_path_length);
      *out_path_length = path.size();
     

    int WINAPI BrowseForDirectory(
     int allow_portable, int allow_online,
      wchar_t *t_directory, int *e_directory_length,
       wchar_t *m_directory, int *m_directory_length)
     .
     .
     . //initializing new forms and checking product keys

    StorePath(form->SelectedEDirectory().TopDir(), e_directory,
     e_directory_length);
    StorePath(form->SelectedMDirectory(), m_directory,
     m_directory_length);
    

头文件:

    #if defined(_WIN32) && !BUILD_WITHOUT_DLLS &&!defined(ECLIPSE_CBUILDER_WORKAROUNDS)
    # if BUILDING_EXPORT_LIBRARY
    #  define EXPORT_DLL __declspec(dllexport)
    # else
    #  define EXPORT_DLL __declspec(dllimport)
    # endif
    #else
    #  define EXPORT_DLL
    #endif

    extern "C" 
        int WINAPI BrowseForDirectory(
         int allow_portable, int allow_online,
          wchar_t *t_directory, int *e_directory_length,
           wchar_t *m_directory, int *m_directory_length)
    

然后,我尝试通过执行以下操作在我自己的托管 C# 类库中调用此函数:

    [DllImport("MyDLL.dll", CharSet = CharSet.Ansi)]
    public static extern int BrowseForDirectory(Int32 allowOnline, 
     Int32 allowPortable,
      [MarshalAs(UnmanagedType.LPStr)] StringBuilder eDirectory, 
       ref Int32 eDirLength, 
        [MarshalAs(UnmanagedType.LPStr)] StringBuilder mDirectory, 
         ref Int32 mDirLength);

最后,我尝试在 C# 应用程序中使用它,方法是:

    var eDir = new StringBuilder(260);
    var mDir = new StringBuilder(260);
    var eDirLength = eDir.Length;
    var mDirLength = mDir.Length;
    try
    
        var result = Viewer.BrowseForDirectory(1, 1, eDir, 
         ref eDirLength, mDir, ref mDirLength);
    
    catch(Exception ex)
    
        MessageBox.Show(ex.ToString());
    

但是,我遇到了堆损坏,但现在我的应用程序正在退出,因为 STATUS_STACK_BUFFER_OVERRUN - 与嵌入式断点有关。更改 C++ 代码不是一种选择。我有适当的参考和程序集。

我做错了什么?

【问题讨论】:

因为字符串是宽字符,你应该使用UnmanagedType.LPWStr 不是谁投了反对票。我试图投票,但没有代表。我尝试更改调用约定和字符集属性,但都不起作用 我不知道他为什么也否决了我的问题... 如果有人知道怎么做,我仍然需要帮助! @edtheprogrammerguy WINAPI 扩展为 __stdcallCallingConvention.StdCall 一直是框架桌面版本的默认设置。我没有对您的答案投反对票,但如果我在您删除它之前看到它,我肯定会。这完全是错误的,并且投反对票很好。您评论了 我的回答来自该领域的丰富经验 经验不相关。重要的是正确性。无论我们中的任何人多么有经验,我们都会犯错误。 【参考方案1】:

我可以看到的问题是您的字符集不匹配。非托管代码将文本返回为 UTF-16,但您的 p/invoke 指定 ANSI 编码的文本。将 p/invoke 更改为:

[DllImport("MyDLL.dll", CharSet = CharSet.Unicode)]
public static extern int BrowseForDirectory(
    int allowOnline, 
    int allowPortable,
    StringBuilder eDirectory, 
    ref int eDirLength, 
    StringBuilder mDirectory, 
    ref int mDirLength
);

我假设 c_str_w() 采用 8 位编码字符串并返回指向以空结尾的 wchar_t 数组的指针。

【讨论】:

以上是关于通过 DllImport 调用非托管函数时堆损坏的主要内容,如果未能解决你的问题,请参考以下文章

堆已损坏:调用非托管函数时

函数调用导致堆栈不对称。原因可能是托管的 PInvoke 签名与非托管的目标签名不匹配。

C# DllImport“调用导致堆栈不对称。原因可能是托管的 PInvoke 签名与非托管的目标签名不匹配。请检查 PInvoke 签名的调用约定和参数与非托管的目标签名是否匹配 ”

从工厂函数返回右值引用时堆/内存损坏

使用 DLLImport 找不到非托管 DLL

[.NET] 平台调用(P/Invoke) 与 DllImport 使用的相关讲解与注意事项,