A few API/UI questions

Windows specific questions.
Post Reply
Z!re

A few API/UI questions

Post by Z!re »

[Content removed at author's request]
Sisophon2001
Posts: 1706
Joined: May 27, 2005 6:34
Location: Cambodia, Thailand, Lao, Ireland etc.
Contact:

Re: A few API/UI questions

Post by Sisophon2001 »

EDIT: Double post of same mesage in error
Garvan
Last edited by Sisophon2001 on Sep 16, 2005 14:29, edited 3 times in total.
Sisophon2001
Posts: 1706
Joined: May 27, 2005 6:34
Location: Cambodia, Thailand, Lao, Ireland etc.
Contact:

Re: A few API/UI questions

Post by Sisophon2001 »

Hey, my I keep getting errors when I try posting, or editing posts, and my message is having children before my eyes. If this does not work I am leaving it be.

Garvan
Last edited by Sisophon2001 on Sep 16, 2005 14:20, edited 1 time in total.
Sisophon2001
Posts: 1706
Joined: May 27, 2005 6:34
Location: Cambodia, Thailand, Lao, Ireland etc.
Contact:

Re: A few API/UI questions

Post by Sisophon2001 »

Z!re wrote:How do you make a program "minimize to tray"?
Use
Shell_NotifyIcon()

Z!re wrote:How do you check if a program is already running?
Use
FindWindow()

Z!re wrote:How do you force focus on a process other than the one started?
Use
SetForegroundWindow()

See sample code for details

Garvan


Code: Select all

''References: BCX sample code, FB hello.bas

option explicit

#include once "windows.bi"
#include once "win\shellapi.bi"

const WM_SHELLNOTIFY = WM_USER + 5
const ID_FB   = 1001
const ID_EXIT = 1002


declare function  WinMain  ( byval hInstance as HINSTANCE, byval hPrevInstance as HINSTANCE, _
		szCmdLine as string, byval iCmdShow as integer ) as integer

dim shared note as NOTIFYICONDATA
dim shared TB_CREATED as integer
dim shared szAppName as string
dim shared MainMenu as HANDLE
dim shared FileMenu as HANDLE

end WinMain( GetModuleHandle( null ), null, command$, SW_NORMAL )

function WndProc ( byval hWnd as HWND, byval message as UINT, byval wParam as WPARAM, _
		byval lParam as LPARAM ) as LRESULT

	static pt as point
	function = 0

	select case (message)
	case WM_CREATE
		TB_CREATED = RegisterWindowMessage ("TaskbarCreated")
		exit function

	case WM_DESTROY
		UnregisterClass (szAppName, GetModuleHandle( null ))
		Shell_NotifyIcon (NIM_DELETE, @note)
		PostQuitMessage( 0 )
		exit function

	case WM_COMMAND
		if LOWORD (wParam)= ID_FB then
			Messagebox (0,"Hello from FreeBasic", "Sample", 0)
		end if
		if LOWORD (wParam) = ID_EXIT then
			DestroyWindow (hWnd)
		end if

	case WM_CREATE
		TB_CREATED = RegisterWindowMessage ("TaskbarCreated")

	case WM_SHELLNOTIFY
		if lParam = WM_RBUTTONDOWN then
			GetCursorPos (@pt)
			SetForegroundWindow (hWnd)
			TrackPopupMenuEx (FileMenu, TPM_LEFTALIGN or TPM_RIGHTBUTTON, pt.x, pt.y, hWnd, NULL)
			PostMessage (hWnd, WM_NULL, 0, 0)
		end if

	case TB_CREATED
		Shell_NotifyIcon (NIM_ADD, @note)
	end select

	function = DefWindowProc( hWnd, message, wParam, lParam )
end function

function WinMain ( byval hInstance as HINSTANCE, _
		byval hPrevInstance as HINSTANCE, _
		szCmdLine as string, _
		byval iCmdShow as integer ) as integer

	dim wMsg as MSG
	dim wcls as WNDCLASS
	dim hWnd as HWND

	function = 0

	szAppName = "TrayApp"

    hWnd=FindWindow(szAppName,NULL)
    if hWnd <> 0 then
        SetForegroundWindow(hWnd)
        exit function
    end if

	with wcls
		.style = CS_HREDRAW or CS_VREDRAW
		.lpfnWndProc = @WndProc
		.cbClsExtra = 0
		.cbWndExtra = 0
		.hInstance  = hInstance
		.hIcon = LoadIcon( NULL, IDI_APPLICATION )
		.hCursor = LoadCursor( NULL, IDC_ARROW )
		.hbrBackground = GetStockObject( WHITE_BRUSH )
		.lpszMenuName = NULL
		.lpszClassName = strptr( szAppName )
	end with

	if( RegisterClass( @wcls ) = FALSE ) then
		MessageBox( null, "This program requires Windows NT!", szAppName, MB_ICONERROR )
		exit function
	end if

	'' Create the window and _BUT DONT_ show it
	hWnd = CreateWindowEx( 0, szAppName, "", _
        0, _
        0, 0, 0, 0, _
		NULL, NULL, _
		hInstance, _
		NULL )

	note.cbSize = sizeof (NOTIFYICONDATA)
	note.hWnd = hWnd
	note.hIcon = LoadIcon (NULL, MAKEINTRESOURCE (IDI_QUESTION))
	note.uFlags = NIF_ICON or NIF_TIP or NIF_MESSAGE
	note.uCallbackMessage = WM_SHELLNOTIFY
	note.szTip= szAppName
	Shell_NotifyIcon (NIM_ADD, @note)

	MainMenu = CreateMenu ()
	FileMenu = CreateMenu ()
	AppendMenu (FileMenu, MF_STRING, ID_FB, "&FreeBASIC")
	AppendMenu (FileMenu, MF_STRING, ID_EXIT, "E&xit")
	InsertMenu (MainMenu, 0, MF_POPUP, FileMenu, "invisible menu")

	while( GetMessage( @wMsg, NULL, 0, 0 ) <> FALSE )
		TranslateMessage( @wMsg )
		DispatchMessage( @wMsg )
	wend
	function = wMsg.wParam
end function
[/quote]
Hexadecimal Dude!
Posts: 360
Joined: Jun 07, 2005 20:59
Location: england, somewhere around the middle
Contact:

Post by Hexadecimal Dude! »

wow! i allways wanted to put a notifyicon on the taskbar, but i was looking through MSDN in circular ways (ie func1 needs a result from func2.... which needs a result from func1...), it's a good thing you didn't go looking in that maze! thx Sisophon2001! (+ i assume you use a standard icon, do you know how you use a custom one, eg "arbatraryicon.ico" instead?)

Am i right in thinking windows.bi is in the CVS release..... hmm maybe thins example is incentive enough for me to learn how to use CVS...

Many thx,

Joe :)
Sisophon2001
Posts: 1706
Joined: May 27, 2005 6:34
Location: Cambodia, Thailand, Lao, Ireland etc.
Contact:

Post by Sisophon2001 »

You need to use a resource file (text file with .rc extension) with a single line naming your icon.

123 ICON DISCARDABLE "mario.ico"

And then change the code that loads the IDI_QUESTION icon, telling it the id number of your icon (123 in this case).

''note.hIcon = LoadIcon (NULL, MAKEINTRESOURCE (IDI_QUESTION))
note.hIcon = LoadIcon (hInstance, MAKEINTRESOURCE(123) )

Finally compile your program with the resource file

fbc.exe" -s gui "trayicon.bas" "trayicon.rc"

I think I saw that mjs posted a link to a testing version with the new headers included? Not sure now.

Have fun

Garvan
Z!re

Post by Z!re »

[Content removed at author's request]
Sisophon2001
Posts: 1706
Joined: May 27, 2005 6:34
Location: Cambodia, Thailand, Lao, Ireland etc.
Contact:

Post by Sisophon2001 »

Hi:

The code I posted minimizes to tray. The problem may be different versions of FB. This code needs the latest headers. To make it behave as you described would require only a few changes. I think I hid the window, and you want to hide/show it on demand.

But be Warned!

This is not going to work with a simple console program or a simple SCREEN program. If I really tried I might be able to do something with a native SCREEN program, but it would still be WinAPI code. You need access to the message loop, regardless of how you go about things, and the easiest way to do that is to write your own, rather than let FB do it for you.

The problem with this simple statement is that it takes a lot of work to learn the WinAPI, I would say it took me three or four days, 8 to 10 hours per day going through Petzold’s Programming Windows before I had an idea of how it all worked. And then I spend the next five years using it, which helps.

Are you still interested?

Garvan
jofers
Posts: 1525
Joined: May 27, 2005 17:18

Post by jofers »

In FreeBASIC 0.15b, you can use a NULL GfxLib driver with SCREEN ("-1" in the options parameter), and then copy the data to a window you've set up with the WinAPI SetDIBits() and a BitmapV4Header format. There's an example of this in that screensaver kit I posted in the projects section.

You can get the hWnd from a GfxLib screen, but the WndProc is a different story. It might be a simple task to add "Minimize/Maximize/Restore" INKEY events to GfxLib's message loop though.
Z!re

Post by Z!re »

[Content removed at author's request]
yetifoot
Posts: 1710
Joined: Sep 11, 2005 7:08
Location: England
Contact:

Post by yetifoot »

checking if your already running..

heres a function in c, shouldnt be a problem to convert

int IsAlreadyRunning()
{
if (!CreateMutex(NULL,TRUE,"_YourProgNameMutex"))
{
return 0;
}
if (GetLastError()==ERROR_ALREADY_EXISTS)
{
return 0;
}
return 1;
}
yetifoot
Posts: 1710
Joined: Sep 11, 2005 7:08
Location: England
Contact:

Post by yetifoot »

When minimized, the window should go to the tray, when clicked, the program (read: window) should normalize back up.
i think you should look at ShowWindow API , and also in the window messages switch WM_MINIMIZE (I Think) , you can put the code to move to tray
JJ2005
Posts: 30
Joined: Sep 29, 2005 14:41

Re: A few API/UI questions

Post by JJ2005 »

Z!re wrote: How do you force focus on a process other than the one started?
Use SetForegroundWindow() ??

Not recommended - M$ doesn't like it, and puts lots of obstacles between your feet, see http://msdn.microsoft.com/library/defau ... window.asp

What works always is the

ShowWindow(h&,SW_MINIMIZE)
ShowWindow(h&,SW_RESTORE)

combination.
Z!re

Post by Z!re »

[Content removed at author's request]
jofers
Posts: 1525
Joined: May 27, 2005 17:18

Post by jofers »

You're asking a very unorthodox question. This took forever to figure out, but you'd have hijack GfxLib's WindowProc function and check for the necessary events.

Then, to minimize to tray you have to:
1) "Animate" the Window minimizing to tray with AnimateRect
2) Hide the Window with ShowWindow
3) Add the tray icon

Then, to show the tray
1) Stick the app back into the taskbar with ShowWindow
2) "Restore" it with SendMessage
3) Delete the tray icon

Pretty good learning experience. Anyways:

Code: Select all

Option Explicit

#include "windows.bi"
#include "win/shellapi.bi"
#define WM_SHELLNOTIFY WM_USER + 5

' Some defines and typedefs for the Win32Driver structure
#define WINDOW_TITLE_SIZE   128
#define WINDOW_CLASS_PREFIX "fbgfxclass_"

Type BLITTER As Sub(As uByte Ptr, As Integer)

' Structure containing GfxLib's internal Windows info
Type Win32Driver
    Version As Integer
    hInstance As HINSTANCE
    WndClass As WNDCLASS
    Wnd As HWND   
    Palette(0 To 255) As PaletteEntry
    Blitter As Blitter Ptr
    Is_Running As Integer
    Is_Palette_Changed As Integer
    Is_Active As Integer
    w As Integer
    h As Integer
    Depth As Integer
    FullScreen As Integer
    Refresh_Rate As Integer
    Window_Title As uByte Ptr
    WindowClass(0 To WINDOW_TITLE_SIZE + Len(WINDOW_CLASS_PREFIX)-1) As uByte
    Init As Function() As Integer
    Exit As Function() As Integer
    Paint As Function() As Integer
    Thread As Sub(ByVal Running_Event As HANDLE)
End Type

' Make that structure available
Extern FB_Win32 Alias "fb_win32" As Win32Driver

Declare Function WindowProc(ByVal hWnd As HWND, ByVal Message As uInteger, ByVal wParam As WPARAM, ByVal lParam As LPARAM) As LRESULT

Screen 13

' Hijack Gfxlib's window procedure
Dim Shared OldWindowProc As WNDPROC
OldWindowProc = FB_Win32.WndClass.lpfnWndProc
SetWindowLong FB_Win32.Wnd, GWL_WNDPROC, @WindowProc

' Set up our notify icon
Dim Shared NotifyIcon As NOTIFYICONDATA
With NotifyIcon
    .cbSize = SizeOf (NOTIFYICONDATA)
    .hWnd = FB_Win32.Wnd
    .hIcon = LoadIcon (NULL, MAKEINTRESOURCE (IDI_QUESTION))
    .uFlags = NIF_ICON Or NIF_TIP Or NIF_MESSAGE
    .uCallbackMessage = WM_SHELLNOTIFY
    .szTip = "Yay Tooltips!"
End With

' Put some stuff on the screen
Do Until Inkey = Chr(255) + "X"
    Pset(Rnd * 320, Rnd * 200), Rnd * 255
Loop

Function WindowProc(ByVal hWnd As HWND, ByVal Message As uInteger, ByVal wParam As WPARAM, ByVal lParam As LPARAM) As LRESULT
    Dim rcWindow As RECT
    Dim rcTray As RECT
    Dim hWndTaskBar As HWND
    Dim hWndTray As HWND
    
    Select Case As Const Message
        Case WM_SYSCOMMAND
            If wParam = SC_MINIMIZE Then
                ' Get the system tray's hWnd
                hWndTaskBar = FindWindow("Shell_TrayWnd", NULL)
                hWndTray = FindWindowEx(hWndTaskBar, 0, "TrayNotifyWnd", NULL)
                
                ' Show the notify icon
                Shell_NotifyIcon (NIM_ADD, @NotifyIcon) 

                ' Animate the window closing to the tray
                GetWindowRect hWnd, @rcWindow
                GetWindowRect hwndTray, @rcTray
                DrawAnimatedRects hWnd, IDANI_CAPTION, @rcWindow, @rcTray

                ' Hide the window
                ShowWindow(hWnd, SW_HIDE)
                
                Return False
            End If
            
        Case WM_SHELLNOTIFY
            If lParam = WM_LBUTTONDBLCLK Then
                ' Restore the window
                ShowWindow(hWnd, SW_SHOWMINIMIZED)
                SendMessage hWnd, WM_SYSCOMMAND, SC_RESTORE,0
                
                ' Hide the notify icon
                Shell_NotifyIcon (NIM_DELETE, @NotifyIcon) 
            End If
    End Select
    
    ' Let GfxLib then do it's own thing
    Return OldWindowProc(hWnd, Message, wParam, lParam)
End Function
Post Reply