|
Joined: Jan 2011
Posts: 11
Pikka bird
|
OP
Pikka bird
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).
#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
Hoopy frood
|
Hoopy frood
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
Pikka bird
|
OP
Pikka bird
Joined: Jan 2011
Posts: 11 |
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). 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. 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
Hoopy frood
|
Hoopy frood
Joined: Oct 2003
Posts: 3,918 |
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
Ameglian cow
|
Ameglian cow
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
#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;
};
};
#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
Pikka bird
|
OP
Pikka bird
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).
#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
Ameglian cow
|
Ameglian cow
Joined: Mar 2008
Posts: 27 |
#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
#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
Pikka bird
|
OP
Pikka bird
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
Ameglian cow
|
Ameglian cow
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
Pikka bird
|
OP
Pikka bird
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
Ameglian cow
|
Ameglian cow
Joined: Mar 2008
Posts: 27 |
|
|
|
|
Joined: Jan 2011
Posts: 11
Pikka bird
|
OP
Pikka bird
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
Hoopy frood
|
Hoopy frood
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:
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
Pikka bird
|
OP
Pikka bird
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:
#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:
#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
Hoopy frood
|
Hoopy frood
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
Pikka bird
|
OP
Pikka bird
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
Hoopy frood
|
Hoopy frood
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
Hoopy frood
|
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
Pikka bird
|
OP
Pikka bird
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
Hoopy frood
|
Hoopy frood
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"
|
|
|
|
Joined: Oct 2003
Posts: 3,918
Hoopy frood
|
Hoopy frood
Joined: Oct 2003
Posts: 3,918 |
I copied the example listed above pretty much verbatim into a new VS2010 project and then fixed any missing references / compiler errors (using Unicode): #include <windows.h>
int createWindow(HWND mWnd, HWND aWnd, char *data, char *parms, BOOL show, BOOL nopause)
{
WNDCLASSEX wc;
HWND hwnd;
TCHAR className[] = L"MyWindow";
HINSTANCE hInstance = GetModuleHandle(0);
//Step 1: Registering the Window Class
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = DefWindowProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.lpszMenuName = NULL;
wc.lpszClassName = className;
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if(!RegisterClassEx(&wc))
{
MessageBox(NULL, L"Window Registration Failed!", L"Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// Step 2: Creating the Window
hwnd = CreateWindowEx(
WS_EX_CLIENTEDGE,
className,
L"The title of my window",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 240, 120,
NULL, NULL, hInstance, NULL);
if(hwnd == NULL)
{
MessageBox(NULL, L"Window Creation Failed!", L"Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
ShowWindow(hwnd, 1);
UpdateWindow(hwnd);
return 1;
} The above works perfectly for me. Note that you can only run the command once. The second time will fail because the RegisterClassEx function will fail, since you've already registered that window class. Ideally the registration of window classes should be done in the dll initialization, not for each /dll call, but this is just a proof of concept.
- argv[0] on EFnet #mIRC - "Life is a pointer to an integer without a cast"
|
|
|
|
Joined: Jan 2011
Posts: 11
Pikka bird
|
OP
Pikka bird
Joined: Jan 2011
Posts: 11 |
More mIRC crashes for me. I'm giving up on this. Sorry for the noise.
|
|
|
|
Joined: Oct 2003
Posts: 3,918
Hoopy frood
|
Hoopy frood
Joined: Oct 2003
Posts: 3,918 |
What version of mIRC are you running? What version of windows?
- argv[0] on EFnet #mIRC - "Life is a pointer to an integer without a cast"
|
|
|
|
Joined: Jan 2011
Posts: 11
Pikka bird
|
OP
Pikka bird
Joined: Jan 2011
Posts: 11 |
Windows 7 (64-bit) and the latest version of mIRC (7.19). I have to say that since I started working on this (from before I created this thread here -- literally a year ago) I used several versions of Windows, mIRC, and different compilers, all to no avail. Tried this with Windows XP, Windows Vista, and Windows 7 (32-bit and 64-bit). As far as compilers are concerned I used Visual Studio, Bloodshed Dev-C++ (which relies on MinGW I believe, and which is what I'm currently using), and even Cygwin. I've tried literally tens of examples/tutorials and setting variations (with unicode, without unicode, different frameworks and build options for each compiler (e.g. standard Win32 DLL, MFC DLL, etc), etc). I never once got it to work. Would you mind uploading your DLL somewhere so I can test it with my mIRC? If it causes the same problem then it must be my environment (although, like I said before, I've tried this on several different setups, i.e. different software settings but also different physical computers, e.g. my laptop, my desktop, from within a virtual machine, etc).
|
|
|
|
Joined: Oct 2003
Posts: 3,918
Hoopy frood
|
Hoopy frood
Joined: Oct 2003
Posts: 3,918 |
Sure. Source and dll here. The dll is in the Debug\ directory.
- argv[0] on EFnet #mIRC - "Life is a pointer to an integer without a cast"
|
|
|
|
Joined: Nov 2011
Posts: 15
Pikka bird
|
Pikka bird
Joined: Nov 2011
Posts: 15 |
deadbeef, did you solve your problem? I need to show a MessageBox from a DLL but it doesn't work well for me.
Can you post your source code?
|
|
|
|
Joined: Oct 2003
Posts: 3,918
Hoopy frood
|
Hoopy frood
Joined: Oct 2003
Posts: 3,918 |
The source was already posted. But showing a MessageBox is considerably easier than opening a window, it's nothing more than MessageBox(NULL, "Hello world", "Title", MB_OK);
I've added many a messagebox into mIRC dlls, and I know they absolutely work fine.
- argv[0] on EFnet #mIRC - "Life is a pointer to an integer without a cast"
|
|
|
|
Joined: Nov 2011
Posts: 15
Pikka bird
|
Pikka bird
Joined: Nov 2011
Posts: 15 |
Thanks for your reply argv0. Reading this post make me thought that there are more people with my same problem, but the DLL I was tring to create was for XChat (another IRC client). I successfully compiled the DLL without errors, but in runtime, the MessageBox didn't work well, here it is what happened with it: http://forum.xchat.org/viewtopic.php?t=5661Does somebody know how to solve this?
Last edited by jomalin; 29/11/11 10:54 PM.
|
|
|
|
Joined: Oct 2003
Posts: 3,918
Hoopy frood
|
Hoopy frood
Joined: Oct 2003
Posts: 3,918 |
You do realize that DLLs for mIRC are probably incompatible with XChat, right?
- argv[0] on EFnet #mIRC - "Life is a pointer to an integer without a cast"
|
|
|
|
Joined: Nov 2011
Posts: 15
Pikka bird
|
Pikka bird
Joined: Nov 2011
Posts: 15 |
I know...
I'm only asking if somebody know how to do that, independently if it is for mIRC or XChat. I believed showing a MessagaBox from a DLL also in mIRC gave the same problem...
So, does somebody know why this happen in XChat?
|
|
|
|
Joined: Oct 2003
Posts: 3,918
Hoopy frood
|
Hoopy frood
Joined: Oct 2003
Posts: 3,918 |
Again, this is a forum for mIRC. If you have questions about XChat you should ask users of XChat. I showed you how to display a messagebox. The method works in mIRC. If it doesn't work in XChat it's something you have to deal with in XChat's support forums. Frankly, we don't really care why it wouldn't work in that client. As a sidenote, looking at the very forum topic you posted on XChat, it seems they already explained to you why it doesn't work (you need to use threads). Perhaps you should ask them to explain this to you, since it seems you either didn't read the answer, or didn't understand it.
- argv[0] on EFnet #mIRC - "Life is a pointer to an integer without a cast"
|
|
|
|
|