How can you perform other functions simultaneously with PlaySound? - c++

I'm writing a C++ Windows Application program which needs to Play a randomly chosen song from an array and the user needs to be able to play another song on the list at the click of a button. How would you stop the sound from the first song as to play the second song?
By pressing my button which plays the first song, the audio resource is played, however my program no longer responds to any clicks and the code does not continue until the song is complete. I have attempted using CreateThread(), however to no avail as my program remains in an unresponsive state.
The Function containing PlaySound:
LPTHREAD_START_ROUTINE WINAPI PlayWavFile(int resource) {
PlaySound(MAKEINTRESOURCE(resource), hInst, SND_RESOURCE | SND_SYNC);
return 0;
}
The case in the WndProc function which determines the result of button presses:
case WM_COMMAND:
if (LOWORD(wParam) == 1) {
HANDLE hThread = CreateThread(
NULL,
0,
PlayWavFile(i),
NULL,
NULL,
NULL
);
}
else if (LOWORD(wParam) == 2) {
HANDLE hPlaySecond = CreateThread(
NULL,
0,
PlayWavFile(j),
NULL,
NULL,
NULL
);
}
else if (LOWORD(wParam) == 3) {
HANDLE hStop = CreateThread(
NULL,
0,
PlayWavFile(NULL),
NULL,
NULL,
NULL
);
}
break;
Also if if I try clicking my button which should give the wParam value of 3, the program reaches an exception which states:
Exception thrown at 0x00000000 in Audio Player.exe: 0xC0000005: Access violation executing location 0x00000000. occurred.
The end goal is that the program would just be responsive whilst the music plays in the background.

Just call PlaySound directly with SND_ASYNC instead of SND_SYNC. That would cause PlaySound to return immediately and play the sound in the background.
There's no need to mess around with threads for that.

When you do
HANDLE hThread = CreateThread(
NULL,
0,
PlayWavFile(i),
NULL,
NULL,
NULL
);
you are calling the function PlayWavFile, and are using the value it returns as the thread function to call. And since the function is always returning 0, which is a null pointer, it's equivalent to
HANDLE hThread = CreateThread(
NULL,
0,
nullptr,
NULL,
NULL,
NULL
);
The thread function arguments is of the type LPTHREAD_START_ROUTINE, which is a pointer to a function which takes an LPVOID argument, and returns a DWORD.
You need to change your thread function accordingly, and then call CreateThread as
HANDLE hThread = CreateThread(
NULL,
0,
&PlayWavFile, // Pass a pointer to the function
reinterpret_cast<LPVOID>(i), // The argument to pass
NULL,
NULL
);
With this the PlayWavFile could look something like
DWORD PlayWavFile(LPVOID argument) {
int resource = reinterpret_cast<int>(argument);
PlaySound(MAKEINTRESOURCE(resource), hInst, SND_RESOURCE | SND_SYNC);
return 0;
}

Related

C++ CreateWindow: button position gets offset at window maximize

I have a weird problem over here. I created a DLL proxy for Spotify so I can "overlay" a button onto it. Basicially, thats how it works:
DllMain
-> Creates CMain class
-> Creates CToggleButton class
-> Hooks the button onto the Spotify window
It has two methods, one static one which I use for the thread since threads can't call member functions, and one non-static function which gets called by the member function.
With this, I create the thread and pass an instance of the CToggleButton class via lpParam:
CreateThread(0, NULL, WindowThreadStatic, (void*)this, NULL, NULL);
Then, the WindowThreadStatic function:
DWORD WINAPI CToggleButton::WindowThreadStatic(void* lpParam)
{
return ((CToggleButton*)lpParam)->WindowThread();
}
And the main window thread function inside the class:
DWORD CToggleButton::WindowThread()
{
MSG msg;
hButton = CreateWindowA("BUTTON", "Test", (WS_VISIBLE | WS_CHILD), 0, 0, 100, 20, parenthWnd, NULL, hInst, NULL);
bool bQueueRunning = true;
while (bQueueRunning)
{
if (PeekMessage(&msg, parenthWnd, 0, 0, PM_REMOVE))
{
switch (msg.message)
{
case WM_QUIT:
bQueueRunning = false;
break;
case WM_LBUTTONDOWN:
if (msg.hwnd == hButton)
{
MessageBoxA(parenthWnd, "Button!", "Button", MB_OK);
continue;
}
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Sleep(10);
}
return 0;
}
As you can see, this also contains the message loop for the button (I didn't use GetMessage() here because it was very unresponsive so I decided to use PeekMessage() together with a 10ms delay, which works fine.)
Little picture to show how it looks like:
All great, but if I maximize the window, the button disappears. When I minimize and maximize the window a few times, the button can be seen again, but with very weird coordinates (not the original 0,0 I gave him).
So what is my problem here? Why do the coordinates get offset?
Thanks for reading my long post :)

Identify and resize newly opened external window

I've created a program to identify existing maximized windows and log their locations using EnumWindows and GetWindowPlacement.
Assuming I know the location of a desired EXE, I can open it just by calling the external process caller. But what is the best way to identify the newly opened window and set its location? It's safe to assume that a program with the same name may already be open (two instances of cmd, for instance).
I believe once the window is identified I can set its location with SetWindowPos.
Is this the right question to be asking? Is there a way to open a program and receive a HWND handle back instead?
In the Event Hook vein, I have this code:
HWINEVENTHOOK hook = SetWinEventHook(EVENT_OBJECT_CREATE, EVENT_OBJECT_CREATE, NULL, proc, 0, 0, WINEVENT_OUTOFCONTEXT );
BOOL result = CreateProcess(NULL, szPath, &saProcess, &saThread, FALSE, 0, NULL, NULL, &si, &piProcessC);
if (hook) {
UnhookWinEvent(hook);
}
Which creates a hook and then creates a process (I'm opening up Notepad++).
The proc function it calls is:
void CALLBACK proc(HWINEVENTHOOK hook, DWORD event, HWND hWnd, LONG obj, LONG child, DWORD thr, DWORD time) {
if (IsWindow(hWnd)){
WINDOWPLACEMENT *wp = new WINDOWPLACEMENT();
wp->length = sizeof(WINDOWPLACEMENT);
GetWindowPlacement(hWnd, wp);
wp->rcNormalPosition.top = (long) 363;
wp->rcNormalPosition.bottom = (long) 1021;
wp->rcNormalPosition.left = (long) 1444;
wp->rcNormalPosition.right = (long) 2551;
BOOL tmp = SetWindowPlacement(hWnd, wp);
cout << "FOUND IT\n";
}
}
The proc function does not appear to get called, so is the hook not catching anything?
Oh, and I'm not sure the WINEVENT_OUTOFCONTEXT is right, this is just a simple EXE doing the calling.

Send return value to dialog from thread MFC

I'm creating dialog with do modal:
MainHamsterDlg MainHamsterDlg;
if (MainHamsterDlg.DoModal() == IDCANCEL)
break;
Then in that dialog create worker thread:
BOOL MainHamsterDlg::OnInitDialog()
{
AfxBeginThread(WorkerThreadProc, m_hWnd, THREAD_PRIORITY_NORMAL, 0, 0, NULL);
CDialogEx::OnInitDialog();
return TRUE;
}
the thread must exit dialog by sending return value IDCANCEL.
UINT WorkerThreadProc(LPVOID Param) //Sample function for using in AfxBeginThread
{
Sleep(1000); // process simulation
MainHamsterDlg * self = (MainHamsterDlg *)Param;
self->EndDialog(IDCANCEL);
return FALSE;
}
When compiling I do not get any error. when processing then getting on the point:
self->EndDialog(IDCANCEL);
error message:
Unhandled exception at 0x01503AD4 in L2Hamster.exe: 0xC0000005:
Access violation reading location 0x00000020.
that pointing my to dlgcore.cpp file:
void CDialog::EndDialog(int nResult)
{
ASSERT(::IsWindow(m_hWnd)); <<<<<===== to that line
m_bClosedByEndDialog = TRUE;
if (m_nFlags & (WF_MODALLOOP|WF_CONTINUEMODAL))
EndModalLoop(nResult);
::EndDialog(m_hWnd, nResult);
}
I don't know what I'm doing wrong. any solution?
I assume you intended to pass a pointer to the dialog to the thread proc. But you passed null instead:
AfxBeginThread(WorkerThreadProc, NULL, THREAD_PRIORITY_NORMAL, 0, 0, NULL);
Should be:
AfxBeginThread(WorkerThreadProc, this, THREAD_PRIORITY_NORMAL, 0, 0, NULL);
You will also need to coordinate the end of the thread with the end of the dialog to ensure the dialog isn't dismissed before the thread ends. (I assume it is some sort of progress dialog).
This looks like it is the problem to me:
AfxBeginThread(WorkerThreadProc, NULL, THREAD_PRIORITY_NORMAL, 0, 0, NULL);
followed by
UINT WorkerThreadProc(LPVOID Param) //Sample function for using in AfxBeginThread
{
MainHamsterDlg * self = (MainHamsterDlg *)Param;
self->EndDialog(IDCANCEL);
...
}
Here, Param is NULL, so self is NULL. You then dereference a null pointer.

c++ creating a window in a new thread

I have a basic window program, the problem is when i try to create a window in a new thread after the message loop has already started the window displays for a second and disappears. does anyone no the reason for this? can a window be created in a separate thread?
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
::hInstance =hInstance; // initialize global variables
::nCmdShow =nCmdShow;
// start thread
HANDLE threadHandle = startThread(StartUp);
MSG msg;
while(GetMessage(&msg, 0, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
::CloseHandle(threadHandle);
return static_cast<int>(msg.wParam);
}
DWORD WINAPI StartUp(LPVOID lpParam) // new thread runs here
{
//code to create a new window...
}
what i figured out so far is that the GetMessage(&msg, 0, 0, 0) returns false if there are no windows in the current thread that it is in... is there a way to get a round this?
GetMessage() does not return FALSE if there are no windows. It is only looking for messages in the calling thread's message queue. You are specifying NULL for its hWnd parameter, so it will not care how messages get queued, whether by PostMessage() to a window, or by PostThreadMessage() to the thread's ID.
Each thread has its own local message queue and thus requires its own message loop. You can most certainly create a new window in a worker thread after the main thread has started its message loop. They are independent of each other. So whatever problem you are having in the main thread is not related to creating a window in a worker thread. Something else is going on.
With that said, keep in mind that GetMessage() returns a BOOL, which is actually an int, not a true bool. GetMessage() can return one of 3 different return values:
-1 if an error occurs
0 if a WM_QUIT message is retrieved
>0 if any other message is retrieved
You are only checking for 0 and != 0, so if GetMessage() returns -1 on error, you are treating it as a success instead of a failure. Even MSDN says not to do that:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms644936.aspx
Because the return value can be nonzero, zero, or -1, avoid code like this:
while (GetMessage( lpMsg, hWnd, 0, 0)) ...
The possibility of a -1 return value means that such code can lead to fatal application errors. Instead, use code like this:
BOOL bRet;
while( (bRet = GetMessage( &msg, hWnd, 0, 0 )) != 0)
{
if (bRet == -1)
{
// handle the error and possibly exit
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}

Thread starts before UI is updated

When I run this code, the text is updated AFTER the message box in the thread is popped.
void PnlOptions::ClickHandler() {
SetWindowText(txt_progress_, "CLASS MEMBER FUNCTION");
HANDLE hThread = (HANDLE) _beginthreadex(0, 0, &ThreadProcess, 0, CREATE_SUSPENDED, 0);
ResumeThread(hThread);
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
}
unsigned int __stdcall ThreadProcess(void * data0) {
MessageBox(NULL, "THREAD FREE FUNCTION", "Alert", MB_OK);
}
I thought it was because
If the thread is created in a runnable state (that is, if the CREATE_SUSPENDED flag is not used), the thread can start running before CreateThread returns and, in particular, before the caller receives the handle and identifier of the created thread.
but using a non-suspended thread: same result.
Also tried:
Using CreateThread
Changing thread priority
Using SendMessage instead of SetWindowText
PeekMessage
Why does the thread start before the UI is updated?
Declarations:
pnl_options.h:
unsigned int __stdcall ThreadProcess(void *);
public PnlOptions:
void Init(HWND);
void ClickHandler();
private:
HWND txt_progress_;
pnl_options.cpp (other than above code):
void PnlOptions::Init(HWND hwnd0) {
txt_progress_ = CreateWindowEx (0,
TEXT("EDIT"), "Press \"GO\" to process all selected files.",
SS_LEFT | SS_CENTERIMAGE | WS_VISIBLE | WS_CHILD,
0, 0, 0, 0,
hwnd0, (HMENU) IDT_PROGRESSTEXT, NULL, NULL
);
}
I reproduced this behavior and it seems that there is a kind of deadlock between the EDIT control and his parent window because of a posted WM_PAINT message.
"Curioser", it works if you replace the EDIT with a STATIC control.
I don't have real explanation/solution for this, so it's more a clue than an answer...
PS: Note that SS_LEFT and SS_CENTERIMAGE are not valid for an EDIT control, use ES_* defines instead.
You need to let the event loop roll before waiting for the other thread. In .NET it would be Applicaiton.DoEvents(). Search with that, SO seems to have bunch of related questions...
SetWindowText sends a message in order to update the windows text, and the message will be processed in your message loop when it processes the message queue.
However, you're blocking in ClickHandler (via the call to WaitForSingleObject) which means that the message queue won't be processed until you return from ClickHandler