Click here to Skip to main content
15,884,298 members
Please Sign up or sign in to vote.
2.50/5 (2 votes)
See more:
Hello

I have been trying to create a DLL in C++ using Visual Studio 2012. The aim is to convert the MFPLayer sample which uses the Microsoft Media Foundation to play a video file into a DLL that does the same thing. The MFPlayer samples is a win32 application and it compiles and runs.

The sample can be found in MFSamples.zip at :
http://archive.msdn.microsoft.com/mediafoundation

Previously I successfully created a Dll version of the BasicPlayback sample which also plays videos. I managed to call the dll functions from a c# application. I use the same procedure for this new dll.

However, MFPlayer sample doesn't work. The dll calls the following function

C++
BOOL MainDialog::ShowDialog(HINSTANCE hinst)
{
    // Show the dialog. Pass a pointer to ourselves as the LPARAM
    INT_PTR ret = DialogBoxParam(
        hinst, 
        MAKEINTRESOURCE(m_nID), 
        NULL, 
        DialogProc, 
        (LPARAM)this
        );

    if (ret == 0 || ret == -1)
    {
        MessageBox( NULL, L"Could not create dialog", L"Error", MB_OK | MB_ICONERROR );
        return FALSE;
    }

    return (IDOK == ret);
}


After calling DialogBoxParam the DialogProc is called to handle several messages but the WM_INITDIALOG message is never sent.

Here's some of the other relevant code....

C++
BOOL APIENTRY DllMain (HINSTANCE hInstance, DWORD reason, LPVOID reserved)
{
	g_hInstance = hInstance;
	return TRUE;
}
extern "C"
{
__declspec(dllexport) int PlayMovie()
{
    HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);

    // Initialize the COM library.
    HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);

    if (FAILED(hr))
    {
        MessageBox(NULL, L"CoInitialize failed.", NULL, MB_ICONSTOP);
        return 0;
    }

    // Initialize the common control library.
    INITCOMMONCONTROLSEX icc;
    icc.dwSize = sizeof(icc);
    icc.dwICC = ICC_STANDARD_CLASSES | ICC_BAR_CLASSES;
    if (!InitCommonControlsEx(&icc))
    {
        MessageBox(NULL, L"InitCommonControlsEx failed.", NULL, MB_ICONSTOP);
        CoUninitialize();
        return 0;
    }

    // Initialize our custom slider class.
    hr = Slider_Init();
    if (FAILED(hr))
    {
        MessageBox(NULL, L"Slider_Init failed.", NULL, MB_ICONSTOP);
        CoUninitialize();
        return 0;
    }

    // Create and show the dialog.
    MainDialog *pDlg = new (std::nothrow) MainDialog();
    if (pDlg == NULL)
    {
        MessageBox(NULL, L"Out of memory.", NULL, MB_ICONSTOP);
    }
    else
    {
        pDlg->ShowDialog(g_hInstance);

        delete pDlg;
    }

    CoUninitialize();
    return 0;
}


You will notice that I capture the HINSTANCE sent to DllMain and use this for the API calls. Play PlayMovie function is called from C#.

I have to say that I am completely new to windows programming and I might be missing something simple. However, I was hoping someone could give me some clues as to where to look.


In summary, the dll compiles. It is called from a c# application. It runs without error but the WM_INITDIALOG message is never sent to DialogProc. Any ideas..

Here's some additional information...

I put a breakpoint in DialogProc and get the following messages:
VB
48 = WM_SETFONT 
85 = WM_NOTIFYFORMAT 
297 = WM_QUERYUISTATE  
144 = Not Defined
2 = WM_DESTROY 
130 = WM_NCDESTROY 


C++
INT_PTR CALLBACK MainDialog::DialogProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
    MainDialog *pDlg = 0;  // Pointer to the dialog class that manages the dialog 

    LRESULT lresult = 0;

    if (msg == WM_INITDIALOG)
    {
        // Get the pointer to the dialog object and store it in 
        // the window's user data

        SetWindowLongPtr(hDlg, DWLP_USER, (LONG_PTR)lParam);

        pDlg = (MainDialog*)lParam;
        if (pDlg)
        {
            pDlg->m_hDlg = hDlg;
            HRESULT hr = pDlg->OnInitDialog();
            if (FAILED(hr))
            {
                pDlg->EndDialog(0);
            }
        }
        return FALSE;
    }

    // Get the dialog object from the window's user data
    pDlg = (MainDialog*)(DWORD_PTR) GetWindowLongPtr(hDlg, DWLP_USER);

    if (pDlg != NULL)
    {
        switch (msg)
        {
        case WM_COMMAND:
            switch (LOWORD(wParam))
            {
            case IDOK:
            case IDCANCEL:
                pDlg->EndDialog(LOWORD(wParam));
                return TRUE;

            default:
                return pDlg->OnCommand((HWND)lParam, LOWORD(wParam), HIWORD(wParam));
            }
            break;

        case WM_NOTIFY:
            lresult = pDlg->OnNotify((NMHDR*)lParam);

            // The LRESULT from WM_NOTIFY can be meaningful. Store the result in DWLP_MSGRESULT.
            SetWindowLongPtr(hDlg, DWLP_MSGRESULT, (LONG_PTR)lresult);
            return TRUE;

        default:
            return pDlg->OnReceiveMsg(msg, wParam, lParam);
        }
    }
    else
    {
        return FALSE;
    }
}


The DialogBoxParam returns -1 but a call to GetLastError() gives 0 signifying that there has not been an error.

Many thanks....
Posted
Updated 12-Feb-13 17:47pm
v2
Comments
Richard MacCutchan 12-Feb-13 11:20am    
How do you know that the WM_INITDIALOG message is never sent?
FlurryKnox 12-Feb-13 23:48pm    
Hi, I put a breakpoint in DialogProc and check each message. I've updated the question with the messages and the code for DialogProc.
Richard MacCutchan 13-Feb-13 4:03am    
I have seen this sequence of messages in the past, when the dialog, or some part of it, is invalid. You need to do some more investigation to try and discover why the system is rejecting the dialog.
chaau 12-Feb-13 17:47pm    
Can you show your CMainDialog::DialogProc function implementation
FlurryKnox 12-Feb-13 23:49pm    
Hi, sure, I've updated the question with the DialogProc code.

The only thing that makes sense to me in this scenario, other than a straight bug in the Windows code, is that the DLL version must be trying to load the dialog template resource from the resources of the executable rather than the DLL itself.
See if you can load the dialog template resource yourself in the dllmain function and if you can make that work you can create the dialog from the already loaded resource to avoid the default code going and looking in the wrong place.
 
Share this answer
 
Hello,

I think you should check if dialog resource is valid and exists with ID you specify, if exists and loads properly problem could be with dialog styles.

Regards,
Maxim.
 
Share this answer
 
Hello

I just did an experiment. I created a empty win32 exe project. I copied the .h,.cpp, the .rc (resource file for dialog) and some images used by the dialog over to the project folder and add them to the project. I then remove the DllMain function and change the PlayMovie function from
C++
<pre lang="cs">extern "C"
{
__declspec(dllexport) int PlayMovie()
{


to this
C++
<pre lang="cs">int WINAPI wWinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)


I copy over the additional dependencies. Now the application compiles and runs without error or warning..The dialog is displayed successfully for the .exe.

This suggests that the original problem must be with the conversion to Dll. Originally, to create the Dll I create an empty win32 dll project. I copy the same files over and add them to project. I add the DllMain function and convert the WinMain function to the PlayMovie() (however, I leave the function body the same.

I call PlayMovie from c# application. The function is called but the WM_INITDIALOG is never sent.

Any ideas as to where I might look...????
 
Share this answer
 
Comments
Maxim Kartavenkov 13-Feb-13 7:49am    
Had you try to debug and check what g_hInstance value? Does your code get to the DialogBoxParam function once you call it from .NET (bcs CoInitialize already called in case you call dll from .NET and may return failure)? Had you try to call LoadResource and SizeOfResource to check proper resource ID once you call it from .NET?
Maxim Kartavenkov 13-Feb-13 7:51am    
Maybe your Dialog have style that it is main application window but you call it from other application?
Hi folks,

I tried this...

C++
BOOL MainDialog::ShowDialog(HINSTANCE hinst)
{

HRSRC hSrc = NULL;
HGLOBAL hHndl = NULL;
HMODULE hMod = GetModuleHandle(TEXT("DLLTEST.dll"));
hSrc = FindResource(hMod, MAKEINTRESOURCE(IDD_DIALOG1), MAKEINTRESOURCE(5));
if(hSrc == NULL)
      return 0;
hHndl = LoadResource(hMod, hSrc);
if(hHndl == NULL)
      return 0;
hSrc = FindResource(hMod, MAKEINTRESOURCE(IDR_MENU1), MAKEINTRESOURCE(4));
if(hSrc == NULL)
      return 0;
hHndl = LoadResource(hMod, hSrc);
if(hHndl == NULL)
      return 0;
hSrc = FindResource(hMod, MAKEINTRESOURCE(IDD_OPENURL), MAKEINTRESOURCE(5));
if(hSrc == NULL)
      return 0;
hHndl = LoadResource(hMod, hSrc);
if(hHndl == NULL)
      return 0;
hSrc = FindResource(hMod, MAKEINTRESOURCE(IDB_MUTE), MAKEINTRESOURCE(2));
if(hSrc == NULL)
      return 0;
hHndl = LoadResource(hMod, hSrc);
if(hHndl == NULL)
      return 0;
hSrc = FindResource(hMod, MAKEINTRESOURCE(IDB_PLAY), MAKEINTRESOURCE(2));
if(hSrc == NULL)
      return 0;
hHndl = LoadResource(hMod, hSrc);
if(hHndl == NULL)
      return 0;
hSrc = FindResource(hMod, MAKEINTRESOURCE(IDB_SLIDER), MAKEINTRESOURCE(2));
if(hSrc == NULL)
      return 0;
hHndl = LoadResource(hMod, hSrc);
if(hHndl == NULL)
      return 0;
hSrc = FindResource(hMod, MAKEINTRESOURCE(IDB_VOLUME), MAKEINTRESOURCE(2));
if(hSrc == NULL)
      return 0;
hHndl = LoadResource(hMod, hSrc);
if(hHndl == NULL)
      return 0;

	// Show the dialog. Pass a pointer to ourselves as the LPARAM
    INT_PTR ret = DialogBoxParam(
        hinst, 
        MAKEINTRESOURCE(m_nID), 
        NULL, 
        DialogProc, 
        (LPARAM)this
        );

    if (ret == 0 || ret == -1)
    {
        MessageBox( NULL, L"Could not create dialog", L"Error", MB_OK | MB_ICONERROR );
        return FALSE;
    }

    return (IDOK == ret);
}


All the project resources are found and loaded successfully...however DialogBoxParam returns -1 and dialog fails to display...MSDN docs says this "If the function fails because the hWndParent parameter is invalid, the return value is zero. The function returns zero in this case for compatibility with previous versions of Windows. If the function fails for any other reason, the return value is -1. To get extended error information, call GetLastError.". However, GetLastError returns 0 signifying no error....

I am at a loss....am I looking in the wrong place???

Update...I tried this....
C++
BOOL MainDialog::ShowDialog(HINSTANCE hinst)
{

HMODULE hModule = ::GetModuleHandle(TEXT("DLLTEST.dll"));
  HINSTANCE hInst = hModule;

  HRSRC hrsrc = ::FindResource(hModule, 
                  MAKEINTRESOURCE(IDD_DIALOG1), RT_DIALOG);

  HGLOBAL hglobal = ::LoadResource(hModule, hrsrc);

 INT_PTR ret =  ::DialogBoxIndirectParam(hInst, 
                  (LPCDLGTEMPLATE) hglobal, 0, 0, 0);
   return (IDOK == ret);
}


The hInst == hinst (e.g. getmodulehandle returns same handled as passed to dllmain)...DialogBoxIndirectParam returns -1.
 
Share this answer
 
v4
Comments
Matthew Faithfull 14-Feb-13 6:08am    
OK so you can get all the resource bits you need from the DLL but you've not tried changing the handle passed to DialogBoxParam to hMod or tried loading the actual template resource and making a call to DialogBoxIndirectParam instead. I would give that a go before you call Steve Ballmer to complain, it's a small change compared to what you've already done. ;-)
FlurryKnox 14-Feb-13 6:46am    
I've tried DialogBoxIndirectParam quickly (copied and pasted from internet). I tried using hMod in the previous example....

Hehehe..Thinking of launching a crowded sourcing project to kidnap Ballmer and get him to solve this....
FlurryKnox 14-Feb-13 6:49am    
I'm not sure how to load the resource template...???
Matthew Faithfull 14-Feb-13 7:03am    
You've done that bit, it's attached to hrsrc now if the load worked.
Matthew Faithfull 14-Feb-13 7:02am    
DialogBoxIndirectParam needs the dialog proc as the 4th parameter and your data parameter ( this pointer ) as the 5th parameter to send with WM_INITDIALOG if it ever gets that far. You should be able to leave the 3rd parameter ( parent window ) null but if that worries you replace it with handle of the Desktop, GetDesktopWindow?- not sure but you'll find it.
Have you checked that the hrsrc and hglobal handles are non-null before the call?

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900