从 C# 使用 C++ DLL 时出现 AccessViolationException [英] AccessViolationException when using C++ DLL from C#

查看:68
本文介绍了从 C# 使用 C++ DLL 时出现 AccessViolationException的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个用于 C# 的 C++ DLL.我有一个函数,它接受一个传递给它的字符串,我在 C++ 函数参数上将这些设置为 const char *,如下所示:

I have a C++ DLL for use from C#. I have a function which takes a string passed to it, and I have those set on the C++ function parameters as const char * like so:

int __stdcall extract_all_frames(const char* szDestination, float scaleFactor)

这个函数的主体是直接从一个正在运行的 FFmpeg 示例函数中复制的,所以我几乎可以肯定问题不存在.我觉得问题出在我对它所做的修改中:

The main body of this function is copied directly from a working FFmpeg example function so I'm almost certain the problem isn't there. I feel like the problem is in this modification I made to it:

//Open file
char szFilename[32];
sprintf_s(szFilename, sizeof(szFilename), "frame%d.ppm", iFrame);

// JOIN szFILENAME AND szDESTINATION
std::string buffer(szDestination, sizeof(szDestination));
buffer.append("\");
buffer.append(szDestination);

这应该是一个连接的路径和目录.然后我将 buffer.c_str() 传递给 fopen_s(),它需要 const char * 而不是 std::string>.每当从 C# 调用此函数时,我都会收到以下异常:

Which is supposed to be a concatenated path and directory. I then pass buffer.c_str() into fopen_s(), which takes const char * not std::string. Whenever calling this function from C#, I get the following exception:

A first chance exception of type 'System.AccessViolationException' occurred in XRF FFmpeg Invoke Test.exe

Additional information: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

这是完整的代码:

#include "stdafx.h"
#pragma comment (lib, "avcodec.lib")
#pragma comment (lib, "avformat.lib")
#pragma comment (lib, "avutil.lib")
#pragma comment (lib, "swscale.lib")

extern "C"
{
    #include <libavcodecavcodec.h>
    #include <libavformatavformat.h>
    #include <libavutilavutil.h>
    #include <libswscaleswscale.h>
}

#include <string>
#include "Xrf.FFmpeg.hpp"

void save_frame(AVFrame* pFrame, int iFrame, const char* szDestination)
{
    //Open file
    char szFilename[32];
    sprintf_s(szFilename, sizeof(szFilename), "frame%d.ppm", iFrame);

    // JOIN szFILENAME AND szDESTINATION
    std::string buffer(szDestination, sizeof(szDestination));
    buffer.append("\");
    buffer.append(szDestination);

    FILE* pFile;
    errno_t openError = fopen_s(&pFile, buffer.c_str(), "wb");

    if (pFile == NULL)
    {
        return;
    }

    //Write header
    int width = pFrame->width;
    int height = pFrame->height;

    fprintf(pFile, "P6
%d %d
255
", width, height);

    //Write pixel data
    for (int y = 0; y < height; y++)
    {
        fwrite(pFrame->data[0] + y * pFrame->linesize[0], 1, width * 3, pFile);
    }

    // Close file
    fclose(pFile);
}

    int __stdcall extract_all_frames(const char* szPath, const char* szDestination, float scaleFactor)
    {
        // Check if scaleFactor is valid
        if ((scaleFactor != 0.f) && 
            (scaleFactor > 3.f))
        {
            fprintf(stderr, "Xrf: Scale factor '%f' out of bounds!
Must be greater than 0, and less then or equal to 3.0.
", scaleFactor);
            return -1;
        }

        // Register all formats and codecs
        av_register_all();

        AVFormatContext* pFormatCtx;
        if (avformat_open_input(&pFormatCtx, szPath, nullptr, nullptr) != 0)
        {
            fprintf(stderr, "libavformat: Couldn't open file '%s'!
", szPath);
            return -1;
        }

        // Retrieve stream information
        if (avformat_find_stream_info(pFormatCtx, nullptr) < 0)
        {
            fprintf(stderr, "libavformat: Unable to find stream information!
");
            return -1;
        }

        // Dump information about file onto standard error
        av_dump_format(pFormatCtx, 0, szPath, 0);

        // Find the first video stream
        size_t i;
        int videoStream = -1;
        for (i = 0; i < pFormatCtx->nb_streams; i++)
        {
            if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
            {
                videoStream = i;
                break;
            }
        }
        if (videoStream == -1)
        {
            fprintf(stderr, "libavformat: No video stream found!
");
            return -1;
        }

        // Get a pointer to the codec context for the video stream
        AVCodecContext* pCodecCtx = pFormatCtx->streams[videoStream]->codec;

        // Scale the frame
        int scaleHeight = static_cast<int>(floor(pCodecCtx->height * scaleFactor));
        int scaleWidth = static_cast<int>(floor(pCodecCtx->width * scaleFactor));

        //Check if frame sizes are valid (not 0, because that's dumb)
        if (scaleHeight == 0 || scaleWidth == 0)
        {
            fprintf(stderr, "Xrf: Scale factor caused a zero value in either width or height!
");
            return -1;
        }

        // Find the decoder for the video stream
        AVCodec* pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
        if (pCodec == NULL)
        {
            fprintf(stderr, "libavcodec: Unsupported codec!
");
            return -1; // Codec not found
        }

        // Open codec
        AVDictionary* optionsDict = nullptr;
        if (avcodec_open2(pCodecCtx, pCodec, &optionsDict) < 0)
        {
            fprintf(stderr, "libavcodec: Couldn't open codec '%s'!
", pCodec->long_name);
            return -1;
        }

        // Allocate video frame
        AVFrame* pFrame = av_frame_alloc();
        // Allocate an AVFrame structure
        AVFrame* pFrameRGB = av_frame_alloc();

        if (pFrameRGB == NULL)
        {
            fprintf(stderr, "libavformat: Unable to allocate a YUV->RGB resampling AVFrame!
");
            return -1;
        }

        // Determine required buffer size and allocate buffer
        int numBytes = avpicture_get_size(PIX_FMT_RGB24, scaleWidth, scaleHeight);
        uint8_t* buffer = static_cast <uint8_t *> (av_malloc(numBytes * sizeof(uint8_t)));

        struct SwsContext* sws_ctx = sws_getContext(pCodecCtx->width,
            pCodecCtx->height,
            pCodecCtx->pix_fmt,
            scaleWidth,
            scaleHeight,
            PIX_FMT_RGB24,
            SWS_BILINEAR,
            nullptr, nullptr, nullptr);

        // Assign appropriate parts of buffer to image planes in pFrameRGB
        // Note that pFrameRGB is an AVFrame, but AVFrame is a superset
        // of AVPicture
        avpicture_fill(reinterpret_cast <AVPicture *> (pFrameRGB),
            buffer,
            PIX_FMT_RGB24,
            scaleWidth,
            scaleHeight);

        // Read frames and save first five frames to disk
        AVPacket packet;
        int frameFinished;
        while (av_read_frame(pFormatCtx, &packet) >= 0)
        {
            // Is this a packet from the video stream?
            if (packet.stream_index == videoStream)
            {
                // Decode video frame
                avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);

                // Did we get a video frame?
                if (frameFinished)
                {
                    // Convert the image from its native format to RGB
                    sws_scale(sws_ctx,
                        static_cast <uint8_t const * const *> (pFrame->data),
                        pFrame->linesize,
                        0,
                        pCodecCtx->height,
                        pFrameRGB->data,
                        pFrameRGB->linesize);

                    // Save the frame to disk
                    if (++i <= 5)
                    {
                        save_frame(pFrameRGB, i, szDestination);
                    }
                }
            }

            // Free the packet that was allocated by av_read_frame
            av_free_packet(&packet);
        }

        av_free(buffer); // Free the RGB image
        av_free(pFrameRGB);
        av_free(pFrame); // Free the YUV frame
        avcodec_close(pCodecCtx); // Close the codec
        avformat_close_input(&pFormatCtx); // Close the video file

        return 0;
    }

我不知道错误是在我的修改中(很可能,我对 C++ 非常陌生)还是其他代码,因为异常只在 C# 中的调用行上抛出,而不是在实际的 C++ 行上导致问题.

I don't know if the error is in my modification (most likely, I'm extremely new to C++), or the other code, as the exception only throws on the invocation line in C#, not the actual C++ line causing the problem.

推荐答案

这是错误的:

std::string buffer(szDestination, sizeof(szDestination));

szDestination 是一个指针,因此 sizeof(szDestination) 将返回指针大小,以字节为单位,而不是字符数.

szDestination is a pointer, thus sizeof(szDestination) will return the pointer size, in bytes, not the number of characters.

如果szDestination 是空终止字符串,使用strlen 或类似函数来确定字符数.如果不是空终止,则需要将要复制的字节数作为参数传递.

If szDestination is a null terminated string, use strlen or similar function to determine the number of characters. If it isn't null terminated, then you need to pass the number of bytes to copy as a parameter.

最好的做法是在调用 DLL 函数时:

The better thing to do is when your DLL function is called:

int __stdcall extract_all_frames(const char* szPath, const char* szDestination, float scaleFactor)

获取这些指针并立即将它们分配给std::string.然后删除所有使用的 char* 或 const char* 从那里.您的辅助函数无需处理哑"字符指针.

take those pointers and immediately assign them to std::string. Then drop all usage of char* or const char* from there. There is no need for your helper functions to deal with "dumb" character pointers.

示例:

int __stdcall extract_all_frames(const char* szPath, const char* szDestination, float scaleFactor)
{
   std::string sPath = szPath;
   std::string sDestination = sDestination;
   // From here, use sPath and sDestination
  //...
}

// redefinition of save_frame
//...
void save_frame(AVFrame* pFrame, int iFrame, const std::string& szDestination)
{
   //Open file
   std::string buffer = "frame" + to_string(iFrame) + ".ppm";
   buffer.append("\");
   buffer.append(szDestination);
      //...
}

这篇关于从 C# 使用 C++ DLL 时出现 AccessViolationException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆