سؤال

أواجه مشكلة في استدعاء وظائف المكتبة الأصلية من داخل رمز C# المدارة. أقوم بتطوير إطار عمل Compact 3.5 (Windows Mobile 6.x) فقط في حالة قيام ذلك بإحداث أي فرق.

أنا أعمل مع وظائف Wavein* من coredll.dll (هذه في Winmm.dll في النوافذ العادية التي أعتقد). هذا هو ما خطرت لي:

// namespace winmm; class winmm
[StructLayout(LayoutKind.Sequential)]
public struct WAVEFORMAT
{
    public ushort wFormatTag;
    public ushort nChannels;
    public uint nSamplesPerSec;
    public uint nAvgBytesPerSec;
    public ushort nBlockAlign;
    public ushort wBitsPerSample;
    public ushort cbSize;
}
[StructLayout(LayoutKind.Sequential)]
public struct WAVEHDR
{
    public IntPtr lpData;
    public uint dwBufferLength;
    public uint dwBytesRecorded;
    public IntPtr dwUser;
    public uint dwFlags;
    public uint dwLoops;
    public IntPtr lpNext;
    public IntPtr reserved;
}

public delegate void AudioRecordingDelegate(IntPtr deviceHandle, uint message, IntPtr instance, ref WAVEHDR wavehdr, IntPtr reserved2);

[DllImport("coredll.dll")]
public static extern int waveInAddBuffer(IntPtr hWaveIn, ref WAVEHDR lpWaveHdr, uint cWaveHdrSize);
[DllImport("coredll.dll")]
public static extern int waveInPrepareHeader(IntPtr hWaveIn, ref WAVEHDR lpWaveHdr, uint Size);
[DllImport("coredll.dll")]
public static extern int waveInStart(IntPtr hWaveIn);

// some other class
private WinMM.WinMM.AudioRecordingDelegate waveIn;
private IntPtr handle;
private uint bufferLength;

private void setupBuffer()
{
    byte[] buffer = new byte[bufferLength];
    GCHandle bufferPin = GCHandle.Alloc(buffer, GCHandleType.Pinned);
    WinMM.WinMM.WAVEHDR hdr = new WinMM.WinMM.WAVEHDR();
    hdr.lpData = bufferPin.AddrOfPinnedObject();
    hdr.dwBufferLength = this.bufferLength;
    hdr.dwFlags = 0;

    int i = WinMM.WinMM.waveInPrepareHeader(this.handle, ref hdr, Convert.ToUInt32(Marshal.SizeOf(hdr)));
    if (i != WinMM.WinMM.MMSYSERR_NOERROR)
    {
        this.Text = "Error: waveInPrepare";
        return;
    }
    i = WinMM.WinMM.waveInAddBuffer(this.handle, ref hdr, Convert.ToUInt32(Marshal.SizeOf(hdr)));
    if (i != WinMM.WinMM.MMSYSERR_NOERROR)
    {
        this.Text = "Error: waveInAddrBuffer";
        return;
    }
}

private void setupWaveIn()
{
    WinMM.WinMM.WAVEFORMAT format = new WinMM.WinMM.WAVEFORMAT();
    format.wFormatTag = WinMM.WinMM.WAVE_FORMAT_PCM;
    format.nChannels = 1;
    format.nSamplesPerSec = 8000;
    format.wBitsPerSample = 8;
    format.nBlockAlign = Convert.ToUInt16(format.nChannels * format.wBitsPerSample);
    format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
    this.bufferLength = format.nAvgBytesPerSec;
    format.cbSize = 0;

    int i = WinMM.WinMM.waveInOpen(out this.handle, WinMM.WinMM.WAVE_MAPPER, ref format, Marshal.GetFunctionPointerForDelegate(waveIn), 0, WinMM.WinMM.CALLBACK_FUNCTION);
    if (i != WinMM.WinMM.MMSYSERR_NOERROR) 
    {
        this.Text = "Error: waveInOpen";
        return;
    }

    setupBuffer();

    WinMM.WinMM.waveInStart(this.handle);
}

قرأت كثيرًا عن تنظيم الأيام القليلة الماضية ، ومع ذلك لا أحصل على هذا الرمز. عندما تسمى وظيفة رد الاتصال الخاصة بي (الموجة) عندما يكون المخزن المؤقت ممتلئًا ، من الواضح أن بنية HDR التي تم تمريرها مرة أخرى في WaveHDR تالف. فيما يلي امتثال لكيفية ظهور الهيكل في تلك المرحلة:

-      wavehdr {WinMM.WinMM.WAVEHDR}   WinMM.WinMM.WAVEHDR
         dwBufferLength 0x19904c00  uint
         dwBytesRecorded    0x0000fa00  uint
         dwFlags    0x00000003  uint
         dwLoops    0x1990f6a4  uint
+       dwUser  0x00000000  System.IntPtr
+       lpData  0x00000000  System.IntPtr
+       lpNext  0x00000000  System.IntPtr
+       reserved    0x7c07c9a0  System.IntPtr

هذا ليس هو ما كنت أتوقع أن يتم تمريره. أنا قلق بوضوح بشأن ترتيب الحقول في الرأي. لا أعرف ما إذا كان Visual Studio .NET يهتم بترتيب الذاكرة الفعلي عند عرض السجل في عرض "المحلي" ، لكن من الواضح أنه لم يتم عرضه بالترتيب الذي قمت به في البنية.

ثم لا يوجد مؤشر بيانات وحقل الطول المتفوق على ارتفاع. ومن المثير للاهتمام أن الحقل المسجل بايت هو بالضبط 64000 - الطول الممتاز و Bytesrecored ، كنت أتوقع أن يكون كلاهما 64000. لا أعرف ما هو الخطأ الذي يحدث بالضبط ، ربما يمكن لشخص ما مساعدتي في هذا. أنا noob المطلق لبرمجة الكود المدارة والحميدة ، لذا من فضلك لا تكون قاسيًا جدًا بالنسبة لي على كل الأشياء الغبية التي قمت بها.

أوه ، إليك تعريف رمز C لـ WaveHdr الذي وجدته هنا, ، أعتقد أنني ربما فعلت شيئًا خاطئًا في تعريف البنية C#:

/* wave data block header */
typedef struct wavehdr_tag {
    LPSTR       lpData;                 /* pointer to locked data buffer */
    DWORD       dwBufferLength;         /* length of data buffer */
    DWORD       dwBytesRecorded;        /* used for input only */
    DWORD_PTR   dwUser;                 /* for client's use */
    DWORD       dwFlags;                /* assorted flags (see defines) */
    DWORD       dwLoops;                /* loop control counter */
    struct wavehdr_tag FAR *lpNext;     /* reserved for driver */
    DWORD_PTR   reserved;               /* reserved for driver */
} WAVEHDR, *PWAVEHDR, NEAR *NPWAVEHDR, FAR *LPWAVEHDR;

إذا كنت معتادًا على العمل مع كل هذه الأدوات ذات المستوى المنخفض مثل المؤشر الأنيثمي ، واللواصات ، وما إلى ذلك ، فإن بدء كتابة الكود المدارة هو ألم في المؤخرة. إنه مثل محاولة تعلم كيفية السباحة بيديك المربوطة على ظهرك. بعض الأشياء التي جربتها (بلا تأثير): لا يبدو أن .NET Compact Framework يدعم التوجيه = 2^X في [structLayout]. حاولت [structLayout (layoutkind.explicit)] واستخدمت 4 بايت و 8 بايت محاذاة. 4 بايت محاذاة لي نفس النتيجة مثل الكود أعلاه و 8 بايت محاذاة فقط جعلت الأمور أسوأ - ولكن هذا ما كنت أتوقعه.ومن المثير للاهتمام إذا قمت بنقل الكود من SetupBuffer إلى setupwavein ولم تعلن gchandle في سياق الفصل ، ولكن في سياق محلي لـ setupwavein ، لا يبدو أن البنية التي يتم إرجاعها بواسطة وظيفة رد الاتصال قد تالفة. لست متأكدًا من السبب في أن هذا هو الحال وكيف يمكنني استخدام هذه المعرفة لإصلاح الكود الخاص بي. انس هذا. لقد قمت بخلط الأشياء مع الكود الأقدم كثيرًا الذي استخدمته.

أقدر حقًا أي روابط جيدة حول التنميقة ، والاتصال بالدولة غير المُدارة من C#، وما إلى ذلك. ثم سأكون سعيدًا جدًا إذا تمكن شخص ما من الإشارة إلى أخطائي. ما الخطأ الذي افعله؟ لماذا لا أحصل على ما أتوقعه.

هل كانت مفيدة؟

المحلول 3

حسنًا ، لقد اكتشفت ذلك. كان كل رمز الخاص بي صحيحا بشكل أساسي. ومع ذلك ، أنا ثمل مع بنية wavehdr. لا تتوقع Wavein* وظائف فقط الإشارة إلى بنية WaveHDR ولكنها تتوقع أيضًا أن يحافظ على هذا الهيكل حتى يتم استدعاء WaveinHeaderUnprepare. لذلك ، يجب إنشاء بنية WaveHDR في سياق عالمي أو على الأقل كافي بما فيه الكفاية للحفاظ على untl waveinheaderunprepare يتم استدعاؤه ويحتاج إلى تثبيت Aswell باستخدام gchandle ، بحيث لن يغير وضعه في الذاكرة (وهو AFAIK غير مضمون في التعليمات البرمجية المدارة). هنا SIS بلدي رمز تحديث وتنظيف:

    private WinMM.WinMM.AudioRecordingDelegate waveIn;
    private IntPtr handle;
    private WinMM.WinMM.WAVEHDR header;
    private GCHandle headerPin;
    private GCHandle bufferPin;
    private byte[] buffer;
    private uint bufferLength;

    private void setupBuffer()
    {
        header.lpData = bufferPin.AddrOfPinnedObject();
        header.dwBufferLength = bufferLength;
        header.dwFlags = 0;
        header.dwBytesRecorded = 0;
        header.dwLoops = 0;
        header.dwUser = IntPtr.Zero;
        header.lpNext = IntPtr.Zero;
        header.reserved = IntPtr.Zero;

        int i = WinMM.WinMM.waveInPrepareHeader(handle, ref header, Convert.ToUInt32(Marshal.SizeOf(header)));
        if (i != WinMM.WinMM.MMSYSERR_NOERROR)
        {
            this.Text = "Error: waveInPrepare " + i.ToString();
            return;
        }
        i = WinMM.WinMM.waveInAddBuffer(handle, ref header, Convert.ToUInt32(Marshal.SizeOf(header)));
        if (i != WinMM.WinMM.MMSYSERR_NOERROR)
        {
            this.Text = "Error: waveInAddrBuffer";
            return;
        }
    }

    private void setupWaveIn()
    {
        handle = new IntPtr();
        WinMM.WinMM.WAVEFORMAT format;
        format.wFormatTag = WinMM.WinMM.WAVE_FORMAT_PCM;
        format.nChannels = 1;
        format.nSamplesPerSec = 8000;
        format.wBitsPerSample = 8;
        format.nBlockAlign = Convert.ToUInt16(format.nChannels * format.wBitsPerSample);
        format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
        bufferLength = format.nAvgBytesPerSec / 800;
        headerPin = GCHandle.Alloc(header, GCHandleType.Pinned);
        buffer = new byte[bufferLength];
        bufferPin = GCHandle.Alloc(buffer, GCHandleType.Pinned);
        format.cbSize = 0;

        int i = WinMM.WinMM.waveInOpen(out handle, WinMM.WinMM.WAVE_MAPPER, ref format, Marshal.GetFunctionPointerForDelegate(waveIn), IntPtr.Zero, WinMM.WinMM.CALLBACK_FUNCTION);
        if (i != WinMM.WinMM.MMSYSERR_NOERROR) 
        {
            this.Text = "Error: waveInOpen";
            return;
        }

        setupBuffer();

        WinMM.WinMM.waveInStart(handle);
    }

    private void callbackWaveIn(IntPtr deviceHandle, uint message, IntPtr instance, ref WinMM.WinMM.WAVEHDR wavehdr, IntPtr reserved2)
    {
        if (message == WinMM.WinMM.WIM_DATA)
            if (this.InvokeRequired)
                this.Invoke(waveIn, deviceHandle, message, instance, wavehdr, reserved2);
            else
            {
                if (wavehdr.dwBytesRecorded > 0)
                {
                    foreach (byte buf in buffer)
                    {
                        // do something cool with your byte stream
                    }
                }

                int i = WinMM.WinMM.waveInUnprepareHeader(deviceHandle, ref header, Convert.ToUInt32(Marshal.SizeOf(wavehdr)));
                if (i != WinMM.WinMM.MMSYSERR_NOERROR)
                {
                    this.Text = "Error: waveInUnprepareHeader " + i;
                }
                setupBuffer();
            }
    }

شكرا لك على مساعدتك. آمل أن يتمكن شخص ما من استخدام الكود الذي تمكنت من الخروج به.

نصائح أخرى

تصريحات P/استدعاء لا تشوبها شائبة. ومع ذلك ، هناك شيء جداً غريب عن تفريغ WaveHdr الذي نشرته. إنه في عداد المفقودين حقل LPDATA. إذا قمت بإدراج ذلك ، فإن جميع الأرقام تصطف بشكل صحيح (أي LPDATA = 0x19904C00 ، DWBufferLength = 0x0000FA00 ، إلخ).

لست متأكدًا من كيفية حدوث ذلك ، لكنه يستخدم بطريقة ما إعلان WaveHdr الخاطئ. winmm.winmm يجب أن يكون تلميحا.

Pinvoke.net هو المكان المناسب للعثور على إعلانات Pinvoke.هذه الصفحة يصف طريقة waveinaddbuffer ومكافئها ، وكذلك الروابط إلى WaveHdr.

لقد ألقيت نظرة على الطرق المختلفة التي تستخدمها ولكن لم أتمكن من العثور على أي شيء مفيد في قضيتك. هناك اختلاف في إصدار Pinvoke.net وكملك هو أن Pinvoke يستخدم propery charset of structlayout ، لكنني أعتقد أنه غير ذي صلة.

كتاب جيد عن موضوع التشغيل البيني هو: صافي و com

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top