With a keyHook I receive messages.
With:
GetKeyNameText(Message.LParam, @Character, 100);
I receive the Name of the key from the message. e.g. J, L, SHIFT, ENTER, F1
So far so good, but the problem is, I can display only the 'first set' of chars, not considering the status of the shift key. (e.g. I press :, but GetKeyNameText returns first 'SHIFT' and then ';'
At another place I receive and store the status of the shift key. With SHIFT and message.lparam I should be able to get the real key, the user pressed!?
So I tried this
. which should normaly produce the same text like the memo which the user have entered originally.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
AddKey(Message.WParam,STRING(CHARACTER));
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Procedure TSpyKeyForm.AddKey(key:word;STR:STRING);
VAR ch:STRING;
CONST
AddAscii: integer = 32;
begin
IF shift THEN AddAscii := 0 ELSE AddAscii := 32;
case KEY of
16: SHIFT:= true; //set shift private var,
13: S:= S + #10#13;
32: begin ch:= ' '; S:= S + CH ; end;
186 . 230: s:=s+STR;
65 . 90: s:=s+char(KEY + addAscii);
else
s:=s+' ['+STR+'] ';//char(KEY + addAscii);
END;
end;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This procedure is far away from being finished, but I think there must be a API funktion to perform this kind of translation?!
Question:
Maybe a kind of GetKeyNameText which also take the shift key in order to give me the real character the user pressed, or a function like the one above using the message or ascii code and the SHIFT status.
.........................
Here the unit:
UNIT KeyHook_DLL_U2;
INTERFACE
USES
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
CONST
My_Hook_DLL = 'KHD.dll';
MY_HOOK_MSSG = WM_USER + $1000;
TYPE
THookTeclado = PROCEDURE; stdcall;
TYPE
TSpyKeyForm = CLASS(TForm)
Memo1: TMemo;
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
PROCEDURE FormCreate(Sender: TObject);
PROCEDURE FormDestroy(Sender: TObject);
PROCEDURE FormClick(Sender: TObject);
Private
{ Private declarations }
shift: boolean;
File_in_Mem: THandle;
PReceiver: ^Integer;
HandleDLL: THandle;
HookOn,
HookOff: THookTeclado;
s: STRING;
PROCEDURE Sys_Hook(VAR message: TMessage); Message MY_HOOK_MSSG;
procedure AddKey(key: word;STR:STRING);
Public
{ Public declarations }
END;
VAR
SpyKeyForm: TSpyKeyForm;
IMPLEMENTATION
{$R *.DFM}
PROCEDURE TSpyKeyForm.FormCreate(Sender: TObject);
BEGIN
{We dont want that the memo read the keyboard..}
Memo1.ReadOnly := TRUE;
//Find DLL
HandleDLL := LoadLibrary(PChar(ExtractFilePath(Application.Exename) + My_Hook_DLL));
IF HandleDLL = 0 THEN
RAISE Exception.Create('DLL not found');
//get address of dll functions
@HookOn := GetProcAddress(HandleDLL, 'HookOn');
@HookOff := GetProcAddress(HandleDLL, 'HookOff');
IF NOT assigned(HookOn) OR
NOT assigned(HookOff) THEN
RAISE Exception.Create('Cannot find the required DLL functions');
File_in_Mem := CreateFileMapping($FFFFFFFF,
NIL,
PAGE_READWRITE,
0,
SizeOf(Integer),
'MyMappedFile'); //in dll
IF File_in_Mem = 0 THEN
RAISE Exception.Create('Error while create file');
PReceiver := MapViewOfFile(File_in_Mem, FILE_MAP_WRITE, 0, 0, 0);
PReceiver^ := Handle;
HookOn;
END;
PROCEDURE TSpyKeyForm.Sys_Hook(VAR message: TMessage);
VAR
Character: ARRAY[0.100] OF char;
Key_Action: STRING; ch:string;
ScanCode:word;
BEGIN
{Virtual key code to Key Name}
GetKeyNameText(Message.LParam, @Character, 100);
ch:=string(character);
//message from dll
ScanCode:= MapVirtualKey(Message.wParam,0);
label2.caption:=inttostr(scancode);
label3.caption:=char(scancode);
{Look if the key was pressed, released o re-pressed}
IF ((Message.lParam SHR 31) AND 1) = 1 THEN
BEGIN
Key_Action := 'Released'; {Released}
IF message.wparam = 16 THEN shift := false;
END
ELSE
IF ((Message.lParam SHR 30) AND 1) = 1 THEN Key_Action := 'Repressed' {repressed}
ELSE
BEGIN Key_Action := 'pressed'; {pressed}
AddKey(Message.WParam,STRING(CHARACTER));
// s := s + ch;//STRING(Character);
// end;
END;
//if message.wparam = 16 then shift:= true else shift := false;
//IF shift THEN showmessage('shift');
END;
Procedure TSpyKeyForm.AddKey(key:word;STR:STRING);
VAR ch:STRING;
CONST
AddAscii: integer = 32;
begin
IF shift THEN AddAscii := 0 ELSE AddAscii := 32;
case KEY of
16: shift := true;
13: S:= S + #10#13;
32: begin ch:= ' '; S:= S + CH ; end;
186 . 230: s:=s+STR;
65 . 90: s:=s+char(KEY + addAscii);
else
s:=s+' ['+STR+'] ';//char(KEY + addAscii);
END;
label1.caption :=
inttostr(KEY) + ' '
+ STR + ' '
+ char(KEY + addAscii);
end;
PROCEDURE TSpyKeyForm.FormDestroy(Sender: TObject);
BEGIN
{Uninstall the Hook}
IF Assigned(HookOff) THEN
HookOff;
{Free the DLL}
IF HandleDLL <> 0 THEN
FreeLibrary(HandleDLL);
{Close the memfile and the View}
IF File_in_Mem <> 0 THEN
BEGIN
UnmapViewOfFile(PReceiver);
CloseHandle(File_in_Mem);
END;
END;
PROCEDURE TSpyKeyForm.FormClick(Sender: TObject);
var sl:TStringlist;
BEGIN
sl:=tstringlist.create;
//memo1.text:=s;
//memo1.text:=s;// := s;
//showmessage(s);
sl.text:=s;
sl.SaveToFile(extractfilepath(application.exename)+'test1.txt');
memo1.Lines.LoadFromFile('test1.txt');
//memo1.Lines.SaveToFile('test2.txt');
END;
END.
-->
With:
GetKeyNameText(Message.LParam, @Character, 100);
I receive the Name of the key from the message. e.g. J, L, SHIFT, ENTER, F1
So far so good, but the problem is, I can display only the 'first set' of chars, not considering the status of the shift key. (e.g. I press :, but GetKeyNameText returns first 'SHIFT' and then ';'
At another place I receive and store the status of the shift key. With SHIFT and message.lparam I should be able to get the real key, the user pressed!?
So I tried this
. which should normaly produce the same text like the memo which the user have entered originally.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
AddKey(Message.WParam,STRING(CHARACTER));
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Procedure TSpyKeyForm.AddKey(key:word;STR:STRING);
VAR ch:STRING;
CONST
AddAscii: integer = 32;
begin
IF shift THEN AddAscii := 0 ELSE AddAscii := 32;
case KEY of
16: SHIFT:= true; //set shift private var,
13: S:= S + #10#13;
32: begin ch:= ' '; S:= S + CH ; end;
186 . 230: s:=s+STR;
65 . 90: s:=s+char(KEY + addAscii);
else
s:=s+' ['+STR+'] ';//char(KEY + addAscii);
END;
end;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This procedure is far away from being finished, but I think there must be a API funktion to perform this kind of translation?!
Question:
Maybe a kind of GetKeyNameText which also take the shift key in order to give me the real character the user pressed, or a function like the one above using the message or ascii code and the SHIFT status.
.........................
Here the unit:
UNIT KeyHook_DLL_U2;
INTERFACE
USES
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
CONST
My_Hook_DLL = 'KHD.dll';
MY_HOOK_MSSG = WM_USER + $1000;
TYPE
THookTeclado = PROCEDURE; stdcall;
TYPE
TSpyKeyForm = CLASS(TForm)
Memo1: TMemo;
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
PROCEDURE FormCreate(Sender: TObject);
PROCEDURE FormDestroy(Sender: TObject);
PROCEDURE FormClick(Sender: TObject);
Private
{ Private declarations }
shift: boolean;
File_in_Mem: THandle;
PReceiver: ^Integer;
HandleDLL: THandle;
HookOn,
HookOff: THookTeclado;
s: STRING;
PROCEDURE Sys_Hook(VAR message: TMessage); Message MY_HOOK_MSSG;
procedure AddKey(key: word;STR:STRING);
Public
{ Public declarations }
END;
VAR
SpyKeyForm: TSpyKeyForm;
IMPLEMENTATION
{$R *.DFM}
PROCEDURE TSpyKeyForm.FormCreate(Sender: TObject);
BEGIN
{We dont want that the memo read the keyboard..}
Memo1.ReadOnly := TRUE;
//Find DLL
HandleDLL := LoadLibrary(PChar(ExtractFilePath(Application.Exename) + My_Hook_DLL));
IF HandleDLL = 0 THEN
RAISE Exception.Create('DLL not found');
//get address of dll functions
@HookOn := GetProcAddress(HandleDLL, 'HookOn');
@HookOff := GetProcAddress(HandleDLL, 'HookOff');
IF NOT assigned(HookOn) OR
NOT assigned(HookOff) THEN
RAISE Exception.Create('Cannot find the required DLL functions');
File_in_Mem := CreateFileMapping($FFFFFFFF,
NIL,
PAGE_READWRITE,
0,
SizeOf(Integer),
'MyMappedFile'); //in dll
IF File_in_Mem = 0 THEN
RAISE Exception.Create('Error while create file');
PReceiver := MapViewOfFile(File_in_Mem, FILE_MAP_WRITE, 0, 0, 0);
PReceiver^ := Handle;
HookOn;
END;
PROCEDURE TSpyKeyForm.Sys_Hook(VAR message: TMessage);
VAR
Character: ARRAY[0.100] OF char;
Key_Action: STRING; ch:string;
ScanCode:word;
BEGIN
{Virtual key code to Key Name}
GetKeyNameText(Message.LParam, @Character, 100);
ch:=string(character);
//message from dll
ScanCode:= MapVirtualKey(Message.wParam,0);
label2.caption:=inttostr(scancode);
label3.caption:=char(scancode);
{Look if the key was pressed, released o re-pressed}
IF ((Message.lParam SHR 31) AND 1) = 1 THEN
BEGIN
Key_Action := 'Released'; {Released}
IF message.wparam = 16 THEN shift := false;
END
ELSE
IF ((Message.lParam SHR 30) AND 1) = 1 THEN Key_Action := 'Repressed' {repressed}
ELSE
BEGIN Key_Action := 'pressed'; {pressed}
AddKey(Message.WParam,STRING(CHARACTER));
// s := s + ch;//STRING(Character);
// end;
END;
//if message.wparam = 16 then shift:= true else shift := false;
//IF shift THEN showmessage('shift');
END;
Procedure TSpyKeyForm.AddKey(key:word;STR:STRING);
VAR ch:STRING;
CONST
AddAscii: integer = 32;
begin
IF shift THEN AddAscii := 0 ELSE AddAscii := 32;
case KEY of
16: shift := true;
13: S:= S + #10#13;
32: begin ch:= ' '; S:= S + CH ; end;
186 . 230: s:=s+STR;
65 . 90: s:=s+char(KEY + addAscii);
else
s:=s+' ['+STR+'] ';//char(KEY + addAscii);
END;
label1.caption :=
inttostr(KEY) + ' '
+ STR + ' '
+ char(KEY + addAscii);
end;
PROCEDURE TSpyKeyForm.FormDestroy(Sender: TObject);
BEGIN
{Uninstall the Hook}
IF Assigned(HookOff) THEN
HookOff;
{Free the DLL}
IF HandleDLL <> 0 THEN
FreeLibrary(HandleDLL);
{Close the memfile and the View}
IF File_in_Mem <> 0 THEN
BEGIN
UnmapViewOfFile(PReceiver);
CloseHandle(File_in_Mem);
END;
END;
PROCEDURE TSpyKeyForm.FormClick(Sender: TObject);
var sl:TStringlist;
BEGIN
sl:=tstringlist.create;
//memo1.text:=s;
//memo1.text:=s;// := s;
//showmessage(s);
sl.text:=s;
sl.SaveToFile(extractfilepath(application.exename)+'test1.txt');
memo1.Lines.LoadFromFile('test1.txt');
//memo1.Lines.SaveToFile('test2.txt');
END;
END.
-->
MessageHandler
is the name of the function identified by the second parameter of the MESSAGE_HANDLER macro in your message map.Syntax
Parameters
uMsg
Specifies the message.
Specifies the message.
wParam
Additional message-specific information.
Additional message-specific information.
lParam
Additional message-specific information.
Additional message-specific information.
bHandled
The message map sets bHandled to TRUE before
The message map sets bHandled to TRUE before
MessageHandler
is called. If MessageHandler
does not fully handle the message, it should set bHandled to FALSE to indicate the message needs further processing.Return Value
The result of message processing. 0 if successful.
Remarks
For an example of using this message handler in a message map, see MESSAGE_HANDLER.
See also
Implementing a Window
Message Maps
WM_NOTIFY
Message Maps
WM_NOTIFY
I have an MFC-based program which worked fine just before applying update 3059317:
MS15-060: Vulnerability in Microsoft common controls could allow remote code execution: June 9, 2015)
The update replaces
Comctl32.dll
with a new version.After applying this update, opening a dialog just hangs the program. Not all dialogs seem to be affected, but the print preview is consistently hanging, without crashing the program, though. It just becomes unresponsive.
I've no idea how I could try to debug this problem in order to find a way around it. Creating a memory dump when the program is hung gives following stack trace:
So we are indeed stuck in
Comctl32.dll
- now what?By the way, in some cases, uninstalling the update solves the issue.
UPDATE
In all cases, turning off Aero always solves the issue. And KB3059317 seemsnot to be the only June 2015 update to Windows Vista which exhibits thisweird behaviour.
Pierre Arnaud
Pierre ArnaudPierre Arnaud6,54377 gold badges5959 silver badges100100 bronze badges
2 Answers
It fails with Areo Vista at least for one dialog in our app: the dialog is just not painted. It is present, but it is just not visible.
To resolved the problem on Vista Aero, remove any calls to
SetRedraw()
function while executing OnInitDialog()
and OnSize()
methods.It seems that
Pierre ArnaudSetRedraw(TRUE)
has no (positive) effect, at least in these two cases.6,54377 gold badges5959 silver badges100100 bronze badges
Skywalker13Skywalker1317111 gold badge33 silver badges1111 bronze badges
I have the same problem with a custom program written in .NET 4 which uses a customized version of the ListView control. The program doesn't hang it produces and error 'Attempted to read or write protected memory. This is often an indication that other memory is corrupt.'. After uninstalling this update everything is fine.
ManFromKyManFromKy
Not the answer you're looking for? Browse other questions tagged c++windowsmfccomctl32 or ask your own question.
PermalinkJoin GitHub today
GitHub is home to over 36 million developers working together to host and review code, manage projects, and build software together.
Sign up Find file Copy path
Cannot retrieve contributors at this time
Lparam Msdn
#include'vrpn_Shared.h' |
#ifdef _WIN32 |
#include<windowsx.h> |
#include<conio.h> |
#include'resource.h' |
#include<commctrl.h> |
#endif |
#include<stdlib.h> |
#include<math.h> |
#include<string.h> |
#include<stdio.h> |
#include<fcntl.h> |
#include<iostream> |
#ifdef _WIN32 |
#include'Console.hpp' |
#endif |
#ifndef _USE_OLD_OSTREAMS |
usingnamespacestd; |
#endif |
#include'vrpn_Connection.h' |
#include'vrpn_Tracker.h' |
#include'vrpn_Sound.h' |
#ifdef _WIN32 |
#include'vrpn_Sound_Miles.h' |
#endif |
#include'vrpn_ForwarderController.h' |
#defineSOUND_DEBUG1 |
//Sound Server Specifics |
#ifdef _WIN32 |
vrpn_Sound_Server_Miles *soundServer = NULL; |
#defineDIALOG_TIMEOUT5000 |
#defineUPDATE_TIMEOUT2000 |
#defineSPIN_TIMEOUT2000 |
#definecboxTech100 |
#definebtnPlay101 |
#definebtnStop110 |
#definebtnClose112 |
#defineErrorBox114 |
#defineVolumeBox115 |
#defineReplayBox116 |
#defineSoundIdBox131 |
#defineSPEAKERTYPE_BOX102 |
#definePosX120 |
#definePosY121 |
#definePosZ122 |
#defineLPosX141 |
#defineLPosY142 |
#defineLPosZ143 |
#defineFMin151 |
#defineFMax152 |
#defineBMin153 |
#defineBMax154 |
#defineSoundIdTBox161 |
#defineMoreInfoBtn162 |
#defineSound2Listener181 |
#defineSPIN_BOX434 |
#defineSorX300 |
#defineSorY301 |
#defineSorZ302 |
#defineLorX303 |
#defineLorY304 |
#defineLorZ305 |
#defineUpdateTimerId1001 |
#defineDialogTimerId1002 |
#defineSpinTimerId1003 |
int blanked = 0; |
int ProviderSet = 0; |
float spinrate; |
int spincounter; |
int got_report; |
HWND SoundProvideCombo; |
HWND SpeakerTypeCombo; |
HWND SoundIdCombo; |
HWND SoundWnd; |
HWND InfoWnd; |
vrpn_Tracker_Remote * tracker_connection; |
char tracker_device[512]; |
char tracker_name[512]; |
vrpn_ListenerDef listener, oldlistener; |
int USE_TRACKER; |
enum BoxAction{add, del}; |
vrpn_int32 CurrentSoundId = -1; |
#endif |
vrpn_Connection * connection; |
// TCH October 1998 |
// Use Forwarder as remote-controlled multiple connections. |
vrpn_Forwarder_Server * forwarderServer; |
//Console for Win32 version of server |
#ifdef _WIN32 |
CConsole *con; |
#endif |
// install a signal handler to shut down the trackers and buttons |
#ifndef WIN32 |
#include<signal.h> |
voidcloseDevices(); |
voidsighandler (int) |
{ |
closeDevices(); |
delete connection; |
exit(0); |
} |
#endif |
voidcloseDevices (void) { |
cerr << endl << 'All devices closed. Exiting ..' << endl; |
} |
voidhandle_tracker(void *, const vrpn_TRACKERCB t) { |
q_xyz_quat_type sensor_f_tracker; // aka hiball_f_tracker or hiball_f_source |
qogl_matrix_type sensor_f_tracker_m; |
qogl_matrix_type eyeball_f_tracker_m; |
q_xyz_quat_type eyeball_f_tracker; |
sensor_f_tracker.xyz[0] = t.pos[0]; |
sensor_f_tracker.xyz[1] = t.pos[1]; |
sensor_f_tracker.xyz[2] = t.pos[2]; |
sensor_f_tracker.quat[0] = t.quat[0]; |
sensor_f_tracker.quat[1] = t.quat[1]; |
sensor_f_tracker.quat[2] = t.quat[2]; |
sensor_f_tracker.quat[3] = t.quat[3]; |
q_xyz_quat_to_ogl_matrix(sensor_f_tracker_m, &sensor_f_tracker); |
// need to compose with the eye_from_sensor matrix here |
qogl_matrix_mult(eyeball_f_tracker_m, soundServer->eye_f_sensor_m, sensor_f_tracker_m); |
q_ogl_matrix_to_xyz_quat(&eyeball_f_tracker, eyeball_f_tracker_m); |
listener.pose.position[0] = eyeball_f_tracker.xyz[0]; |
listener.pose.position[1] = eyeball_f_tracker.xyz[1]; |
listener.pose.position[2] = eyeball_f_tracker.xyz[2]; |
listener.pose.orientation[0] = eyeball_f_tracker.quat[0]; |
listener.pose.orientation[1] = eyeball_f_tracker.quat[1]; |
listener.pose.orientation[2] = eyeball_f_tracker.quat[2]; |
listener.pose.orientation[3] = eyeball_f_tracker.quat[3]; |
got_report = 1; |
return; |
} |
voidUpdateListenerDef() { |
if (listener.pose.orientation != oldlistener.pose.orientation) |
soundServer->changeListenerStatus(listener); |
oldlistener = oldlistener; |
} |
inthandle_dlc (void *, vrpn_HANDLERPARAM p) |
{ |
closeDevices(); |
delete connection; |
exit(0); |
return0; |
} |
voidshutDown (void) |
{ |
closeDevices(); |
#ifdef _WIN32 |
if (soundServer) |
soundServer->shutDown(); |
con->DestroyConsole(); |
#endif |
delete connection; |
exit(0); |
return; |
} |
#ifdef _WIN32 |
voidChangeSoundIdBox(vrpn_int32 action, vrpn_int32 newId) { |
char numbuf[3]; |
sprintf(numbuf,'%d',newId); |
ComboBox_AddString(SoundIdCombo,numbuf); |
} |
voidUpdateDialog(HWND SoundWnd) { |
float posx, posy, posz, orx, ory, orz; |
float lposx, lposy, lposz; |
char buf[15]; |
SetDlgItemText(SoundWnd,ErrorBox,soundServer->GetLastError()); |
soundServer->GetCurrentPosition(CurrentSoundId, &posx, &posy, &posz); |
sprintf(buf,'%4.3f',posx); |
SetDlgItemText(SoundWnd,PosX,buf); |
sprintf(buf,'%4.3f',posy); |
SetDlgItemText(SoundWnd,PosY,buf); |
sprintf(buf,'%4.3f',posz); |
SetDlgItemText(SoundWnd,PosZ,buf); |
soundServer->GetListenerPosition(&lposx, &lposy, &lposz); |
sprintf(buf,'%4.3f',lposx); |
SetDlgItemText(SoundWnd,LPosX,buf); |
sprintf(buf,'%4.3f',lposy); |
SetDlgItemText(SoundWnd,LPosY,buf); |
sprintf(buf,'%4.3f',lposz); |
SetDlgItemText(SoundWnd,LPosZ,buf); |
soundServer->GetCurrentOrientation(CurrentSoundId, &orx, &ory, &orz); |
sprintf(buf,'%4.3f',orx); |
SetDlgItemText(SoundWnd,SorX,buf); |
sprintf(buf,'%4.3f',ory); |
SetDlgItemText(SoundWnd,SorY,buf); |
sprintf(buf,'%4.3f',orz); |
SetDlgItemText(SoundWnd,SorZ,buf); |
soundServer->GetListenerOrientation(&orx, &ory, &orz); |
sprintf(buf,'%4.3f',orx); |
SetDlgItemText(SoundWnd,LorX,buf); |
sprintf(buf,'%4.3f',ory); |
SetDlgItemText(SoundWnd,LorY,buf); |
sprintf(buf,'%4.3f',orz); |
SetDlgItemText(SoundWnd,LorZ,buf); |
sprintf(buf,'%4.3f',sqrt((lposx-posx)*(lposx-posx)+ |
(lposy-posy)*(lposy-posy)+ |
(lposz-posz)*(lposz-posz))); |
SetDlgItemText(SoundWnd,Sound2Listener,buf); |
} |
/****************************************************************************** |
Sound Server Window Section |
*****************************************************************************/ |
LRESULT AILEXPORT SoundServerProc(HWND SoundWnd, UINT message, WPARAM wParam, LPARAM lParam) |
{ |
HWND h; |
floatfmin, fmax; |
char buf[15]; |
switch (message) |
{ |
case WM_SETFOCUS: // deal with the focus in this weird dialog-window |
h=GetWindow(SoundWnd,GW_CHILD); |
while (h) { |
if ((GetWindowLong(h,GWL_STYLE)&0x2f)BS_DEFPUSHBUTTON) { |
SetFocus(h); |
goto found; |
} |
h=GetNextWindow(h,GW_HWNDNEXT); |
} |
SetFocus(GetWindow(SoundWnd,GW_CHILD)); |
found: |
break; |
case WM_CTLCOLORBTN: |
case WM_CTLCOLORSTATIC: |
SetBkColor((HDC)wParam,RGB(192,192,192)); |
return((LRESULT)GetStockObject(LTGRAY_BRUSH)); |
case WM_HSCROLL: |
return0; |
case WM_COMMAND: |
switch (LOWORD(wParam)) |
{ |
case cboxTech: |
if (HIWORD(wParam) CBN_SELENDOK) |
if (ComboBox_GetCurSel(SoundProvideCombo)-1 >= 0) { |
ProviderSet = 1; |
soundServer->setProvider(ComboBox_GetCurSel(SoundProvideCombo)-1); |
} else ProviderSet = 0; |
break; |
case SPEAKERTYPE_BOX: |
if (HIWORD(wParam) CBN_SELENDOK) |
soundServer->setSpeakerType(ComboBox_GetCurSel(SpeakerTypeCombo)+1); |
break; |
case MoreInfoBtn: |
SetDlgItemInt(InfoWnd,VolumeBox,soundServer->GetCurrentVolume(CurrentSoundId),1); |
SetDlgItemInt(InfoWnd,ReplayBox,soundServer->GetCurrentPlaybackRate(CurrentSoundId),1); |
soundServer->GetCurrentDistances(CurrentSoundId, &fmin, &fmax); |
sprintf(buf,'%4.3f',fmin); |
SetDlgItemText(InfoWnd,FMin,buf); |
sprintf(buf,'%4.3f',fmax); |
SetDlgItemText(InfoWnd,FMax,buf); |
if (CurrentSoundId >= 0) |
sprintf(buf,'Sound: %d',CurrentSoundId); |
else |
sprintf(buf,'Sound: none selected'); |
SetWindowText(InfoWnd,buf); |
ShowWindow(InfoWnd,SW_SHOW); |
SetTimer(SoundWnd,DialogTimerId,DIALOG_TIMEOUT,NULL); |
break; |
case SoundIdBox: |
if (HIWORD(wParam) CBN_SELENDOK) { |
if (ComboBox_GetCurSel(SoundProvideCombo)-1 >= 0) { |
CurrentSoundId = ComboBox_GetCurSel(SoundIdCombo); |
UpdateDialog(SoundWnd); |
} |
} |
break; |
case btnStop: |
soundServer->stopAllSounds(); |
break; |
case IDCLOSE: |
ShowWindow(InfoWnd,SW_HIDE); |
break; |
case btnClose: |
DestroyWindow(SoundWnd); |
soundServer->shutDown(); |
break; |
} |
return0; |
case WM_DESTROY: |
PostQuitMessage(0); |
return0; |
case WM_TIMER: |
// if there is no provider set then updating will cause an error! |
if (wParam UpdateTimerId) { |
if (ProviderSet) |
UpdateDialog(SoundWnd); |
} |
elseif (wParam SpinTimerId) { |
char buf[15]; |
spinrate = ((float) spincounter / (float)SPIN_TIMEOUT) * ((float)SPIN_TIMEOUT/1.0); |
spincounter = 0; |
sprintf(buf,'%4.3f',spinrate); |
SetDlgItemText(SoundWnd,SPIN_BOX,buf); |
} |
else { |
ShowWindow(InfoWnd, SW_HIDE); |
KillTimer(SoundWnd, DialogTimerId); |
} |
break; |
} |
returnDefWindowProc(SoundWnd,message,wParam,lParam); |
} |
staticvoidadd_providers() |
{ |
char* name; |
HPROVIDER provider; |
HPROENUM next = HPROENUM_FIRST; |
SoundProvideCombo=GetDlgItem(SoundWnd,cboxTech); |
ComboBox_AddString(SoundProvideCombo,'Choose a provider..'); |
while (AIL_enumerate_3D_providers(&next, &provider, &name)) |
{ |
ComboBox_AddString(SoundProvideCombo,name); |
soundServer->addProvider(provider); |
} |
ComboBox_SetCurSel(SoundProvideCombo,0); |
} |
boolInitSoundServerWindow(HINSTANCE hInstance) |
{ |
WNDCLASS wc; |
BOOL rc; |
staticchar szAppName[] = 'SoundServerWin32'; |
wc.lpszClassName = szAppName; |
wc.lpfnWndProc = (WNDPROC) SoundServerProc; |
wc.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW; |
wc.hInstance = hInstance; |
wc.hIcon = LoadIcon(hInstance,'VRPN'); |
wc.hCursor = LoadCursor(NULL, IDC_ARROW); |
wc.hbrBackground = reinterpret_cast<HBRUSH>(GetStockObject(LTGRAY_BRUSH)); |
wc.cbClsExtra = 0; |
wc.cbWndExtra = DLGWINDOWEXTRA; |
wc.lpszMenuName = NULL; |
rc = RegisterClass(&wc); |
if (!rc) |
returnfalse; |
InitCommonControls(); |
SoundWnd = CreateDialog(hInstance,(LPCSTR)'SOUNDSERVERWIN32',0,NULL); |
DWORD error = GetLastError(); |
if (!SoundWnd) { |
fprintf(stderr,'%ldn',error); |
returnfalse; |
} |
// set a timer to update server stats |
SetTimer(SoundWnd,UpdateTimerId,UPDATE_TIMEOUT,NULL); |
SoundIdCombo=GetDlgItem(SoundWnd, SoundIdBox); |
SpeakerTypeCombo=GetDlgItem(SoundWnd,SPEAKERTYPE_BOX); |
ComboBox_AddString(SpeakerTypeCombo,'Normal stereo speakers'); |
ComboBox_AddString(SpeakerTypeCombo,'Headphones'); |
ComboBox_AddString(SpeakerTypeCombo,'3+ speakers in surround sound'); |
ComboBox_AddString(SpeakerTypeCombo,'Four speakers (quad-channel)'); |
ComboBox_SetCurSel(SpeakerTypeCombo,1); |
ShowWindow(SoundWnd,SW_SHOW); |
InfoWnd = CreateDialog(hInstance,(LPCSTR)'SND_INFO',0,NULL); |
error = GetLastError(); |
if (!InfoWnd) { |
fprintf(stderr,'%ldn',error); |
returnfalse; |
} |
ShowWindow(InfoWnd,SW_HIDE); |
returntrue; |
} |
#endif//Win32 |
/****************************************************************************** |
End Sound Server Window Section |
*****************************************************************************/ |
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) |
{ |
#ifdef _WIN32 |
MSG msg; |
#endif |
char * config_file_name = 'vrpn.cfg'; |
FILE * config_file; |
char * client_name = NULL; |
int client_port; |
int bail_on_error = 1; |
int verbose = 1; |
int auto_quit = 0; |
int realparams = 0; |
int loop = 0; |
int port = vrpn_DEFAULT_LISTEN_PORT_NO; |
#ifdef WIN32 |
WSADATA wsaData; |
int status; |
if ((status = WSAStartup(MAKEWORD(1,1), &wsaData)) != 0) |
{ |
fprintf(stderr, 'WSAStartup failed with %dn', status); |
return(1); |
} |
#else |
#ifdef sgi |
sigset( SIGINT, sighandler ); |
sigset( SIGKILL, sighandler ); |
sigset( SIGTERM, sighandler ); |
sigset( SIGPIPE, sighandler ); |
#else |
signal( SIGINT, sighandler ); |
signal( SIGKILL, sighandler ); |
signal( SIGTERM, sighandler ); |
signal( SIGPIPE, sighandler ); |
#endif// sgi |
#endif |
con = newCConsole(TRUE); |
con->RedirectToConsole(0); |
// TCH 2 Nov 99 - debugging code |
vrpn_Connection * trackerCon; |
USE_TRACKER = 0; |
// Need to have a global pointer to it so we can shut it down |
// in the signal handler (so we can close any open logfiles.) |
connection = newvrpn_Synchronized_Connection (port); |
// Open the configuration file |
if (verbose) printf('Reading from config file %sn', config_file_name); |
if ( (config_file = fopen(config_file_name, 'r')) NULL) |
{ |
perror('Cannot open config file'); |
fprintf(stderr,' (filename %s)n', config_file_name); |
return -1; |
} |
// Read the configuration file, creating a device for each entry. |
// Each entry is on one line, which starts with the name of the |
// class of the object that is to be created. |
// If we fail to open a certain device, print a message and decide |
// whether we should bail. |
{ |
char line[512]; // Line read from the input file |
char *pch; |
char scrap[512], s2[512]; |
// Read lines from the file until we run out |
while ( fgets(line, sizeof(line), config_file) != NULL ) |
{ |
// Make sure the line wasn't too long |
if (strlen(line) >= sizeof(line)-1) |
{ |
fprintf(stderr,'Line too long in config file: %sn',line); |
if (bail_on_error) { return -1; } |
else { continue; } // Skip this line |
} |
if ((strlen(line)<3)||(line[0]'#')) |
{ |
// comment or empty line -- ignore |
continue; |
} |
// copy for strtok work |
strncpy(scrap, line, sizeof(line) - 1); |
// Figure out the device from the name and handle appropriately |
// WARNING: SUBSTRINGS WILL MATCH THE EARLIER STRING, SO |
// ADD AN EMPTY SPACE TO THE END OF STATIC STRINGS!!!! |
// #define isit(s) !strncmp(line,s,strlen(s)) |
#defineisit(s) !strcmp(pch=strtok(scrap,'t'),s) |
#definenext() pch += strlen(pch) + 1 |
#ifdef _WIN32 |
if(isit('vrpn_Sound_Server')) |
{ |
fprintf(stderr,'%sn',pch); |
next(); |
fprintf(stderr,'%sn',pch); |
if (sscanf(pch,'%511st%dt%511st%511s',s2,&USE_TRACKER,tracker_name, tracker_device) != 4) |
{ |
fprintf(stderr,'Bad vrpn_Server_Sound line: %sn',line); |
if (bail_on_error) |
{ |
return -1; |
} |
else |
{ |
continue; |
} // Skip this line |
} |
fprintf(stderr,'okn'); |
if (InitSoundServerWindow(hInstance) != true) |
fprintf(stderr, 'Didn't open window!n'); |
soundServer = NULL; |
soundServer = newvrpn_Sound_Server_Miles(s2, connection); |
if (soundServer NULL) |
fprintf(stderr,'Can't create sound servern'); |
add_providers(); |
} |
#endif |
} |
} |
// Close the configuration file |
fclose(config_file); |
// Open remote tracker if we are to use one |
if (USE_TRACKER) { |
char newname[1024]; |
sprintf(newname,'%s@%s',(constchar*)tracker_device, (constchar*)tracker_name); |
fprintf(stderr,'Using tracker: %sn',newname); |
trackerCon = vrpn_get_connection_by_name(tracker_name); |
tracker_connection = newvrpn_Tracker_Remote((constchar *) newname); |
//if (tracker_connection!=NULL) fprintf(stderr,'Connection set up correctlyn'); |
int val = tracker_connection->register_change_handler(NULL,handle_tracker); |
fprintf(stderr,' Handler set up %dn',val); |
if (trackerCon->doing_okay()) { |
fprintf(stderr, 'TC OK.n'); |
} else { |
fprintf(stderr, 'TC Not OK.n'); |
} |
} |
elsefprintf(stderr,'Not using trackern'); |
// Open the Forwarder Server |
forwarderServer = newvrpn_Forwarder_Server (connection); |
loop = 0; |
if (auto_quit) |
{ |
int dlc_m_id = connection->register_message_type( |
vrpn_dropped_last_connection); |
connection->register_handler(dlc_m_id, handle_dlc, NULL); |
} |
if (client_name) |
{ |
fprintf(stderr, 'vrpn_serv: connecting to client: %s:%dn', |
client_name, client_port); |
if (connection->connect_to_client(client_name, client_port)) |
{ |
fprintf(stderr, 'server: could not connect to client %s:%dn', client_name, client_port); |
shutDown(); |
} |
} |
// ******************************************************************** |
// ** ** |
// ** MAIN LOOP ** |
// ** ** |
// ******************************************************************** |
// SetTimer(SoundWnd,SpinTimerId,SPIN_TIMEOUT,NULL); |
// spincounter = 0; |
while (1) { |
// spincounter++; |
GetMessage(&msg, 0, 0, 0); |
if (!IsDialogMessage(SoundWnd,&msg)) |
{ |
TranslateMessage(&msg); |
DispatchMessage(&msg); |
} |
// Let the sound server do it's thing |
if (USE_TRACKER && trackerCon->doing_okay() && ProviderSet) |
UpdateListenerDef(); |
soundServer->mainloop(); |
if (soundServer->noSounds()) { |
EnableWindow(SoundProvideCombo, true); |
if (!blanked) { |
ComboBox_ResetContent(SoundIdCombo); |
Edit_SetText(GetDlgItem(SoundWnd, ErrorBox), ''); |
Edit_SetText(GetDlgItem(SoundWnd, LPosX), ''); |
Edit_SetText(GetDlgItem(SoundWnd, LPosY), ''); |
Edit_SetText(GetDlgItem(SoundWnd, LPosZ), ''); |
Edit_SetText(GetDlgItem(SoundWnd, SorX), ''); |
Edit_SetText(GetDlgItem(SoundWnd, SorY), ''); |
Edit_SetText(GetDlgItem(SoundWnd, SorZ), ''); |
Edit_SetText(GetDlgItem(SoundWnd, LorX), ''); |
Edit_SetText(GetDlgItem(SoundWnd, LorY), ''); |
Edit_SetText(GetDlgItem(SoundWnd, LorZ), ''); |
Edit_SetText(GetDlgItem(SoundWnd, PosX), ''); |
Edit_SetText(GetDlgItem(SoundWnd, PosY), ''); |
Edit_SetText(GetDlgItem(SoundWnd, PosZ), ''); |
blanked = 1; |
} |
} |
else { |
EnableWindow(SoundProvideCombo, false); |
blanked = 0; |
} |
// ensure we get a new report! |
if (USE_TRACKER) { |
tracker_connection->mainloop(); |
got_report = 0; |
if (trackerCon->doing_okay()) |
while (!got_report) |
tracker_connection->mainloop(); |
} |
// Send and receive all messages |
connection->mainloop(); |
if (!connection->doing_okay()) shutDown(); |
// Handle forwarding requests; send messages |
// on auxiliary connections |
forwarderServer->mainloop(); |
} |
return0; |
} |
Copy lines Copy permalink
I have a program that is supposed to display a count that increments every second. The counter is in a separate thread. I call WindowUpdate when a change has occurred but the count in the window does not update unless I hover the mouse pointer over the window or resize the window. I have tried InvalidateRect and RedrawWindow but they don't work either.
Why won't the counter updates display?
TomTom
3 Answers
(Disclaimer: This answer was written by the seat of my pants. Please correct me if I made an error somewhere.)
You are not doing what you are trying to do properly.
First, as Jonathan Potter has noted,
UpdateWindow()
by itself will not update the window unless it has an invalid region. The InvalidateRect()
call will invalidate a rectangle. So you need both....but in reality you don't really want to call
UpdateWindow()
directly, because it bypasses the Windows drawing model. After calling InvalidateRect()
, if there are no pending messages next time GetMessage()
is called, and Windows itself decides it's time to refresh the screen contents with new data, you'll get a WM_PAINT
message. The system knows when it's best to paint; you'll make your life easier by using it. Only use UpdateWindow()
when it is vital that you want the window to redraw right now.(The point of the invalid region is also due to the Windows drawing model: drawing can be very expensive, so Windows tries to optimize drawing by only redrawing what is needed. Invalidate only the part of your window that needs to be updated, and you'll get better performance.)
But there is a deeper issue: you do not implement multithreading properly.
Your worker thread is generating data every second and overwriting a shared structure. Your window is reading from that shared structure. There is nothing in place to ensure that only one thread is accessing that shared structure at a time. As a result, you'll wind up with ptential mixed state if your worker thread ever grows from a simple integer to a large and complex data structure.
You need to synchronize your data accesses. Communicate between threads.
How? The obvious method is to use a synchronization object, like a mutex. And you can totally do that.
But there's a better way: since one of your threads has a window, just use a window message! Window messages sent using
SendMessage()
will be received on the window's thread (with the calling thread blocked); messages posted using PostMessage()
will be placed on the window's thread's message queue.Plus you can pass not one but two pieces of information in that message! Encyclopedia oxford de filosofia pdf download. Both
wParam
and lParam
are pointer-sized integers, so just stuff a pointer in one of them and use SendMessage()
or PostMessage()
!But what message do you use? Every* message in the range [
WM_USER
, WM_APP
) is available to the window class to decide what to use it for, and every message in the range [WM_APP
, 0xC000
) is for the application to decide what to use it for. So just pick one and use it!Just remember how
SendMessage()
and PostMessage()
work. If the data is allocated on the heap, and each piece of data is allocated separately, then it doesn't matter; if the data is a pointer to a local variable, SendMessage()
is the correct answer; if the data is a numeric value that can fit in a pointer-sized integer, either will work. If you need a response, your worker thread will either need a window of its own or the use of SendMessage()
to get a value back from the window.There are lots of ways to do this. It all depends on what data you need to communicate. As the Go programming language's designers say (altered somewhat): don't communicate by sharing data; share data by communicating.
*until you call
andlabsandlabsIsDialogMessage()
, at which point you lose WM_USER
and WM_USER + 1
(and possibly WM_USER + 2
as well?). But that's only three messages out of over 32,000.8,77011 gold badge1515 silver badges4141 bronze badges
Your problem is in your message loop:
GetMessage is synchronous, and doesn't return until a message is available for retrieval. The code sits there, waiting for a message to arrive most of the time, and will not evaluate pPayload->Updated. Consequently, no updates happen, until a message arrives. But even then, nothing happens, since UpdateWindow sends 'a WM_PAINT message to the window if the window's update region is not empty.'
UpdateWindow
by itself doesn't do anything useful.1)You could solve this by reworking your message loop. The following issues need to be addressed:
- A call to InvalidateRect must precede the call to
UpdateWindow
. - Access to the shared data must be synchronized.
- The message loop needs to terminate, when
GetMessage
returns 0 or -1.
None of this is necessary, though. You don't need a background thread. A single-threaded application will do just fine. Just use a Timer to trigger updates to the stored value, and the visual representation. Here is a rough sketch for a window procedure implementing a timer-based solution:
A few notes on the implementation:
- The timer uses a fixed time-out value of 1000 milliseconds. This will occasionally skip an update. The error doesn't accumulate, though, as the Counter is re-evaluated whenever the timer expires. If you more steady updates, calculate the remaining time-out value based on the Counter, startTime, and currTime.
- Counter is the variable that holds the current time in seconds. It is a replacement for
TRANSFER::Counter
, that needs to be accessible from both the window procedure and the rendering code.
27.3k66 gold badges5050 silver badges109109 bronze badges
i marked my alterations with comment
milevyomilevyo<<updated
instead of setting flag Updated, i changed it to be hWnd, and then update the window instantly and directly from the thread1,88911 gold badge77 silver badges1717 bronze badges