从C#使用C ++ DLL时出现AccessViolationException

时间:2014-12-09 19:11:28

标签: c++ string dll access-violation

我有一个C ++ DLL可供C#使用。我有一个函数,它接受一个字符串传递给它,我在C ++函数参数上设置为const char *,如下所示:

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

此函数的主体直接从工作的FFmpeg示例函数复制,所以我几乎可以肯定问题不在那里。我觉得这个问题出现在我对它的修改中:

//Open file
char szFilename[32];
sprintf_s(szFilename, sizeof(szFilename), "frame%d.ppm\0", 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#调用此函数时,我都会遇到以下异常:

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 <libavcodec\avcodec.h>
    #include <libavformat\avformat.h>
    #include <libavutil\avutil.h>
    #include <libswscale\swscale.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\0", 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\n%d %d\n255\n", 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!\nMust be greater than 0, and less then or equal to 3.0.\n", 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'!\n", szPath);
            return -1;
        }

        // Retrieve stream information
        if (avformat_find_stream_info(pFormatCtx, nullptr) < 0)
        {
            fprintf(stderr, "libavformat: Unable to find stream information!\n");
            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!\n");
            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!\n");
            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!\n");
            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'!\n", 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!\n");
            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 ++行导致问题。

1 个答案:

答案 0 :(得分:1)

这是错误的:

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

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

如果szDestination是以空字符结尾的字符串,请使用strlen或类似函数来确定字符数。如果它不是以null结尾,那么你需要传递要复制的字节数作为参数。

更好的办法是调用DLL函数时:

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

接受这些指针,立即将它们分配给std::string。然后从那里删除char *或const char *的所有用法。你的帮助函数不需要处理“哑”字符指针。

示例:

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\0";
   buffer.append("\\");
   buffer.append(szDestination);
      //...
}