|
Joined: Dec 2002
Posts: 580
Fjord artisan
|
OP
Fjord artisan
Joined: Dec 2002
Posts: 580 |
There seems to be a small bug will loaded DLL files...
I have a DLL that pre-processes windows messages and sends certain ones to mirc as signals... Two bugs to report with this.
In C++ Using "SetWindowLong(hwnd,GWL_WNDPROC,(LONG)newProc);"
The first, and I've said before in the past, but I feel is worth adding here again... When doing this on the mirc hwnd it will crash on exit if not released ("unhooked") prior to quit being called (confim quit may also be a requirement for the crash). Restoring the old proc from the new proc or on DLL unload (either explicitly in :exit: or mirc's unload on exit) doesn't make a difference.
Second when closing a query window the hwnd is "un-hooked"... If this is done when mirc is minimized to the tray and the query window being closed was opened on the desktop, mirc crashes.
Naquada
|
|
|
|
Joined: Jan 2003
Posts: 249
Fjord artisan
|
Fjord artisan
Joined: Jan 2003
Posts: 249 |
That's not a bug, that's because you are the one not following subclassing API rules in your code. mIRC has no clue that someone will subclass it's main window as that is part of the Win32 API. It is your responsability to assure that the mIRC window will be restored it's original Window Procedure when you have finished with your subclassing. Also, you should pass to mIRC's original Window Procedure any message that is not parsed by you (if you don't return another value to modify the message chain). A good way to unsubclass is to do it, when the DLL is successfully unloaded or when mIRC is closed via the WM_DESTROY message. Hope these code snippets will help you understand.
case WM_DESTROY:
{
LRESULT lRes = CallWindowProc( g_OldmIRCWindowProc, mHwnd, uMsg, wParam, lParam );
SetWindowLong( mIRCLink.m_mIRCHWND, GWL_WNDPROC, (LONG) g_OldmIRCWindowProc );
return lRes;
}
break;
int WINAPI UnloadDll( int timeout ) {
// DLL unloaded because mIRC exits or /dll -u used
if ( timeout == 0 ) {
SetWindowLong( mIRCLink.m_mIRCHWND, GWL_WNDPROC, (LONG) g_OldmIRCWindowProc );
UnmapViewOfFile( mIRCLink.m_pData );
CloseHandle( mIRCLink.m_hFileMap );
return 1;
}
// keep DLL in memory
else
return 0;
}
|
|
|
|
Joined: Apr 2004
Posts: 871
Hoopy frood
|
Hoopy frood
Joined: Apr 2004
Posts: 871 |
int WINAPI UnloadDll( int timeout ) { (...) UnmapViewOfFile( mIRCLink.m_pData ); CloseHandle( mIRCLink.m_hFileMap );
On the other hand, you're not following the rules for file mapping... link
Saturn, QuakeNet staff
|
|
|
|
Joined: Jan 2003
Posts: 249
Fjord artisan
|
Fjord artisan
Joined: Jan 2003
Posts: 249 |
You are correct, but I never looked at anything about those commands assuming that this behavior was the correct one and used everywhere.
Never found a good explanation on how that would change anything.
And moving the code to open the mapped file just before and close it after I access mIRC via SendMessage for passing or evaluating commands is an easy change if that is the "standard" way of using it.
|
|
|
|
Joined: Apr 2004
Posts: 871
Hoopy frood
|
Hoopy frood
Joined: Apr 2004
Posts: 871 |
Never found a good explanation on how that would change anything. Basically, having two processes or threads that do it your way can lead to race conditions resulting in memory corruption and (at worst) odd crashes, because the two processes can be reading from and/or writing to the file mapping at the same time: access to shared memory is not synchronized. A simple scenario (and keep in mind here that the WM_MCOMMAND/WM_MEVALUATE functionality is also usable from outside mIRC as well as DLL-created threads inside mIRC): process A: strcpy(hMapPtr, "$my_identifier"); process A's timeslice ends after this callprocess B is activated, and first does some other stuffprocess B: strcpy(hMapPtr2, "/some_command"); process B's timeslice ends while copying, so that it only copied "/some_"process A is resumedprocess A: SendMessage(mWnd, WM_MEVALUATE, ..); As hMapPtr and hMapPtr2 ultimately point to the same shared memory, mIRC now sees "/some_entifier" on the file mapping object - and this is a relatively harmless example. If at most one process has the file mapping open at the same time, such race conditions can not occur. That's why processes have to check whether the file mapping already existed when creating it, and simply closing it & retrying if it did exist. That scheme obviously fails if one process keeps the file mapping open all the time.
Last edited by Sat; 07/11/05 11:19 AM.
Saturn, QuakeNet staff
|
|
|
|
Joined: Jan 2003
Posts: 249
Fjord artisan
|
Fjord artisan
Joined: Jan 2003
Posts: 249 |
Then by what you say basically all DLLs are worthless and shouldn't be used as if I implement to "good" way then my DLL won't work because people use the other way in 99% of the time and when my DLl will want to access the shared map, it will fail.
|
|
|
|
Joined: Apr 2004
Posts: 871
Hoopy frood
|
Hoopy frood
Joined: Apr 2004
Posts: 871 |
Exactly. If as much as one coder screws up here, the others (who did conform to the specification) end up with problems - hence the feature suggestion I provided a link to above. But if at least the authors of the most widely used DLLs (such as yourself) get it right, chances are the rest will follow suit in order to remain compatible...
Saturn, QuakeNet staff
|
|
|
|
Joined: Jan 2003
Posts: 249
Fjord artisan
|
Fjord artisan
Joined: Jan 2003
Posts: 249 |
I could provide the correct way, but that would mean all my DLLs will be incompatible with any other DLLs until they are all updated in which case we know that this won't happen.
Anyways, I'll see what I can do to fix the problem in my DLLs and at least have them working together.
|
|
|
|
Joined: Dec 2002
Posts: 580
Fjord artisan
|
OP
Fjord artisan
Joined: Dec 2002
Posts: 580 |
I'm not using more than one DLL... Here the code as striped down as possible that still shows the crash on exit error, please tell me what I'm doing wrong...
//Linker Options
#pragma check_stack(off)
#pragma comment(linker,"/OPT:NOWIN98")
//Include Files
#include <stdarg.h>
#define WIN32_LEAN_AND_MEAN
#define WINVER 0x0400
#define _X86_
#include <stdlib.h>
#include <stdio.h>
#include <windows.h>
//Structures
typedef struct {
DWORD mVersion;
HWND mHwnd;
BOOL mKeep;
} LOADINFO;
//Global Variables
HINSTANCE hInstance;
HANDLE hMap;
LPSTR mData;
HWND mIRC_hwnd;
WNDPROC old_proc;
//Function Declarations
LRESULT CALLBACK mIRCProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam);
//Functions
extern "C" void WINAPI LoadDll(LOADINFO *load)
{
int mapNumber;
char *lpMapFile = NULL;
srand(5000);
mapNumber = rand();
sprintf(lpMapFile, "mIRC%d", mapNumber);
hMap = CreateFileMapping(INVALID_HANDLE_VALUE,0,PAGE_READWRITE,0,4096,lpMapFile);
mData = (LPSTR)MapViewOfFile(hMap,FILE_MAP_ALL_ACCESS,0,0,0);
mIRC_hwnd = load->mHwnd;
load->mKeep = TRUE;
old_proc = (WNDPROC)SetWindowLong(mIRC_hwnd,GWL_WNDPROC,(LONG)mIRCProc);
}
extern "C" int WINAPI UnloadDll(int timeout)
{
if (!timeout) {
SetWindowLong(mIRC_hwnd,GWL_WNDPROC,(LONG)old_proc);
UnmapViewOfFile(mData);
CloseHandle(hMap);
return 1;
}
return 0;
}
LRESULT CALLBACK mIRCProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
LRESULT lRes;
switch (uMsg) {
case (WM_DESTROY):
lRes = CallWindowProc(old_proc,hwnd,uMsg,wParam,lParam);
SetWindowLong(mIRC_hwnd, GWL_WNDPROC, (LONG)old_proc);
return lRes;
break;
}
return CallWindowProc(old_proc,hwnd,uMsg,wParam,lParam);
}
LIBRARY QServe
EXPORTS
LoadDll = _LoadDll@4
UnloadDll = _UnloadDll@4
|
|
|
|
Joined: Dec 2002
Posts: 580
Fjord artisan
|
OP
Fjord artisan
Joined: Dec 2002
Posts: 580 |
I found out the "proper way" to do it from another persons source.
case (WM_CLOSE):
PostMessage(hwnd,uMsg,wParam,lParam);
SetWindowLong(mIRC_hwnd, GWL_WNDPROC, (LONG)old_proc);
return 0;
break;
|
|
|
|
Joined: Dec 2002
Posts: 580
Fjord artisan
|
OP
Fjord artisan
Joined: Dec 2002
Posts: 580 |
Although using WM_CLOSE rather than WM_DESTROY still craps out when an "confirm exit" is canceled. The same code that works in WM_CLOSE will not work with WM_DESTROY without crashing... Any suggestions?
|
|
|
|
|