Problems with PostMessage from C++ .dll to Delphi Forms application - c++

I have Windows Forms Application written on Delphi 7 and C++ .dll written using MFC.
Currently I'm trying to implement basic message posting from .dll to main executable to show user calculation process on progressbar, but several problems were faced.
Let me describe my approach first. I register simple message in my Delphi application like:
WM_MSG := RegisterWindowMessage('WM_MSG');
and do the same at the library part:
UINT nMsgID = RegisterWindowMessage(_T("WM_MSG"));
This is OK: I can see same values on both sides when debugging.
My library function looks like this (just a dummy example to test progress bar):
extern "C" __declspec(dllexport) int MyFunction() {
UINT nMsgID = RegisterWindowMessage(_T("WM_MSG"));
HWND hWnd = FindWindow(NULL, "Form1");
if (hWnd > 0)
for (int i = 0; i < 100000; i++) {
int param = ceil(100 * (double) i / (double) 100000);
PostMessage(hWnd, nMsgID, param, NULL);
}
return 1;
}
Executable OnMessage event:
procedure TForm1.OnMessageEvent(var Msg: tagMSG; var Handled: Boolean);
begin
Handled := True;
if Msg.message = WM_MSG then
ProgressBar1.Position := Msg.wParam
else Handled := False;
end;
C++ function call from executable:
procedure TMyFunctionDLL.Execute;
var
i: Integer;
tHWND: HWND;
begin
tHWND := FindWindow(nil, 'mainF');
i := Func;
end;
First problem is that tHWND and hWnd variables values are inexplicably different. After some research I've discovered 3 situations:
1. Negative or positive huge hWnd
2. Zero hWnd
3. Undefined ('???')
In all cases variable hWnd is marked as unused and I don't know what does that mean. The most interesting thing is that code DOES work if I test it in very simple Delphi form (with only one unit). That simple Delphi form works well with my real C++ .dll code where real data is calculated. But when I use my general Delphi application (many units but still one form) it seems main application OnMessage event doesn't catch any events from C++ dll.
So, there are 2 questions:
1. why are hWnd values are always different and why are they 'unused'?
2. how can I force my main application to work correctly with progressbar?
I've been using different approaches to resolve this. Such as passing Application.Handle or Form1.Handle as function parameter to C++ library. None of them worked not even saying about parameter value changed while passing (I guess that should be separate question). Also I've tried using ::FindWindow() and ::PostMessage() instead of FindWindow() and PostMessage() (what is difference between them?), that didn't helped either. I'm trying to improve situtuation for whole day already but have no idea how to solve it. Help me with any ideas please.

In addition to what others have stated, a better design would be to have the EXE pass its HWND into the DLL directly, then the DLL does not have to go hunting for it. This has the added benefit that the EXE can then decide which HWND the DLL should post its messages to. I would use AllocateHWnd() to create a dedicated window for that.
Try this:
UINT nMsgID = RegisterWindowMessage(_T("WM_MSG"));
extern "C" __declspec(dllexport) int __stdcall MyFunction(HWND hWnd) {
if ((nMsgID != 0) && (hWnd != NULL)) {
for (int i = 0; i < 100000; i++) {
int param = ceil(100 * (double) i / (double) 100000);
PostMessage(hWnd, nMsgID, param, 0);
}
}
return 1;
}
.
unit Unit1;
interface
...
var
DllWnd: HWND = 0;
implementation
var
WM_MSG: UINT = 0;
procedure TForm1.FormCreate(Sender: TObject);
begin
DllWnd := AllocateHWnd(DllWndProc);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
if DllWnd <> 0 then
begin
DeallocateHWnd(DllWnd);
DllWnd := 0;
end;
end;
procedure TForm1.DllWndProc(var Message: TMessage);
begin
if (Message.Msg = WM_MSG) and (WM_MSG <> 0) then
ProgressBar1.Position := Message.WParam
else
Message.Result := DefWindowProc(DllWnd, Message.Msg, Message.WParam, Message.LParam);
end;
...
initialization
WM_MSG := RegisterWindowMessage('WM_MSG');
end.
.
uses
Unit1;
function DllFunc(Wnd: HWND): Integer; stdcall; external 'My.dll' name 'MyFunction';
procedure TMyFunctionDLL.Execute;
var
i: Integer;
begin
i := DllFunc(DllWnd);
end;

Results from FindWindow can either be zero or non-zero. Handle values don't lie on the number line. They're just distinct values, so it makes no sense to apply inequality operators to them. Put another way, handle values can appear to be negative, so don't assume that valid handles will always be greater than zero.
If the window-handle values don't match, then it's no wonder nothing else works. You're in no position to be debugging the functionality of your messages since you're not even sure you're sending them to the right window. Focus on fixing that first.
Only use FindWindow as a last resort. It has offers no facility for detecting when there are multiple windows that fit your search criteria. It always returns exactly one result. When possible, avoid searching at all. Instead, tell the sender exactly what window to send messages to. You say you tried this and failed, but I urge you to pursue that avenue some more. The problem you had was probably a mismatched calling convention. Make sure both the DLL and the host application use stdcall.
Once you're sure you're sending messages to the right window, then you can worry about why your messages aren't operating the progress bar correctly. I can see at least two reasons:
While the DLL function is running, your main program, which called the DLL, is not. It's waiting for the DLL code to return. That means your main program is not handling any messages. The DLL is posting a bunch of messages, but they're not getting handled yet. They won't get handled until the program gets back to its message loop.
The default size of the Windows message queue is 10,000. You're posting 10 times that many messages to the queue and not handling any before you stop, so even if the queue was completely empty before you started (which is unlikely, since you're probably triggering this functionality from keyboard or mouse input), you'd only get one-tenth the messages. When the queue is full, PostMessage just discards the message. And since the value you're sending with the message is an integer between 0 and 100, it's rather pointless to send 100,000 of them when only 101 of them will hold meaningful information.

If identical calls to FindWindow return different windows then you must have multiple windows with the name Form1. Try giving these different windows different names so that they can be uniquely identified.
The unused question is a little unclear. Perhaps you mean that the compiler has noticed that the value assigned to tHWND is never used and is thus pointless.
I would make a final comment that the question is imprecise and this is probably part of your problem. For example you say that all the variables are unused but we have no clear idea what you mean. You will have more success in debugging if you are more precise and methodical.

Allright, seems I've resolved the problem...
I tried Remy's proposal to declare exported function
function MyFunction (fHWND: HWND): Integer; cdecl; external 'My.dll'
with cdecl calling convention as David adviced. My previous function declaration looked like this
TMyFunction = function (fHWND: HWND): Integer;
and I suppose that was the problem. Thank you all for your help!
P.S. Now, how can I close the question?

Related

c++ Call function before logout

I'm trying to execute a function on user logout, but the program close before function call, is there any way to wait function finish the function execution?
#include "stdafx.h"
#include <windows.h>
#include <fstream>
using namespace std;
bool done = false;
void createFile() {
ofstream outfile("test.txt");
outfile << "test" << std::endl;
outfile.close();
}
BOOL WINAPI consoleHandler(DWORD signal) {
switch (signal)
{
case CTRL_LOGOFF_EVENT:
printf("Logoff");
done = true;
Sleep(20000); // force exit after 20 seconds
return TRUE;
case CTRL_C_EVENT:
printf("Ctrl+c");
done = true;
Sleep(20000); // force exit after 20 seconds
return TRUE;
case CTRL_CLOSE_EVENT:
printf("close");
done = true;
Sleep(20000); // force exit after 20 seconds
return TRUE;
default:
// Pass signal on to the next handler
return FALSE;
}
}
int main()
{
if (!SetConsoleCtrlHandler(consoleHandler, TRUE)) {
printf("\nERROR: Could not set control handler");
return 1;
}
printf("Runing");
while (!done) {
printf(".");
Sleep(1000);
}
createFile(); //Dont called on user logout but called on close and ctrl+c
printf("\nEnding\n");
Sleep(1000);
return 0;
}
On CTRL_C_EVENT and CTRL_CLOSE_EVENT
The file test.txt is created but on
CTRL_LOGOFF_EVENT
The program close instantly, without call the function.
First, let me apologise : I'll not answer the specific 'CTRL_LOGOFF_EVENT' you are referring, but the generic feature you're trying to accomplish : 'execute a function on user logout'.
A generic way to do:
As far as I know, there is no generic way to do.
You may try various solutions :
catching events as you did
using try catch / finally
spawn windows timers and react to it
spawn threads that will wait for the main thread to end
But (as far as I know) all of them have a weakness to a way to close or another.
For instance, I recently had to struggle with a poorly coded dll I had to use. This dll was calling "exit(0)". I found no elegant way to gracefully handle this behaviour.
I had to launch it in a separated process (not thread), that I was monitoring from my main process. It's a lot of work to implement and to maintain for a simple result.
Another approach:
Depending on what you are trying to do, it may be a good practice to make your "final" operation gradually, using a format that allows you to recover partially written files.
It's what I would generally do, but it always depends on what you are trying to achieve.
For instance, if you're trying to clean your workspace, you may want to:
do it at start in the case it was not done in the previous run
work in temporary folders
Another case, if you have a very long process and want to generate summary file, you may want to:
generate a partial summary file from time to time
as you generate a new file, you move the previous one
that way, you'll have a partial summary file at every step of your process.
Since my answer has been down voted, I guess I need to be more pedantic.
first of all, since you are not getting the CTRL_LOGOFF_EVENT you may want to consult the this doc (https://learn.microsoft.com/en-us/windows/console/setconsolectrlhandler). The critical section being:
If a console application loads the gdi32.dll or user32.dll library, the
HandlerRoutine function that you specify when you call SetConsoleCtrlHandler
does not get called for the CTRL_LOGOFF_EVENT and CTRL_SHUTDOWN_EVENT events. The
operating system recognizes processes that load gdi32.dll or user32.dll as Windows
applications rather than console applications. This behavior also occurs for
console applications that do not call functions in gdi32.dll or user32.dll
directly, but do call functions such as Shell functions that do in turn call
functions in gdi32.dll or user32.dll.
To receive events when a user signs out or the device shuts down in these
circumstances, create a hidden window in your console application, and then handle
the WM_QUERYENDSESSION and WM_ENDSESSION window messages that the hidden window
receives. You can create a hidden window by calling the CreateWindowEx method with
the dwExStyle parameter set to 0.
You might also want to read ( https://msdn.microsoft.com/en-us/library/windows/desktop/aa376890(v=vs.85).aspx ).
Now, what I had previously suggested is to use the SetWindowsHookEx which should work as all windows messages pass through it (it is what Spy uses to view the message queues (https://blogs.msdn.microsoft.com/vcblog/2007/01/16/spy-internals/)). 20 years ago, you would have to do something like this for certain messages that were not passed through to your window - as in your case where the windows message is processed up stream of the current window. You may come across old code that does this, or there may be situations that you still need to do this.

MFC C++ CListBox get selected item

First let me say that I've been searching for a solution for couple of days now...
I'm trying to get selected item for ListBox. This is my code:
CListBox * pList1 = (CListBox *)GetDlgItem(IDC_LIST1);
CString ItemSelected;
// Get the name of the item selected in the Sample Tables list box
// and store it in the CString variable declared above
pList1->GetText(pList1->GetCurSel(), ItemSelected);
MessageBox(ItemSelected, "TEST", MB_OK);
Now when i try this i get an error message saying "The Parameter is incorect"
Your code looks OK except error handling. Also MessageBox parameters look incorrect. The first parameter should be of type HWND. I believe that this is the root cause of your problems. Use MFC standard AfxMessageBox instead:
CListBox * pList1 = (CListBox *)GetDlgItem(IDC_LIST1);
int nSel = pList1->GetCurSel();
if (nSel != LB_ERR)
{
CString ItemSelected;
pList1->GetText(nSel, ItemSelected);
AfxMessageBox(ItemSelected);
}
If the CListBox is in single selection mode, the CListBox::GetCurSel will return the selected index.
If the CListBox is in multi-selection mode, you should use CListBox::GetSelItems which will return a list of indices.
You cannot mix'n'match the functions.
And always check return codes (as others already wrote).
If You already have a data member MyList(of classCListBox) :
int nSel = MyList.GetCurSel();
CString ItemSelected;
if (nSel != LB_ERR)
{
MyList.GetText(nSel, ItemSelected);
}
CWnd class has a MessageBox function which does not need a HWND parameter. But yes, AfxMessageBox is a little bit more easier to use and can be called anywhere in the MFC code without having a CWnd-derived object. And a beside note: if call a WinAPI function inside MFC code (not needed here, but possible in other cases) it's good to prepend it with scope resolution operator in order to avoid any confusion, mistake and/or name conflict (e.g. ::MessageBox...).
One possible cause for "invalid parameter" error in OP code is that it uses an ANSI string literal ("TEST") in a UNICODE build configuration. This case, must use an UNICODE string literal (L"TEST") or a little bit better, use _T macro (_T("TEST")) that makes it possible to build in both ANSI and UNICODE configurations.

How to avoid WM_APP

I am writing a CFrameWnd wrapper and I have this line in the header file :
#define WM_CFW_MESSAGE (WM_APP + 100)
Is this is a good practice ? Does it require that users of this wrapper will have to remember not to use this particular number (WM_APP + 100) ?
No, it's not a good practice. The WM_USER range is more suitable. The WM_APP range is intended for messages that must be understood by multiple window classes in a single program. The WM_USER range is intended for messages that are intended for a single window class.
Therefore, you can safely use values in WM_USER range. You can write #define WM_CFW_MESSAGE (WM_USER+0) because you know that your window class has no other WM_USER messages. When you add a second custom message to the same window class, you can use (WM_USER+1), etcetera. If you implement another window class, it can start at WM_USER+0 again.
It would only be a problem if users tried to send that message to the window handle of your wrapper.
"users of this wrapper will have to remember not to use this particular number"
And why would they want to do that. Shouldn't you as the component writer, provide methods, rather than let your user accessing your control directly.
This is an "unwritten" rule that you might want to know.
WM_USER.
Use this message within you control.
Supposed, you write a control and some methods INSIDE YOUR CONTROLS need to alter your control, you might want to use WM_USER.
WM_APP.
If your application needs to send message to OBJECTS INSIDE YOU APPLICATION, you can use WM_APP.
RegisterWindowMessage
If you have two (more then one) applications which need to communicate each other, you should REGISTER your WINDOWS MESSAGE with RegisterWindowMessage.
Examples. This example uses Delphi
{ WM_USER }
procedure TYourControl.RefreshColor;
begin
SendMessage(Self.Handle,WM_USER+YourNumber,wParam,lParam);
{ Pay attention to Self.Handle, use WM_USER within YOUR CONTROL }
end;
{ WM_APP }
procedure CheckValue;
var
IResult: Integer;
begin
IResult:=SendMessage(OtherForm.Handle,WM_APP+YourConstant,wParam,lParam);
{ Watch OtherForm.Handle }
end;
{ RegisterWindowMessage }
procedure SendCommand(OtherAppHandle: Integer);
var
MessageNumber: Integer;
MessageName: ShortString;
begin
MessageName:='YourMessageName';
Inc(MessageName[0]);MessageName[Ord(MessageName[0])]:=#0;
MessageNumber:=RegisterWindowMessage(#MessageName[1]);
SendMessage(OtherAppHandle,MessageNumber,wParam,lParam);
end;
{ Hope this will help }

"Debug Assertion" Runtime Error on VS2008?

I'm writing a C++ MFC program on VS2008 and I'm getting this "Debug Assertion Error" when I first run the program sometimes. When I try to debug it, it takes me to this winhand.cpp file which is not part of the program I wrote so I'm not sure how to debug this.
It takes the error to this place in winhand.cpp
CObject* pTemp = LookupTemporary(h);
if (pTemp != NULL)
{
// temporary objects must have correct handle values
HANDLE* ph = (HANDLE*)((BYTE*)pTemp + m_nOffset); // after CObject
ASSERT(ph[0] == h || ph[0] == NULL);
if (m_nHandles == 2)
ASSERT(ph[1] == h);
}
So why does this error happen? Why does it only happen sometimes (50% of the time)? How would I debug this?
I'll provide some code if is needed.
THANKS!
The code that is asserting is part of MFC's CHandleMap class. MFC deals with windows as CWnd objects, but Windows deals with them as HWND handles. the handle map allows MFC to 'convert' an HWND into a pointer to the MFC object representing that object.
What the assertion seems to be doing is checking that when a lookup of the handle finds an MFC object, that the MFC object also thinks it's wrapping the same handle.
If they're different, then you get the assertion.
So it would appear that something is corrupting the handle map or the MFC object for that handle or you're doing something incorrect that gets these 2 data structures out of sync.
Some things you might do to try to debug the problem is to determine:
what MFC object is being found in the lookup (that's what's being pointed to by pObject)
what the MFC object thinks it's wrapping (that's the handle ph[0] and/or ph[1] - I'm not sure why there can be 2 of them)
what the handle is for (that's h)
Do the handles look like handle values or do they look like garbage? Does pObject point to something that looks like an MFC object, or garbage? Do any of these these things seem related?
The answers to these questions may point to what you need to do next (maybe set a debug write breakpoint on the item that looks like it's trashed).
I got this same assertion few days ago, and after some google search,
I found the solution for my case here:
http://forums.codeguru.com/showthread.php?216770-What-would-cause-this-assertion
In my case, change to misused
CDC* dc = GetDC();
CSize spaceSize = dc->GetTextExtent(" ");
dc->DeleteDC();
to
CDC* dc = GetDC();
CSize spaceSize = dc->GetTextExtent(" ");
ReleaseDC(dc);
would fix it.
Look out for code along those lines (from memory from Stroustrup's book):
c1 = (t2+t3).c_str();
(in spirit, could be other commands and expressions of course).Temporary objects are destroyed after their enclosing full expression has been evaluated, or at least the standard allows them to be. That means that what you would like to allocate to c1 may, or may not, still be in memory where it can be assigned to c1. The compiler may alert you to this issue, and the issue may or may not arise depending on what exactly you assign and other circumstances (I am not compiler writer), which would also explain why you get this error message only sometimes.
So in your shoes, I'd scan my code for similar expressions and clean them up.
When the debugger breaks, head up the call stack to the first bit of your code (if there is any - hopefully there is!). Ideally it's as simple as something in your code calling a library function incorrectly, and the library is catching the error with an assert and alerting you to that. (I don't think anyone will be able to tell what's wrong from the library code, we need to see your code.)
Otherwise, you're in for some tricky debugging: you're doing something wrong with the library that is asserting (looks like MFC) so go back and review all your MFC code and make sure everything is correct and according to the documentation.
This looks suspiciously like an error I had this morning. Is this happening in OnIdle()?
I know this is a very old post, but hoping that someone may get a little help from my answer.
I also faced a similar issue recently because of my simple mistake, then I came across this post and got a hint from "pac"'s post.
What I found is that if I use DeleteDC() to release DC returned from GetWindowDC() or GetDC() I will get the above assertion in MFC frame once CPaintDC object instance goes out of scope.
CDC * pDC = GetWindowDC();
...
ReleaseDC(pDC);
You have to use DeleteDC() only in conjunction with CreateDC() API.
CDC * pDC = new CDC();
pDC->CreateDC();
....
pDC->DeleteDC();
We had this problem when some of our project dlls were linking MFC as static library and some as shared library (check "Use of MFC" in Project settings)

Explicit Linking DLL and Program Hangs

I've the following piece of code in my program which dynamically links wtsapi32.dll file for session notifications like WTS_SESSION_LOCK and WTS_SESSION_UNLOCK and runs in background. After the first lock/unlock the program hangs and not responding.
Is this a right way of doing explicit linking ?
void RegisterSession(HWND hwnd)
{
typedef DWORD (WINAPI *tWTSRegisterSessionNotification)( HWND,DWORD );
tWTSRegisterSessionNotification pWTSRegisterSessionNotification=0;
HINSTANCE handle = ::LoadLibrary("wtsapi32.dll");
pWTSRegisterSessionNotification = (tWTSRegisterSessionNotification) :: GetProcAddress(handle,"WTSRegisterSessionNotification");
if (pWTSRegisterSessionNotification)
{
pWTSRegisterSessionNotification(hwnd,NOTIFY_FOR_THIS_SESSION);
}
::FreeLibrary(handle);
handle = NULL;
}
Edited:
I have another method UnRegisterSession() function which calls WTSUnRegisterSessionNotification, I am calling the RegisterSession() in WinMain method ( removed FreeLibrary as suggested by 1800) and calling UnRegisterSession() in WM_DESTROY of CALLBACK WindowProcedure function. But still the application hangs.
I'd say you probably cannot safely call FreeLibrary like that - you will be unloading the code you want to have call you. You should probably ensure not to free the dll until after you are finished getting notifications.
MS documentation suggests that you must call WTSUnRegisterSessionNotification before re-registering the session - as it only happens on your second attempt to lock it perhaps this is your issue?
With 1800 wrt the free library - you must keep this library loaded while you use it.
According to the documentation (http://msdn.microsoft.com/en-us/library/aa383841(VS.85).aspx):
"When a window no longer requires these notifications, it must call WTSUnRegisterSessionNotification before being destroyed."
I would try unregistering the notification during WM___CLOSE instead of WM_DESTROY.