Ask the user and send the response back to the Message-Receiver

I want to ask the user to enter a password. Since the password is sometimes needed in a different thread than in the main thread where the VCL is running, I tried to send a message to the main window and request a password. Then the main window asks the user.

As I ask the user:

procedure TMainForm.WMGetPassword(var Msg: TMessage); var Password: String; begin if QueryPassword(Password) then // function QueryPassword(out Password: String): boolean; begin Password := Password + #0; // Add #0-Terminator Move(Password[1], Msg.wParam, Length(Password) * sizeOf(Char)); // Copy the String in my buffer Msg.Result := 1; end else begin Msg.Result := 0; end; end; 

As I ask the main window:

 var PasswordBuffer: PChar; Password: String; begin PasswordBuffer := AllocMem(100 * sizeof(Char)); PasswordResult := SendMessage(MainFormHWND, WM_GetPassword, Integer(PasswordBuffer), 0); Result := (PasswordResult <> -1); if not Result then Exit; SetString(Password, PasswordBuffer, 100); ShowMessage(Password); end; 

But Password and PasswordBuffer then empty. What am I doing wrong?

+4
source share
3 answers

While the thread is in the same process (so it has the same address space), your code should work. This, however, is unnecessarily complicated and has a memory leak ( PasswordBuffer never freed).

You can use a string variable in the stream and pass the address to your internal pre-allocated buffer in the main stream:

 type TTestThread = class(TThread) private fHwnd: HWND; protected procedure Execute; override; public constructor Create(AWnd: HWND); end; constructor TTestThread.Create(AWnd: HWND); begin fHwnd := AWnd; inherited Create(False); end; procedure TTestThread.Execute; const MAXLEN = 1024; var s: string; begin SetLength(s, MAXLEN); if SendMessage(fHwnd, WM_GETPASSWORD, MAXLEN, LPARAM(@s[1])) > 0 then begin s := PChar(s); // don't use VCL here Windows.MessageBox(0, PChar('password is "' + s + '"'), 'password', MB_ICONINFORMATION or MB_OK); end; end; 

In the main stream, the password is placed in a buffer whose length is limited by the size of the buffer:

 procedure TForm1.WMGetPassword(var AMsg: TMessage); var Pwd: string; begin if InputQuery('Password Entry', 'Please enter the password:', Pwd) and (Pwd <> '') then begin StrPLCopy(PChar(AMsg.LParam), Pwd, AMsg.WParam); AMsg.Result := 1; end else AMsg.Result := -1; end; 
+3
source

Since you pass Msg.wParam as the second parameter, it writes your string to that place, not the location you are trying to point to. This will overwrite the values ​​stored in Msg.wParam, Msg.lParam, Msg.Result +, possibly also some other stack information.

Instead:

 Move(Password[1], Msg.wParam, Length(Password) * sizeOf(Char)); 

You should use:

 Move(Password[1], PChar(Msg.wParam)^, Length(Password) * sizeOf(Char)); 

or use MoveMemory if you want to use pointers.

+3
source

@Dan figured this out, plus @mghie noticed a leak.

Here is an alternative way that does not include pointers:

 type TMyMessage = class msg: string; end; procedure TMainForm.WMGetPassword(var Msg: TMessage); var SMessage: TMyMessage; Password: string; begin if QueryPassword(Password) then begin SMessage := TMyMessage(msg.WParam); SMessage.msg := Password; msg.Result := 1; end else begin Msg.Result := 0; end; end; var MyMsg: TMyMessage; begin MyMsg := TMyMessage.Create(''); try PasswordResult := SendMessage(FormHandleHWND,WM_GetPassword,WPARAM(MyMsg),0); Result := (PasswordResult <> -1); if Result then Password := MyMsg.msg; finally MyMsg.Free; end; end; 
+2
source

All Articles