使用avimanager从图像生成视频会引发错误

时间:2017-04-13 22:49:16

标签: c#

我正在使用webclient从网址获取图片数据,并尝试使用它生成视频:

        //http://www.codeproject.com/Articles/7388/A-Simple-C-Wrapper-for-the-AviFile-Library

        WebClient client = new WebClient();

        System.Net.WebRequest request = System.Net.WebRequest.Create(images);
        System.Net.WebResponse response = request.GetResponse();
        System.IO.Stream responseStream = response.GetResponseStream();
        Bitmap bitmap = new Bitmap(responseStream);


        //create a new AVI file
        AviManager aviManager = new AviManager(@"C:\Users\Laptop\Documents\tada.avi", false);

        //add a new video stream and one frame to the new file
        //set IsCompressed = false
        VideoStream aviStream = aviManager.AddVideoStream(false, 2, bitmap);

        aviManager.Close();

但它削减了以下内容。在这一行的图书馆

int result = Avi.AVIFileCreateStream(aviFile, out aviStream, ref strhdr);

我收到以下错误:

  

System.AccessViolationException:'尝试读取或写入受保护的   记忆。这通常表明其他内存已损坏。'

1 个答案:

答案 0 :(得分:1)

你现在可能有“Any CPU”所以你的应用程序会被编译/运行为64位进程(在64位Windows版本上)。

问题似乎是AVI Wrapper库可能从未使用64位.NET应用程序进行测试....它没有正确定义“pinvoke”定义,因此参数被正确地推送/弹出在进行64位API调用时进行堆栈。

将您的项目设置“平台目标”更改为x86 ...以便您可以避免此问题....并且可以在32位模式下调用“avifil32.dll”。

Windows确实附带了32位和64位的AVI库,因此理论上可以在64位进程时调用AVI库....但是您需要正确定义互操作/编组pinvoke。

  • c:\ windows \ system32 \ avifil32.dll(64bit)
  • c:\ windows \ syswow64 \ avifil32.dll(32位)

在32位(Microsoft使用ILP32数据模型)......

  • 一个int是4个字节
  • 指针是4个字节

在64位(Microsoft使用LLP64或P64数据模型)....

  • 一个int是(仍然)4个字节
  • 指针是(现在)8个字节

(见https://msdn.microsoft.com/en-us/library/windows/desktop/aa384083(v=vs.85).aspx

经常发生的错误是“pinvoke”定义在定义指针类型时使用了“int”,而不是更正确的IntPtr类型。

因此,“call”在32bit上运行正常(因为“int”与“指针”的大小相同)....而在64bit上它们的大小不同。

当你64位时其他的东西也会改变...比如默认的边界对齐...这可以改变结构中类型的偏移 - 所以你在定义你的pinvoke c#结构时必须要小心......所以他们匹配。

如果您对函数调用AVIFileCreateStream感兴趣,其WIN32签名如下:

STDAPI AVIFileCreateStream(
   PAVIFILE       pfile,
   PAVISTREAM     *ppavi,
   AVISTREAMINFO  *psi
);

其参数的“类型”是:

typedef IAVIFile *PAVIFILE; // i.e. just a pointer

typedef IAVIStream *PAVISTREAM; // i.e. just a pointer

typedef struct {
  DWORD fccType;
  DWORD fccHandler;
  DWORD dwFlags;
  DWORD dwCaps;
  WORD  wPriority;
  WORD  wLanguage;
  DWORD dwScale;
  DWORD dwRate;
  DWORD dwStart;
  DWORD dwLength;
  DWORD dwInitialFrames;
  DWORD dwSuggestedBufferSize;
  DWORD dwQuality;
  DWORD dwSampleSize;
  RECT  rcFrame;
  DWORD dwEditCount;
  DWORD dwFormatChangeCount;
  TCHAR szName[64];
} AVISTREAMINFO;

该封装库使用以下内容将.NET“pinvoke”定义为AVIFileCreateStream

//Create a new stream in an open AVI file
[DllImport("avifil32.dll")]
public static extern int AVIFileCreateStream(
    int pfile,
    out IntPtr ppavi, 
    ref AVISTREAMINFO ptr_streaminfo);

您可以立即看到第一个参数定义错误。

当“调用”时....只有4个字节将被放入堆栈中,用于第一个参数...而不是8,然后用于第二个参数(这是一个指向指针的指针)8个字节被推(因为使用了IntPtr)(“写”地址的地址),第三个参数是AVISTREAMINFO结构的地址。

因此,当调用AVIFileCreateStream时,它访问堆栈上的那些参数,但它们基本上是垃圾......它将尝试使用具有错误值的指针(即只有4个字节的(第一个参数)指针地址已经通过堆栈...而剩余的4个字节(8字节指​​针)从堆栈中的“下一个”东西填充...因此指针地址很可能是垃圾....这就是你获得访问权限的原因。

它应该被定义的方式是这样的(注意还有其他方法可以实现相同的目的):

[DllImport("avifil32.dll", SetLastError=true)]
public static extern int AVIFileCreateStream(IntPtr pfile, out IntPtr ppavi, ref AVISTREAMINFO psi);