mIRC Home    About    Download    Register    News    Help

Print Thread
Page 1 of 2 1 2
Joined: Jan 2011
Posts: 11
D
Pikka bird
OP Offline
Pikka bird
D
Joined: Jan 2011
Posts: 11
Hi,

I'm fairly new to win32 programming. I've been trying to create a window from within my DLL (don't ask) and I've had limited success so far. I've followed several tutorials and read many discussion forums but no matter what I do, my code seems to always cause mIRC to crash. The window creation code below is something I found in one of the tutorials I've read with very little modifications by me (variable names etc).

Code:
#include <windows.h>

#define MYMENU_EXIT         (WM_APP + 101)
#define MYMENU_MESSAGEBOX   (WM_APP + 102) 

// WndProc for the new window
LRESULT CALLBACK DLLWindowProc (HWND, UINT, WPARAM, LPARAM);

HINSTANCE dllInstance;
HWND mIRC_window;

typedef struct {
	DWORD  mVersion;
	HWND   mHwnd;
	BOOL   mKeep;
	BOOL   mUnicode;
} LOADINFO;

void __stdcall LoadDll(LOADINFO*);
int __stdcall UnloadDll(int);

int __stdcall UnloadDll(int mTimeout) {
	return 0; // keep the dll loaded
}

void __stdcall LoadDLL(LOADINFO *load) {
	load->mKeep = TRUE;
}

// Register our window's Class
BOOL RegisterDLLWindowClass(wchar_t szClassName[])
{
    WNDCLASSEX wc;
    wc.hInstance =  dllInstance;
	wc.lpszClassName = (LPCWSTR)L"DLLWindowClass";
    wc.lpszClassName = (LPCWSTR)szClassName;
    wc.lpfnWndProc = DLLWindowProc;
    wc.style = CS_DBLCLKS;
    wc.cbSize = sizeof (WNDCLASSEX);
    wc.hIcon = LoadIcon (NULL, IDI_APPLICATION);
    wc.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor (NULL, IDC_ARROW);
    wc.lpszMenuName = NULL;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hbrBackground = (HBRUSH) COLOR_BACKGROUND;
    if (!RegisterClassEx (&wc))
		return 0;
}
// Creating our window's Menu
HMENU CreateDLLWindowMenu()
{
	HMENU hMenu;
	hMenu = CreateMenu();
	HMENU hMenuPopup;
    if(hMenu==NULL)
        return FALSE;
    hMenuPopup = CreatePopupMenu();
	AppendMenu (hMenuPopup, MF_STRING, MYMENU_EXIT, TEXT("Exit"));
    AppendMenu (hMenu, MF_POPUP, (UINT_PTR) hMenuPopup, TEXT("File")); 

	hMenuPopup = CreatePopupMenu();
    AppendMenu (hMenuPopup, MF_STRING,MYMENU_MESSAGEBOX, TEXT("MessageBox")); 
    AppendMenu (hMenu, MF_POPUP, (UINT_PTR) hMenuPopup, TEXT("Test")); 
	return hMenu;
}

// The new thread
DWORD WINAPI ThreadProc( LPVOID lpParam )
{
    MSG messages;
	wchar_t *pString = reinterpret_cast<wchar_t * > (lpParam);
	HMENU hMenu = CreateDLLWindowMenu();
	RegisterDLLWindowClass(L"DLLWindowClass");
	HWND hwnd = CreateWindowEx (0, L"DLLWindowClass", pString, WS_EX_PALETTEWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 400, 300, mIRC_window, hMenu, dllInstance, NULL );
	ShowWindow (hwnd, SW_SHOWNORMAL);
    while (GetMessage (&messages, NULL, 0, 0))
    {
		TranslateMessage(&messages);
        DispatchMessage(&messages);
    }
    return 1;
}
// Our new window's proc
LRESULT CALLBACK DLLWindowProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
		case WM_COMMAND:
               switch(wParam)
               {
                    case MYMENU_EXIT:
						SendMessage(hwnd, WM_CLOSE, 0, 0);
                        break;
                    case MYMENU_MESSAGEBOX:
						MessageBox(hwnd, L"Test", L"MessageBox",MB_OK);
                        break;
               }
               break;
		case WM_DESTROY:
			PostQuitMessage (0);
			break;
		default:
			return DefWindowProc (hwnd, message, wParam, lParam);
    }
    return 0;
}



BOOL APIENTRY DllMain( HINSTANCE hModule, 
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved
					 )
{
	dllInstance = hModule;
    return TRUE;
}

int __stdcall dllname(HWND mWnd, HWND aWnd, char *data, char *parms, BOOL show, BOOL nopause) {
	wchar_t szFileName[MAX_PATH];
	GetModuleFileName((HMODULE)dllInstance, szFileName, MAX_PATH);
	MessageBox(aWnd, szFileName, L"This DLL is:", MB_OK | MB_ICONINFORMATION);
	lstrcpyA(data, "yay");
	return 3;
}

int __stdcall window(HWND mWnd, HWND aWnd, char *data, char *parms, BOOL show, BOOL nopause) {
	mIRC_window = mWnd;
	HANDLE hThread = CreateThread(0, NULL, ThreadProc, (LPVOID)L"Window Title", NULL, NULL);
	if (hThread) {
		lstrcpyA(data, "Thread (and window) created!");
	}
	else {
		lstrcpyA(data, "Thread creation failed!");
	}
	return 3;
}


As you can see, the code above creates a new thread for the window, which I'm assuming is the saner way of doing this. I tried creating my window without using a new thread and the result wasn't really different (mIRC crashed).

Most of my attempts = no window appears + mIRC crashes
If I'm lucky = window appears, but mIRC crashes shortly after that (the window appears because I would have changed something in the code, but mIRC still crashes).

//echo -a $dll(test.dll,dllname,1) works fine (pops up a message box, and as soon as I click OK, "yay" is echoed)

//echo -a $dll(test.dll,window,1) immediately echoes "Thread (and window) created!" and mIRC crashes (mIRC window remains until I close the Visual Studio JIT debugger dialog)

Has anyone successfully created a window/dialog from within their DLLs? If so, could you please share how and/or see what I'm doing wrong here?

Joined: Oct 2003
Posts: 3,918
A
Hoopy frood
Offline
Hoopy frood
A
Joined: Oct 2003
Posts: 3,918
Obvious questions:

Have you tried your window creation code outside of a mIRC dll? Specifically: are you sure your window / threading code is working? I assume you posted this on an mIRC discussion board because you're having an issue that is specific to mIRC, and not just a general programming issue. If you haven't yet tested this, I suggest you do so first so we can narrow down and find out whether the problem is actually mIRC related.

If you have tried it and it crashes without mIRC, perhaps a better place to ask about threading / winapi code is on an MSDN / winapi discussion forum, not mIRC's forums.

Some pointers:

Threading + mIRC == bad. Instead of spawning a thread you should hook into mIRC's windowproc and do your processing from there to run mIRC's runloop along. Check MSDN on how to either subclass a window or hook into a windowproc.

Using "reinterpret_cast" smells fishy in general. Is this your code, or was it copied? You should probably stick to straight C.

Why are you passing a string literal as your thread parameter? Seems pointless...


- argv[0] on EFnet #mIRC
- "Life is a pointer to an integer without a cast"
Joined: Jan 2011
Posts: 11
D
Pikka bird
OP Offline
Pikka bird
D
Joined: Jan 2011
Posts: 11
Originally Posted By: argv0
Obvious questions:

Have you tried your window creation code outside of a mIRC dll? Specifically: are you sure your window / threading code is working? I assume you posted this on an mIRC discussion board because you're having an issue that is specific to mIRC, and not just a general programming issue. If you haven't yet tested this, I suggest you do so first so we can narrow down and find out whether the problem is actually mIRC related.

If you have tried it and it crashes without mIRC, perhaps a better place to ask about threading / winapi code is on an MSDN / winapi discussion forum, not mIRC's forums.

I'm asking here because the problem is specific to mIRC. I've tried multiple complete window creation code examples taken from various different tutorials and tried them all to create standalone Windows applications. They worked perfectly. When I use the same code in an mIRC DLL, mIRC crashes. Now I can't say the same thing about what I posted earlier because I didn't test that as a standalone application, so I'll try it out first to confirm that it's not the problem (I'll reply to this thread when I've done this).

Originally Posted By: argv0

Threading + mIRC == bad. Instead of spawning a thread you should hook into mIRC's windowproc and do your processing from there to run mIRC's runloop along. Check MSDN on how to either subclass a window or hook into a windowproc.

Thanks, I made a quick look-up for 'wndproc hook' and google returned lots of promising results. I suppose I'll find enough information/examples on the subject. Looks like threading is a bit of trouble when mixed with IRC clients. I've had similar problems with Xchat. Hopefully hooking into mIRC's WndProc will save me the trouble.

Originally Posted By: argv0

Using "reinterpret_cast" smells fishy in general. Is this your code, or was it copied? You should probably stick to straight C.

Why are you passing a string literal as your thread parameter? Seems pointless...

reinterpret_cast was part of the copied code. Likewise, the CreateThread() call was copied as is. I guess the author of the code preferred to set the title of the window by passing it to CreateThread().

Joined: Oct 2003
Posts: 3,918
A
Hoopy frood
Offline
Hoopy frood
A
Joined: Oct 2003
Posts: 3,918
Originally Posted By: deadbeef
Thanks, I made a quick look-up for 'wndproc hook' and google returned lots of promising results. I suppose I'll find enough information/examples on the subject. Looks like threading is a bit of trouble when mixed with IRC clients. I've had similar problems with Xchat. Hopefully hooking into mIRC's WndProc will save me the trouble.


Note that Tcl4mIRC does this. The source is available in the download, if you want to see how it's done. Beware: Tcl4mIRC also uses threads, but this is for specific threading functionality-- most of the code is run through a single threaded message loop. So ignore the CreateThread call if you do happen to look at the source.


- argv[0] on EFnet #mIRC
- "Life is a pointer to an integer without a cast"
Joined: Mar 2008
Posts: 27
7
Ameglian cow
Offline
Ameglian cow
7
Joined: Mar 2008
Posts: 27
i was going into similar problem, i used to create the window and then it froze, then i realized it was a threading issue, anyway here is how it worked for me

Code:
#include <afx.h>
#include <windows.h>
#include <windowsx.h>
#include <afxwin.h>

#define WinClass _T("UrWinClass")

LRESULT WINAPI WinParser(HWND handle, UINT message, WPARAM wParam, LPARAM parameters) {
	switch (message) {
	default:
		return DefWindowProc(handle, message, wParam, parameters);
	case WM_COMMAND:
		break;
	case WM_PAINT:
		break;
	case WM_DESTROY:
		break;
	case WM_COPYDATA:
		break;
	}
	return 0;
}

class Win : public CWinThread {
public:
	Win() {};
	~Win() {};
private:
	HACCEL Table;
public:
	virtual int Run() {
		MSG Msg;
		while (GetMessage(&Msg, NULL, 0, 0)) {
			if (!TranslateAccelerator(Msg.hwnd, Table, &Msg)) {
				TranslateMessage(&Msg);
				DispatchMessage(&Msg);
			}
		}
		return 0;
	};
	virtual BOOL InitInstance() {
		WNDCLASSEX WinC;
		WinC.hInstance		= NULL;
		WinC.lpszClassName	= WinClass;
		WinC.lpfnWndProc	= WinParser;
		WinC.style			= CS_HREDRAW | CS_VREDRAW;
		WinC.hIcon			= LoadIcon(NULL, IDI_APPLICATION);
		WinC.hIconSm		= LoadIcon(NULL, IDI_APPLICATION);
		WinC.hCursor		= LoadCursor(NULL, IDC_ARROW);
		WinC.lpszMenuName	= NULL;
		WinC.cbClsExtra		= 0;
		WinC.cbWndExtra		= 0;
		WinC.hbrBackground	= (HBRUSH)COLOR_BACKGROUND;
		WinC.cbSize			= sizeof(WNDCLASSEX);
		RegisterClassEx(&WinC);
		HWND Handle = CreateWindow(WinClass, _T(""), WS_DISABLED, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, NULL, NULL);
		if (Handle) {
			UpdateWindow(Handle);
			Table = LoadAccelerators(NULL, WinClass);
			return true;
		}
		return false;
	};
};


Code:

#include "Win.h"

Win* x;

EXTERN_C int WINAPI UnloadDll(int TimeOut) {
	//x->ExitInstance();
	//delete x;
	return mIRC->Unload(TimeOut);
}

EXTERN_C void WINAPI LoadDll(LoadInfo *Load) {
	Load->Keep = 1; // stay loaded
	mIRC = new Dll;
	mIRC->Load(Load->Handle);
	x = new Win;
	x->InitInstance();
}

Func(Start) {
	x->CreateThread(CREATE_SUSPENDED) ;
	x->m_bAutoDelete = true ;
	x->ResumeThread() ;
	return 1;
}


Joined: Jan 2011
Posts: 11
D
Pikka bird
OP Offline
Pikka bird
D
Joined: Jan 2011
Posts: 11
OK, so I've hooked into the mIRC WndProc, but now CreateWindowEx seems to always fail (message box informs me so, see code below).

Code:
#include <windows.h>

#define MYMENU_EXIT         (WM_APP + 101)
#define MYMENU_MESSAGEBOX   (WM_APP + 102) 
#define MY_MSGLOOP	    (WM_USER+900)

// WndProc for the new window
LRESULT CALLBACK DLLWindowProc (HWND, UINT, WPARAM, LPARAM);

static HINSTANCE dllInstance;
static HWND mIRC_window;
static WNDPROC mWndProc;

typedef struct {
	DWORD  mVersion;
	HWND   mHwnd;
	BOOL   mKeep;
	BOOL   mUnicode;
} LOADINFO;

void __stdcall LoadDll(LOADINFO*);
int __stdcall UnloadDll(int);

int __stdcall UnloadDll(int mTimeout) {
	if (mTimeout == 0) { /* user called /dll -u (or mIRC is shutting down) */
		/* Remove window hook */
		SetWindowLong(mIRC_window, GWL_WNDPROC, (LONG)mWndProc);
        }
	return 0; // keep the dll loaded
}

void __stdcall LoadDLL(LOADINFO *load) {
	load->mKeep = TRUE;
	// setup window subclass to hook the dll window event loop
	mIRC_window = load->mHwnd;
	mWndProc = (WNDPROC)SetWindowLong(mIRC_window, GWL_WNDPROC, (LONG)DLLWindowProc); 
}

// Create our window's Menu
HMENU CreateDLLWindowMenu()
{
	HMENU hMenu;
	hMenu = CreateMenu();
	HMENU hMenuPopup;
        if(hMenu==NULL)
            return FALSE;
        hMenuPopup = CreatePopupMenu();
	AppendMenu (hMenuPopup, MF_STRING, MYMENU_EXIT, TEXT("Exit"));
        AppendMenu (hMenu, MF_POPUP, (UINT_PTR) hMenuPopup, TEXT("File")); 

	hMenuPopup = CreatePopupMenu();
        AppendMenu (hMenuPopup, MF_STRING,MYMENU_MESSAGEBOX, TEXT("MessageBox")); 
        AppendMenu (hMenu, MF_POPUP, (UINT_PTR) hMenuPopup, TEXT("Test")); 
	return hMenu;
}

// Our new window's proc
LRESULT CALLBACK DLLWindowProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	if (message == MY_MSGLOOP) {
	    switch (message) {
	        case WM_COMMAND:
	            switch(wParam) {
		        case MYMENU_EXIT:
			    SendMessage(hwnd, WM_CLOSE, 0, 0);
                            break;
                        case MYMENU_MESSAGEBOX:
			    MessageBox(hwnd, L"Test", L"MessageBox",MB_OK);
                            break;
                    }
                    break;
		case WM_DESTROY:
		    PostQuitMessage (0);
		    break;
	        default:
		    return DefWindowProc (hwnd, message, wParam, lParam);
	    }
	    return 0;
	} 

	/* Send the rest to mIRC */
	return CallWindowProc(mWndProc, hwnd, message, wParam, lParam);
}

BOOL APIENTRY DllMain(HINSTANCE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
	dllInstance = hModule;
        return TRUE;
}

int __stdcall dllname(HWND mWnd, HWND aWnd, char *data, char *parms, BOOL show, BOOL nopause) {
	wchar_t szFileName[MAX_PATH];
	GetModuleFileName((HMODULE)dllInstance, szFileName, MAX_PATH);
	MessageBox(aWnd, szFileName, L"This DLL is:", MB_OK | MB_ICONINFORMATION);
	lstrcpyA(data, "yay");
	return 3;
}

int __stdcall window(HWND mWnd, HWND aWnd, char *data, char *parms, BOOL show, BOOL nopause) {
	HMENU hMenu = CreateDLLWindowMenu();
	WNDCLASSEX wc;
        wc.hInstance = dllInstance;
	wc.lpszClassName = (LPCWSTR)L"DLLWindowClass";
        wc.lpfnWndProc = DLLWindowProc;
        wc.style = CS_DBLCLKS;
        wc.cbSize = sizeof (WNDCLASSEX);
        wc.hIcon = LoadIcon (NULL, IDI_APPLICATION);
        wc.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
        wc.hCursor = LoadCursor (NULL, IDC_ARROW);
        wc.lpszMenuName = NULL;
        wc.cbClsExtra = 0;
        wc.cbWndExtra = 0;
        wc.hbrBackground = (HBRUSH) COLOR_BACKGROUND;
	if (!RegisterClassEx (&wc)) {
		MessageBox(mIRC_window, L"Failed to register window class!", L"Info", MB_OK | MB_ICONINFORMATION);
		//return 1;
	}
	HWND hwnd = CreateWindowEx (0, L"DLLWindowClass", L"Window Title", WS_EX_PALETTEWINDOW, CW_USEDEFAULT,
		CW_USEDEFAULT, 400, 300, mIRC_window, hMenu, dllInstance, NULL );
	if (!hwnd) {
		MessageBox(mIRC_window, L"Failed to create window!", L"Info", MB_OK | MB_ICONINFORMATION);
		//return 1;
	}
	UpdateWindow(hwnd); // possibly unnecessary
	ShowWindow (hwnd, SW_SHOWNORMAL);
	/* the while loop below would obviously block, but this function has to return
	   to let mirc know what we want to do, so how do I dispatch messages to my
	   message loop without creating a different thread? (code commented for now)
	while (1) {
		PostMessage(mIRC_window, MY_MSGLOOP, 0, 0);
		Sleep(1);
	} */
	lstrcpyA(data, "meh");
	return 3;
}

Any help is greatly appreciated. Also, 7ramy, I couldn't compile your code. What language is this written in? The Func(start) at the bottom doesn't look like C (or C++).

Last edited by deadbeef; 14/01/11 12:13 AM.
Joined: Mar 2008
Posts: 27
7
Ameglian cow
Offline
Ameglian cow
7
Joined: Mar 2008
Posts: 27

Code:
#define Func(x) EXTERN_C int WINAPI x(HWND mWnd, HWND aWnd, TCHAR *data, TCHAR *parms, BOOL show, BOOL nopause)


that's what Func() meant

Code:
#define WIN32_LEAN_AND_MEAN

#include <afx.h>
#include <afxwin.h>
#include <windows.h>

typedef struct {
	DWORD  mVersion;
	HWND   mHwnd;
	BOOL   mKeep;
	BOOL   mUnicode;
} LOADINFO;

HWND mIRC_window;

#define ClassName "DLLWindowClass"
#define MYMENU_EXIT         (WM_APP + 101)
#define MYMENU_MESSAGEBOX   (WM_APP + 102) 

LRESULT WINAPI DLLWindowProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
    switch (message)
    {
		case WM_COMMAND:
               switch(wParam)
               {
                    case MYMENU_EXIT:
						SendMessage(hwnd, WM_CLOSE, 0, 0);
                        break;
                    case MYMENU_MESSAGEBOX:
						MessageBox(hwnd, _T("Test"), _T("MessageBox"), MB_OK);
                        break;
               }
               break;
		case WM_DESTROY:
			PostQuitMessage (0);
			break;
		default:
			return DefWindowProc (hwnd, message, wParam, lParam);
    }
    return 0;
}

// Creating our window's Menu
HMENU CreateDLLWindowMenu() {
	HMENU hMenu;
	hMenu = CreateMenu();
	HMENU hMenuPopup;
    if(hMenu==NULL)
        return FALSE;
    hMenuPopup = CreatePopupMenu();
	AppendMenu (hMenuPopup, MF_STRING, MYMENU_EXIT, _T("Exit"));
    AppendMenu (hMenu, MF_POPUP, (UINT_PTR) hMenuPopup, _T("File")); 

	hMenuPopup = CreatePopupMenu();
    AppendMenu (hMenuPopup, MF_STRING, MYMENU_MESSAGEBOX, _T("MessageBox")); 
    AppendMenu (hMenu, MF_POPUP, (UINT_PTR) hMenuPopup, _T("Test")); 
	return hMenu;
}

class Win : public CWinThread {
public:
	Win() {};
	~Win() {};

	virtual int Run() {
		MSG messages;
		while (GetMessage (&messages, NULL, 0, 0))
		{
			TranslateMessage(&messages);
			DispatchMessage(&messages);
		}
		return 0;
	};
	virtual BOOL InitInstance() {
		WNDCLASSEX wc;
		wc.hInstance = NULL;
		wc.lpszClassName = ClassName;
		wc.lpfnWndProc = DLLWindowProc;
		wc.style = CS_DBLCLKS;
		wc.cbSize = sizeof (WNDCLASSEX);
		wc.hIcon = LoadIcon (NULL, IDI_APPLICATION);
		wc.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
		wc.hCursor = LoadCursor (NULL, IDC_ARROW);
		wc.lpszMenuName = NULL;
		wc.cbClsExtra = 0;
		wc.cbWndExtra = 0;
		wc.hbrBackground = (HBRUSH) COLOR_BACKGROUND;
		RegisterClassEx (&wc);

		HMENU hMenu = CreateDLLWindowMenu();
		HWND hwnd = CreateWindowEx (0, ClassName, NULL, WS_EX_PALETTEWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 400, 300, mIRC_window, hMenu, NULL, NULL );

		if (hwnd) {
			UpdateWindow(hwnd);
			ShowWindow (hwnd, SW_SHOWNORMAL);
			return true;
		}
		return false;
	};
};

Win* x;

EXTERN_C int WINAPI UnloadDll(int TimeOut) {
	//x->ExitInstance();
	//delete x;
	return 0;
}

EXTERN_C void WINAPI LoadDll(LOADINFO *Load) {
	Load->mKeep = 1; // stay loaded
	mIRC_window = Load->mHwnd;
	x = new Win;
	x->InitInstance();
}

EXTERN_C int WINAPI window(HWND mWnd, HWND aWnd, TCHAR *data, TCHAR *parms, BOOL show, BOOL nopause) {
	return 1;
}


and this compiled flawlessly
tested with mirc both window menu functions worked

Joined: Jan 2011
Posts: 11
D
Pikka bird
OP Offline
Pikka bird
D
Joined: Jan 2011
Posts: 11
That's strange. It compiles fine here (after having to add #include "stdafx.h" and changing some TCHARs to char*), but when I call window() from mIRC (/echo -a $dll(windowtest.dll,window,1)) it doesn't return the stuff I copied into the data buffer (I had added lstrcpyA(data, "test"); return 3;). Besides, no window shows up.

I'm starting to think it might be the project type/settings. Normally I create a regular, empty Win32 dll, but I couldn't compile the code with that so I created an MFC DLL and it worked fine. What kind of project do you use? (shared, statically linked, extension dll, etc)

Joined: Mar 2008
Posts: 27
7
Ameglian cow
Offline
Ameglian cow
7
Joined: Mar 2008
Posts: 27
i compiled dynamically linked to mfc
and using multi character bytes

u dont have to remove TCHAR because it works with both unicode and multibyte charactersets

u should create an empty win32 dll

copy the code i gave u into the header
Window.h

create an empty Window.cpp
#include "Window.h"

then add a definition file
Window.def

and exports load, unload and window

Joined: Jan 2011
Posts: 11
D
Pikka bird
OP Offline
Pikka bird
D
Joined: Jan 2011
Posts: 11
I did as you said. Everything compiles fine, but the mIRC $dll() call fails. Hrmph...

I've been testing this with bloodshed dev-cpp as well. Nothing seems to work (mIRC crashes).

What version of Visual Studio are you using? I know this has nothing to do with it, but I'm out of ideas as to why I can't get this to work.

Last edited by deadbeef; 22/01/11 08:21 AM.
Joined: Mar 2008
Posts: 27
7
Ameglian cow
Offline
Ameglian cow
7
Joined: Mar 2008
Posts: 27
vs2010 on win7 x64

Joined: Jan 2011
Posts: 11
D
Pikka bird
OP Offline
Pikka bird
D
Joined: Jan 2011
Posts: 11
Would anyone please comment on the message loop problem I mentioned in my commented out code (in the window() function)? I'm still not sure how I'm supposed to create a message loop AND return something to mIRC.

Joined: Oct 2003
Posts: 3,918
A
Hoopy frood
Offline
Hoopy frood
A
Joined: Oct 2003
Posts: 3,918
You shouldn't be running the message loop at all in window(), this is already done by mIRC. By hooking mIRC's windowproc, you only need to process that window procedure and return. Create the window, hook the windowprocs, and return. If you *do* need to run your own message loops, you should be initializing them in LoadDll (or via the global window hook as a message), not from within window(), otherwise you will create an infinite loop in your dll call. Note that this is the whole point of a "message loop". It is an infinite loop to keep the "program" from returning (and by extension, to keep it processing messages). A message loop must inherently run until the "program" (dll in this case) finishes doing everything it needs to do. This means that everything you do needs to work within your message loop, not the other way around. DLL calls should simply post messages to your existing message loop (mIRC's message loop if you want) which is processed elsewhere.

In short, you don't need a message loop. Since you set your DLLWindowProc function as the WindowProc callback for your newly created window, all window messages are already being passed to that function automatically. It's being done through mIRC's main message loop-- you don't need to implement your own.

The only problem is that the messages won't come in with some MY_MSGLOOP flag. In fact, that wouldn't have worked anyway, so you should ditch that code. Instead, you should simply check the HWND parameter in your WindowProc callback function-- if it's one of your hwnd's, process, else call mIRC's callback:

Code:
if (hwnd == MY_HWND) {
  switch(message) { ... }
}
else {
  return CallWindowProc(mWndProc, ...);
}


If you want to support multiple windows you'll need to loop over a list instead of a single == MY_HWND check, obviously. Either that or, if multiple windows is a serious goal, you could consider actually making your own custom message loops for each window, but that's way more complicated than you need to deal with, and it would still require you to loop over each window, so it's more work, if anything.

On another note, I'm a little confused as to what you're trying to return to mIRC? Why do you even need to return something? It just looks like you're initializing a window. you could just return 1;


- argv[0] on EFnet #mIRC
- "Life is a pointer to an integer without a cast"
Joined: Jan 2011
Posts: 11
D
Pikka bird
OP Offline
Pikka bird
D
Joined: Jan 2011
Posts: 11
I've followed your suggestions but CreateWindowEx() still fails. GetLastError() returns 0, which isn't very helpful. As to your question, I was referring to the integer that EVERY function has to return (as per the mIRC help file) so that mIRC knows whether we want to halt processing, return the data to a $dll call, continue processing, etc. My code is below (changed slightly as I don't use unicode now):

dll.h:

Code:
#ifndef _DLL_H_
#define _DLL_H_

#include <windows.h>

#define DLLIMPORT __declspec (dllexport) int __stdcall

#define MYMENU_EXIT         (WM_APP + 101)
#define MYMENU_MESSAGEBOX   (WM_APP + 102) 

void __stdcall LoadDll(LOADINFO*);
int __stdcall UnloadDll(int);
DLLIMPORT HelloWorld (HWND mWnd, HWND aWnd, char *data, char *parms, BOOL show, BOOL nopause);
DLLIMPORT dllname (HWND mWnd, HWND aWnd, char *data, char *parms, BOOL show, BOOL nopause);
DLLIMPORT window(HWND mWnd, HWND aWnd, char *data, char *parms, BOOL show, BOOL nopause);

#endif /* _DLL_H_ */


And dllmain.c:

Code:
#include "dll.h"
#include <stdio.h>
#include <stdlib.h>

// WndProc for the new window
LRESULT CALLBACK DLLWindowProc (HWND, UINT, WPARAM, LPARAM);

static HINSTANCE dllInstance;
static HWND mIRC_window;
static WNDPROC mWndProc;
static HWND myHwnd;

typedef struct {
	DWORD  mVersion;
	HWND   mHwnd;
	BOOL   mKeep;
	BOOL   mUnicode;
} LOADINFO;

int __stdcall UnloadDll(int mTimeout) {
	if (mTimeout == 0) { /* user called /dll -u (or mIRC is shutting down) */
		/* Remove window hook */
		SetWindowLong(mIRC_window, GWL_WNDPROC, (LONG)mWndProc);
    }
	return 0; // keep the dll loaded
}

void __stdcall LoadDLL(LOADINFO *load) {
	load->mKeep = TRUE;
	// setup window subclass to hook the dll window event loop
	mIRC_window = load->mHwnd;
	mWndProc = (WNDPROC)SetWindowLong(mIRC_window, GWL_WNDPROC, (LONG)DLLWindowProc); 
}

// /dll mydll.dll HelloWorld works as expected
DLLIMPORT HelloWorld (HWND mWnd, HWND aWnd, char *data, char *parms, BOOL show, BOOL nopause)
{
    MessageBox (0, "Hello World from DLL!\n", "Hi", MB_ICONINFORMATION);
    return 1;
}

// this one works too
DLLIMPORT dllname (HWND mWnd, HWND aWnd, char *data, char *parms, BOOL show, BOOL nopause)
{
    char szFileName[MAX_PATH];
	GetModuleFileName((HMODULE)dllInstance, szFileName, MAX_PATH);
	MessageBox(aWnd, szFileName, "This DLL is:", MB_OK | MB_ICONINFORMATION);
	lstrcpyA(data, "yay");
	return 3;
}

// Create our window's Menu
HMENU CreateDLLWindowMenu()
{
	HMENU hMenu;
	hMenu = CreateMenu();
	HMENU hMenuPopup;
        if(hMenu==NULL)
            return FALSE;
        hMenuPopup = CreatePopupMenu();
	AppendMenu (hMenuPopup, MF_STRING, MYMENU_EXIT, TEXT("Exit"));
        AppendMenu (hMenu, MF_POPUP, (UINT_PTR) hMenuPopup, TEXT("File")); 

	hMenuPopup = CreatePopupMenu();
        AppendMenu (hMenuPopup, MF_STRING,MYMENU_MESSAGEBOX, TEXT("MessageBox")); 
        AppendMenu (hMenu, MF_POPUP, (UINT_PTR) hMenuPopup, TEXT("Test")); 
	return hMenu;
}

// this one fails at CreateWindowEx()
DLLIMPORT window(HWND mWnd, HWND aWnd, char *data, char *parms, BOOL show, BOOL nopause) {
	HMENU hMenu = CreateDLLWindowMenu();
	WNDCLASSEX wc;
        wc.hInstance = dllInstance;
	wc.lpszClassName = "DLLWindowClass";
        wc.lpfnWndProc = DLLWindowProc;
        wc.style = CS_DBLCLKS;
        wc.cbSize = sizeof (WNDCLASSEX);
        wc.hIcon = LoadIcon (NULL, IDI_APPLICATION);
        wc.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
        wc.hCursor = LoadCursor (NULL, IDC_ARROW);
        wc.lpszMenuName = NULL;
        wc.cbClsExtra = 0;
        wc.cbWndExtra = 0;
        wc.hbrBackground = (HBRUSH) COLOR_BACKGROUND;
	if (!RegisterClassEx (&wc)) {
		MessageBox(mIRC_window, "Failed to register window class!", "Info", MB_OK | MB_ICONINFORMATION);
		//return 1;
	}

	myHwnd = CreateWindowEx (0, "DLLWindowClass", "Window Title", WS_EX_PALETTEWINDOW, CW_USEDEFAULT,
		CW_USEDEFAULT, 400, 300, mIRC_window, hMenu, dllInstance, NULL );
	if (myHwnd == NULL) {
		DWORD Error = GetLastError();
		char ErrorBuffer[ 1024 ];
		wsprintf( ErrorBuffer, "Error creating window. Error code, decimal %d, hexadecimal %X.", Error, Error );
		MessageBox(mIRC_window, ErrorBuffer, "Error", MB_ICONHAND );
		MessageBox(mIRC_window, "Failed to create window!", "Info", MB_OK | MB_ICONINFORMATION);
		//return 1;
	}
	UpdateWindow(myHwnd); // possibly unnecessary
	ShowWindow (myHwnd, SW_SHOWNORMAL);
	lstrcpyA(data, "meh");
	return 3;
}

LRESULT CALLBACK DLLWindowProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	if (hwnd == myHwnd) {
	    switch (message) {
	        case WM_COMMAND:
	            switch(wParam) {
		                       case MYMENU_EXIT:
                         		    SendMessage(hwnd, WM_CLOSE, 0, 0);
                                    break;
                               case MYMENU_MESSAGEBOX:
		                       	    MessageBox(hwnd, "Test", "MessageBox",MB_OK);
                                    break;
                               }
                break;
		    case WM_DESTROY:
		         PostQuitMessage (0);
		         break;
	        default:
		            return DefWindowProc (hwnd, message, wParam, lParam);
	    }
	    // return 0;
	} 

	/* Send the rest to mIRC */
	else { return CallWindowProc(mWndProc, hwnd, message, wParam, lParam); }
}

BOOL APIENTRY DllMain (HINSTANCE hInst     /* Library instance handle. */ ,
                       DWORD reason        /* Reason this function is being called. */ ,
                       LPVOID reserved     /* Not used. */ )
{
    dllInstance = hInst;
    switch (reason)
    {
      case DLL_PROCESS_ATTACH:
        break;

      case DLL_PROCESS_DETACH:
        break;

      case DLL_THREAD_ATTACH:
        break;

      case DLL_THREAD_DETACH:
        break;
    }

    /* Returns TRUE on success, FALSE on failure */
    return TRUE;
}

Joined: Oct 2003
Posts: 3,918
A
Hoopy frood
Offline
Hoopy frood
A
Joined: Oct 2003
Posts: 3,918
Yes, every call needs to "return" control back to mIRC, but that doesn't mean you need to fill `data`. See /help dll support for the other return values you can use.

The fact that CreateWindowEx "fails" means that your problem has little to do with your message loop, and you're simply not creating the window properly. There's a lot of possible failure points, so I can't tell you what you're doing wrong, but I would suggest doing a clean build and being vigilant about any compiler WARNINGS you may get. Specifically, if you're using a unicode build (which afaik is default in vs2010), the compiler will expect wide strings, not regular ones.. in other words, you're passing in "Window Title" when you should actually be passing in L"Window Title" (with the L prefix). Same goes for wsprintf and MessageBox. I think this was alluded to above in this thread. Again, this depends on your build settings, but the compiler should warn you.

Also note that you should not have a DllMain entry point.


- argv[0] on EFnet #mIRC
- "Life is a pointer to an integer without a cast"
Joined: Jan 2011
Posts: 11
D
Pikka bird
OP Offline
Pikka bird
D
Joined: Jan 2011
Posts: 11
Yes argv0, that's what I meant by returning a value to mIRC (1, 3, etc -- I didn't mean to say that 'data' has to be filled in every function).

Also, like I mentioned in my previous post, the code has slightly changed because I'm not using unicode anymore, so that's why I removed the L's from my strings. My code produces no warnings.

As for DllMain(), it's what I use to get a handle to the DLL instance, but I removed it to see if it was the problem, and it changes nothing. Window creation still fails. Once again, GetLastError() gives me nothing but a useless "0" which doesn't help me identify the problem at all.

Anyway, I really appreciate all the help you've been giving me here. If you or anyone else know why this may be happening, please post here. I'll try and mess around with it some more and see if I can get it to work.

Joined: Oct 2003
Posts: 3,918
A
Hoopy frood
Offline
Hoopy frood
A
Joined: Oct 2003
Posts: 3,918
You should probably be setting the instance to mIRC's hInstance. There should be a way to get that (I forget exactly how).

Anyhow, I would suggest making a non-mIRC-dll project in VS2010, just a plain old executable, and see if you can create a window there (this time you *will* need a message loop to keep the program active). If it's failing there, it's not an mIRC issue, you're simply using the CreateWindowEx function incorrectly. I can't really narrow down what you're doing wrong, since I'm not a winapi expert.


- argv[0] on EFnet #mIRC
- "Life is a pointer to an integer without a cast"
Joined: Apr 2004
Posts: 871
Sat Offline
Hoopy frood
Offline
Hoopy frood
Joined: Apr 2004
Posts: 871
you're passing an extended window style into the base style field of CreateWindowEx


Saturn, QuakeNet staff
Joined: Jan 2011
Posts: 11
D
Pikka bird
OP Offline
Pikka bird
D
Joined: Jan 2011
Posts: 11
Thanks Sat, I don't know what combination of flags WS_EX_PALETEWINDOW corresponded to, but that wasn't the problem.

An interesting thing is if I remove the "if (hwnd == myHwnd)" then the window will be created and will cause mIRC to crash as usual. (myHwnd for some reason beyond me is zero inside DLLWindowProc even though the CreateWindowEx() call succeeds!).

Also, if I remove or say replace DefWindowProc() with something like "break;" then window creation will fail as well, so it seems my problem is in my WndProc.

But anyway, this is driving me nuts, so let me see if I can ask for something else. Can anyone here provide a working window creation example in C (not C++) for an mIRC DLL?

Last edited by deadbeef; 05/08/11 11:47 PM.
Joined: Oct 2003
Posts: 3,918
A
Hoopy frood
Offline
Hoopy frood
A
Joined: Oct 2003
Posts: 3,918
Window creation is window creation. You should be able to copy paste the thousands of working CreateWindow samples on the internet into a dll and run it.

Note that you don't *need* to use a windowproc on a custom window. There's a default which just noops all the messages (DefWindowProc). If you want a really simple create window sample, http://www.winprog.org/tutorial/simple_window.html should work. Just replace WinMain with your dll entry. Remove the message loop, of course, and remove the WndProc function and just reference "DefWindowProc" as the function in your window class instead. If *that* doesn't work, nothing will.

It's best to start simple with these things. Adding your own logic before the you have the basics down is just going to confuse you. I'd suggest getting the above sample code working, and THEN add your own logic.


- argv[0] on EFnet #mIRC
- "Life is a pointer to an integer without a cast"
Page 1 of 2 1 2

Link Copied to Clipboard