none
AccessViolationException with memcpy() in C++ DLL and C# RRS feed

  • Question

  • Hi all,

    I am developing a player based on the Microsoft article http://support.microsoft.com/kb/155360/en-us

    I developed a C++ CLR DLL and used it in a c# project in the same solution.

    In C++ code memcpy() crashes with error : 
    An unhandled exception of type 'System.AccessViolationException' occurred in mciplayer.dll
    Additional information: Attempted to read or write protected memory.
    This is often an indication that other memory is corrupt.

    My CPP:
    #include "stdafx.h"
    #include <windows.h>
    #include <mmsystem.h>
    #include <stdlib.h>
    #include <malloc.h>
    #pragma comment(lib, "winmm.lib")
    #include "mciplayer.h"
    using namespace System;
    using namespace System::Text;
    using namespace System::Runtime::InteropServices;
    // Global Variables
    HWND notifyFormHandle;
    static char * lpData = NULL;
    static long filesize = 0;
    // main play function
    void mciplayer::Media::play(IntPtr videodata, long datalength)
    {
        filesize = datalength;
        lpData = (
    char *) malloc(datalength);
        lpData = (
    char *) videodata.ToPointer();
        
    installIOProc();
        openMediaFile();
        playMediaFile();
    }
    // custom function IOProc to play files from memory
    LRESULT __clrcall mciplayer::Media::IOProc(LPMMIOINFO lpMMIOInfo, UINT uMessage, LPARAM lParam1, LPARAM lParam2)
    {
        
    static BOOL alreadyOpened = FALSE;
        
    switch(uMessage) {    
            
    case MMIOM_OPEN:
                
    if(alreadyOpened)
                    
    return 0;
                alreadyOpened = TRUE;
                lpMMIOInfo->lDiskOffset = 0;
                
    return 0;
            
    case MMIOM_CLOSE:
                
    return 0;
            
    case MMIOM_READ:
                memcpy((
    void *) lParam1, lpData + lpMMIOInfo->lDiskOffset, lParam2); // Crashes HERE ****
                lpMMIOInfo->lDiskOffset += lParam2;
                
    return (lParam2); 
            
    case MMIOM_SEEK:
                
    switch(lParam2) {
                    
    case SEEK_SET: 
                        lpMMIOInfo->lDiskOffset = lParam1;
                        
    break;
                    
    case SEEK_CUR:
                        lpMMIOInfo->lDiskOffset += lParam1;
                        
    break;
                    
    case SEEK_END:
                        lpMMIOInfo->lDiskOffset =filesize - 1 - lParam1;
                        
    break;
                }
                
    return lpMMIOInfo->lDiskOffset;
            
    default:
                
    return -1; // Unexpexted messages.
        
    }
        //...........
        Other functions here
        //...........
    }


    My C# Code :
    Media myMedia;
    byte[] buffer;
    IntPtr ptrToUnmanaged;
    private void button1_Click(object sender, EventArgs e)
    {
        FileStream fs = File.OpenRead(@"C:\sample.avi");
        buffer =
    new byte[fs.Length];
        fs.Read(buffer, 0, buffer.Length);
        fs.Close();
        ptrToUnmanaged =
    Marshal.AllocHGlobal(buffer.Length);    
        Marshal.Copy(buffer, 0, ptrToUnmanaged, buffer.Length);
        myMedia.play(ptrToUnmanaged, buffer.Length);
    }

    I think the pointer ptrToUnmanaged (lpData in C++ code) does not point to buffer or C++ DLL has not permission to access to the memory portion which is allocated in C# application.

    I have read all about AccessViolationException but none of them can solve this problem.
    Can you read my code and tell me anything to do, I have been working on it for several days but cannot solve the problem.

    Thanks.










    Monday, August 18, 2008 12:49 AM

Answers

  • I noticed that your IOProc uses the _clrcall calling convention, why is that? AFAIK it should be using _stdcall like most other Win32 callback procedures.

    Also, do you ever free the memory you allocate in C# with AllocHGlobal? You should free it to avoid leaking memory, but not until you have finished using the memory (and ideally unistalled the MMIO proc).
    Mattias, C# MVP
    • Marked as answer by noanyx Monday, August 18, 2008 10:52 PM
    Monday, August 18, 2008 11:53 AM
    Moderator

All replies

  • Stream.Read isn't guaranteed to read as many bytes into the buffer as you ask for. You really should call it in a loop to ensure that all the file content is read into memory.

    You're leaking memory in the play method with the unecessary malloc call.

    Have you set a breakpoint on the line where it crashes and looked at what lpData points at to verify that the data matches your source file?

    Mattias, C# MVP
    Monday, August 18, 2008 7:35 AM
    Moderator
  • Thank you for your reply.

    I have copy the file stream in a loop, but result is the same.

    Also, I have written a new avi file from the lpData in Play function in CPP, the new file is exactly the same as the orginal.
    So, there is no wrong with passing the memory pointer to C++ DLL.

    In IOProc memcpy() crashes again with AccessViolation. Now,

    IOProc is called within another function in mmsystem.h and winmm.lib by this: 
    mmioInstallIOProcA(mmioFOURCC('M', 'E', 'Y', ' '), (LPMMIOPROC) mciplayer::Media::IOProc, MMIO_INSTALLPROC | MMIO_GLOBALPROC);
    Maybe function from mmsystem has not permission to access to this memory portion. (managed-unmanaged maybe). 

    I am not sure. Did you have any idea ? Why this could be? I have read every forum posts and articles about this error but cannot find anything.

    Thank you.   
    Monday, August 18, 2008 11:15 AM
  • I noticed that your IOProc uses the _clrcall calling convention, why is that? AFAIK it should be using _stdcall like most other Win32 callback procedures.

    Also, do you ever free the memory you allocate in C# with AllocHGlobal? You should free it to avoid leaking memory, but not until you have finished using the memory (and ideally unistalled the MMIO proc).
    Mattias, C# MVP
    • Marked as answer by noanyx Monday, August 18, 2008 10:52 PM
    Monday, August 18, 2008 11:53 AM
    Moderator
  • Thank you Mattias,

    Now, I will change the project type from CLR class library to class library or I will start from an empty project.

    But, while debugging, I noticed that now the value of lParam1 is always 3. It is used in IOProc() :

    memcpy((void *) lParam1, lpData + lpmmioinfo->lDiskOffset, lParam2); 

    Is not the 3rd cell or 3rd byte of memory reserved for operating system like begining portions of memory? If so, of course application cannot write there.
    Monday, August 18, 2008 12:54 PM
  • The value 3 is most likely not a valid memory address. My guess is that your calling convention mismatch is causing the parameter values to be incorrect. 3 happens to be the value of MMIOM_OPEN - one of the possible values for uMessage.
    Mattias, C# MVP
    Monday, August 18, 2008 2:01 PM
    Moderator
  • Thanks Mattias,

    Unfortunately I cannot change the calling convention into __stdcall, because the dll project uses /clr. It givies warning and ignores __stdcall and uses __clrcall again.

    I created a new Win32 dll project but I can't use System members in it. Maybe I can use without System members but C# project did not accept it as project reference.

    Thank you for your help.
    Monday, August 18, 2008 8:47 PM
  •  I have done it with Win32 DLL (of course without System members). Then I have imported the dll with [DllImport(...), ...] into the c# project and it works great.

    Thank you very very much Mattias, I could not succeed it without your helps.

    See you in future posts.
    Thanks.

    Monday, August 18, 2008 10:51 PM