AccessViolationException 使用 C# 中的 C++ DLL

AccessViolationException when using C++ DLL from C#

本文关键字:C++ DLL 中的 使用 AccessViolationException      更新时间:2023-10-16

我有一个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", 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 <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, "P6n%d %dn255n", 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++行。

这是

错误的:

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

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

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

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

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

获取这些指针并立即将它们分配给std::string。 然后从那里删除所有字符*或常量字符*的使用。 您的帮助程序函数不需要处理"哑"字符指针。

例:

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);
      //...
}