AuthorwareXtras
asked on
Create Tool Tips from DLL
I am trying to create a DLL that will allow another development system (not Delphi) to set up tool tips for areas of the screen. The other development system does not have tools tips capabilities but is capable of calling functions in Delphi DLLs, hence the approach I am making.
What I have tried so far is to create a borderless form from the DLL specifying it as fsStayOnTop and creating it at a specific position and width and height. I have turned on tool tips for the form.
Calling this function in the DLL creates the form and the tool tips work. If this approach is the correct one though, I need a way to make the form transparent so that it is just the 'region' it occupies on screen that forces the tool tip to appear. I am not sure though, if the tool tips will still appear if the form is transparent??
Alternatively I need some other way of specifying a rectangular (or even any other shape if that is possible) area on screen where a tool tip can be made to appear all created from a DLL.
Thanks
What I have tried so far is to create a borderless form from the DLL specifying it as fsStayOnTop and creating it at a specific position and width and height. I have turned on tool tips for the form.
Calling this function in the DLL creates the form and the tool tips work. If this approach is the correct one though, I need a way to make the form transparent so that it is just the 'region' it occupies on screen that forces the tool tip to appear. I am not sure though, if the tool tips will still appear if the form is transparent??
Alternatively I need some other way of specifying a rectangular (or even any other shape if that is possible) area on screen where a tool tip can be made to appear all created from a DLL.
Thanks
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
In essence I want to set up tool tipd from a dll over areas of another application that calls the DLL. The other application (not Delphi) does not have the ability to natively have tool tips.
I have got a lot of the way there with the link provided by atul_parmar and I can set arbitrary areas of the application to have tool tips, but if I set a region over a button in the other application, then it seems the mouse over the button is masking the tool tip area and the tool tip is not showing. This is counter productive unfortunately as it is over buttons and the like that I want to set the tool tips.
I have got a lot of the way there with the link provided by atul_parmar and I can set arbitrary areas of the application to have tool tips, but if I set a region over a button in the other application, then it seems the mouse over the button is masking the tool tip area and the tool tip is not showing. This is counter productive unfortunately as it is over buttons and the like that I want to set the tool tips.
Since this is for a single program window that is calling this DLL, , ,you can do what the delphi THints does, and have an in process mouse hook, which can solve the problem of program areas that have a child control in them, since it gets all mouse messages for all windows in that program
ASKER
Sounds good, do you have any pointers for that?
I have been busy today, but I will look into it soon, maybe tomorrow, ,
I guess that you know that a system "Tool Tip" can use the child controls (buttons) as tool tip display, you just have to set the child control handle and tip string into the tool tip window
I guess that you know that a system "Tool Tip" can use the child controls (buttons) as tool tip display, you just have to set the child control handle and tip string into the tool tip window
OK, , First, I take it from your comments that this will be implemented in a program that is not Delphi and has some limited programming methods. So I am at a Loss as to what to do for coding the DLL, since I have NO IDEA what can be called or what variable types are available, and I do not want to ask about that, I do not really care much. . . . . .
I think the system tool tip window will be a much more easy (shorter code) way, instead of trying the in-process mouse hook like the THints uses. Here is some code for a Library that will find the top window in that thread that has buttons and add a system "Tool Tip" for all of the buttons on that window.
I have made it so the FindThreadWindows procedure is called when this loads, , and this works for Dynamic loading of the DLL, but if it is Static loading, it do not think it will work, since no windows are created at that time. However you can call the FindThreadWindows procedure after window creation.
Also all of the MessageBox functions are for debugging and can be cut out.
Code for DllTips library below -->
library DllTips;
uses
Windows, Messages, CommCtrl, SysUtils;
{$R *.RES}
type
TAryHnds = Array of THandle;
const
TTS_BALLOON = $40;
TTM_SETTITLE = (WM_USER + 32);
iUseDef: Integer = Integer(CW_USEDEFAULT);
var
MInstance: THandle = 0;
hMainWnd: THandle = 0;
hToolTip: THandle = 0;
aryButtons: TAryHnds;
function GetWindows(hWnd: Integer; var aryWnds: TAryHnds): BOOL; StdCall;
begin
Result := True;
{This will take the TAryHnds passed to it
and add all window handles returned}
SetLength(aryWnds, Succ(Length(aryWnds)));
// make Array larger and add window handle
aryWnds[High(aryWnds)] := hWnd;
end;
procedure FindThreadWindows;
var
aryWnds, aryChild: TAryHnds;
w, c: Integer;
cName: Array[Byte] of Char;
begin
// first get all of the top level windows in thread
EnumThreadWindows(GetCurre ntThreadId , @GetWindows, Integer(@aryWnds));
if Length(aryWnds) > 0 then
begin
MInstance := GetWindowLong(aryWnds[0], GWL_HINSTANCE);
// This MInstance is used in the createWindowEx function
if MInstance <> 0 then
hMainWnd := aryWnds[0] else Exit;
end else
Exit;
// the messageBox function below is for DeBugging and can be left out
MessageBox(aryWnds[0], PChar('aryWnds '+IntToStr(Length(aryWnds) )), 'Length Array', MB_ICONINFORMATION);
// go through all top level windows until some buttons are found
for w := 0 to High(aryWnds) do
begin
EnumChildWindows(aryWnds[w ], @GetWindows, Integer(@aryChild));
if Length(aryChild) < 1 then
MessageBox(aryWnds[0], 'No Child Windows Found', 'No Children', MB_ICONINFORMATION)
else
begin
for c := 0 to High(aryChild) do
if GetClassName(aryChild[c], @cName, SizeOf(cName)) > 0 then
begin
StrUpper(@cName);
// search for "BUTTON" controls, if to use a Delphi VCL app then use TBUTTON instead of BUTTON
if StrComp(@cName, 'BUTTON') = 0 then
begin
SetLength(aryButtons, Succ(Length(aryButtons)));
aryButtons[High(aryButtons )] := aryChild[c];
end;
end;
if Length(aryButtons) > 0 then
begin
hMainWnd := aryWnds[w];
Break;
end;
end;
end;
// the messageBox function below is for DeBugging and can be left out
MessageBox(aryWnds[0], PChar('aryButton '+IntToStr(Length(aryButto ns))), 'Length Array', MB_ICONINFORMATION);
// if you like you can call MakeButtonToolTip here for a single function call to ToolTip
end;
function GetMainWnd(out hIns, hBut: Cardinal): THandle;
begin
// this is a DeBugging function to verify that there are some Buttons to Tool Tip
Result := hMainWnd;
hIns := MInstance;
if length(aryButtons) > 0 then
hBut := aryButtons[0]
else
hBut := 0;;
end;
function MakeButtonToolTip: Cardinal;
var
i: Integer;
InfoTool: TToolInfo;
cText: Array[Byte] of Char;
begin
// this function creates the Tool Tip and adds the buttons
Result := 0;
if (hMainWnd = 0) or (Length(aryButtons) < 1) or (MInstance = 0) then Exit;
hToolTip := CreateWindowEx(0, TOOLTIPS_CLASS, nil,
{TTS_BALLOON} 0, iUseDef, iUseDef,
iUseDef, iUseDef, hMainWnd, 0, MInstance, nil);
if hToolTip = 0 then Exit;
// code below adds the buttons to toolTip
for i := 0 to High(aryButtons) do
if GetClientRect(aryButtons[i ], InfoTool.Rect) then
begin
GetWindowText(aryButtons[i ], @cText, SizeOf(cText));
with InfoTool do
begin
cbSize := SizeOf(toolInfo);
uId := 0;
hwnd := aryButtons[i];
uFlags := TTF_SUBCLASS;
hInst := 0;
if lStrLen(@cText) > 0 then
lpszText := @cText
else
lpszText := 'No Text';
if SendMessage(hToolTip, TTM_ADDTOOL, 0, Integer(@InfoTool)) <> 0 then
Result := hToolTip;
end;
end;
if Result = 0 then
begin
DestroyWindow(hToolTip);
hToolTip := 0;
end;
end;
procedure LibraryProc(Reason: Cardinal);
begin
if (Reason = Dll_Process_Detach) then
DestroyWindow(hToolTip);
end;
exports
FindThreadWindows,
GetMainWnd,
MakeButtonToolTip;
begin
DLLProc := @LibraryProc;
FindThreadWindows;
// I call this when this DLL loads to find the Buttons
// but you can leave this out and call it latter
end.
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
below is the code for a Delphi VCL program that I tested it in.
If you use Delphi VCL the be sure to change Library code above -
if StrComp(@cName, 'BUTTON') = 0 then
to
if StrComp(@cName, 'TBUTTON') = 0 then
// VCL button click procedure below
procedure TForm1.but_LoadTipDllClick (Sender: TObject);
var
hLibTip, hMain, hInst, hButton, hToolTip: THandle;
GetMainWnd1: function(out hIns, hBut: Cardinal): THandle;
MakeButtonToolTip: function: Cardinal;
// FindThreadWindows: procedure;
begin
hLibTip := LoadLibrary('DllTips.dll') ;
if hLibTip = 0 then
begin
ShowMessage('Did not load the DllTips.DLL');
Exit;
end;
//ShowMessage('Loaded the DllTips.DLL');
@GetMainWnd1 := GetProcAddress(hLibTip, 'GetMainWnd');
if @GetMainWnd1 = nil then
begin
FreeLibrary(hLibTip);
ShowMessage('Did not load the GetMainWnd function');
Exit;
end;
hMain := GetMainWnd1(hInst, hButton); // debugging function to see if DLL found anything
ShowMessage('hMain '+IntToStr(hMain)+' hInst '+IntToStr(hInst)+' hButton '+IntToStr(hButton));
if hMain <> Handle then
begin
FreeLibrary(hLibTip);
ShowMessage('hMain is Not Equal To This Form Handle');
Exit;
end;
if hButton <> 0 then
begin
@MakeButtonToolTip := GetProcAddress(hLibTip, 'MakeButtonToolTip');
if @MakeButtonToolTip = nil then
begin
FreeLibrary(hLibTip);
ShowMessage('Did not load the MakeButtonToolTip function');
Exit;
end;
hToolTip := MakeButtonToolTip;
if hToolTip = 0 Then
begin
FreeLibrary(hLibTip);
ShowMessage('hToolTip is Zero ');
Exit;
end;
end;
end;
== = = = = = = = = = = = = = = = = = = = = =
this works for me, ask questions if you need more info
I think the system tool tip window will be a much more easy (shorter code) way, instead of trying the in-process mouse hook like the THints uses. Here is some code for a Library that will find the top window in that thread that has buttons and add a system "Tool Tip" for all of the buttons on that window.
I have made it so the FindThreadWindows procedure is called when this loads, , and this works for Dynamic loading of the DLL, but if it is Static loading, it do not think it will work, since no windows are created at that time. However you can call the FindThreadWindows procedure after window creation.
Also all of the MessageBox functions are for debugging and can be cut out.
Code for DllTips library below -->
library DllTips;
uses
Windows, Messages, CommCtrl, SysUtils;
{$R *.RES}
type
TAryHnds = Array of THandle;
const
TTS_BALLOON = $40;
TTM_SETTITLE = (WM_USER + 32);
iUseDef: Integer = Integer(CW_USEDEFAULT);
var
MInstance: THandle = 0;
hMainWnd: THandle = 0;
hToolTip: THandle = 0;
aryButtons: TAryHnds;
function GetWindows(hWnd: Integer; var aryWnds: TAryHnds): BOOL; StdCall;
begin
Result := True;
{This will take the TAryHnds passed to it
and add all window handles returned}
SetLength(aryWnds, Succ(Length(aryWnds)));
// make Array larger and add window handle
aryWnds[High(aryWnds)] := hWnd;
end;
procedure FindThreadWindows;
var
aryWnds, aryChild: TAryHnds;
w, c: Integer;
cName: Array[Byte] of Char;
begin
// first get all of the top level windows in thread
EnumThreadWindows(GetCurre
if Length(aryWnds) > 0 then
begin
MInstance := GetWindowLong(aryWnds[0], GWL_HINSTANCE);
// This MInstance is used in the createWindowEx function
if MInstance <> 0 then
hMainWnd := aryWnds[0] else Exit;
end else
Exit;
// the messageBox function below is for DeBugging and can be left out
MessageBox(aryWnds[0], PChar('aryWnds '+IntToStr(Length(aryWnds)
// go through all top level windows until some buttons are found
for w := 0 to High(aryWnds) do
begin
EnumChildWindows(aryWnds[w
if Length(aryChild) < 1 then
MessageBox(aryWnds[0], 'No Child Windows Found', 'No Children', MB_ICONINFORMATION)
else
begin
for c := 0 to High(aryChild) do
if GetClassName(aryChild[c], @cName, SizeOf(cName)) > 0 then
begin
StrUpper(@cName);
// search for "BUTTON" controls, if to use a Delphi VCL app then use TBUTTON instead of BUTTON
if StrComp(@cName, 'BUTTON') = 0 then
begin
SetLength(aryButtons, Succ(Length(aryButtons)));
aryButtons[High(aryButtons
end;
end;
if Length(aryButtons) > 0 then
begin
hMainWnd := aryWnds[w];
Break;
end;
end;
end;
// the messageBox function below is for DeBugging and can be left out
MessageBox(aryWnds[0], PChar('aryButton '+IntToStr(Length(aryButto
// if you like you can call MakeButtonToolTip here for a single function call to ToolTip
end;
function GetMainWnd(out hIns, hBut: Cardinal): THandle;
begin
// this is a DeBugging function to verify that there are some Buttons to Tool Tip
Result := hMainWnd;
hIns := MInstance;
if length(aryButtons) > 0 then
hBut := aryButtons[0]
else
hBut := 0;;
end;
function MakeButtonToolTip: Cardinal;
var
i: Integer;
InfoTool: TToolInfo;
cText: Array[Byte] of Char;
begin
// this function creates the Tool Tip and adds the buttons
Result := 0;
if (hMainWnd = 0) or (Length(aryButtons) < 1) or (MInstance = 0) then Exit;
hToolTip := CreateWindowEx(0, TOOLTIPS_CLASS, nil,
{TTS_BALLOON} 0, iUseDef, iUseDef,
iUseDef, iUseDef, hMainWnd, 0, MInstance, nil);
if hToolTip = 0 then Exit;
// code below adds the buttons to toolTip
for i := 0 to High(aryButtons) do
if GetClientRect(aryButtons[i
begin
GetWindowText(aryButtons[i
with InfoTool do
begin
cbSize := SizeOf(toolInfo);
uId := 0;
hwnd := aryButtons[i];
uFlags := TTF_SUBCLASS;
hInst := 0;
if lStrLen(@cText) > 0 then
lpszText := @cText
else
lpszText := 'No Text';
if SendMessage(hToolTip, TTM_ADDTOOL, 0, Integer(@InfoTool)) <> 0 then
Result := hToolTip;
end;
end;
if Result = 0 then
begin
DestroyWindow(hToolTip);
hToolTip := 0;
end;
end;
procedure LibraryProc(Reason: Cardinal);
begin
if (Reason = Dll_Process_Detach) then
DestroyWindow(hToolTip);
end;
exports
FindThreadWindows,
GetMainWnd,
MakeButtonToolTip;
begin
DLLProc := @LibraryProc;
FindThreadWindows;
// I call this when this DLL loads to find the Buttons
// but you can leave this out and call it latter
end.
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
below is the code for a Delphi VCL program that I tested it in.
If you use Delphi VCL the be sure to change Library code above -
if StrComp(@cName, 'BUTTON') = 0 then
to
if StrComp(@cName, 'TBUTTON') = 0 then
// VCL button click procedure below
procedure TForm1.but_LoadTipDllClick
var
hLibTip, hMain, hInst, hButton, hToolTip: THandle;
GetMainWnd1: function(out hIns, hBut: Cardinal): THandle;
MakeButtonToolTip: function: Cardinal;
// FindThreadWindows: procedure;
begin
hLibTip := LoadLibrary('DllTips.dll')
if hLibTip = 0 then
begin
ShowMessage('Did not load the DllTips.DLL');
Exit;
end;
//ShowMessage('Loaded the DllTips.DLL');
@GetMainWnd1 := GetProcAddress(hLibTip, 'GetMainWnd');
if @GetMainWnd1 = nil then
begin
FreeLibrary(hLibTip);
ShowMessage('Did not load the GetMainWnd function');
Exit;
end;
hMain := GetMainWnd1(hInst, hButton); // debugging function to see if DLL found anything
ShowMessage('hMain '+IntToStr(hMain)+' hInst '+IntToStr(hInst)+' hButton '+IntToStr(hButton));
if hMain <> Handle then
begin
FreeLibrary(hLibTip);
ShowMessage('hMain is Not Equal To This Form Handle');
Exit;
end;
if hButton <> 0 then
begin
@MakeButtonToolTip := GetProcAddress(hLibTip, 'MakeButtonToolTip');
if @MakeButtonToolTip = nil then
begin
FreeLibrary(hLibTip);
ShowMessage('Did not load the MakeButtonToolTip function');
Exit;
end;
hToolTip := MakeButtonToolTip;
if hToolTip = 0 Then
begin
FreeLibrary(hLibTip);
ShowMessage('hToolTip is Zero ');
Exit;
end;
end;
end;
== = = = = = = = = = = = = = = = = = = = = =
this works for me, ask questions if you need more info
ASKER
Thaks for your efforts Slick812, but I need to be able to specify more than just buttons and so I think that specyfying a position on screen is the way to go. I am almost there from the link that atul_parmar gave me. I can get tools tips to appear in the correct position on screen, but the behaviour over 'buttons' is not 100% predictable. Some times the mouse needs to move very close to the centre of the button before the tip will show, other times not (on the same button).
When I am referring to buttons here, they may not be buttons in the true Windows sense as they are not created directly with a true progamming language, but in Macromedia Authorware.
When I am referring to buttons here, they may not be buttons in the true Windows sense as they are not created directly with a true progamming language, but in Macromedia Authorware.
OK, I have little Idea what you may be dealing with here, as far as the Windows API of your Macromedia Authorware program. Somehow I thought you were dealing with system controls (like buttons, edits), how ever, I used buttons just for a "How To example", but you can place it on any windowed control, if there are ant windowed controls, If you have windowed controls you can just get the window rectangle and see if it is were you want a tip, , I guess you did not understand my code or even try it . . . .
But if they are visual controls (like TSpeedButton), you should NOT have any problems like you are describing with your "buttons" and the "area" Tool tip not working, , maybe I can do an inprocess mouse hook in DLL, , if I have time I might try
But if they are visual controls (like TSpeedButton), you should NOT have any problems like you are describing with your "buttons" and the "area" Tool tip not working, , maybe I can do an inprocess mouse hook in DLL, , if I have time I might try
ASKER
I have not tried it, but I will give it a go. I am not sure how Authorware instantiates buttons and whether they are buttons in the true Windows sense. I will need to specify tool tips in different areas though and it will not necessarily be just buttons. I think therefore that setting a tool tip over an area of the application window is therefore the best approach.
Thanks again for your efforts.
Thanks again for your efforts.
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Although I didn't explain myself very well, when I said " setting a tool tip over an area of the application window is therefore the best approach " I do actually mean setting it in a rectangle as you are here.
I am unable to load a library in the context you mean from Authorware. Authorware needs at least one exported function that it can call to use the DLL. I will see if I can modify your answer to make it work.
Thanks again for your comprehensive answer.
I am unable to load a library in the context you mean from Authorware. Authorware needs at least one exported function that it can call to use the DLL. I will see if I can modify your answer to make it work.
Thanks again for your comprehensive answer.
ASKER
Firther to my last I built the DLL as provided and loaded it in Delphi and it works as advertised, so I know your code is good.
I then tried commenting out this section
procedure LibraryProc(Reason: Cardinal);
begin
if (Reason = Dll_Process_Detach) then
begin
KillTimer(hMain, ID_Hover);
KillTimer(hMain, ID_Wait);
DestroyWindow(hHintWin);
UnRegisterClass(pClassName ,MInstance );
UnHookWindowsHookEx(hHook) ;
end;
end;
begin
DLLProc := @LibraryProc;
GetWindowAndHook;
end.
and then creating a single function that is exported that simply calls GetWindowAndHook;
The function is visible to Authorware and I can call it, I get no error messages, but I also get no tool tips. I am not sure what is going wrong.
I then tried commenting out this section
procedure LibraryProc(Reason: Cardinal);
begin
if (Reason = Dll_Process_Detach) then
begin
KillTimer(hMain, ID_Hover);
KillTimer(hMain, ID_Wait);
DestroyWindow(hHintWin);
UnRegisterClass(pClassName
UnHookWindowsHookEx(hHook)
end;
end;
begin
DLLProc := @LibraryProc;
GetWindowAndHook;
end.
and then creating a single function that is exported that simply calls GetWindowAndHook;
The function is visible to Authorware and I can call it, I get no error messages, but I also get no tool tips. I am not sure what is going wrong.
I can not tell much of anything about why it doees not seem to work? ?
I would first try to keep the code like it is, (more or less, , maybe leave out the UnRegisterClass(pClassName ,MInstance ); , UnHookWindowsHookEx(hHook) ;) if it unloads the DLL right after the call to the DLL function?
and add a do-nothing function to export, just to have a function to call, , however, if you load this DLL before any windows are created (during program startup) then it will find no windows and not do anything. . .
If you still have problemms, then you should add the "MessageBox( )" functions at debug positions to see what your DLL is doing in the GetWindowAndHook procedure , , and the values of important program information, variables. You can make judgements about what is wrong by the variable data values.
I would first try to keep the code like it is, (more or less, , maybe leave out the UnRegisterClass(pClassName
and add a do-nothing function to export, just to have a function to call, , however, if you load this DLL before any windows are created (during program startup) then it will find no windows and not do anything. . .
If you still have problemms, then you should add the "MessageBox( )" functions at debug positions to see what your DLL is doing in the GetWindowAndHook procedure , , and the values of important program information, variables. You can make judgements about what is wrong by the variable data values.
???
I am not sure about the Authorware loading and unloading of a called DLL function, , BUT, if the DLL function is called, , , and then, as soon as the execution of the function is finished, the DLL is Unloaded, my DLL will Not work, it is required to be in accesable memory (loaded as DLL) for as long as the rectangle detection is needed, if the DLL is unloaded, there is no memory for the system to access the HookFunc( ) function or the timer function
I am not sure about the Authorware loading and unloading of a called DLL function, , BUT, if the DLL function is called, , , and then, as soon as the execution of the function is finished, the DLL is Unloaded, my DLL will Not work, it is required to be in accesable memory (loaded as DLL) for as long as the rectangle detection is needed, if the DLL is unloaded, there is no memory for the system to access the HookFunc( ) function or the timer function
ASKER
You may be right, Thanks for your patience and help.
I thought about the loading and unloading of DLLs, as I remember, the Load of DLL is associated with the thread that calls LoadLibrary. .
so I wondered if I loaded a DLL and called a function in that DLL that would load the DllHint.dll, if the DllHint.dll would be unloaded when that DLL was unloaded. . . I tried it and the DllHint.dll was Not unloaded, and continued to work, even when the StartHint dll was unloaded.
here is the code for the library that loads the DllHint.dll, the code in the DllHint.dll is unchanged from what I posted here. . .
library StartHint;
uses
Windows;
{$R *.RES}
var
hLibH: THandle;
function LoadHints: Boolean;
begin
hLibH := LoadLibrary('DllHint.dll') ;
Result := hLibH <> 0;
end;
exports
LoadHints;
begin
end.
= = = = = = = = = = = = = = = = = = =
I hope this may help, but you seem to have closed this question, , so I guess you have already found a solution.
so I wondered if I loaded a DLL and called a function in that DLL that would load the DllHint.dll, if the DllHint.dll would be unloaded when that DLL was unloaded. . . I tried it and the DllHint.dll was Not unloaded, and continued to work, even when the StartHint dll was unloaded.
here is the code for the library that loads the DllHint.dll, the code in the DllHint.dll is unchanged from what I posted here. . .
library StartHint;
uses
Windows;
{$R *.RES}
var
hLibH: THandle;
function LoadHints: Boolean;
begin
hLibH := LoadLibrary('DllHint.dll')
Result := hLibH <> 0;
end;
exports
LoadHints;
begin
end.
= = = = = = = = = = = = = = = = = = =
I hope this may help, but you seem to have closed this question, , so I guess you have already found a solution.
ASKER
I have actually managed to get the DLL to load now by exposing a function that just calls GetWindowAndHook. As before I have removed this section:
procedure LibraryProc(Reason: Cardinal);
begin
if (Reason = Dll_Process_Detach) then
begin
KillTimer(hMain, ID_Hover);
KillTimer(hMain, ID_Wait);
DestroyWindow(hHintWin);
UnRegisterClass(pClassName ,MInstance );
UnHookWindowsHookEx(hHook) ;
end;
end;
begin
DLLProc := @LibraryProc;
GetWindowAndHook;
end.
I am now trying to modify your example to be able to supply the coordinates and text of the hint at run time. Not managed to achieve this at the minute as I try and unpick your example. I will specify one tool tip at a time and call the dll as many times a needed to set up the required number of tooltips. I plan to call the exposed function (the one that calls GetWindowAndHook currently) and supply parameters for the tooltip. Do you see an issue with that approach?
procedure LibraryProc(Reason: Cardinal);
begin
if (Reason = Dll_Process_Detach) then
begin
KillTimer(hMain, ID_Hover);
KillTimer(hMain, ID_Wait);
DestroyWindow(hHintWin);
UnRegisterClass(pClassName
UnHookWindowsHookEx(hHook)
end;
end;
begin
DLLProc := @LibraryProc;
GetWindowAndHook;
end.
I am now trying to modify your example to be able to supply the coordinates and text of the hint at run time. Not managed to achieve this at the minute as I try and unpick your example. I will specify one tool tip at a time and call the dll as many times a needed to set up the required number of tooltips. I plan to call the exposed function (the one that calls GetWindowAndHook currently) and supply parameters for the tooltip. Do you see an issue with that approach?
I tried to read through your last comment and get an idea of what you may mean to do, I beleive I have a "General" idea of what you may be doing, and as far as I can tell (without seeing actual code), it sounds as if it may work, can not see any "issues", but I have little idea about what methods you actually may use?
I do not beleive that initializing the many rectangles and hint text, one at a time in a single function is efficient, , ,
I believe that having a single dll function that passes an array of rectanlges and PChar text (I would never pass a delphi string to a DLL), would be a better way
I do not beleive that initializing the many rectangles and hint text, one at a time in a single function is efficient, , ,
I believe that having a single dll function that passes an array of rectanlges and PChar text (I would never pass a delphi string to a DLL), would be a better way
here is a DLL function that might be used to initialize an array to use as the hints -
// the InitHints will use the input array to set the rectangles
function InitHints(pAryHints: PAryInput; Count: Integer): Integer;
var
i: Integer;
sDC: THandle;
Size1: TSize;
begin
Result := -1;
if IsBadCodePtr(pAryHints) then Exit;
Result := -2;
if (Count < 0) or (Count > MaxByte) then Exit;
SetLength(Hints1, Count);
Result := 1;
sDC := GetDC(0);
SelectObject(sDC, GetStockObject(ANSI_VAR_FO NT));
for i := 0 to Pred(Count) do
with Hints1[i], pAryHints^[i] do
begin
HintRect := RectH;
if StrLen(pHint) > 0 then
Hint := pHint
else
Hint := ' ';
GetTextExtentPoint32(sDC, PChar(Hint), Length(Hint), Size1);
Width := Size1.cx + 12;
Height := Size1.cy + 4;
Inc(Result);
end;
ReleaseDC(0, sDC);
end;
= = = = = = = = = = = = = = = =
but I have no idea what code programming options you may or may not have in your DLL calling program, , Authorware
// the InitHints will use the input array to set the rectangles
function InitHints(pAryHints: PAryInput; Count: Integer): Integer;
var
i: Integer;
sDC: THandle;
Size1: TSize;
begin
Result := -1;
if IsBadCodePtr(pAryHints) then Exit;
Result := -2;
if (Count < 0) or (Count > MaxByte) then Exit;
SetLength(Hints1, Count);
Result := 1;
sDC := GetDC(0);
SelectObject(sDC, GetStockObject(ANSI_VAR_FO
for i := 0 to Pred(Count) do
with Hints1[i], pAryHints^[i] do
begin
HintRect := RectH;
if StrLen(pHint) > 0 then
Hint := pHint
else
Hint := ' ';
GetTextExtentPoint32(sDC, PChar(Hint), Length(Hint), Size1);
Width := Size1.cx + 12;
Height := Size1.cy + 4;
Inc(Result);
end;
ReleaseDC(0, sDC);
end;
= = = = = = = = = = = = = = = =
but I have no idea what code programming options you may or may not have in your DLL calling program, , Authorware
ASKER
Thanks, I will have a look.
I can't pass pointers to the DLL....Actually that's not strictly true, it is possible, but 'fiddly'. I am attempting to write the DLL so your 'avegarge' Authorware user has the complexity of this procedure hidden from them. Therefore, the easiest option is an exported function something like this:
setUpHint(intLeft, intTop, intRight, IntBottom:Integer;strHintT ext:String ):Boolean // or maybe return something relevant?
This reflects the method I had in mind. So for example, if I needed 4 hints setting up, I would call the function in the DLL 4 times with the appropriate parameters. I was wondering if, in your experience, you thought this may work, or there may be issues?? I have this working using the method that atul_parmar suggested with the link right at the top of this page, but as I mentioned before there seems to be a problem with some 'buttons' in Authorware (again, I think this is an Authorware issue based on how 'buttons' are set up, which may not be 'true' windows buttons). Your method works perfectly, I just need to set up the function so an Authorware user can set the hints at run time.
Authorware also has a 'List' type which is effectively an array and may, for example, look like this [100,100,300,300]. I can also set up a 'list of lists' (multidimensional array) [[100,100,300,300,"Hint 1'],[400,400,600,600,"Hint 2"]] which I think would reflect the method you are using in your function, but I have yet to figure out how to pass a list to a DLL and extract the data from it, so at the minute, passing 4 integers in the easiest way for me.
I am working on passing a list though, so as soon as I get that working, your function will be invaluable.
Thanks again...
I can't pass pointers to the DLL....Actually that's not strictly true, it is possible, but 'fiddly'. I am attempting to write the DLL so your 'avegarge' Authorware user has the complexity of this procedure hidden from them. Therefore, the easiest option is an exported function something like this:
setUpHint(intLeft, intTop, intRight, IntBottom:Integer;strHintT
This reflects the method I had in mind. So for example, if I needed 4 hints setting up, I would call the function in the DLL 4 times with the appropriate parameters. I was wondering if, in your experience, you thought this may work, or there may be issues?? I have this working using the method that atul_parmar suggested with the link right at the top of this page, but as I mentioned before there seems to be a problem with some 'buttons' in Authorware (again, I think this is an Authorware issue based on how 'buttons' are set up, which may not be 'true' windows buttons). Your method works perfectly, I just need to set up the function so an Authorware user can set the hints at run time.
Authorware also has a 'List' type which is effectively an array and may, for example, look like this [100,100,300,300]. I can also set up a 'list of lists' (multidimensional array) [[100,100,300,300,"Hint 1'],[400,400,600,600,"Hint
I am working on passing a list though, so as soon as I get that working, your function will be invaluable.
Thanks again...
???
I will just add a thought, since I have no experience with author ware, , you said that you can not pass "Pointers" in a funtion to DLL, , if that is the case, I do not see how you can ever pass text info to the DLL ? ? You have this in your function --> strHintText:String
WARNING, , the delphi string is a DELPHI ONLY variable, and I would not ever use a "String" passed to a DLL, The Delphi "Memory manager" is the specific tracker for all "String" variables, so it is NOT a good idea to try and use "String" variables to places that the same memory manager in not in existance (like a DLL). Use a PChar variable TYPE instead . . As far as I have seen text is usually passed as a pointer to a DLL, I would guess that is some author ware thing to use for text, probally a windows system "PChar" null terminated type of thing.
I will just add a thought, since I have no experience with author ware, , you said that you can not pass "Pointers" in a funtion to DLL, , if that is the case, I do not see how you can ever pass text info to the DLL ? ? You have this in your function --> strHintText:String
WARNING, , the delphi string is a DELPHI ONLY variable, and I would not ever use a "String" passed to a DLL, The Delphi "Memory manager" is the specific tracker for all "String" variables, so it is NOT a good idea to try and use "String" variables to places that the same memory manager in not in existance (like a DLL). Use a PChar variable TYPE instead . . As far as I have seen text is usually passed as a pointer to a DLL, I would guess that is some author ware thing to use for text, probally a windows system "PChar" null terminated type of thing.
ASKER
Yes I pass it as a PChar. I have no issue with strings and I have a tried and tested method. All of this is transparent to the Authorware user as they just pass a sring, but it is actually a PChar as you suggest that is passed.
I have not yet successfully passed a List or a 'List of Lists' and be able to work with it in Delphi yet though. I am working on that one !
I have not yet successfully passed a List or a 'List of Lists' and be able to work with it in Delphi yet though. I am working on that one !
You talk about creating a " borderless form from the DLL " . . . and then say some conditions about Form transparency, and tool tips appearing or not ? ? I am not sure about the "Form" being on screen and not interfering with any windows (programs) that may be below it (mouse click and such). In the last part of your question you say something about having "Areas" like a rectangle or polygon , , defined on the Screen (not a window, program) that can do the " tool tip " or " hint " kind of information window pop-up, , is that what you mean?, that you do not really have a form showing? It sems to me that the user would have a difficult time telling id the " hint " info window was from your DLL or from some other program?
Maybe you can say some more about what you are tring to do, and not so much about how you want to do it?