Solved

Create Tool Tips from DLL

Posted on 2006-06-20
23
492 Views
Last Modified: 2010-04-04
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
0
Comment
Question by:AuthorwareXtras
  • 12
  • 10
23 Comments
 
LVL 10

Accepted Solution

by:
atul_parmar earned 200 total points
ID: 16949000
0
 
LVL 33

Expert Comment

by:Slick812
ID: 16962721
hello AuthorwareXtras, , I am not sure I understand what you are trying to do? ? ?
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?
0
 

Author Comment

by:AuthorwareXtras
ID: 16963745
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.
0
 
LVL 33

Expert Comment

by:Slick812
ID: 16965884
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
0
 

Author Comment

by:AuthorwareXtras
ID: 16966612
Sounds good, do you have any pointers for that?
0
 
LVL 33

Expert Comment

by:Slick812
ID: 16972782
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
0
 
LVL 33

Expert Comment

by:Slick812
ID: 16975746
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(GetCurrentThreadId, @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(aryButtons))), '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
0
 

Author Comment

by:AuthorwareXtras
ID: 16978872
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.
0
 
LVL 33

Expert Comment

by:Slick812
ID: 16981059
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
0
 

Author Comment

by:AuthorwareXtras
ID: 16996628
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.
0
 
LVL 33

Assisted Solution

by:Slick812
Slick812 earned 300 total points
ID: 16997622
???
I am unable to really get your meaning about the -->
   " setting a tool tip over an area of the application window is therefore the best approach "

you can  do the tool tip for controls (buttons or whatever) and also Do another tootip for the areas not on controls (rectangles). . .

anyway, since you have said very little about your code for the specific problem, I can offer no code suggestion to help you. . . .
anyway, , I found some time (sorry I'm busy these days) and did a library that uses a technique of the in process mouse hook to get mouse movement and then show a small info (hint) window, much like the Delphi does it's "Hints"



library DllHint;

uses
  Windows, Messages, SysUtils;

{$R *.RES}


const
ID_Hover = 51;
ID_Wait = 52;
pClassName: PChar = 'Hint Class6';
aryPHints: Array[0..4] of PChar = ('First Position', 'Number Two', 'The Third',
                                   'Another Hint 3', 'Last Hint 4'); // set same size as Hints1

type
  TAryHnds = Array of THandle;
 
  THintInfo = record
    HintRect: TRect;
    Width, Height: Cardinal;
    end;

var
  MInstance: THandle = 0;
  hMain: THandle = 0;
  hHook: THandle = 0;
  hHintWin: THandle = 0;
  hStatic: THandle;
  DoTimer, Showing: Boolean;
  HintIndex: Integer = -1;
  Hints1: Array[0..4] of THintInfo; // set the rectangle you want amount here and in aryPHints


procedure TimerFunc(Wnd: HWnd; Mesg, TimerID, SysTime: Integer); stdcall;
var
width1,height1,PosX,PosY: Integer;
MousePt: TPoint;
fRect: TRect;
IconInfo: TIconInfo;
Msg: TMSG;
begin
KillTimer(hMain,TimerID);
if (GetActiveWindow <> hMain) then Exit;
PosX := 6;
case TimerID of
  ID_Hover:
    begin // 1
    if HintIndex < 0 then Exit;
    DoTimer := False;
    GetCursorPos(MousePt);
    GetWindowRect(hMain, fRect);
    width1 := Hints1[HintIndex].Width;
    height1 := Hints1[HintIndex].Height;
    PosY := GetSystemMetrics(SM_CYCURSOR);
    if GetIconInfo(GetCursor, IconInfo) then
      begin
      PosX := 4;
      PosY := abs(Integer(IconInfo.yHotspot)-PosY);
      DeleteObject(IconInfo.hbmMask);
      DeleteObject(IconInfo.hbmColor);
      end;
    if fRect.Right < MousePt.X+PosX+width1 then
      PosX := fRect.Right-width1
      else
      PosX := MousePt.X+PosX;
    if fRect.Bottom < MousePt.Y+PosY+height1 then
      PosY := fRect.Bottom-height1
      else
      PosY := MousePt.Y+PosY;

      if not IsWindow(hHintWin) then
        begin
        hHintWin := CreateWindowEx(WS_EX_TOOLWINDOW,pClassName,
        '?', WS_POPUP or WS_DISABLED or WS_BORDER, PosX, PosY, width1,
        height1, hMain, 0, MInstance, nil);
        hStatic := CreateWindow('Static',aryPHints[HintIndex],
              WS_VISIBLE or WS_CHILD or SS_CENTER,1,1,width1-3,height1-4,
              hHintWin,0,MInstance,nil);
        SendMessage(hStatic,WM_SETFONT,GetStockObject(ANSI_VAR_FONT),0);

        if hHintWin = 0 then
          MessageBox(hMain, 'High more than 0', 'High Array', MB_ICONINFORMATION)
          else
          begin
          AnimateWindow(hHintWin, 130, {AW_SLIDE or} AW_VER_POSITIVE);
          //ShowWindow(hHintWin,SW_SHOWNA);
          while PeekMessage(Msg, 0, 0, 0, PM_REMOVE) do
            DispatchMessage(Msg);
          Showing := True;
          end;
        end;
      end;  // 1

     ID_Wait: DoTimer := True;
     else DoTimer := True;
     end; //Case TimerID
  end;




function HookFunc(Code, wParam: Integer; var MouseStrut: TMOUSEHOOKSTRUCT): Integer; stdcall;
var
i: Integer;
inRect: Boolean;
begin
result:=CallNextHookEx(0,Code,wParam,Longint(@MouseStrut));
if Code < 0 then Exit;
if Showing  then
  begin
  KillTimer(hMain,ID_Hover);
  KillTimer(hMain,ID_Wait);
  Showing := False;
  if hHintWin <> 0 then DestroyWindow(hHintWin);
  hHintWin := 0;
  HintIndex := -1;
  SetTimer(hMain,ID_Wait,140,@TimerFunc);
  Exit;
  end;

if (wParam = WM_LBUTTONDOWN) then
  begin
  KillTimer(hMain,ID_Hover);
  KillTimer(hMain,ID_Wait);
  DoTimer := False;
  SetTimer(hMain,ID_Wait,200,@TimerFunc);
  Exit;
  end;

if (wParam = WM_MOUSEMOVE) then//if  DoTimer then
  begin
  ScreenToClient(hMain, MouseStrut.pt);
  inRect := False;
  for i := 0 to High(Hints1) do
    begin
    if PtInRect(Hints1[i].HintRect{inRect}, MouseStrut.pt) then
      begin
      if DoTimer or (HintIndex <> i) then
        begin
        HintIndex := i;
        SetTimer(hMain,ID_Hover,1000,@TimerFunc);
        end;
      DoTimer := False;
      InRect := True;
      Break;
      end;
    end;
    if not inRect then  KillTimer(hMain,ID_Hover);
  end;
end;


function HintFunc(hWnd,Msg,wParam,lParam:Integer):Integer; stdcall;
begin
if Msg = WM_CTLCOLORSTATIC then
  begin
  SetTextColor(wParam,GetSysColor(COLOR_INFOTEXT));
  SetBkColor(wParam,GetSysColor(COLOR_INFOBK));
  Result:= GetSysColorBrush(COLOR_INFOBK);
  Exit;
  end;
Result := DefWindowProc(hWnd,Msg,wParam,lParam);
end;


function GetWindows(hWnd: Integer; var aryWnds: TAryHnds): BOOL; StdCall;
begin
Result := True;
SetLength(aryWnds, Succ(Length(aryWnds)));

aryWnds[High(aryWnds)] := hWnd;
end;


procedure GetWindowAndHook;
var
aryWnds: TAryHnds;
w, Left, Top: Integer;
sDC: THandle;
Size1: TSize;
wHint:   TWndClass;
begin
EnumThreadWindows(GetCurrentThreadId, @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
    hMain := aryWnds[0] else Exit;
  end else
  Exit;
DoTimer := True;
hHintWin := 0;
Showing := False;

ZeroMemory(@wHint, SizeOf(wHint));
with wHint do
  begin
  Style:= CS_SAVEBITS;
  lpfnWndProc := @HintFunc;
  hInstance := MInstance;
  hbrBackground := GetSysColorBrush(COLOR_INFOBK);
  lpszClassName := pClassName;
  hCursor := LoadCursor(0,IDC_ARROW);
  end;
RegisterClass(wHint);
hHook := SetWindowsHookEx(WH_MOUSE, @HookFunc, 0, GetCurrentThreadID);

Left := 0;
Top := 0;
sDC := GetDC(0);
SelectObject(sDC, GetStockObject(ANSI_VAR_FONT));
SelectObject(sDC, GetStockObject(NULL_BRUSH));
for w := 0 to High(Hints1) do
  begin
  SetRect(Hints1[w].HintRect, Left, Top, Left+100, Top+100);
// You must place whatever 4 Rectangle coordinates you want to have  as hints here in HintRect
  Inc(Left, 100); // I just load 4 100x100 rects
  Inc(Top, 100);

  GetTextExtentPoint32(sDC, aryPHints[w], lstrlen(aryPHints[w]), Size1);
  Hints1[w].Width := Size1.cx + 12;
  Hints1[w].Height := Size1.cy + 4;;
  end;
ReleaseDC(0, sDC);
end;

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.


 = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

This does not export any functions, and loads every thing when it id loaded as a library, I would think this would have to be loaded during run time and not staticaly loaded

this seems to work for me, it will show the hints in any rect, even if a Windowd control is under the cursor
0
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 

Author Comment

by:AuthorwareXtras
ID: 16999353
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.
0
 

Author Comment

by:AuthorwareXtras
ID: 16999525
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.
0
 
LVL 33

Expert Comment

by:Slick812
ID: 17003986
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.
0
 
LVL 33

Expert Comment

by:Slick812
ID: 17004181
???
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
0
 

Author Comment

by:AuthorwareXtras
ID: 17008144
You may be right,  Thanks for your patience and help.
0
 
LVL 33

Expert Comment

by:Slick812
ID: 17012000
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.
0
 

Author Comment

by:AuthorwareXtras
ID: 17036050
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?
0
 
LVL 33

Expert Comment

by:Slick812
ID: 17038376
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
0
 
LVL 33

Expert Comment

by:Slick812
ID: 17039985
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_FONT));
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
0
 

Author Comment

by:AuthorwareXtras
ID: 17040394
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;strHintText: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...
0
 
LVL 33

Expert Comment

by:Slick812
ID: 17055182
???
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.
0
 

Author Comment

by:AuthorwareXtras
ID: 17056680
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 !
0

Featured Post

What Should I Do With This Threat Intelligence?

Are you wondering if you actually need threat intelligence? The answer is yes. We explain the basics for creating useful threat intelligence.

Join & Write a Comment

A lot of questions regard threads in Delphi.   One of the more specific questions is how to show progress of the thread.   Updating a progressbar from inside a thread is a mistake. A solution to this would be to send a synchronized message to the…
In this tutorial I will show you how to use the Windows Speech API in Delphi. I will only cover basic functions such as text to speech and controlling the speed of the speech. SAPI Installation First you need to install the SAPI type library, th…
Access reports are powerful and flexible. Learn how to create a query and then a grouped report using the wizard. Modify the report design after the wizard is done to make it look better. There will be another video to explain how to put the final p…
This video shows how to remove a single email address from the Outlook 2010 Auto Suggestion memory. NOTE: For Outlook 2016 and 2013 perform the exact same steps. Open a new email: Click the New email button in Outlook. Start typing the address: …

743 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

8 Experts available now in Live!

Get 1:1 Help Now