【问题标题】:Calling c++ delegate from c# out of memory exception从c#调用c++委托内存不足异常
【发布时间】:2014-01-30 02:28:52
【问题描述】:

我为 c++ dll 创建了一个 c# 包装器,但我没有 c++ dll 的源代码。现在 c++ 有一个委托函数,我在 c# 包装器中创建了委托函数并为其提供了必要的参数。 面临的问题是,每当委托功能完成时,我都会收到内存不足异常,并且我还发现委托使用了一个新线程。我将演示下面的代码:

1) C# 包装器

public struct WISCN_RUN_OPTS
{
    public uint Version;
    public WISCN_CALLBACK_CODELINE_DONE CodelineDoneCallback;
}


[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public delegate bool WISCN_CALLBACK_CODELINE_DONE(
    uint doc_index,
    uint user_data,
    uint codelines_count,
    WISCN_CODELINE[] codelines,ref
    WISCN_CODELINE_DOC_CTRL p_doc_ctrl);

[DllImport(@"wiscn.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
public static extern WISCN_ERROR WiScn_Run(_WISCN_HINST_STRUCT hinst, uint num_of_docs, ref uint p_docs_done, ref WISCN_RUN_OPTS p_opts);

2) C# windows 应用程序

private void ButtonProcessDocsWithOcr_Click(object sender, EventArgs e)
{
    var run_opts = new DllLoad.WISCN_RUN_OPTS
    {
        Version = DefineConstants.WISCN_STRUCT_VERSION_RUN_OPTS
        CodelineDoneCallback = DocDoneCallback;
    };

    //The callback delegate is called when this method is triggered
    WiScn_Run(hinst, 0, ref docNum, ref run_opts);
}

bool DocDoneCallback(uint doc_index, uint user_data,
    uint codelines_count,
    WISCN_CODELINE[] codelines,ref
    WISCN_CODELINE_DOC_CTRL p_doc_ctrl)
{
    return false;
}//After this line i receive the out of memory exception 
 // when it tries to resume ButtonProcessDocsWithOcr_Click event.

3) C++ 封装头文件

typedef struct 
{
    DWORD   Version;
    WISCN_CALLBACK_CODELINE_DONE    CodelineDoneCallback;                                                              
}
WISCN_RUN_OPTS;

typedef BOOL (*WISCN_CALLBACK_CODELINE_DONE)(DWORD doc_index, 
    DWORD user_data, DWORD codelines_count, const WISCN_CODELINE codelines[],
    WISCN_CODELINE_DOC_CTRL *p_doc_ctrl);

typedef WISCN_ERROR (WISCN_API *WISCN_RUN)(WISCN_HINST hinst, DWORD num_of_docs, LPDWORD p_docs_done, const WISCN_RUN_OPTS *p_opts);

WISCN_ERROR WISCN_API WiScn_Run(WISCN_HINST hinst, DWORD num_of_docs, LPDWORD p_docs_done, const WISCN_RUN_OPTS *p_opts);

4) c++ 示例

BOOL DocDoneCallback(DWORD doc_index, DWORD user_data, 
    DWORD codelines_count, const WISCN_CODELINE codelines[], 
     WISCN_CODELINE_DOC_CTRL *p_doc_ctrl)
{
    return FALSE;
}
void main()
{
    WISCN_RUN _WiScn_Run;
    WISCN_RUN_OPTS run_opts;
    _WiScn_Run = (WISCN_RUN)GetProcAddress(hmod, "WiScn_Run");
    run_opts.Version = WISCN_STRUCT_VERSION_RUN_OPTS;
    run_opts.DocDoneCallback = DocDoneCallback;
    _WiScn_Run(hinst, 0, NULL, &run_opts);
}

【问题讨论】:

  • 我无法理解这一点。您谈论的是非托管代码,但这里的一切都是 C#。界面的另一边在哪里?
  • 这是用c++编写的dll文件,但我没有c++的源代码,但如果有帮助,我可以添加dll包装器的c++代码
  • 这是一个互操作问题。没有界面的两边是没有希望的。我们需要查看所有涉及的类型,包括 C++ 和 C#。以及所有函数声明。
  • 显示WiScn_Run 的C# 声明和WISCN_RUN 的C++ 声明。以及所有缺失的结构声明。

标签: c# c++ memory delegates


【解决方案1】:

您提到非托管代码创建了一个新线程。在非托管线程的上下文中调用回调对我来说似乎是合理的。致电GetCurrentThreadId 进行检查。

[DllImport("kernel32.dll")]
static extern uint GetCurrentThreadId();

在调用WiScn_Run 之前调用此函数,然后再调用DocDoneCallback。我假设您会发现您的回调在不同的线程中运行。

如果我的预感是正确的,那么您永远无法从 C# 调用该库。您需要做的是将 C++ 代码包装在混合模式 C++/CLI 包装器中,以便您可以在非托管代码中实现回调函数。

【讨论】:

    猜你喜欢
    • 2012-01-23
    • 2021-10-30
    • 2016-07-27
    • 2010-09-17
    • 1970-01-01
    • 2012-07-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多