dcsimg
Results 1 to 12 of 12

Thread: mcisendCOMMAND

  1. #1

    Thread Starter
    New Member
    Join Date
    May 2019
    Posts
    7

    mcisendCOMMAND

    Hello everybody
    I have been pulling my hair out over this for long enough and realised that it might be time to ask those who actually know what they are doing. I am using a Windows 7 Professional 64 bit system with visual studio 2017. Nearly everything I have found on the net seems to date back to VB6 so I guess it did work once but most are missing the Marshalling. I found an example that had it and it looks to me like it is right but I am obviously wrong as it gives me a stack imbalance error when calling the mcisendcommand function. Here is the full error.

    Exception Thrown
    WindowsApp1.Form1::mciSendCommand' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the calling convention and parameters of the PInvoke signature match the target unmanaged signature.'
    Here is the code I have at the moment. (It has been changing a lot for a while now)

    Code:
       Public Sub GetStatus()
            ' http://www.scottandmichelle.net/scott/code/index2.mv?codenum=028
            'Dim StatPARMS As MCI_STATUS_PARMS !Declared earlier
            Dim m_DeviceID As Long 'Private 
            Dim Stats As UInteger
    
            mciSendString("open new Type waveaudio Alias recsound", "", 0, 0)
            mciSendString("record recsound", "", 0, 0)
    
            Debug.WriteLine("DeviceID = " & mciGetDeviceID("recsound"))
            m_DeviceID = mciGetDeviceID("recsound") 'Get DeviceID 
            StatPARMS.dwCallback = 0
            StatPARMS.dwItem = MCI_STATUS
            StatPARMS.dwReturn = 0
            StatPARMS.dwTrack = 0
            Debug.Print("StatPARMS.dwCallback = " & StatPARMS.dwCallback.ToString & " StatPARMS.dwItem = " & StatPARMS.dwItem &
                        " StatPARMS.dwReturn = " & StatPARMS.dwReturn & " StatPARMS.dwTrack = " & StatPARMS.dwTrack)
    
            Dim dwParam1 As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(GetType(Integer)))
            Marshal.WriteInt32(dwParam1, MCI_WAIT Or MCI_DGV_STATUS_AUDIO_INPUT)
            'Dim dwParam1 As Long = MCI_WAIT Or MCI_DGV_STATUS_AUDIO_INPUT
    
            'Dim dwParam2 As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(GetType(MCI_STATUS_PARMS)))
            'Marshal.StructureToPtr(StatPARMS, dwParam2, False)
            Dim dwParam2 As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(StatPARMS))
            Marshal.StructureToPtr(StatPARMS, dwParam2, False)
            Debug.WriteLine("dwParam2 = " & dwParam2.ToString)
    
            mciSendCommand(m_DeviceID, MCI_STATUS, dwParam1, dwParam2)
    
            Stats = StatPARMS.dwReturn
            Debug.Print(If(MsgBox(Stats, vbYesNo, "Yay?") > 6, "No", "Yes"))
    
            Marshal.FreeHGlobal(dwParam1)
            Marshal.FreeHGlobal(dwParam2)
    
            SaveDialog.ShowDialog()
            mciSendString("stop recsound ", "", 0, 0)
            mciSendString("save recsound " & """" + Me.SaveDialog.FileName + """", "", 0, 0)
            mciSendString("close recsound ", "", 0, 0)
    
        End Sub
    My signatures have also gone through many variations but here is the latest version

    Code:
        Public Declare Function mciSendCommand Lib "winmm.dll" Alias "mciSendCommandA" (ByVal wDeviceID As Long, ByVal uMessage As Long, ByVal dwParam1 As Long, ByVal dwParam2 As Object) As Long
        Public Declare Function mciGetErrorString Lib "winmm.dll" Alias "mciGetErrorStringA" (ByVal dwError As Integer, ByVal lpstrBuffer As String, ByVal uLength As Integer) As Integer
        Public Declare Function mciSendString Lib "winmm.dll" Alias "mciSendStringA" (ByVal lpstrCommand As String, ByVal lpstrReturnString As String, ByVal uReturnLength As Integer, ByVal hwndCallback As Integer) As Integer
        Public Declare Function mciGetDeviceID Lib "winmm.dll" Alias "mciGetDeviceIDA" (ByVal lpstrName As String) As Integer
    There have been many attempts to tweak this into working but if they changed anything it was only to change the wording of the error message so could somebody please tell me what exactly I have got wrong here. Even the Marshalling bit which I don't really know looks about right.

    It just does not work.

  2. #2
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    5,338

    Re: mcisendCOMMAND

    Well, the first thing that jumps out is that you have the return type As Long (64-bit) instead of As Integer (32-bit) for the mciSendCommand.
    In VB6 a Long was 32-bit and an Integer was 16-bit, which is why for VB6 As Long was specified.

    You also have the parameters being passed as Long as well, which should 32-bit values.

    One declaration I found has it defined like this:
    Code:
    Declare Function mciSendCommand Lib "winmm.dll" Alias "mciSendCommandA" (ByVal wDeviceID As Integer, ByVal uMessage As Integer, ByVal dwParam1 As Integer, ByVal dwParam2 As Object) As Integer
    I would select an X86 target when compiling, as I don't know whether As Object would be 32-bit or 64-bit if you chose an X64 or AnyCpu target.
    Last edited by passel; May 18th, 2019 at 07:01 AM.

  3. #3
    Hyperactive Member
    Join Date
    Jun 2018
    Posts
    412

    Re: mcisendCOMMAND

    What is it you want to do?

    passel is correct you have the old vb6 code declares for the apis. You can look up the correct api declares at sites like pinvoke, get the ones for vb.net. Just search for the name of the api.

    Here is a working example that will record what is playing on the system speakers (line out).

    The example makes two buttons for starting and stopping the recording. Just cut and paste the code into an empty form. Change the form name as reqd.

    Code:
    Imports System.Runtime.InteropServices
    Imports System.Text
    
    Public Class Form1
    
        Private WithEvents Button1 As New Button With {.Parent = Me, .Location = New Point(20, 20), .Text = "Start"}
        Private WithEvents Button2 As New Button With {.Parent = Me, .Location = New Point(20, 100), .Text = "Stop"}
        Private Sb As New StringBuilder
    
        <DllImport("winmm.dll", EntryPoint:="mciSendStringW")>
        Private Shared Function mciSendStringW(<MarshalAs(UnmanagedType.LPWStr)> ByVal lpstrCommand As String, <MarshalAs(UnmanagedType.LPWStr)> ByVal lpstrReturnString As StringBuilder, ByVal uReturnLength As Integer, ByVal hwndCallback As IntPtr) As Integer
        End Function
    
        Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
            Me.Text = "Output Recorder"
            Button2.Enabled = False
        End Sub
    
        Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
            mciSendStringW("stop capture", Sb, 0, IntPtr.Zero)
            Using sfd As New SaveFileDialog
                sfd.FileName = "Untitled"
                sfd.DefaultExt = "wav"
                sfd.AddExtension = True
    
                If sfd.ShowDialog = DialogResult.OK Then
                    Button2.Enabled = False
                    Dim saveas As String = Chr(34) & sfd.FileName & Chr(34)
                    mciSendStringW("save capture " & saveas, Nothing, 0, IntPtr.Zero)
                End If
            End Using
            mciSendStringW("close capture", Sb, 0, IntPtr.Zero)
            Button2.Enabled = False
            Button1.Enabled = True
        End Sub
    
        Private Sub Button1_Click_1(sender As Object, e As EventArgs) Handles Button1.Click
            Button2.Enabled = True
            Button1.Enabled = False
            mciSendStringW("open new Type waveaudio Alias capture", Sb, 0, IntPtr.Zero)
            mciSendStringW("set capture time format ms bitspersample 16 channels 2 samplespersec 48000 bytespersec 192000 alignment 4", Nothing, 0, IntPtr.Zero)
            mciSendStringW("record capture", Nothing, 0, IntPtr.Zero)
        End Sub
    End Class
    Last edited by tommytwotrain; May 18th, 2019 at 07:37 AM. Reason: remove broken links

  4. #4
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    5,338

    Re: mcisendCOMMAND

    I think the pinvoke site may have mciSendString defined, but perhaps not mciSendCommand. I could be wrong though.

  5. #5
    Hyperactive Member
    Join Date
    Jun 2018
    Posts
    412

    Re: mcisendCOMMAND

    Quote Originally Posted by passel View Post
    I think the pinvoke site may have mciSendString defined, but perhaps not mciSendCommand. I could be wrong though.
    Oh I did not notice that one in there.

    So back to what is it exactly that is desired to be done?


  6. #6

    Thread Starter
    New Member
    Join Date
    May 2019
    Posts
    7

    Re: mcisendCOMMAND

    Thanks for that guys.
    I have tried longs, Integers and UInts everywhere I can think of but I have yet to figure out in VS2017 where I can work out which parameter is causing it. Microsoft gives me this for mcisendcommand.

    https://docs.microsoft.com/en-us/pre...57160(v=vs.85)

    MCIERROR mciSendCommand(
    MCIDEVICEID IDDevice,
    UINT uMsg,
    DWORD_PTR fdwCommand,
    DWORD_PTR dwParam
    );
    and I get this from a C++ to VB convertor.

    Public Declare Function mciSendCommand Lib "winmm.dll" Alias "mciSendCommandA" (ByVal IDDevice As MCIDEVICEID, ByVal uMsg As UInteger, ByVal fdwCommand As System.IntPtr, ByVal dwParam As System.IntPtr) As MCIERROR
    Obviously some modification required as VB has no more idea of what a MCIDEVICEID or MCIERROR is than I do. However mciGetDeviceID according to Microsoft looks like this.

    https://docs.microsoft.com/en-us/pre...156(v%3Dvs.85)


    MCIDEVICEID mciGetDeviceID(
    LPCTSTR lpszDevice
    );
    It returns an integer number (1,2,3 etc) so I am guessing but I have seen other examples using it.



    I have seen some people declaring uMsg as a string but that makes no sense and I tried it anyway. It didn't work either. The really suspicious one is fdwCommand which in the C++ declaration is defined as a DWORD_PTR the same as dwParam. I can understand dwParam as a pointer to a structure but why would you need a pointer to a flag value? Anyway none of these have been a success.

    'Public Declare Function mciSendCommand Lib "winmm.dll" Alias "mciSendCommandA" (ByVal IDDevice As MCIDEVICEID, ByVal uMsg As UInteger, ByVal fdwCommand As System.IntPtr, ByVal dwParam As System.IntPtr) As MCIERROR
    Public Declare Function mciSendCommand Lib "winmm.dll" Alias "mciSendCommandA" (ByVal wDeviceID As Integer, ByVal uMessage As Integer, ByVal dwParam1 As Object, ByVal dwParam2 As Object) As Integer
    'Public Declare Function mciSendCommand Lib "winmm.dll" Alias "mciSendCommandA" (ByVal wDeviceID As Integer, ByVal uMessage As String, ByVal dwParam1 As Integer, ByVal dwParam2 As Object) As Integer
    I should also point out that I am using a structure for MCI_STATUS_PARMS as I often see it defined as a type and I get told that those are deprecated and to use a structure. Is that where I am going wrong? The long winded debug parts show that the data in the structure is working but the passing of the structure is the same line where it falls over.

    https://docs.microsoft.com/en-us/pre...156(v%3Dvs.85)
    <StructLayout(LayoutKind.Sequential)>
    Public Structure MCI_STATUS_PARMS
    Public dwCallback As IntPtr
    Public dwReturn As UInteger
    Public dwItem As UInteger
    Public dwTrack As UInteger
    End Structure
    @tommytwotrain One thing I have noticed about the majority of the links I have followed out there is that they all seem to end up using mcisendstring and not mentioning mcisendcommand. I have mcisendstring working very nicely and am beginning to regret moving on to mcisendcommand. It has some more interesting capabilities but what I really want to do right now is just get a working function that can execute the thing. I can look into the various flags later but first I want to post an example that poor unfortunates like myself can use today and save everybody from a lot of grief. Maybe then I will remember what I originally started out to do.

  7. #7

    Thread Starter
    New Member
    Join Date
    May 2019
    Posts
    7

    Re: mcisendCOMMAND

    You are correct passel. pinvoke.net has a nice blank page for mcisendcommand.
    Last edited by Ridgy; May 18th, 2019 at 05:49 PM.

  8. #8

    Thread Starter
    New Member
    Join Date
    May 2019
    Posts
    7

    Re: mcisendCOMMAND

    And the winner is

    Code:
    https://docs.microsoft.com/en-us/dotnet/api/system.intptr?view=netframework-4.8
    Private Declare Auto Function mciSendCommand Lib "winmm.dll" Alias "mciSendCommandA" (ByVal wDeviceID As UInteger, ByVal uMessage As UInteger, ByVal dwFlags As IntPtr, ByRef dwParam2 As IntPtr) As Integer
    Who would have known that Visual basic has access to an actual IntPtr type. But at least it all makes sense now. Of course I am still getting errors but at least they are coming from the mcisendcommand API so it is just a matter of getting the right parameters for the right command now.

    Still looking for a working (modern day) example if anyone has seen one and knows where to find it though.

  9. #9

    Thread Starter
    New Member
    Join Date
    May 2019
    Posts
    7

    Re: mcisendCOMMAND

    And the winner is

    Code:
    https://docs.microsoft.com/en-us/dotnet/api/system.intptr?view=netframework-4.8
    Private Declare Auto Function mciSendCommand Lib "winmm.dll" Alias "mciSendCommandA" (ByVal wDeviceID As UInteger, ByVal uMessage As UInteger, ByVal dwFlags As IntPtr, ByRef dwParam2 As IntPtr) As Integer
    Who would have known that Visual basic has access to an actual IntPtr type. But at least it all makes sense now. Of course I am still getting errors but at least they are coming from the mcisendcommand API so it is just a matter of getting the right parameters for the right command now.

    Still looking for a working (modern day) example if anyone has seen one and knows where to find it though. The missing documentation would make a nice find too but so would a Unicorn.

  10. #10
    Hyperactive Member
    Join Date
    Jun 2018
    Posts
    412

    Re: mcisendCOMMAND

    Quote Originally Posted by Ridgy View Post
    And the winner is

    Code:
    https://docs.microsoft.com/en-us/dotnet/api/system.intptr?view=netframework-4.8
    Private Declare Auto Function mciSendCommand Lib "winmm.dll" Alias "mciSendCommandA" (ByVal wDeviceID As UInteger, ByVal uMessage As UInteger, ByVal dwFlags As IntPtr, ByRef dwParam2 As IntPtr) As Integer
    Who would have known that Visual basic has access to an actual IntPtr type. But at least it all makes sense now. Of course I am still getting errors but at least they are coming from the mcisendcommand API so it is just a matter of getting the right parameters for the right command now.

    Still looking for a working (modern day) example if anyone has seen one and knows where to find it though. The missing documentation would make a nice find too but so would a Unicorn.

    There is no sense using sendcommand if you dont know what you want to do as there may be sendstring that does the same?

    I dont know but in this doc sounds like sendcommand is for c. it says:

    "The command-message interface is designed to be used by applications requiring a C-language interface to control multimedia devices. It uses a message-passing paradigm to communicate with MCI devices. You can send a command by using the mciSendCommand function."

    If you look at the "commands" they are mosty available in send string as well. So without having a purpose it sounds like a goose chase.

  11. #11

    Thread Starter
    New Member
    Join Date
    May 2019
    Posts
    7

    Re: mcisendCOMMAND

    See below
    Last edited by Ridgy; May 22nd, 2019 at 10:29 PM. Reason: Double post

  12. #12

    Thread Starter
    New Member
    Join Date
    May 2019
    Posts
    7

    Re: mcisendCOMMAND

    designed to be used by
    My last post clearly shows that this was clearly not an obstacle. Nor have I ever heard of a case where it ever has been. An interface can be designed to suit any methodology but it would have to be specifically designed to make that an exclusive requirement. Most of the basic API calls do use 'C' conventions given that they are actually written in 'C' in the first place. I think they have been implemented in about every other language out there by now. I have even done API calls as a VBA macro with a bit of research.

    Do you have an identical mcisendstring version of this one

    Code:
    MCI_DGV_STATUS_AUDIO_INPUT
    
    The dwReturn member returns the approximate instantaneous audio level of the analog audio signal. A value greater than 1000 implies there is clipping distortion. Some devices can determine this value only while recording audio. This status value has no associated MCI_SET or MCI_SETAUDIO command. This value is related to, but normalized differently from, the waveform-audio command MCI_WAVE_STATUS_LEVEL.
    There is a very large number of other ones we would have to sort through every time I needed to use it. I can ask the members of this forum for help to solve one problem and then leave a solution for everybody else to quickly solve all of theirs in the future or I can come back here every time I find something in mcisendcommand that I might want to use and expect everybody else to run around for me and find it's alternative in mcisendstring. Or I could just learn how to use mcisendcommand in the first place. I have never known refusing to learn something new to be of any use or the slightest form of achievement and my first post clearly shows me using sendstring to quickly and easily open and obtain the DeviceID followed by saving and closing it. I already know how to use sendstring which is why I made the title of the thread mcisendCOMMAND. If you only want to see a goose then that is all you will ever see the pot of gold to be.

    Of course I already have this

    mciSendString("status recsound level ", ErrBuff, Len(ErrBuff), 0)
    But without a working response from mcisendcommand I have no alternative to compare it to
    Last edited by Ridgy; May 22nd, 2019 at 02:06 AM. Reason: Further information

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Featured


Click Here to Expand Forum to Full Width