zTrace 1.52Is a small Win32 SDK DLL to display debugging information into a popup window tool, and/or a text file.
zTrace uses a distinct thread to work in parallel of the current application you want to debug.
zTrace is very useful at development time to check whether a program operates properly, it has been modeled onto the WinDev's Trace API.
Declaration:DECLARE FUNCTION zTrace LIB "zTrace.DLL" ALIAS "zTrace" (zMessage AS ASCIIZ) AS LONG
DECLARE FUNCTION zDebug LIB "zTrace.DLL" ALIAS "zDebug" (zMessage AS ASCIIZ) AS LONG
Syntax to use:zTrace("StringInformation")
zDebug("StringInformation")
Parameter detail:QuoteStringInformation, holds the information that will be displayed in the Trace window.
Trace Window:
- The information passed as parameter is displayed on the next line of the zTrace window.
- The trace window is automatically opened when zTrace is called, by default, this window is opened at the top left corner of the screen.
- The zTrace window shuts down automatically when you close the application being debugged.
QuoteStringInformation, holds the information that will be stored in the zDebug.txt file.
zDebug.txt report:
- Works exactly like the zTrace window, except that the information is written to a text file.
- The report is automatically created when zDebug is called, it is saved into the same folder than the debugged application.
- zDebug can be used alone or combined with zTrace (when the option is checked in the popup menu).
- zDebug is very handy when the debugged application shuts down unexpectedly or when the application as a short life duration, that won't give you enough time to read what is written in the zTrace window.
- A new fresh zDebug.txt is created each time you start a new zDebug session.
Contextual popup menu (right mouse click on the trace window):
- Use horizontal scrollbar, show or hide the horizontal scrollbar.
- Send selection to printer, print the selected lines (or the whole list when none).
- Copy selection to clipboard, copy the selected lines (or the whole list when none) to clipboard.
- Clear content, clear the content of the Trace window.
- Trace window TopMost, open the Trace window on top of all the other windows (including the windows from the other applications).
- Create zDebug.txt report, the zDebug.txt report is created into the debugged application's folder.
- Save window coordinates, store the size and position of the zTrace window into the registry when closed. Next time the window will be shown using the previous size and location.
Screen shot:(http://www.zapsolution.com/pictures/zDebug.jpg)
This utility is freeware, not public domain. This means that you can use it for your own purposes, even in commercial applications, without paying a fee, but not to make derivative works from, sell or redistribute without permission. Also you must assume the entire risk of using it. Downloading the software indicates that you accept these terms.PS:
zTrace is another example of what could be done only with the plain low level Win32 SDK flat API.
...
zTrace.zip is attached to the first post of this thread.
Thank you to report any problem you may encounter with it.
...
Here are a few insights about
zTrace.
1 - The message pump:
WHILE GetMessage(Msg, %NULL, 0, 0)
'// Easier to detect the right mouse button click on the listBox from the message pump
IF Msg.hWnd = hCtrl AND Msg.Message = %WM_RBUTTONDOWN THEN
CALL ToolProc(hWnd, %WM_RBUTTONDOWN, 0, 0)
ELSE
IF IsDialogMessage(hWnd, Msg) = %FALSE THEN
CALL TranslateMessage(Msg)
CALL DispatchMessage(Msg)
END IF
END IF
WEND
And the masterpiece is this
QuoteIF Msg.hWnd = hCtrl AND Msg.Message = %WM_RBUTTONDOWN THEN
CALL ToolProc(hWnd, %WM_RBUTTONDOWN, 0, 0)
ELSE
It is the easiest way to detect the right mouse button click onto the listBox to popup the contextual menu.2 - The CASE %WM_DESTROY:While
%WM_DESTROY is being called when you click on the close button of the tool window.
It is never called when zTrace shuts down after the closing of the main debugged application,
the solution is to clear and save the zTrace parameters from DllMain:
IF Reason = %DLL_PROCESS_DETACH THEN
IF gnBackBrush THEN
CALL DeleteObject(gnBackBrush): gnBackBrush = 0
CALL zLoadSaveCoordinates (gnX, gnY, gnW, gnH, 1)
END IF
END IF
3 - Save settings:This code is responsible to save/restore the settings from the registry
SUB zLoadSaveCoordinates (BYREF x AS LONG, BYREF y AS LONG, BYREF w AS LONG, BYREF h AS LONG, BYVAL RW AS LONG)
STATIC WasCoordinates AS STRING
IF RW THEN '// Save settings
WasCoordinates = LTRIM$(STR$(x)) + "," + LTRIM$(STR$(y)) + "," + LTRIM$(STR$(w)) + "," + LTRIM$(STR$(h)) + "," + LTRIM$(STR$(gbUseTopMost)) + "," + LTRIM$(STR$(gbSaveCoordinates))
IF LEN(WasCoordinates) THEN CALL zSetReg(%HKEY_LOCAL_MACHINE, $RegistryKey, $RegistryCoordinates, (WasCoordinates))
ELSE '// Restore settings
IF LEN(WasCoordinates) = 0 THEN
WasCoordinates = zGetReg(%HKEY_LOCAL_MACHINE, $RegistryKey, $RegistryCoordinates)
x = VAL(PARSE$(WasCoordinates, 1))
y = VAL(PARSE$(WasCoordinates, 2))
w = VAL(PARSE$(WasCoordinates, 3))
h = VAL(PARSE$(WasCoordinates, 4))
gbUseTopMost = VAL(PARSE$(WasCoordinates, 5))
gbSaveCoordinates = VAL(PARSE$(WasCoordinates, 6)): IF gbSaveCoordinates = 0 THEN w = 0
END IF
IF w = 0 OR h = 0 THEN w = %MIN_WIDTH: h = %MIN_HEIGHT: x = 0: y = 0
END IF
END SUB
...
Thank's Patrice.
could you add to the zTrace tool the ability to print the windows content ?
It would be better than to have to copy the content and paste in an editor
to finaly print it . . .
Thank's
Dominique
Dominique
You can do the same than the code below that copy to clipboard
CASE %IDM_CopyToClipboard
hCtrl = GetDlgItem(hWnd, %ID_LISTBOX)
nCount = SendMessage(hCtrl, %LB_GETCOUNT, 0, 0)
IF nCount > 0 THEN
sBuffer = ""
FOR K = 0 TO nCount - 1
sBuffer = sBuffer + zGetTextListbox(hCtrl, K) + $CRLF
NEXT
' CLIPBOARD SET TEXT sBuffer '// requires PB 9.0+
LOCAL hClipData, hGlob AS DWORD
hClipData = GlobalAlloc(%GMEM_MOVEABLE OR %GMEM_DDESHARE, LEN(sBuffer) + 1)
hGlob = GlobalLock(hClipData)
POKE$ hGlob, sBuffer + CHR$(0)
CALL GlobalUnlock(hClipData)
IF OpenClipboard(0) THEN
CALL EmptyClipboard()
CALL SetClipboardData(%CF_TEXT, hClipData)
CALL CloseClipboard()
ELSE
CALL GlobalFree(hClipData)
END IF
END IF
and instead of sending to clipboard then send it to printer using XPRINT.
I shall update the zip file with the clipboard feature for the next version.
...
Under request of Dominique Bodin,
i have added two new options:
- Send selection to printer
- Copy selection to clipboard
The new zip file is attached to the first post of this thread.
...
Patrice,
Nice tool, thanks !
I sometimes have to show (very) large strings while debugging, so I took the liberty of adding a horizontal scrollbar to zTraces listbox.
Modifications shown in bold:
Quote
hCtrl = CreateWindowEx(0, "ListBox", BYVAL %NULL, _
%WS_CHILD OR %WS_VISIBLE OR %WS_VSCROLL OR %WS_HSCROLL OR %LBS_MULTIPLESEL OR _
%LBS_HASSTRINGS OR %LBS_NOINTEGRALHEIGHT OR %LBS_EXTENDEDSEL OR _
%LBS_DISABLENOSCROLL, _
0, 0, UseW, UseH, _
hWnd, %ID_LISTBOX, zInstance(), BYVAL %NULL)
CALL SENDMESSAGE(hCtrl, %LB_SETHORIZONTALEXTENT, 6000, 0) '<-- change scrollwidth here if necessary
Kind regards
Eddy
www.devotechs.com (http://www.devotechs.com) -- HIME Huge Integer Math and Encryption library--
Eddy,
Thank you for the feedback, i think showing the horizontal srollbar could become another option of the popup menu.
...
Under request of Eddy Van Esch,
i have added a new option:
It can be turned On/Off on the fly.
The new zip file is attached to the first post of this thread.
...
Because no problem was reported, i shall consider that zTrace is working well 8)
...
I have been using it since the day that you posted it.
I love it. :)
No problems to report from me. I will post if something comes up.
Thank you Paul.
...
Patrice,
You know, the danger in publishing a tool like zTrace is that everyone keeps asking for more and more features ... ;D
Speaking about new features ... ;)
Before you published zTrace, I was using a console window (from PBWin) to print text to.
I used that console window often to test algorithms in a small test program without any GUI at all. The console window was the 'GUI'.
At the end of the test code, I place a SLEEP xxx statement to halt the program and to enable me to study the programs output in the console window.
The nice thing of using a console window as output, is that, by closing the console window (clicking the "X") the entire program is terminated. So no need to foresee any code in the test program to properly shut it down. Just clicking the X closes down the program.
The difference with zTrace is that, when I close the zTrace window, the main calling program continues to run.
In some cases, this might be the desired situation, but usually in my case, I prefer that the calling program is terminated along with the debug window.
This is probably asking much, but any chance of implementing this in zTrace ....? :-\
Kind regards
www.devotechs.com (http://www.devotechs.com) -- HIME Huge Integer Math and Encryption library--
In such a case, DestroyWindow(hParent), should do it.
However zTrace is not of much interrest for a console window, because you can use "?" already ...
Hi Patrice,
Is it too late to make another feature request ? :)
When printing a lot of data to the zTrace window, the text disappears on the bottom of the zTrace window.
To view the text that is added when the window is 'full', you have to use the vertical scrollbar to scroll downward to see the text.
Would it be possible to make zTrace scroll the text automatically upwards so that the text that was printed last is always visible ?
This feature would be most helpful to me.
Kind regards
Eddy
I did it already!
(because i had the same need myself)
But i didn't took the time to post it there, i shall do it tomorrow 8)
...
Splendid, Patrice !! :)
Thanks a lot !
Kind regards
Eddy
Thanks again, Patrice !!
Kind regards
Eddy
Thank's a lot patrice.
I see the .bas isn't updated in the first post .zip . . .
Thank's
Dominique
Ok :)
...
Thank's patrice
If you would like to change the background color of zTrace, make following changes to the source code:
Add the line in
bold:
Quote#COMPILE DLL "zTrace.dll"
#INCLUDE "WIN32API.INC"
'----------------------------------------------------------------------
%zTrace_BGColor = &HF4F4F4 'zTrace background color
%dwStyle = %WS_VISIBLE OR %WS_CLIPSIBLINGS OR %WS_CLIPCHILDREN OR %WS_CAPTION OR %WS_SYSMENU OR %WS_THICKFRAME
Change the bold parts:
QuoteCASE %WM_CREATE
'A good place to initiate things, declare variables,
'create controls and read/set settings from a file, etc.
'-------------------------------------------------------
IF gnBackBrush = 0 THEN gnBackBrush = CREATESOLIDBRUSH(%zTrace_BGColor)
Quote
CASE %WM_CTLCOLORBTN, %WM_CTLCOLOREDIT, %WM_CTLCOLORLISTBOX, %WM_CTLCOLORSTATIC
' wParam is handle of control's display context (hDC)
' lParam is handle of control
'------------------------------------------------------
IF lParam = GetDlgItem(hWnd, %ID_LISTBOX) THEN
CALL SETBKCOLOR(wParam, %zTrace_BGColor)
PS. I had to use 'quote' instead of 'code' or I could not indicate the text in bold in the code ... :)
Kind regards
Eddy
Is ztrace thread safe ?
If I ztrace "This is from thread " & Str$(x) from my thread function
Where x = long value passed to the thread from my main app ..
Will this simply add another line to ztrace output from the ztrace window previously opened ?
QuoteWill this simply add another line to ztrace output from the ztrace window previously opened ?
Yes.
...
The first post of this thread has been updated, to fix the ZIP file corruption caused by the "Server Collapse".
...
Patrice,
Thanks for making such a useful tool.
But I'm having problems with zTrace.
I'm running a PB CC v6.03 program that is reading all the normal INC files and capturing certain
bits of information. Normally I'm displaying on the console the files as I process them but needed
a bit more information under strange conditions ( strange based on what I'm trying to capture ...
but the file processing is fairly straight open /read/close ).
So I saw your tool and thought that it would solve my problem and it does the first time thru.
But if when my program is about to end and it's waiting for me to press a key, I change to your
window and select create zDebug output then you create the file. But if I run my program again
and your program reads the ztrace.cfg and sets the option to create the zDebug output, after
22 lines to your window my program aborts and Windows wants to run it thru a debugger.
On the run that works there are around 2000 lines in the zdebug.txt file.
I'm running on Windows XP SP3 with MS Security Essentials with 2 gig ram.
My program has no Threads but does have up to 10 files open at once but they are all closed
before my program ends.
Do you know of anything that could be causing this?
Any suggestions for debugging it? Can I copy most of the source ( minus the DLL main stuff ) into
my CC program and compile it?
Well, it wouldn't compile including most of the zTrace.bas ( minus the dll main ).
Had to add a few variables that were missed ( am using #DIM ALL ). Even tried compiling
the DLL with Win v10.03. Then trying adding #Option AnsiAPI. No difference.
I'll just delete the CFG file and set the Debug save option right before ending my program.
It is a great tool and works for what I need ( with just a little tweaking ).
Could there be some conflict/overlap between CreateFile and PB's FreeFile?
In the main program, I keep opening (based on PB's FreeFile ) & closing files ( eventually but
they may be nested up to 9 levels ). the next file opened is based on PB's FreeFile.
The names will not overlap between main program & DLL but is it possible the underlaying
handle may get closed? If this is possible, how would I check for a handle already in use?
Ended up putting a temporary change in ( until the real reason is found ).
Zeroed gbDebug when it is read in.
In zTrace check for "*CLOSE*IT*" as input string and do the checks/loop to output the
whole listbox and close the file. I send this after I have closed all my files and right before
waiting for a keystroke.
Seems to work for me. Now on to fixing more of my other problems.
Thanks for this program. I'm definatlly gonna use it as I develop my own software.
Great stuff, Patrice
John Aadnoey
That was the first code i ported to 64-bit, because i couldn't develop anymore without it :)
Hello Patrice,
yesterday I made a download for your zTrace 1.52.
Now a question. Can I use that inside a PB-DLL-Code?
Inside a PB-Exe-Sourcecode, it was okay. In a DLL I got an axception error.
To be used from inside DLL(s) was the main purpose of it :)
But perhaps you are using it within a critical section.
And without seeing the code it is hard to say.
Also the PB version uses ASCIIZ (char), while the C++ 32/64-bit uses UNICODE (wchar).
Hi Patric,
I think the reason for the error by using zTrace inside DLL-code was the point, that the EXE is not native code. The EXE goes with a runtime library. With an PB-EXE the error is not repeatable.
So far I have used a solution of MCM -> STDOUT to a console window. Understand I zTrace right, that it is practically the same, only with more comfort?
Quotethe EXE is not native code. The EXE goes with a runtime library
I do not understand, what runtime are you speaking about ::)
This is an EXE that was build with a programming language named PROFAN. A part of the PROFAN EXE is a runtime.
http://xprofan.de/start.htm (http://xprofan.de/start.htm)
Quote from: Patrice Terrier on October 29, 2013, 04:48:46 AM
John Aadnoey
That was the first code i ported to 64-bit, because i couldn't develop anymore without it :)
I can understand why.
On the PB version; why is it using aschiiz and not string? any advantage to asciiz over string?
QuoteOn the PB version; why is it using asciiz and not string? any advantage to asciiz over string?
The answer is obvious: i am using this 32-bit version with several languages, not just PB :)
Use BYCOPY, i.e. zTrace(BYCOPY DumpInfo), instead of zTrace(DumpInfo).
or like this zTrace((PBstring))
that is a short cut for BYCOPY.
I prefer to use the verbose way. It adds clarity. I even use BYREF in the parameters of my declares to be sure that this is what it's intended and not an omission.
I remember that Bob Zale had the project to remove the () short cut, but since his passing it is safe to use it.
Because i can't see who could change this :)
I'm with Jose on the bycopy rather than (()) and byref over undefined. Way easier for us newbies to understand.
Anyway, I load up zTrace from a DLL in my program. And when my program ends, the zTrace DLL is still running, thereby crashing the VB6 IDE. (I Run it inside the IDE rather than compiling it).
My DLL is written in Powerbasic.
Any idea why?
I am not a VB6 user, thus i can't say.
zTrace has been designed to work from a DLL, not to be embedded in your source code.
However if the source code of zTrace doesn't match your level of expectations, feel free to edit it, to let it work the way you want, using basic string rather than ASCIIZ.
You can also write a helper function, just like what i did for the C++ version, to use wstring rather than WCHAR.
Its not embedded in my code. I let my DLL load it.
To me it looks like zTrace is waiting for VB6 to shutdown, not the DLL. (because shutting down the VB6 IDE kills it)
"However if the source code of zTrace doesn't match your level of expectations"
Its definitely up to my expectations. have no doubt about that, I'm just trying to find out why the DLL ain't closing
Again, i have absolutly no knowledge of VB.
Did you try it without the VB6 IDE, i mean as a real standalone EXE ?
Yes just three minutes ago. And then when I run the compiled VB6 code, It shut down as expected.
I wouldn't be surprised if its VB not shutting down the DLL beacuse the DLL has an open window. If window is closed before DLL gets shut down, it'll probably work.
Then this means that when you run it within the VB6 IDE, it runs as p-code not native code, that's it.
...
Found out some more. When I run it in p-Native/VB6 IDE, if;
DLL opens a DialogWindow, program closes = crash
DLL opens a DialogWindow, DLL close DialogWindow, program closes = OK
Probably a VB6 bug/feature.
Hi John,
zTrace is only useable with EXE/DLL of pure native Code. Please see my replys 34 and 36. I made the same experiences with another programming language.
zTrace is using GetModuleHandle("") to retrieve the instance handle, but in case of a run-time this is of course the handle of the run-time!
Ah oki. That explains it. Then I'll just keep on using it on my pure PB hobby projects.
Thanks all
Patrice, late to the party, just stumbled onto this. Thanks so much for sharing this
excellent debugging tool. Here's a little routine I've been using to make coding/debugging
a little easier. I've thrown just about everything at it and it seems to work OK.
Sub DoTrace(ByVal Msg As Variant)
Local sz As AsciiZ * 1000
Select Case VariantVT(Msg)
Case %VT_BStr
sz = Variant$(Msg)
Case %VT_R4, %VT_R8 'single, double
sz = "#" & Format$(Round(Variant#(Msg), 4))
Case Else
sz = "#" & Format$(Variant#(Msg))
End Select
zTrace sz
End Sub
There is no need to use Variant nor Format$ (both are bloated :) ).
You can just use zTrace like this:
zTrace(STR$(Value))
or
zTrace("Value =" + STR$(Value))
and in case of STRING
zTrace((MyString$))
or
zTrace(A$+ B$ + STR$(12))
I absolutely love having zTrace in my toolbox and wanted to give back a slight "helper" that I use with it.
#IF NOT %DEF( %zTrace )
%zTrace = 0
#ENDIF
#IF %zTrace = 0
MACRO zTrace = #UTILITY
MACRO zDebug = #UTILITY
#ELSE
DECLARE FUNCTION zTraceX LIB "zTrace.DLL" ALIAS "zTrace" ( zMessage AS ASCIIZ ) AS LONG
DECLARE FUNCTION zDebugX LIB "zTrace.DLL" ALIAS "zDebug" ( zMessage AS ASCIIZ ) AS LONG
#IF %zTrace > 0
MACRO zTrace = zTraceX
MACRO zDebug = zDebugX
#ELSE
MACRO zTrace = zDebugX
MACRO zDebug = zDebugX
#ENDIF
#ENDIF
This slight mod allows you to leave zTrace code in and never have to remove it. PLUS it adds some flexibility to the output path.
- If %zTrace is defined as a positive number, zTrace and zDebug work exactly as expected.
- If %zTrace is defined as a negative number, zTrace and zDebug both act like zDebug and send to zDebug.txt (allows you to easily switch to "silent" debugging).
- If %zTrace is 0, not defined, or REM'd out, then all zTrace/zDebug lines are compiled as #UTILITY commands, in other words, they are ignored by the compiler.
This allows you to easily include copious real-time logging in your code as well as switch the output (or remove logging completely) with a single constant change.
George--
Thank you.
The current version of zTrace is able to write to zdebug.txt directly from the contextual popup menu.
PBwin 10, now doesn't compile unused code anymore.
Thus using explicit linking rather than implicit is another solution,
and this is what i am using with the 64-bit version (explicit call is the same for PB) :
long zTrace(IN WCHAR* sPtr) {
long nRet = 0;
static HMODULE hDll;
if (hDll == 0) {
if (sizeof(LONG_PTR) == 8) {
hDll = LoadLibrary(L"zTrace64");
}
else {
hDll = LoadLibrary(L"zTrace32");
}
}
if (hDll) {
long_proc(WCHAR*);
static zProc hProc;
if (hProc == 0) { hProc = (zProc)GetProcAddress(hDll, "zTrace"); }
if (hProc) { nRet = hProc(sPtr); }
}
return nRet;
}
The C++ 64-bit DLL version is only 11 Kb in size, it can create ANSI or UNICODE debug file.
In case of ANSI, a UTF-8 BOM header is now being used
this version can be downloaded from www.codeproject.com or from my private forum www.objreader.com
https://www.codeproject.com/Articles/1183063/zTrace-for-bit-only
Patrice,
I reverted back to an older version.
The debug.txt output was persistent once set and I often parse the output so I don't want a BOM.
James
No, it is not, you just have to remember to uncheck the menu option, once you are done, if you don't want it for the next session.
Or feel free to customize the source code to match your preference ;)
...
Emil
zTrace is totaly independent from the compiler, it is a standalone DLL.
It has been inspired by the WinDev Trace API.
...
Hello Patrice
I wanted to use your nice ztrace tool v3 with unicode on window 32
but i've found only the win 64
so i've adapted a litle your code to be able to compile with mingw gcc or g++
//+--------------------------------------------------------------------------+
//| |
//| zTrace 3.00 |
//| |
//| Win32 SDK debugging window DLL |
//| |
//+--------------------------------------------------------------------------+
//| |
//| Author Patrice TERRIER |
//| |
//| copyright(c) 2009-2017 |
//| |
//| Patrice Terrier http://www.zapsolution.com |
//| |
//+--------------------------------------------------------------------------+
//| Project started on : 04--2009 (MM-DD-YYYY) |
//| Last revised : 04-27-2017 (MM-DD-YYYY) |
//+--------------------------------------------------------------------------+
// link to gdi32 and comdlg32 libs with g++ compiler at least for win 32
// for win 64 probably same ?
#ifndef UNICODE
# define UNICODE
#endif
#include <windows.h>
#if defined(__MINGW32__) || defined(__MINGW64__)
# if defined(__MINGW64__)
# define A_CAPTION "zTrace 3.00 64-bit"
# define WND_CAPTION L"zTrace 3.00 64-bit"
# else
# define A_CAPTION "zTrace 3.00 32-bit"
# define WND_CAPTION L"zTrace 3.00 32-bit"
# endif
#endif
#define WND_Style (WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME)
#define WND_ExStyle (WS_EX_TOOLWINDOW)
#define HORIZONTAL_EXTENT 6000 // Maximum value for the horizontal scrollbar
#define MIN_WIDTH 300 // default client width size
#define MIN_HEIGHT 65 // default client height size
#define ID_LISTBOX 1
#define IDM_About 101 // menu popup
#define IDM_Hscroll 103 // menu popup
#define IDM_Print 104 // menu popup
#define IDM_CopyToClipboard 105 // menu popup
#define IDM_ClearContent 106 // menu popup
#define IDM_TopMost 107 // menu popup
#define IDM_Unicode 108 // menu popup
#define IDM_Debug 109 // menu popup
#define IDM_SaveCoordinates 110 // menu popup
typedef struct SETBIN
{
long x;
long y;
long w;
long h;
long topmost;
long savecoordinates;
long usescrollbar;
long debug;
long unicode;
}t_SETBIN;
typedef struct PROP
{
t_SETBIN bin;
HBRUSH backbrush;
HWND hWnd;
HWND hCtrl;
}t_PROP;
t_PROP gP;
BOOL APIENTRY DllMain (HINSTANCE hInst /* Library instance handle. */ ,
DWORD reason /* Reason this function is being called. */ ,
LPVOID reserved /* Not used. */ )
{
switch (reason)
{
case DLL_PROCESS_ATTACH:
break;
case DLL_PROCESS_DETACH:
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
}
/* Returns TRUE on success, FALSE on failure */
return TRUE;
}
void zLoadSaveCoordinates (IN long RW)
{
HANDLE hFile;
DWORD dwBytes = 0;
WCHAR zPath[MAX_PATH];
__stosb((LPBYTE)&zPath, 0, MAX_PATH * 2);
GetTempPath(MAX_PATH - 12, zPath);
lstrcat(zPath, L"zTrace3.cfg");
if (RW)
{
hFile = CreateFile(zPath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile != INVALID_HANDLE_VALUE)
{
WriteFile(hFile, &gP.bin, sizeof(gP.bin), &dwBytes, NULL);
CloseHandle(hFile);
}
}
else
{
__stosb((LPBYTE)&gP.bin, 0, sizeof(gP.bin));
hFile = CreateFile(zPath, GENERIC_READ, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile != INVALID_HANDLE_VALUE)
{
DWORD FileSizeHigh = 0;
if (GetFileSize(hFile, &FileSizeHigh) == sizeof(gP.bin) && FileSizeHigh == 0)
{
ReadFile(hFile, &gP.bin, sizeof(gP.bin), &dwBytes, NULL);
CloseHandle(hFile);
}
}
if ((gP.bin.w == 0) || (gP.bin.h == 0))
{
gP.bin.w = MIN_WIDTH;
gP.bin.h = MIN_HEIGHT;
gP.bin.x = 0;
gP.bin.y = 0;
}
}
}
void zDebug (WCHAR* zMessage)
{
static HANDLE hDebug = NULL;
if (!zMessage)
{
if (hDebug)
{
CloseHandle(hDebug);
hDebug = NULL;
}
return;
}
long Creation = 0;
if (*zMessage)
{
DWORD BytesWritten = 0;
if (!hDebug)
{
hDebug = CreateFile(L"zDebug.txt", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hDebug)
Creation = -1;
}
if (hDebug)
{
if (gP.bin.unicode) // UNICODE
{
WriteFile(hDebug, zMessage, lstrlen(zMessage), &BytesWritten, NULL);
WriteFile(hDebug, L"\r\n", 4, &BytesWritten, NULL);
}
else // ANSI
{
if (Creation)
WriteFile(hDebug, "\xEF\xBB\xBF", 3, &BytesWritten, NULL); // UTF-8 BOM
long length = WideCharToMultiByte(CP_UTF8, 0, zMessage, -1, NULL, 0, NULL, NULL);
if (length > 1) // excluding NUL terminator
{
LPSTR buffer = (LPSTR)GlobalAlloc(GPTR, length);
if (buffer)
{
WideCharToMultiByte(CP_UTF8, 0, zMessage, -1, buffer, length, NULL, NULL);
WriteFile(hDebug, buffer, length - 1, &BytesWritten, NULL);
WriteFile(hDebug, "\r\n", 2, &BytesWritten, NULL);
GlobalFree(buffer);
}
}
}
}
}
}
void SendUnicodeToClipboard(HWND hWnd)
{
long nCount = (long)SendMessage(gP.hCtrl, LB_GETCOUNT, 0L, 0L);
if (nCount > 0)
{
long K, index, cch, bufsize = 0;
long *pSelItems = NULL;
long nSelItems = (long)SendMessage(gP.hCtrl, LB_GETSELCOUNT, 0L, 0L);
if (nSelItems > 0)
{
nCount = 0;
pSelItems = (long *)GlobalAlloc(GPTR, nSelItems * sizeof(long));
if (pSelItems)
{
nCount = nSelItems;
SendMessage(gP.hCtrl, LB_GETSELITEMS, nSelItems, (LPARAM)pSelItems);
}
}
for (K = 0; K < nCount; K++)
{
index = pSelItems ? pSelItems[K] : K;
cch = (long)SendMessage(gP.hCtrl, LB_GETTEXTLEN, (WPARAM)index, 0L);
if (cch > 0)
bufsize += cch + 2;
}
if (bufsize > 0)
{
HGLOBAL hClipData = GlobalAlloc(GHND | GMEM_DDESHARE, (bufsize + 1) * sizeof(WCHAR));
LPTSTR buffer = (LPTSTR)GlobalLock(hClipData);
int offset = 0;
for (K = 0; K < nCount; K++)
{
index = pSelItems ? pSelItems[K] : K;
cch = (long)SendMessage(gP.hCtrl, LB_GETTEXTLEN, (WPARAM)index, 0L);
if (cch > 0)
{
if ((offset + cch + 2) > bufsize)
break;
cch = (long)SendMessage(gP.hCtrl, LB_GETTEXT, (WPARAM)index, (LPARAM)(buffer + offset));
offset += cch;
buffer[offset++] = '\r';
buffer[offset++] = '\n';
}
}
buffer[bufsize] = 0;
GlobalUnlock(hClipData);
if (OpenClipboard(0))
{
EmptyClipboard();
SetClipboardData(CF_UNICODETEXT, hClipData);
CloseClipboard();
}
else
GlobalFree(hClipData);
}
}
}
void DebugRefresh(IN HWND hWnd)
{
if (gP.bin.debug)
{
long nCount = (long)SendMessage(gP.hCtrl, LB_GETCOUNT, 0, 0);
if (nCount > 0)
{
for (long i = 0; i < nCount; i++)
{
long cch = (long)SendMessage(gP.hCtrl, LB_GETTEXTLEN, (WPARAM)i, 0L);
if (cch > 0)
{
LPTSTR pStr = (LPTSTR)GlobalAlloc(GPTR, (cch + 1) * sizeof(WCHAR));
if (pStr)
{
cch = (long)SendMessage(gP.hCtrl, LB_GETTEXT, (WPARAM)i, (LPARAM)pStr);
zDebug((LPWSTR)pStr);
GlobalFree(pStr);
}
}
}
}
}
else
zDebug(NULL); // Close zDebug.txt if already open.
}
LRESULT CALLBACK ToolProc(IN HWND hWnd, IN UINT uMsg, IN WPARAM wParam, IN LPARAM lParam)
{
RECT rc, rw;
long nCount;
HMENU hMenu;
switch (uMsg)
{
case WM_CREATE:
gP.backbrush = CreateSolidBrush(0x00FFFF);
break;
case WM_GETMINMAXINFO:
SetRect(&rc, 0, 0, MIN_WIDTH, MIN_HEIGHT);
AdjustWindowRectEx(&rc, WND_Style, FALSE, WND_ExStyle); // Adjust Window To True Requested Size
((LPMINMAXINFO)lParam)->ptMinTrackSize.x = rc.right;
((LPMINMAXINFO)lParam)->ptMinTrackSize.y = rc.bottom;
break;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDCANCEL:
if (HIWORD(wParam) == BN_CLICKED)
{
SendMessage(hWnd, WM_CLOSE, 0, 0);
return 0;
}
break;
case IDM_About:
// change here to MessageBoxA because pb with // why?
MessageBoxA(0,
" Debugging window\n"
"\n"
" Copyright © 2017 Patrice TERRIER\n"
" pterrier@zapsolution.com\n"
"\n"
" http://www.zapsolution.com \n",
A_CAPTION , MB_OK);
break;
case IDM_Hscroll:
gP.bin.usescrollbar = !gP.bin.usescrollbar;
if (gP.bin.usescrollbar)
{
ShowScrollBar(gP.hCtrl, SB_HORZ, TRUE);
SendMessage(gP.hCtrl, LB_SETHORIZONTALEXTENT, HORIZONTAL_EXTENT, 0);
}
else
{
SendMessage(gP.hCtrl, LB_SETHORIZONTALEXTENT, 1, 0);
ShowScrollBar(gP.hCtrl, SB_HORZ, FALSE);
}
UpdateWindow(gP.hCtrl);
break;
case IDM_Print:
nCount = (long)SendMessage(gP.hCtrl, LB_GETCOUNT, 0L, 0L);
if (nCount > 0)
{
PRINTDLG pd;
__stosb((LPBYTE)&pd, 0, sizeof(pd));
pd.lStructSize = sizeof(pd);
// get rid of PD_RETURNDEFAULT on the line below if you'd like to
// see the "Printer Settings" dialog!
pd.Flags = PD_RETURNDEFAULT | PD_RETURNDC;
if (PrintDlg(&pd))
{
HDC hPrinter = pd.hDC;
HFONT hOldfont, hFont;
hFont = (HFONT)GetStockObject(ANSI_FIXED_FONT);
if (hOldfont = (HFONT)SelectObject(pd.hDC, hFont))
{
TEXTMETRIC tm;
__stosb((LPBYTE)&tm, 0, sizeof(tm));
GetTextMetrics(pd.hDC, &tm);
long yChar = tm.tmHeight + tm.tmExternalLeading;
//long nLinesPerPage = GetDeviceCaps(pd.hDC, VERTRES) / yChar;
//long nCharsPerLine = GetDeviceCaps(pd.hDC, HORZRES) / tm.tmAveCharWidth;
DOCINFO di;
__stosb((LPBYTE)&di, 0, sizeof(di));
di.cbSize = sizeof(di);
StartDoc(hPrinter, &di);
StartPage(hPrinter);
{
long* pSelItems = NULL;
long nSelItems = (long)SendMessage(gP.hCtrl, LB_GETSELCOUNT, 0L, 0L);
if (nSelItems > 0)
{
nCount = 0;
pSelItems = (long*)GlobalAlloc(GPTR, nSelItems * sizeof(long));
if (pSelItems)
{
nCount = nSelItems;
SendMessage(gP.hCtrl, LB_GETSELITEMS, nSelItems, (LPARAM)pSelItems);
}
}
for (long i = 0; i < nCount; i++)
{
long index = pSelItems ? pSelItems[i] : i;
long cch = (long)SendMessage(gP.hCtrl, LB_GETTEXTLEN, (WPARAM)index, 0L);
if (cch > 0)
{
LPTSTR pStr = (LPTSTR)GlobalAlloc(GPTR, (cch + 1) * sizeof(WCHAR));
if (pStr)
{
cch = (long)SendMessage(gP.hCtrl, LB_GETTEXT, (WPARAM)index, (LPARAM)pStr);
TextOut(pd.hDC, 0, (yChar * i), pStr, cch);
GlobalFree(pStr);
}
}
}
if (pSelItems)
GlobalFree(pSelItems);
}
EndPage(hPrinter);
EndDoc(hPrinter);
SelectObject(pd.hDC, hOldfont); // Good pratice
DeleteDC(hPrinter);
}
}
}
break;
case IDM_CopyToClipboard:
SendUnicodeToClipboard(hWnd);
break;
case IDM_ClearContent:
SendMessage(gP.hCtrl, LB_RESETCONTENT, 0, 0);
break;
case IDM_TopMost:
gP.bin.topmost = !gP.bin.topmost;
//if (gP.bin.topmost) {
// SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_SHOWWINDOW);
//} else {
// SetWindowPos(hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_SHOWWINDOW);
//}
SetWindowPos(hWnd, gP.bin.topmost ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_SHOWWINDOW);
break;
case IDM_Unicode:
gP.bin.unicode = !gP.bin.unicode;
DebugRefresh(hWnd);
break;
case IDM_Debug:
gP.bin.debug = !gP.bin.debug;
DebugRefresh(hWnd);
break;
case IDM_SaveCoordinates:
gP.bin.savecoordinates = !gP.bin.savecoordinates;
break;
}
break;
case WM_CTLCOLORDLG:
SetBkColor((HDC)wParam, GetSysColor(COLOR_WINDOW));
return (LRESULT)GetStockObject(NULL_BRUSH);
case WM_CTLCOLORLISTBOX:
// wParam is handle of control's display context (hDC)
// lParam is handle of control
//------------------------------------------------------
if ((HWND)lParam == gP.hCtrl)
{
SetBkColor((HDC) wParam, 0x00FFFF);
return (LRESULT) gP.backbrush;
}
break;
case WM_RBUTTONDOWN:
hMenu = CreatePopupMenu();
if (hMenu)
{
long menuStyle;
AppendMenu(hMenu, MF_STRING, IDM_About , L"About");
AppendMenu(hMenu, MF_SEPARATOR, 102 , L"");
if (gP.bin.usescrollbar)
menuStyle = MF_STRING | MF_CHECKED;
else
menuStyle = MF_STRING;
AppendMenu(hMenu, menuStyle, IDM_Hscroll , L"Use horizontal scrollbar");
AppendMenu(hMenu, MF_STRING, IDM_Print , L"Send selection to printer");
AppendMenu(hMenu, MF_STRING, IDM_CopyToClipboard , L"Copy selection to clipboard");
AppendMenu(hMenu, MF_STRING, IDM_ClearContent , L"Clear content");
if (gP.bin.topmost)
menuStyle = MF_STRING | MF_CHECKED;
else
menuStyle = MF_STRING;
AppendMenu(hMenu, menuStyle, IDM_TopMost , L"Set window TopMost");
if (gP.bin.unicode)
menuStyle = MF_STRING | MF_CHECKED;
else
menuStyle = MF_STRING;
AppendMenu(hMenu, MF_SEPARATOR, 102 , L"");
AppendMenu(hMenu, menuStyle, IDM_Unicode , L"Use Unicode in zDebug.txt");
if (gP.bin.debug)
menuStyle = MF_STRING | MF_CHECKED;
else
menuStyle = MF_STRING;
AppendMenu(hMenu, menuStyle, IDM_Debug , L"Create zDebug.txt report");
if (gP.bin.savecoordinates)
menuStyle = MF_STRING | MF_CHECKED;
else
menuStyle = MF_STRING;
AppendMenu(hMenu, MF_SEPARATOR, 102 , L"");
AppendMenu(hMenu, menuStyle, IDM_SaveCoordinates , L"Save window coordinates");
POINT p;
GetCursorPos(&p);
long nChoice = TrackPopupMenuEx(hMenu, TPM_RETURNCMD, p.x, p.y, hWnd, NULL);
DestroyMenu(hMenu);
if (nChoice)
SendMessage(hWnd, WM_COMMAND, MAKELONG(nChoice, 0), 0);
}
break;
case WM_SIZE:
if (wParam == SIZE_MINIMIZED)
break;
gP.bin.w = LOWORD(lParam);
gP.bin.h = HIWORD(lParam);
MoveWindow(gP.hCtrl, 0, 0, gP.bin.w, gP.bin.h, TRUE);
UpdateWindow(hWnd);
case WM_MOVE:
GetWindowRect(hWnd, &rw);
gP.bin.x = rw.left;
gP.bin.y = rw.top;
break;
case WM_DESTROY:
if (gP.backbrush)
{
DeleteObject(gP.backbrush);
gP.backbrush = 0;
zLoadSaveCoordinates(1);
}
zDebug(NULL);
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
long AddString(IN HWND hCtrl, WCHAR* zPtr)
{
long nRet = LB_ERR;
if (zPtr)
{
if (gP.bin.debug)
zDebug(zPtr);
nRet = (long) SendMessage(hCtrl, LB_ADDSTRING, 0, (LPARAM) zPtr);
SendMessage(hCtrl, LB_SETTOPINDEX, (WPARAM) nRet, 0);
}
return nRet;
}
DWORD WINAPI ShowPopup(IN WCHAR* zPtr)
{
DWORD nRet = 0;
HINSTANCE hInstance = GetModuleHandle(NULL);
WNDCLASSEX wcx;
__stosb((LPBYTE)&wcx, 0, sizeof(wcx));
wcx.cbSize = sizeof(wcx);
long IsInitialized = GetClassInfoEx(hInstance, WND_CAPTION, &wcx);
if (IsInitialized == 0)
{
wcx.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
wcx.lpfnWndProc = &ToolProc;
wcx.cbClsExtra = 0;
wcx.cbWndExtra = 0;
wcx.hInstance = hInstance;
wcx.hIcon = 0;
wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
wcx.hbrBackground = (HBRUSH) COLOR_BTNSHADOW;
wcx.lpszMenuName = NULL;
wcx.lpszClassName = WND_CAPTION;
wcx.hIconSm = wcx.hIcon;
if (RegisterClassEx(&wcx))
IsInitialized = TRUE;
}
if (IsInitialized)
{
zLoadSaveCoordinates(0);
RECT rc;
SetRect(&rc, 0, 0, gP.bin.w, gP.bin.h);
AdjustWindowRectEx(&rc, WND_Style, FALSE, WND_ExStyle); // Adjust Window To True Requested Size
gP.hWnd = CreateWindowEx(WND_ExStyle, WND_CAPTION, WND_CAPTION, WND_Style, gP.bin.x, gP.bin.y, rc.right - rc.left, rc.bottom - rc.top, 0, 0, hInstance, NULL);
if (gP.hWnd)
{
DWORD nStyle = WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_HSCROLL | LBS_MULTIPLESEL | LBS_HASSTRINGS | LBS_NOINTEGRALHEIGHT | LBS_EXTENDEDSEL | LBS_DISABLENOSCROLL;
gP.hCtrl = CreateWindowEx(0, L"ListBox", NULL, nStyle, 0, 0, gP.bin.w, gP.bin.h, gP.hWnd, (HMENU) ID_LISTBOX, hInstance, NULL);
HFONT hFont = CreateFont(-13, 0, 0, 0, FW_NORMAL, 0, 0, 0, DEFAULT_CHARSET, OUT_STROKE_PRECIS, CLIP_STROKE_PRECIS, DRAFT_QUALITY, FF_MODERN | FIXED_PITCH, L"Courier New");
if (hFont) SendMessage(gP.hCtrl, WM_SETFONT, (WPARAM)hFont, 0L);
AddString(gP.hCtrl, zPtr);
if (gP.bin.usescrollbar)
{
SendMessage(gP.hCtrl, LB_SETHORIZONTALEXTENT, HORIZONTAL_EXTENT, 0);
ShowScrollBar(gP.hCtrl, SB_HORZ, TRUE);
}
else
ShowScrollBar(gP.hCtrl, SB_HORZ, FALSE);
if (gP.bin.topmost)
SetWindowPos(gP.hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_SHOWWINDOW);
else
ShowWindow(gP.hWnd, SW_SHOW);
UpdateWindow(gP.hWnd);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
// Easier to detect the right mouse button click on the listBox from the message pump
if ((msg.hwnd == gP.hCtrl) && (msg.message == WM_RBUTTONDOWN))
ToolProc(gP.hWnd, WM_RBUTTONDOWN, 0, 0);
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
DeleteObject(hFont);
nRet = (DWORD)msg.wParam;
}
}
return nRet;
}
#ifdef __cplusplus
extern "C" {
#endif
__declspec(dllexport)long zTrace(WCHAR* zPtr)
{
long nRet = LB_ERR;
if (gP.hWnd == NULL)
{
DWORD dwThreadId = 0;
HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) ShowPopup, zPtr, 0, &dwThreadId);
if (hThread)
{
Sleep(100);
CloseHandle(hThread);
nRet = 0;
}
}
else
nRet = AddString(gP.hCtrl, zPtr);
return nRet;
}
#ifdef __cplusplus
}
#endif
it worked perfectly
the bat file to compile and create the interface for dll libzTrace32.dll.a (for mingw toolchain compilers )
@echo off
Rem check here where is your main compiler folder
SET var=E:\mingw-w64-5.3.0\mingw32\
Rem check here also where are your libgdi32.a & libcomdlg32.a
SET var2=%var%i686-w64-mingw32\
SET lib32=%var2%lib\
@REM TODO: Set PATH's for this session.
SET PATH=%var%bin;%var%i686-w64-mingw32\bin;
SET MINGW_INCLUDE=%var%include
SET MINGW_LIB=%var%lib
echo.
echo Path for compilation : %var%
echo.
echo.
@echo Compile
gcc -c zTrace32.c -o zTrace32.o
@echo.
@echo link
gcc -shared -o zTrace32.dll zTrace32.o %lib32%libgdi32.a %lib32%libcomdlg32.a -s -Wl,--out-implib,libzTrace32.dll.a
@echo.
pause
it produce 25ko dll
i suppose it works also on 64bit compiler
Encore merci pour ce super outil très pratique, et pour avoir publié le code également.
Je le diffuse largement autour de moi... en indiquant l'auteur naturellement.
bye
attached the codes and 32 dll
Le code est également disponible sur codeproject
https://www.codeproject.com/script/Articles/MemberArticles.aspx?amid=1378591