2013-11-23 15 views
1

我在我的一個項目中使用P/Invoke來調用avifil32.dll方法,並且一切正常。今天,幾個月後又回到了這個項目,並在此期間切換到Windows 8,我發現沒有任何工作了。我對avifil32方法的大多數調用導致AccessViolationException(「試圖讀取或寫入受保護的內存......」)。編組從c#LPCTSTR不再工作

一個功能使這是AVIFileOpen,這在以前我用這種方式調用(如在pinvoke.net here還指出):

[DllImport("avifil32.dll", PreserveSig=true)] 
static extern int AVIFileOpen(out IntPtr ppfile, string szFile, uint mode, int pclsidHandler); 

網絡上的一些搜索後,我發現this有益的職位,因此我改變了我的調用(注AVIFileOpenW):

[DllImport("avifil32.dll", PreserveSig=true, CharSet = CharSet.Auto)] 
public static extern int AVIFileOpenW(ref int ppfile, [MarshalAs(UnmanagedType.LPWStr)] String szFile, int uMode, int pclsidHandler); 

,現在它的工作原理。不過,我還有其他問題,例如我在呼叫AVIFileCreateStream

的呼叫上有同樣的例外,那麼我在做什麼錯了?更有趣的是,這可能是導致現在什麼都不能工作的原因?我承認我不知道在此期間會發生什麼。從Windows 7到Windows 8發生了巨大變化,但這能解釋這個問題嗎?

編輯


繼漢斯帕桑特sugestion我已經糾正了P/Invoke調用,現在我已經沒有更多的AccessViolationexception。然而,在工作之前,它對我來說仍然是一種莫名其妙的感覺。事實上,現在(Win8)和之前(Win7)我都有64位操作系統。 但是在項目設置中還有另外一個細節可以幫助解釋。這是一個從我的主項目中調用的庫項目。我的主要項目的目標是x64(針對x86不是一種選擇),而該庫的目標是AnyCPU。我做了另一次試驗,並再次使用這些設置,我的老版本的AVIFileOpen稱該程序在Win7 x64機器下運行,但不在Win8 x64下運行。這可以通過JIT編譯器與AnyCPU的不同「管理」相關聯,還是必須存在一些我缺少的其他設置?

現在,該程序在x86和x64中均可運行。這裏下面的代碼的摘錄我使用:

AVIFileInit(); 
IntPtr aviFile = IntPtr.Zero; 
IntPtr aviStream = IntPtr.Zero; 
string tmp = Path.GetTempFileName(); 
if (AVIFileOpen(out aviFile, tmp.Substring(0, tmp.Length - 4) + ".avi", OF_WRITE | OF_CREATE, IntPtr.Zero) == 0) 
{ 
    AVISTREAMINFO strhdr = new AVISTREAMINFO(); 
    strhdr.fccType = mmioStringToFOURCC("vids", 0); 
    strhdr.fccHandler = mmioStringToFOURCC("CVID", 0); 
    strhdr.dwScale = 1; 
    strhdr.dwRate = 25; 
    strhdr.dwSuggestedBufferSize = 102400; 
    strhdr.dwQuality = -1; 
    strhdr.rcFrame.bottom = 320; 
    strhdr.rcFrame.right = 320; 
    strhdr.szName = ""; 
    if (AVIFileCreateStream(aviFile, out aviStream, ref strhdr) == 0) 
    { 
     AVICOMPRESSOPTIONS_CLASS options = new AVICOMPRESSOPTIONS_CLASS(); 
     options.fccType = (uint)streamtypeVIDEO; 
     options.lpParms = IntPtr.Zero; 
     options.lpFormat = IntPtr.Zero; 
     bool ok = AVISaveOptions(this.Handle, ICMF_CHOOSE_KEYFRAME | ICMF_CHOOSE_DATARATE, 1, ref aviStream, ref options); 
    } 
} 

public static readonly int streamtypeVIDEO = mmioFOURCC('v', 'i', 'd', 's'); 
public const UInt32 ICMF_CHOOSE_KEYFRAME = 0x0001; 
public const UInt32 ICMF_CHOOSE_DATARATE = 0x0002; 
public const UInt32 ICMF_CHOOSE_PREVIEW = 0x0004; 
public const int OF_WRITE = 1; 
public const int OF_READWRITE = 2; 
public const int OF_CREATE = 4096; 

[StructLayout(LayoutKind.Sequential)] 
public struct RECT 
{ 
    public UInt32 left; 
    public UInt32 top; 
    public UInt32 right; 
    public UInt32 bottom; 
} 

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] 
public struct AVISTREAMINFO 
{ 
    public Int32 fccType; 
    public Int32 fccHandler; 
    public Int32 dwFlags; 
    public Int32 dwCaps; 
    public Int16 wPriority; 
    public Int16 wLanguage; 
    public Int32 dwScale; 
    public Int32 dwRate; 
    public Int32 dwStart; 
    public Int32 dwLength; 
    public Int32 dwInitialFrames; 
    public Int32 dwSuggestedBufferSize; 
    public Int32 dwQuality; 
    public Int32 dwSampleSize; 
    public RECT rcFrame; 
    public Int32 dwEditCount; 
    public Int32 dwFormatChangeCount; 
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)] public String szName; 
} 

[StructLayout(LayoutKind.Sequential)] 
public class AVICOMPRESSOPTIONS_CLASS 
{ 
    public UInt32 fccType; 
    public UInt32 fccHandler; 
    public UInt32 dwKeyFrameEvery; 
    public UInt32 dwQuality; 
    public UInt32 dwBytesPerSecond; 
    public UInt32 dwFlags; 
    public IntPtr lpFormat; 
    public UInt32 cbFormat; 
    public IntPtr lpParms; 
    public UInt32 cbParms; 
    public UInt32 dwInterleaveEvery; 
} 

[DllImport("avifil32.dll")] 
public static extern void AVIFileInit(); 

[DllImport("winmm.dll", CharSet = CharSet.Auto)] 
public static extern int mmioStringToFOURCC([MarshalAs(UnmanagedType.LPWStr)] String sz, int uFlags); 

[DllImport("avifil32.dll", PreserveSig = true, CharSet = CharSet.Auto)] 
public static extern int AVIFileOpen(out IntPtr ppfile, String szFile, int uMode, IntPtr pclsidHandler); 

[DllImport("avifil32.dll", CharSet = CharSet.Auto)] 
public static extern int AVIFileCreateStream(IntPtr pfile, out IntPtr ppavi, ref AVISTREAMINFO ptr_streaminfo); 

[DllImport("avifil32.dll")] 
public static extern bool AVISaveOptions(IntPtr hwnd, UInt32 uiFlags, Int32 nStreams, ref IntPtr ppavi, ref AVICOMPRESSOPTIONS_CLASS plpOptions); 
+0

你能分享整個代碼嗎? –

回答

4

最大的變化已經從Windows 7傳遞到Windows 8

,可以解釋的問題,你可能得到的64位版本的Windows 8.當您以64位模式運行程序時,您使用的兩種聲明都是錯誤的,並且很難診斷。相關項目設置爲Project + Properties,Build選項卡,Platform目標設置。最好在這裏使用「x86」(選擇VS2012中的32位或更高),這將會限制您將因這些不良聲明而遭受的痛苦。

第一個參數是PAVIFILE,「指向接收接口指針的緩衝區的指針」。最後一個參數是CLSID,「指向類標識符的指針」。指針在32位模式下是4個字節,在64位模式下是8個字節。相應的C#聲明必須使用refout關鍵字或使用IntPtr來兼容。

所以第一個參數是「指針指針」,使用out IntPtr是正確的。像你一樣。

最後一個參數幾乎從不使用,您傳遞一個空指針。在這種情況下,您要聲明IntPtr並在您撥打電話時傳遞IntPtr.Zero。您最初傳遞0,4個字節,而函數期望8個字節設置爲0.足以生成錯誤的指針值並獲取AccessViolationException。

您使用的替換聲明特別討厭,您只傳遞足夠的空間來存儲int,4字節,但函數在64位模式下寫入8個字節。這會破壞堆棧或GC堆,非常討厭。隨後的調用,如AviFileCreateStream確實很可能失敗,因爲接口指針值不可能在未受影響的情況下保留下來。

我修復了pinvoke.net聲明。

+1

+1包= 1將導致有一天的痛苦.... –

+0

這真的很有幫助,但我仍然不明白爲什麼應用程序停止工作,因爲Win7是64位。此外,即使糾正我仍然存在問題。你能閱讀我的編輯? –

+0

你沒有太注意David的評論,使用Pack = 1是錯誤的。去掉它。 –