From: info@sundialservices.com (Sundial Services)
In article <325B6BAB.7096@hec.unil.ch> Jean-Luc Nicoulin <Jean-Luc.Nicoulin@hec.unil.ch> writes: I get the message 'Data segment too large'. What is the matter and what can I do to solve the problem ?
In a Windows 3.1 application, three major data-areas share one(!) 64K memory segment: t he stack, the Windows 'local heap', and the data-segment containing all globals and initialized constants. This area can become used up extremely quickly.
Consequently, W3.1 applications store almost -nothing- in the stack or in a global. Rather, they store -pointers- to them. This is why, in Delphi, 'an object is a pointer.'
What you have to do is to comb your application looking for large globals and to move those into a common data block which your program allocates on startup (as an object) and destroys when done. You can see a lot about what's in the data segment by setting the 'linker map' to 'detailed' and looking for the 'DATA' segment. Whatever's in that segment is going to be trying to occupy space in the lower part of that 64K-memory segment.
From: Martin Larsson <martin.larsson@delfi-data.msmail.telemax.no>
This was supposed to be a quick summary. It ended up being quite long. Hope it's not too boring...
Under MS-DOS, an application has control of the entire machine. This gives the programmer a lot of freedom. To maximize speed, you can access the hardware directly if necessary.
Under Windows 3.x, this freedom was somewhat limited. You were no longer allowed to write directly to the screen, among other things. The problem is obvious: since the user could have any number of applications running, there was no guarantee that they were not accessing the same hardware simultaneously.
Another problem that showed up was that you had to be nice to the other applications running at the same time. Win 3.x is co-operatively multitasked, meaning that each application determines when it's done and other applications can run. Hogging the CPU for longer periods of time was not considered nice.
But the fact that no applications would run unless we as programmers said so, could be worked to our advantage when accessing the hardware. Since the application were guaranteed full control over the machine for as long as it wished, it could, when it got the CPU, muck with the I/O ports or memory, but not give up control until it was done.
Unfortunately, progress caught up with us; now there's Win32 (Windows NT and Windows 95). T hese are true operating systems, with true pre-emptive multi-tasking. Each thread (the execution unit) gets a certain amount of time with the processor. When the time is up, or a thread with higher priority comes along, the system will switch to the next thread, even though the first thread is not done. This switching can occur between any two assembly instructions; there's no guarantee that a thread will be able to complete any number of instructions before it's pre-empted, and there might be a long time 'till the next timeslot.
This brings up a real problem with direct hardware access. A typical I/O read, for instance, is composed of several assembly instructions:
mov dx, AddressPort mov al, Address out dx, al jmp Wait Wait: mov dx, DataPort in al, dx
While the state of all registers are preserved on a thread-switch, the state of the I/O ports are not. So, it is very possible that three applications have their way with 'your' I/O port between the 'out' and the 'in' instructions above.
The solution to this problem is to somehow tell all other applications that "Currently MyProg is using port 546, and everybody else better stay in line." What's needed is a mutex. Unfortunately, to use a mutex, all applications have to agree on a name for that mutex. But even if that was possible, you'd easily get into some thorny problems. Consider two applications App1 and App2. Both wants to execute the above code. Unfortunately, they're created by different people with different views, so App1 asks for the AddressPortMutex first, while App2 asks for the DataPortMutex first. And, by a sad coincidence, App1 gets the AddressPortMutex, then the system swithes to App2, which aquires the DataPortMutex, and we're deadlocked. App2 can't get the address port, 'cause App1 has that. App1 can't get the data port, 'cause App2 has that. And we're still waiting...
The correct way to solve this problem is to create a device driver that owns the port/memory area. Access to the hardware is supported through an API. A typical function would be
GetIOPortData(AddressPort, DataPort : word) : Byte;
GetIOPortData would aquire a mutex that protects both (possibly all) ports, then access the ports, and finally releasing the mutex before returning to the caller. If different threads are calling this function at the same time, one will get there first, the others must wait.
Writing a device driver is not easy. It must be done in assembler or C, and they are really hard to debug. And just to be safe, a device driver for Windows 95 (a VxD) isn't compatible with a device driver for Windows NT (a VDD, for virtual device driver). They are said to converge, and Windows NT 6.0 and Windows 2000 might have compatible device drivers, but until then, we're stuck with writing two separate pieces of code.
For more info see (for instance):
Microsoft's Windows 95 Device Driver Kit
Microsoft's Windows NT Device Driver Kit
Microsoft Press' "Systems Programming for Windows 95" by Walter Oney
Also, check out Vireo's VtoolsD library for writing VxD's in C. http://www.vireo.com/.
The above problem isn't too real. An application that accesses the hardware directly, is usually using some specialized hardware. A machine-configuration like that tend to run one application only, who's sole purpose is to access that hardware. In such a scenario, writing a device driver seems too much trouble. After all, the reason the thing is running on Windows, is just to get the nice GUI for (almost) free, not that 10 applications can be running simultaneously.
Fortunately, Windows 95 is built to be compatible with Windows 3.x. This means that direct I/O must be allowed, simply because a lot of Win 3.x programs uses it. To access the I/O ports, simply step down to assembly. The following code was supplied by Arthur Hoornweg (hoornweg@hannover.sgh-net.de):
function getport(p:word):byte; stdcall; begin asm push edx push eax mov dx,p in al,dx mov @result,al pop eax pop edx end; end; Procedure Setport(p:word;b:byte);Stdcall; begin asm push edx push eax mov dx,p mov al,b out dx,al pop eax pop edx end; end;
François Piette also has some direct I/O access functions at http://rtfm.netline.be/fpiette/portiofr.htm
The above will not work on Windows NT. NT is a much more robust operating system, and allowing all and everybody access to the hardware anytime they wanted, would seriously endager the stability. In addition, NT is cross platform, and access to I/O ports might be wildly different on different processors.
Even so, it is possible to access the I/O ports directly under NT on x86 processors. This is highly undocumented, and will probably disappear in future versions of the operating system.
I have not much information on the process, but an article by D. Roberts in the May, 1996 issue of Dr. Dobb's Journal looks promising: "Direct Port I/O and Windows NT." This seems to be the only DDJ I'm missing, so I can't verify it. See http://www.ddj.com for ordring of back-issues.
Windows Developer's Journal does have an article on "Port I/O under Windows." It's written by Karen Hazzah, and appeared in the June 1996 issue. See http://www.wdj.com for ordering of back-issues.
(Note, I know very little about these resources, check them out yourself.)
There are newsgroups dedicated to the topic of writing VxD's and VDD's:
comp.os.ms-windows.programmer.nt.kernel-mode (VDD)
comp.os.ms-windows.programmer.vxd (VxD)
Dejanews (http://www.dejanews.com) turned up quite a few hits on 'device driver direct I/O access 95'.
BlueWater Systems have developed OCX's for direct I/O, memory access and interrupt handling under all Win32 platforms. They also seem to offer custom built device drivers. See their page at http://www.bluewatersystems.com/.
I know some other company has been advertising here for their ability to write custom VxD's. But I can't find that reference.
Paul S. Knapp wrote: > > Does anyone know how to make as Delphi app read the Run Minimized > checkbox in the Windows 3.1 or 3.11 StartUp Group. > > I cannot get my application to run minimized when this checkbox is > checked. I would like to be able to give my users the option of > starting the application in their startup group, in either maximized or > minimized mode. Every Windows application I have ever used is able to > start in the mode selected by the checkbox in the Startup Group. I > assume Delphi applications should be able to as well, but so far I a > haven't found a way. > > Thanks in advance for your advice > Paul Knapp
Hi Paul!
Use WinProcs unit and after created main form add call ShowWindow.
You can use HInstance, HPrevInst, CmdShow and CmdLine global variables.
program Project1; uses WinProcs, {*** use WinProcs} Forms, Unit1 in 'UNIT1.PAS' {Form1}; {$R *.RES} begin Application.CreateForm(TForm1, Form1); ShowWindow(Form1.handle, CmdShow); Application.Run; end.
From: Ken Kyler <ken@kyler.com>
Here's and example, it's taken from
Rubenking, Neil (1996). _Delphi Programming Problem Solver_. Foster City, CA: IDG Books. ISBN:1-56884-795-5.
unit Unit1; interface uses SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms, Dialogs; type TForm1 = class(TForm) procedure FormCreate(Sender: TObject); procedure FormActivate(Sender: TObject); private { Private declarations } public { Public declarations } ShowHow : word ; end; var Form1: TForm1; implementation {$R *.DFM} procedure TForm1.FormCreate(Sender: TObject); var SUI : TStartupInfo ; begin if CmdShow = SW_SHOWDEFAULT then begin GetStartupInfo( SUI ) ; ShowHow := SUI.wShowWindow ; end else ShowHow := CmdShow ; if ShowHow = SW_SHOWMAXIMIZED then WindowState := wsMaximized ; end; procedure TForm1.FormActivate(Sender: TObject); begin case ShowHow of SW_SHOWMINIMIZED, SW_MINIMIZE, SW_SHOWMINNOACTIVE : Application.Minimize ; end ; end; end.
This works with NT 4 and Delphi 2.01. It took me one and a half hours to find out: Make your project code look like this:
begin Application.Initialize; Application.CreateForm(TForm1, Form1); Form1.Show; Application.Minimize; Application.Run; end.
From: johnnysc@ix.netcom.com (John Crane)
Sharing Memory Mapped Files... Check out the following code:
var HMapping: THandle; PMapData: Pointer; const MAPFILESIZE = 1000; procedure OpenMap; var llInit: Boolean; lInt: Integer; begin HMapping := CreateFileMapping($FFFFFFFF, nil, PAGE_READWRITE, 0, MAPFILESIZE, pchar('MY MAP NAME GOES HERE')); // Check if already exists llInit := (GetLastError() <> ERROR_ALREADY_EXISTS); if (hMapping = 0) then begin ShowMessage('Can''t Create Memory Map'); Application.Terminate; exit; end; PMapData := MapViewOfFile(HMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0); if PMapData = nil then begin CloseHandle(HMapping); ShowMessage('Can''t View Memory Map'); Application.Terminate; exit; end; if (llInit) then begin // Init block to #0 if newly created memset(PMapData, #0, MAPFILESIZE); end; end; procedure CloseMap; begin if PMapData <> nil then begin UnMapViewOfFile(PMapData); end; if HMapping <> 0 then begin CloseHandle(HMapping); end; end;
Any two or more applications or DLLs may obtain pointers to the same physical block of memory this way. PMapData will point to a 1000 byte buffer in this example, this buffer being initialized to #0's the first time in. One potential problem is synchronizing access to the memory. You may accomplish this through the use of mutexes. Here's an example of that:
{ Call LockMap before writing (and reading?) to the memory mapped file. Be sure to call UnlockMap immediately when done updating. } var HMapMutex: THandle; const REQUEST_TIMEOUT = 1000; function LockMap:Boolean; begin Result := true; HMapMutex := CreateMutex(nil, false, pchar('MY MUTEX NAME GOES HERE')); if HMixMutex = 0 then begin ShowMessage('Can''t create map mutex'); Result := false; end else begin if WaitForSingleObject(HMapMutex,REQUEST_TIMEOUT) = WAIT_FAILED then begin // timeout ShowMessage('Can''t lock memory mapped file'); Result := false; end; end; end; procedure UnlockMap; begin ReleaseMutex(HMixMutex); CloseHandle(HMixMutex); end;
Please excuse my unnecessary begin..end's. I come from a Clipper background, and I prefer to see my logic blocks capped off with end's - easier to follow.
From: "Neil Clayton" <100101.602@compuserve.com>
Rainer Perl <Rainer.Perl@iddc.via.at> wrote in article > I have a question to the Shell_NotifyIcon function: > I can add an icon to the taskbar > I can modify an icon > I can delete an icon. > What I can't do: I can't receive Messages from the Icon!!
To receive messages you must add the NIF_MESSAGE flag to your notify structure and tell it what message to send and to which window. This is the code that I use:
procedure TMainForm.UpdateTaskBar; // update the win95 taskbar icon area var NotifyData: TNotifyIconData; begin With NotifyData do // set up the data structure begin cbSize := SizeOf(TNotifyIconData); Wnd := MyForm.Handle; uID := 0; uFlags := NIF_ICON or NIF_MESSAGE or NIF_TIP; // ... the aspects to modify ... uCallbackMessage := WM_MY_MESSAGE; // ... the message to send back to us ... hIcon := hMyIcon; szTip := 'Tool Tip To Display'; // ... and the tool tip end; Shell_NotifyIcon(dwMessage, @NotifyData); // now do the update end;
WM_MYMESSAGE is a user defined message. Usually defined as:
const WM_MYMESSAGE = WM_USER + <some number - can be zero>;
From: gt6298d@prism.gatech.edu (Chris Randall)
"J.J. Bakker" <J.J.Bakker@stud.rgl.ruu.nl> wrote: >Does anyone know the answer, I', trying to make an app that has an icon in the notification area with a popupmenu. However the application is still visible on the taskbar. Using Application.ShowMainForm:=False; is not enough. >
I have run into the same problem but found the answer. This little bit of code works great.
procedure TMainForm.FormCreate(Sender: TObject); begin Application.OnMinimize:=AppMinimize; Application.OnRestore:=AppMinimize; Application.Minimize; AppMinimize(@Self); end; procedure TMainForm.AppMinimize(Sender: TObject); begin ShowWindow(Application.Handle, SW_HIDE); end;
From: "James D. Rofkar" <jim_rofkar%lotusnotes1@instinet.com>
JAAD wrote: > > I want to make a Delphi-Form to REALLY stay on top, But not only within it own application (thats simpel) No I want it to stay on top even when I am using for instance EXCEL. >
Try using the Windows API function SetWindowPos(). Something like...
with MyForm do SetWindowPos(Handle, HWND_TOPMOST, Left, Top, Width, Height, SWP_NOACTIVATE or SWP_NOMOVE or SWP_NOSIZE);
You may need to call this function in your Form's OnShow(), OnDeactivate(), and OnActivate() event handlers.
function InPort(PortAddr: word): byte; {$IFDEF VER90} assembler; stdcall; asm mov dx,PortAddr in al,dx end; {$ELSE} begin Result := Port[PortAddr]; end; {$ENDIF} procedure OutPort(PortAddr: word; Databyte: byte); {$IFDEF VER90} assembler; stdcall; asm mov al,Databyte mov dx,PortAddr out dx,al end; {$ELSE} begin Port[PortAddr] := DataByte; end; {$ENDIF}
From: "James D. Rofkar" <jim_rofkar%lotusnotes1@instinet.com>
Robert Copier wrote: > Is there a way to hide the Windows 95 statusbar when i start my application made in delphi 2.01. When the user close the application the statusbar must become visible again.
I'm guessing you're referring to the Windows 95 taskbar and system tray window, and not a statusbar. The answer: Sure you can! And what a cool idea! Here's how:
TForm1 = class(TForm) ... private hTaskBar: HWND; ... end;
hTaskBar := FindWindow('Shell_TrayWnd', nil); ShowWindow(hTaskBar, SW_HIDE);
ShowWindow(hTaskBar, SW_SHOW);
From: "Chami" <72223.10@compuserve.com>
> I need to have a form in my application that zooms to half of > the screen when the Maximize button is pressed, not to full > screen. >
you could handle the WM_GETMINMAXINFO message from your form.
for example, add the following declaration to the protected section of your form (interface):
procedure _WM_GETMINMAXINFO( var mmInfo : TWMGETMINMAXINFO ); message wm_GetMinMaxInfo;
then define (implementation) the above message handler as follows (TForm1 being the name of your form of course):
procedure TForm1._WM_GETMINMAXINFO( var mmInfo : TWMGETMINMAXINFO ); begin // set the position and the size of your form when maximized: with mmInfo.minmaxinfo^ do begin ptmaxposition.x := Screen.Width div 4; ptmaxposition.y := Screen.Height div 4; ptmaxsize.x := Screen.Width div 2; ptmaxsize.y := Screen.Height div 2; end; end;
From: mdiluglio@aol.com
Check the API help In the WINPROCS unit try the function GetVersion: LongInt;
The GetVersion function retrieves the current version numbers of the Windows and MS-DOS operation systems.
NOTE there is a error in the orginal API documentation, the major/minor Win version are reversed!
As I have used it:
Windows 3.1 show up as 3.1, WIN95 shows up as 3.95
The following code comes from Loyds Help File (it can be found on most delphi web pages). I haven't tried it but I will use it in one of my apps as soon as I get the bitmap from the client. let me know if it works for you.
unit Unit1; interface uses SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms, Dialogs; type TForm1 = class(TForm) procedure FormCreate(Sender: TObject); procedure FormPaint(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; Bitmap: TBitmap; implementation {$R *.DFM} procedure TForm1.FormCreate(Sender: TObject); begin Bitmap := TBitmap.Create; Bitmap.LoadFromFile('C:\WINDOWS\cars.BMP'); end; procedure TForm1.FormPaint(Sender: TObject); var X, Y, W, H: LongInt; begin with Bitmap do begin W := Width; H := Height; end; Y := 0; while Y < Height do begin X := 0; while X < Width do begin Canvas.Draw(X, Y, Bitmap); Inc(X, W); end; Inc(Y, H); end; end; end.
From: "Dirk Faber " <d.j.faber@student.utwente.nl>
Rob Wilson <wilson@pelops.compd.com> wrote > Does anyone know how I can change the wallpaper at runtime using a > filename that I specifiy?
procedure ChangeWallpaper(bitmap: string); {bitmap contains filename: *.bmp} var pBitmap : pchar; begin bitmap:=bitmap+#0; pBitmap:=@bitmap[1]; SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, pBitmap, SPIF_UPDATEINIFILE); end;
> Also, is there a way of saving it to the INI file for next session?
[LastUsedBitmap] LUBitmap= c:\mybitmap.bmp
procedure WriteToIniFile(bitmap : string); var MyIniFile : TInifile; begin MyIniFile := Tinifile.Create( 'c:\Bitmap.ini' ); MyIniFile.WriteString( 'LastUsedBitmap', 'LUBitmap', bitmap); MyIniFile.Free; end; procedure ReadFromIniFile(var bitmap: string); var MyIniFile : TInifile; begin MyIniFile := Tinifile.Create( 'c:\Bitmap.ini' ); bitmap:= MyIniFile.ReadString('LastUsedBitmap', 'LUBitmap'); MyIniFile.Free; end;
Christian Piene Gundersen <j.c.p.gundersen@jusstud.uio.no>
This is a rather complicated matter, so if it isn't vital to your application, I suggest that you spend your time better than digging into it. However, I'll try to point you in the right direction.The windows 32 operating system is based on a shell which uses virtual folders, like 'my computer', 'desktop' and 'recycle bin'. Some of these folders are part of the physical file system. That is they have a corresponding directory in the file system. This is the case with 'desktop' and 'recycle bin'. These directories can be used as InitialDir for the TOpenDialog, but first you have to get their physical location, which can be different on different computers. To get the physical location of these folders, you have to use some special API calls (see example below). Other folders, like 'my computer' and 'printers' are not part of the file system - they are only virtual. I've noticed that you can browse to these folders using the TOpenDialog, but I don't think they can be used as InitialDir.
Virtual folders are (a bit simplified) of the type SHITEMID (item identifier). They are normally accessed using pointers to item identifiers list (PIDL). To get the PIDL of a special folder, you can use the SHGetSpecialFolder function. The physical location of the corresponding directory can then be obtained by passing the PIDL to the GetPathFromIDList function. If the folder is part of the file system, the function will return the path as a string (which can be used as InitialDir). But if you want the OpenDialog to start in a folder that is only virtual (like 'my computer'), you'll have to make it accept a PIDL as InitialDir, which I don't think it will. My guess is that the TOpenDialog uses PIDLs when browsing, but only accepts physical directories as InitialDir.
Here is an example that shows how to get the 'recent documents' path and use it as InitialDir:
procedure TForm1.Button1Click(Sender: TObject); var PIDL: Pointer; Path: LPSTR; const CSIDL_RECENT = $0008; begin Path := StrAlloc(MAX_PATH); SHGetSpecialFolderLocation(Handle, CSIDL_RECENT, @PIDL); if SHGetPathFromIDList(PIDL, Path) then // returns false if folder isn't part of file system begin OpenDialog1.InitialDir := Path; OpenDialog1.Execute; end; StrDispose(Path); end;
Some constants you may need:
CSIDL_DESKTOP = $0000; CSIDL_PROGRAMS = $0002; CSIDL_CONTROLS = $0003; CSIDL_PRINTERS = $0004; CSIDL_PERSONAL = $0005; CSIDL_STARTUP = $0007; CSIDL_RECENT = $0008; CSIDL_SENDTO = $0009; CSIDL_BITBUCKET = $000a; CSIDL_STARTMENU = $000b; CSIDL_DESKTOPDIRECTORY = $0010; CSIDL_DRIVES = $0011; // My Computer CSIDL_NETWORK = $0012; CSIDL_NETHOOD = $0013; CSIDL_FONTS = $0014; CSIDL_TEMPLATES = $0015;
"Greg Peterson" <maxint@cwnet.com>
Try this:FUNCTION SmallFonts : BOOLEAN; {returns TRUE if small fonts are set, FALSE if using Large Fonts } VAR DC : HDC; { used to check for number of colors available } BEGIN DC := GetDC(0); Result := (GetDeviceCaps(DC, LOGPIXELSX) = 96); { LOGPIXELSX will = 120 if large fonts are in use } ReleaseDC(0, DC); END;
Gene Eighmy <eighmy@scott.net>
> > When my programs run on systems with small fonts, I > often get strange output. Labels too small to hold all > the text, leaving the right, or the bottom, unshown, for > instance. StringGrid's which don't align as expected. >Try this. This will rescale both the form size and also reform small vs. large fonts. Call it in Form.FormCreate. Hope this helps.
unit geScale; interface uses Forms, Controls; procedure geAutoScale(MForm: TForm); implementation Type TFooClass = class(TControl); { needed to get at protected } { font property } procedure geAutoScale(MForm: TForm); const cScreenWidth :integer = 800; cScreenHeight:integer = 600; cPixelsPerInch:integer= 96; cFontHeight:integer = -11; {Design-time value of From.Font.Height} var i: integer; begin { IMPORTANT!! : Set Scaled Property of TForm to FALSE with Object Inspector. The following routine will scale the form such that it looks the same regardless of the screen size or pixels per inch. The following section determines if the screen width differs from the design-time screen size. If it differs, Scaled is set true and component positions are rescaled such that they appear in the same screen location as the design-time location. } if (Screen.width &;lt> cScreenWidth)or(Screen.PixelsPerInch <> cPixelsPerInch) then begin MForm.scaled := TRUE; MForm.height := MForm.height * screen.Height DIV cScreenHeight; MForm.width := MForm.width * screen.width DIV cScreenWidth; MForm.ScaleBy(screen.width, cScreenWidth); end; { This section determines if the run-time font size differs from the design- time font size. If the run-time pixelsperinch differs form the design-time pixelsperinch, the fonts must be rescaled in order for the form to appear as designed. Scaling is calculated as the ratio of the design-time font.height to run-time font.height. Font.size will not work as it may equal the design- time value yet appear physically larger crowding and overrunning other components. For instance, a form designed in 800x600 small fonts has a font.size of 8. When you run the form on in 800x600 large fonts, font.size is also 8 but the text is noticably larger than when run in small font mode. This scaling will make them both appear to be the same size. } if (Screen.PixelsPerInch <> cPixelsPerInch) then begin for i := MForm.ControlCount - 1 downto 0 do TFooClass(MForm.Controls[i]).Font.Height := (MForm.Font.Height div cFontHeight) * TFooClass(MForm.Controls[i]).Font.Height; end; end; end.
DESCRIPTION: Ever notice how professional programs seem to remember in what condition and location you left them and their child windows? Ever notice how most RAD apps don't? You can take that ragged edge off your program with this unit. It Allows apps to save the location, size, and state of windows so that when the user reopens them, they will look as the user left them.
USE: Put WINRSTOR in the uses of clause of your main form and any forms that will be saving or restoring their own state, size, or location. (If you will be doing all the saving and restoring using WinSaveChildren and WinRestoreChildren from the main form, you only need reference it in the main form's uses clause.)
In MainForm.Create, initialize the global WinRestorer object as follows (it's already declared in this file, but needs to be allocated):
GlobalWinRestorer := TWinRestorer.create( Application, TRUE, WHATSAVE_ALL);
GlobalWinRestorer := TWinRestorer.create( Application, TRUE, [location, size, state]);
GlobalWinRestorer.free;
GlobalWinRestorer.SaveChildren(Self, [default]);
GlobalWinRestorer.SaveWin(Self, [WHATSAVE_ALL]);
GlobalWinRestorer.RestoreWin(Self, [default]);
GlobalWinRestorer.RestoreWin(Self, [default]); GlobalWinRestorer.RestoreChildren(Self, [default]);
unit WinRstor; INTERFACE USES SysUtils, Forms; TYPE {=============================================================} {------------------------------------------------------------------ Windows restorer object class and related types. -------------------------------------------------------------------} EWinRestorer = class( Exception); TWhatSave = (default, size, location, state); STWhatSave = set of TWhatSave; TWinRestorer = class(TObject) protected mIniFile: string; mIniSect: string[80]; mIsInitialized: boolean; mDefaultWhat: STWhatSave; public constructor Create( TheApp: TApplication; LocalDir: boolean; DefaultWhatSave: STWhatSave); {If localDir is true, ini dir is the app dir. Else, ini dir is the windows dir.} procedure SaveWin(TheForm: TForm; What: STWhatSave); procedure SaveChildren(TheMDIForm: TForm; What: STWhatSave); procedure RestoreWin( TheForm: TForm; What: STWhatSave); procedure RestoreChildren(TheMDIForm: TForm; What: STWhatSave); property IniFileName: string read mIniFile; end; CONST WHATSAVE_ALL = [size, location, state]; VAR GlobalWinRestorer: TWinRestorer; IMPLEMENTATION Uses IniFiles; constructor TWinRestorer.create; var fname, path: string[100]; begin inherited create; {Calculate ini file name} if default in DefaultWhatSave then raise EWinRestorer.create( 'Attempt to initialize default window position paramaters with set ' + ' containing [default] item. ' + 'Default params may contain only members of [size, location, state]. ') else mDefaultWhat := DefaultWhatSave; fname := ChangeFileExt( ExtractFileName( TheApp.exeName), '.INI'); if LocalDir then begin {parse out path and add to file name} path := ExtractFilePath(TheApp.exeName); if path[length(path)] <> '\' then path := path + '\'; fname := path + fname; end; {fill object fields} mIniFile := fname; mIniSect := 'WindowsRestorer'; {It'd be nice to write some notes to a section called [WinRestorer Notes]} end; procedure TWinRestorer.RestoreWin; var FormNm, SectionNm: string[80]; ini: TIniFile; n,l,t,w,h: integer; {Left, Top Width, Height} begin ini := TIniFile.create( mIniFile); TRY SectionNm := mIniSect; FormNm := TheForm.classname; if default in What then What := mDefaultWhat; {Update Window State if Necessary} if state in What then n := ini.ReadInteger( SectionNm, FormNm + '_WindowState', 0); case n of 1: TheForm.WindowState := wsMinimized; 2: TheForm.WindowState := wsNormal; 3: TheForm.WindowState := wsMaximized; end; {Update Size and Location if necessary.} with TheForm do begin l:=left; t:=top; h:=height; w:=width; end; {Save current vals.} if size in What then begin w := ini.ReadInteger( SectionNm, FormNm + '_Width', w); h := ini.ReadInteger( SectionNm, FormNm + '_Height', h); end; if location in What then begin t := ini.ReadInteger( SectionNm, FormNm + '_Top', t); l := ini.ReadInteger( SectionNm, FormNm + '_Left', l); end; TheForm.SetBounds(l,t,w,h); FINALLY ini.free; END; end; procedure TWinRestorer.RestoreChildren; var i: integer; begin if TheMDIForm.formstyle <> fsMDIForm then raise EWinRestorer.create('Attempting to save window sizes of children for a non MDI parent window.') else for i := 0 to TheMDIForm.MDIChildCount - 1 do RestoreWin( TheMDIForm.MDIChildren[i], what); end; procedure TWinRestorer.SaveWin; var FormNm, SectionNm: string[80]; w : STWhatsave; ini: TIniFile; begin ini := TIniFile.create( mIniFile); TRY SectionNm := mIniSect; FormNm := TheForm.ClassName; if default in What then w := mDefaultWhat else w := mDefaultWhat; if size in w then begin ini.WriteInteger( SectionNm, FormNm + '_Width', TheForm.Width); ini.WriteInteger( SectionNm, FormNm + '_Height', TheForm.Height); end; if location in w then begin ini.WriteInteger( SectionNm, FormNm + '_Top', TheForm.Top); ini.WriteInteger( SectionNm, FormNm + '_Left', TheForm.Left); end; if state in w then case TheForm.WindowState of wsMinimized: ini.WriteInteger( SectionNm, FormNm + '_WindowState', 1); wsNormal: ini.WriteInteger( SectionNm, FormNm + '_WindowState', 2); wsMaximized: ini.WriteInteger( SectionNm, FormNm + '_WindowState', 3); end; FINALLY ini.free; END; end; procedure TWinRestorer.SaveChildren; var i: integer; begin if TheMDIForm.formstyle <> fsMDIForm then raise EWinRestorer.create('Attempting to restore window sizes of children for a non MDI parent window.') else for i := 0 to TheMDIForm.MDIChildCount - 1 do SaveWin( TheMDIForm.MDIChildren[i], what); end; INITIALIZATION END.
In each language version of Windows the 'StartUp' folder has als a different name. Is there any way to determine the correct name of this folder ??There is an entry in the registry under:
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders\Start Menu
Is there a function or API call to find the boot drive?I found it in the Registry.
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Setup
SetSystemModalWindow(Form1.handle);
unit Unit1; interface uses SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Button1: TButton; Button2: TButton; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure FormKeyPress(Sender: TObject; var Key: Char); private AppInst: THandle; AppWind: THandle; public { Public declarations } end; var Form1: TForm1; implementation {$R *.DFM} uses ShellAPI; procedure SendShift(H: HWnd; Down: Boolean); var vKey, ScanCode, wParam: Word; lParam: longint; begin vKey:= $10; ScanCode:= MapVirtualKey(vKey, 0); wParam:= vKey or ScanCode shl 8; lParam:= longint(ScanCode) shl 16 or 1; if not(Down) then lParam:= lParam or $C0000000; SendMessage(H, WM_KEYDOWN, vKey, lParam); end; procedure SendCtrl(H: HWnd; Down: Boolean); var vKey, ScanCode, wParam: Word; lParam: longint; begin vKey:= $11; ScanCode:= MapVirtualKey(vKey, 0); wParam:= vKey or ScanCode shl 8; lParam:= longint(ScanCode) shl 16 or 1; if not(Down) then lParam:= lParam or $C0000000; SendMessage(H, WM_KEYDOWN, vKey, lParam); end; procedure SendKey(H: Hwnd; Key: char); var vKey, ScanCode, wParam: Word; lParam, ConvKey: longint; Shift, Ctrl: boolean; begin ConvKey:= OemKeyScan(ord(Key)); Shift:= (ConvKey and $00020000) <> 0; Ctrl:= (ConvKey and $00040000) <> 0; ScanCode:= ConvKey and $000000FF or $FF00; vKey:= ord(Key); wParam:= vKey; lParam:= longint(ScanCode) shl 16 or 1; if Shift then SendShift(H, true); if Ctrl then SendCtrl(H, true); SendMessage(H, WM_KEYDOWN, vKey, lParam); SendMessage(H, WM_CHAR, vKey, lParam); lParam:= lParam or $C0000000; SendMessage(H, WM_KEYUP, vKey, lParam); if Shift then SendShift(H, false); if Ctrl then SendCtrl(H, false); end; function EnumFunc(Handle: HWnd; TF: TForm1): Bool; Far; begin TF.AppWind:= 0; if GetWindowWord(Handle, GWW_HINSTANCE) = TF.AppInst then TF.AppWind:= Handle; result:= (TF.AppWind = 0); end; procedure TForm1.Button1Click(Sender: TObject); var Text: Array[0..255] of char; begin AppInst:= ShellExecute(Handle, 'open', 'notepad.exe', nil, '', SW_NORMAL); EnumWindows(@EnumFunc, longint(self)); AppWind:= GetWindow(AppWind, GW_CHILD); end; procedure TForm1.Button2Click(Sender: TObject); begin SendKey(AppWind, 'T'); SendKey(AppWind, 'e'); SendKey(AppWind, 's'); SendKey(AppWind, 't'); end; procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char); begin if AppWind <> 0 then SendKey(AppWind, Key); end; end.
Can anybody out there send me some basic stuff about Windows Messages related to Delphi. All this WM_*** stuff is getting on my nerves, since I can't understand it.[Jim Stanley, Jim.Stanley@jacobs.com]
All the Windows messages are listed in the Windows API help in your Delphi help topics. (I'm using D1, assume the same for future versions).
The WM_ (and other) messages are essential to the way Windows works. You're well aware that Delphi is primarily an *event-driven* system; all those OnKeyPress, OnThis, OnThat methods. If you have the VCL source code, you'll find in there somewhere that those event handler methods are designed to *receive* particular Windows messages (and there are some threads in here showing how you can subclass a component and "teach" it to respond to other messages as well). Windows is constantly sending out those messages in response to actions performed by the user, and it's the business of the Delphi app (and of all Windows apps) to intercept them and handle them in ways you decide. Delphi puts a wrapper over most of the message system by creating the event handlers for components described above.
In addition to recieving those messages, you can also *send* them as well. There are a couple of ways to work this: check out SendMessage and PostMessage (both native Win API functions), as well as the Delphi Perform method. The first two require you to use the Handle parameter of the component you're sending the message to, while Perform is a method belonging to that component. The messages go into the standard Windows message queue and are processed like every other message.
Here's a trivial example: I want (for some bizarre reason) to insert a 'y' character whenever I type a '4' in a TMemo. [Think of automatically inserting a begin-end block or a closing parenthesis.) Now I could do a lot with the Memo's Lines property, but that gets pretty complex. A much simpler way of going about it is:
procedure TForm1.Memo1KeyPress(Sender: TObject; var Key: Char); begin if Key = '4' then SendMessage(Memo1.Handle, WM_CHAR, Word('y'), 0); end;
procedure TFormEffortRates.ComboBoxMaterialKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); var iShowing : integer; { other code, then... } begin { This tells you whether the combo is already dropped } iShowing := SendMessage((Sender as TComboBox).Handle, CB_GETDROPPEDSTATE, 0, 0); if iShowing = 0 then { drop the combo box } SendMessage((Sender as TComboBox).Handle, CB_SHOWDROPDOWN, 1,0); end;
function TMDIChild.GetMemoColumn(const TheMemo : TMemo) : integer; begin Result := TheMemo.SelStart - (SendMessage(TheMemo.Handle, EM_LINEINDEX, GetMemoLine(TheMemo), 0)); end; function TMDIChild.GetMemoLine(const TheMemo : TMemo) : integer; begin Result := SendMessage(TheMemo.Handle, EM_LINEFROMCHAR, TheMemo.SelStart, 0); end;
In short, API messages provide you with a way to fine-tune your applications to respond in exactly the way you want them to. I would consider it an advanced Delphi topic, but it sounds like one you're more than ready for.
Can anyone tell me of a way or a component or whatever else that will allow delphi 2 or 3 to place a button on the task bar much like what PowerDesk 2.0 Toolbar does.[Joolz@emarkt.com]
Here are the code snipits to do just that!
// This needs to be in your public declarations @ the top of the pas file procedure TForm1.IconCallBackMessage( var Mess : TMessage ); message WM_USER + 100;
procedure TForm1.FormCreate(Sender: TObject); var nid : TNotifyIconData; begin with nid do begin cbSize := SizeOf( TNotifyIconData ); Wnd := Form1.Handle; uID := 1; uFlags := NIF_ICON or NIF_MESSAGE or NIF_TIP; uCallbackMessage := WM_USER + 100; hIcon := Application.Icon.Handle; szTip := 'This is the hint!'; end; Shell_NotifyIcon( NIM_ADD, @nid ); end; procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); var nid : TNotifyIconData; begin with nid do begin cbSize := SizeOf( TNotifyIconData ); Wnd := Form1.Handle; uID := 1; uFlags := NIF_ICON or NIF_MESSAGE or NIF_TIP; uCallbackMessage := WM_USER + 100; hIcon := Application.Icon.Handle; szTip := 'This is the hint!'; // All the above is probably not needed. end; Shell_NotifyIcon( NIM_DELETE, @nid ); end; procedure TForm1.IconCallBackMessage( var Mess : TMessage ); var sEventLog : String; begin case Mess.lParam of // Do whatever you wish here. For example popup up a menu on a right click. WM_LBUTTONDBLCLK : sEventLog := 'Left Double Click'; WM_LBUTTONDOWN : sEventLog := 'Left Down'; WM_LBUTTONUP : sEventLog := 'Left Up'; WM_MBUTTONDBLCLK : sEventLog := 'M Dbl'; WM_MBUTTONDOWN : sEventLog := 'M D'; WM_MBUTTONUP : sEventLog := 'M U'; WM_MOUSEMOVE : sEventLog := 'movement'; WM_MOUSEWHEEL : sEventLog := 'Wheel'; WM_RBUTTONDBLCLK : sEventLog := 'r dbl'; WM_RBUTTONDOWN : sEventLog := 'r down'; WM_RBUTTONUP : sEventLog := 'r up'; end; end;
>Anyone know how to put a Delphi application inside the Control Panel?If you use Delphi3, add Cpl unit at dpr file.
I show you a sample code. -----------
library Project1; {Change "program" to "library"} uses Cpl, {use Cpl unit} Windows, Forms, Unit1 in 'Unit1.pas' {Form1}; {$R *.RES} procedure ExecuteApp; begin Application.Initialize; Application.CreateForm(TForm1,Form1); Application.Run; end; {A callback function to export at Control Panel} function CPlApplet(hwndCPl: THandle; uMsg: DWORD; lParam1, lParam2: LongInt):LongInt;stdcall; var NewCplInfo:PNewCplInfo; begin Result:=0; case uMsg of {Initialization.Return True.} CPL_INIT: Result:=1; {Number of Applet.} CPL_GETCOUNT: Result:=1; {Transporting informations of this Applet to the Control Panel.} CPL_NEWINQUIRE: begin NewCplInfo:=PNewCplInfo(lParam2); with NewCplInfo^ do begin dwSize:=SizeOf(TNewCplInfo); dwFlags:=0; dwHelpContext:=0; lData:=0; {An icon to display on Control Panel.} hIcon:=LoadIcon(HInstance,'MAINICON'); {Applet name} szName:='Project1'; {Description of this Applet.} szInfo:='This is a test Applet.'; szHelpFile:=''; end; end; {Executing this Applet.} CPL_DBLCLK: ExecuteApp; else Result:=0; end; end; {Exporting the function of CplApplet} exports CPlApplet; begin end.To use this, change the extention from "dll" to "cpl". And put into the System folder.
Applet means a piece of Control Panel.Display,Fonts,Mouse,System are all Applets.
Basically, you need to add two keys to the registry under HKEY_CLASSES_ROOT. Say your extension in ".ext", then the first key you add is the extension itself:
HKEY_CLASSES_ROOT\ .ext\and set the "default" string value of this key to an "internal name" for your file type - for example MyApp.Document:
HKEY_CLASSES_ROOT\ .ext\ Default = "MyApp.Document"You then create another key with this name:
HKEY_CLASSES_ROOT\ MyApp.Document\Create a sub-key of this called "shell", a sub-key of *this* called "open" and a further sub-key of "open" called "command". The default value uder this key is the location and name of your your application folled by "%1" which represents the filename parameter that Windows will pass to your executable:
HKEY_CLASSES_ROOT\ MyApp.Document\ shell\ open\ command\ Default = "C:\myapp\myapp.exe %1"You can do this in code with the TRegistry object, or use InstallShield, which can make registry changes for you. I'd advise doing both, in case the user trashes your registry entry. From: "Rodney E Geraghty" &tt;gerarod@ibm.net>
The easiest way I've found to do this is to modify the Extensions section of the win.ini file that is located in the Windows directory. This also works under Win 95 and will update the registry automatically under Win95. Look at the extensions section of the win.ini to see the format you have to use. Put IniFiles in your uses clause and then use something like this:
var INIFile: TIniFile; begin try INIFile := TInifile.Create('WIN.INI'); INIFile.WriteString('Extensions','txt','c:\windows\notepad.exe ^.txt'); finally INIFile.Free; end; end;This would associate *.txt files with Windows Notepad. If you had an app named MyApp in the c:\MyApps directory and your extension was *.MAP then you would change it like this:
var INIFile: TIniFile; begin try INIFile := TInifile.Create('WIN.INI'); INIFile.WriteString('Extensions','map','c:\myapps\myapp.exe ^.map'); finally INIFile.Free; end; end;This will work in both Win 3.11 and Win 95 and saves you from having to modify the Reqistry under Win 95. Not sure about Win NT (or Win95b) since I don't have a test machine available. Note that this is only the first part of the solution though since it will open the associated application but it won't load the file you clicked. To do this you have to read ParamStr(1), which would hold the full path of the file you clicked, and run the file name through your file opening routine.