2012-08-14 2 views
4

사용자에게 암호를 입력하도록 요청하고 싶습니다. VCL이 실행되는 주 스레드와 다른 스레드에서 암호가 필요한 경우가 있으므로 주 창에 메시지를 보내고 암호를 요청했습니다. 그런 다음 주 창에 사용자에게 묻습니다.사용자에게 질문하고 응답을 메시지 수신기에 다시 보내십시오.

I는 사용자에게 어떻게 :

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; 

그러나 PasswordPasswordBuffer 나중에 비어 : 내가 메인 창을 요청하는 방법

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; 

. 내가 도대체 ​​뭘 잘못하고있는 겁니까?

+0

WMGetPassword에서 Msg.wParam의 유형은 무엇입니까 : 여기

포인터를 포함하지 않는 다른 방법을 무엇입니까? 그것을 PChar에 캐스팅해야합니까? –

+0

Msg.wParam도 정수, Msg.lParam입니다. – rollstuhlfahrer

+0

@rollstuhlfahrer, 아니요, Msg.wParam은'WPARAM'이고 Msg.lParam은'LPARAM' 유형이며이 방법으로 그들은 형변환되어야합니다. 이 매개 변수가 'Integer (x)'로 유형 변환 된 메시지 전송에 대해 인터넷의 많은 자습서에서 본 내용을 잊어 버리십시오. – TLama

답변

3

스레드가 동일한 프로세스에 있으므로 (동일한 주소 공간을 공유하기 때문에) 코드가 작동해야합니다. 그러나 불필요하게 복잡하고 메모리 누수가 있습니다 (PasswordBuffer은 해제되지 않습니다).

당신은 스레드 문자열 변수를 사용하고, 메인 쓰레드에 내부 사전 할당 된 버퍼의 주소를 전달할 수 암호 버퍼에 넣고 메인 글

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; 

로 길이가 한정 버퍼 크기 :

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; 
+0

쿨,이 하나의 작품. 큰 감사를 드린다. – rollstuhlfahrer

3

두 번째 매개 변수로 Msg.wParam을 전달할 때 가리키는 위치가 아닌 해당 위치에 문자열을 쓰고 있습니다. Msg.wParam, Msg.lParam, Msg.Result +에 저장된 값을 덮어 씁니다. 아마도 스택의 다른 정보도 있습니다.

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

사용한다 : 대신

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

또는 포인터를 사용하려는 경우 MoveMemory를 사용합니다.

+0

이것도 작동합니다. :) 나는 그런 간단한 해결책을 생각하지 않았다 – rollstuhlfahrer

2

@ Dan, 맞아 @mghie가 누설을 발견했습니다.

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; 
+0

하나의 String-Value에 대해 클래스를 사용하지 않고 휠에서 나비를 깨지십니까? – rollstuhlfahrer

+0

전혀 아닙니다. 어쨌든 메모리를 할당하고 있으므로 클래스를 사용하지 않는 것이 좋습니다. –

+1

음, 생각만으로 보입니다 : -? – TLama