【问题标题】:Unmanaged DLL in C# not working properlyC# 中的非托管 DLL 无法正常工作
【发布时间】:2011-06-19 01:53:20
【问题描述】:

我一直在研究使用 OpenCV 进行一些跟踪的 DLL。使用 VS 2008(用于测试目的)在 C 控制台项目上完成所有工作。然后我创建了一个新的 DLL 项目并编译它。我设置了所有东西,所以我只在 Thread 类上放了一个函数来调用一个函数。

然后我为 GUI 和其他东西创建了一个 C# 项目。 DLL 加载正常(使用System.Runtime.InteropServices,方法启动(我可以看到 OpenCV 创建的捕获窗口)但没有完成跟踪。为了验证 DLL 是否实际工作,我在 Python 上加载并调用该方法和它运行良好(正在跟踪)。

我不熟悉在托管代码上使用非托管 DLL。关于我做错了什么或如何调试它的任何想法?如果您需要其他任何东西来帮助我解决这个问题,我会提供。

提前致谢。

编辑:

我没有使用 DLL 上的类,我使用的是单个函数,Thread 类来自 C# 上的 System.Threading

我使用DLL的方式是这样的。

namespace GUI
{
    static class NativeTracking
    {
        [DllImport(@"__Tracking.dll")]
        public static extern void _Tracking();
    }
}

我把它放在像他这样的线程上

public GUI()
{
    InitializeComponent();
    _tracking = new Thread(_Tracking);
    _tracking.Start();
}

public _Tracking()
{
    while(True)
    {
        NativeTracking.Tracking();
    }
}

编辑:本机编码

本机代码,抱歉格式混乱。

头文件

#include <cv.h>
#include <stdio.h>
#include <ctype.h>
#include <windows.h>
#include <highgui.h>
#include "..\original\project\myheader.h"
#include "..\original\project\myheader1.h"
#include "..\original\project\myheader2.h"
#include "..\original\project\myheader3.h"
#include "..\original\project\myheader4.h"
#ifdef __cplusplus
extern "C"{
#endif

    _declspec(dllexport) void Tracking();

#ifdef __cplusplus
}
#endif

实施

#include "exposed.h"

void Tracking( )
{
int flag = 1, i=0;
iplImgs imgs;
trackingTool tools;
helperTools helperTools;

CvCapture* capture = 0;
capture = cvCaptureFromCAM( 0 );

cvSetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH, 320);
cvSetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT, 240);
cvSetCaptureProperty(capture, CV_CAP_PROP_FPS, 20.0f);

imgs.image = 0;

    cvNamedWindow( "Window", 0 );
initHelperTools(&helperTools);
initTools(&imgs, &tools);

    for(;;){

    int c;
            IplImage* frame = 0;

    frame = cvQueryFrame( capture );        
    if( !frame )
        break;

    if( !imgs.image ){
            /* allocate all the buffers */
            prepareImages(&imgs, &tools, frame);
        }

        cvCopy( frame, imgs.image, 0 );
        cvCvtColor( imgs.image, imgs.grey, CV_BGR2GRAY );

        if( flag == 1 || conditionB ){
        someOperations(&imgs, &tools);
        if (conditionC)
            flag = 0;
        }
    else if( conditionD ){
        otherOps(&helperTools, imgs.grey);
        someTrack(&imgs, &tools, &helperTools, drawPoints);
        someCorrections(&tools);
        if ( condition ){
            if (!wasReset){
                wasReset = 0;
                continue;
            }
            if ( validation )
            someMoreOperations(&tools, corretions);
        }
    }   

    bufferHandlingOps(&imgs);


        c = cvWaitKey(10);
        if( (char)c == 27 )
            break;
    switch ((char)c){
        case 'p':
            drawPoints ^= 1;
            break;
        default:
            ;
    }
    }

    cvReleaseCapture( &capture );
    cvDestroyWindow("Window");
}

【问题讨论】:

  • C 没有类,所以要么你没有线程类,要么你没有使用 C。
  • 我没有使用DLL上的类,我使用的是单个函数,Thread类来自C#上的System.Threading
  • 您可以编辑问题以包含您的本机“跟踪”方法吗?

标签: c# .net c dll opencv


【解决方案1】:

我看到的一个问题是您在本机代码中使用默认的 C 调用约定,即“__cdecl”,而在您的 P/Invoke“DLLImport”属性中,您让它使用默认调用对于 C#,它是“__stdcall”。每当您调用本机方法时,这可能会引发运行时异常。

另外,我意识到您没有传递任何参数或期望任何结果,但调用约定确实表示函数的修饰方式,这可能导致name mangling 问题。 你可以:

  1. 将本机导出更改为“__declspec(dllexport) void __stdcall Tracking();”
  2. 或更改 DLLImport 属性上的“CallingConvention”属性:[DllImport(@"__Tracking.dll") CallingConvention = CallingConvention=CallingConvention.Cdecl]

【讨论】:

  • 嗨,我试过这个,编码结束像这样__declspec(dllexport) void __stdcall Tracking(); 如果我改变了这个顺序 DLL 将不会编译[DllImport(@"CamComtrolDll.dll", CallingConvention = CallingConvention.StdCall)] 如果我使用CallingConvention.Cdelc 我得到EntryPointNotFound 异常。 DLL 应该是一个黑盒子,没有接收到输入并直接输出到系统,但可以对其进行修改以返回一些值并在托管源上处理
  • @Loki 你是对的,这是我的示例中的一个错字,我已修复它。调用约定必须介于返回类型和方法名称之间。重要的是两者都使用相同的调用约定,所以只进行我建议的更改之一,而不是两者,否则它将与您现在的相同。我也意识到这是一个黑盒例程,调用约定用于平衡堆栈与函数参数,但是我提到它是因为可能的名称修改。我还创建了一个与您的设置完全相同的示例项目(显然减去了 opencv 的内容)
  • 我没有遇到任何问题,一切都按预期工作。您是否尝试过在不创建新线程的情况下执行它来缩小错误的可能原因?
【解决方案2】:

DllImport (P/Invoke) 的一个问题是类型不安全。你知道它只在运行时工作,因为你没有办法在编译时检查它,如果:

  • 方法的签名是正确的;
  • 方法的修改是正确的。

它在 WIN32 方法中非常流行,因为您会发现很多有效的示例,但是对于您正在使用的 lib,这是一个尝试和尝试直到它有效或者您发现阻碍您的特定问题的问题(记住验证 dll 在运行时与可执行文件位于同一目录或系统目录中)。

我的建议是在 C++/CLI 中创建一个合适的包装器。它没有上述问题,并且在出现问题时更容易调试。 Here 是我写的一个更复杂的例子,用于 C 中的回调以在 C# 中公开。

【讨论】:

  • +1 建议 C++/CLI 包装器。这似乎是开始解决这个问题的最简单方法。
猜你喜欢
  • 2015-09-18
  • 2013-11-10
  • 2010-12-17
  • 1970-01-01
  • 2013-12-15
  • 1970-01-01
  • 1970-01-01
  • 2010-09-19
  • 1970-01-01
相关资源
最近更新 更多