i'll keep this post update with all the things about Rich Edit.
#1 The RichEdit Version
The current versions of RichEdit control and their modules are:
ver 1.0 contained in Riched32.dll
ver 2.0 contained in Riched20.dll
ver 3.0 contained in Riched20.dll
ver 4.1 contained in Msftedit.dll
The Rich Edit 3.0 (Riched20.dll) is included in releases of Microsoft Windows 2000 and above.
#2 Initialization
You need to explicitly load the RichEdit library in your application in order to use the control. Hence, to use the 3.0 version call:
if(NULL == LoadLibrary("RichEd20.dll"))
{
// cannot load RichEdit v3.0
}
Thursday, November 15, 2007
RichEdit common contrl
Open and select a file in windows explorer
Many applications can open an windows explorer window an select a file. This is done by running explorer.exe with some command line switches.
There is a KB with the documented windows explorer switches:
The KB says that the supported versions include old OSes like Windows 95 but I think that 'select' switch for example works only for Windows XP and above.
Here is another KB for XP Windows Explorer.
A solution for older Windows OSes is to use Shell API i think.
I have not tried this but one solution would be to:
call CreateProcess( "explorer.exe" )
enumarate from ROT the windows explorer instances
connect to a instance and use the Shell API to navigate to the file.
There is a KB with the documented windows explorer switches:
The KB says that the supported versions include old OSes like Windows 95 but I think that 'select' switch for example works only for Windows XP and above.
Here is another KB for XP Windows Explorer.
A solution for older Windows OSes is to use Shell API i think.
I have not tried this but one solution would be to:
call CreateProcess( "explorer.exe" )
enumarate from ROT the windows explorer instances
connect to a instance and use the Shell API to navigate to the file.
Monday, October 08, 2007
smallest EXEcutable
This is an interesting page discussing techniques to achieve the smallest EXE possible.
http://www.phreedom.org/solar/code/tinype/
More, it says that if you add an UNC path as a name of an imported DLL in an executable, Windows will download and execute that file !
the server needs to be a WebDAV server.
http://www.phreedom.org/solar/code/tinype/
More, it says that if you add an UNC path as a name of an imported DLL in an executable, Windows will download and execute that file !
the server needs to be a WebDAV server.
Thursday, October 04, 2007
the one and only, the TB_ADDSTRING message !
If you try to use TB_ADDSTRING with the string from the resource then you might had blown your head trying to figure out what the heck is happening, why does it fail:
const int iIndex = ::SendMessage(
hToolBar,
TB_ADDSTRING,
hResource,
IDS_TBSEARCH); // string id from module's resource
iIndex is returned -1 (indicating an error).
Now, I looked at my MSDN from disk and then looked at the online version. Nada. But the old friend Mr. Google saved the day (he always does). I got this page:
No, it's not documented at all.
Something was fishy from the start though.
When you create the toolbar using the CreateToolbarEx API, you pass an array of TBBUTTON structs. TBBUTTON has its last field either an zero-based index or a pointer to string buffers (the last string needed to be terminated by an extra null to indicate the end of the list).
But the question is: In the case when you specified in the TB_ADDSTRING a resource string id, what's the zero-based index from TBBUTTON for ?
The problem is that the documentation is incomplete due to what the API tried to achieve. It wanted to provide several ways to add strings but the documention failed to explain exactly.
In toolbar controls you can set buttons text in at least 2 ways:
One way is when you create the toolbar using CreateToolbarEx API.
In each TBBUTTON structure you can put either a straight text buffer or a zero-based index of the button string:
TBBUTTON tbbtn = {0};
...
tbbtn.iString = _T("Text");
or
TBBUTTON tbbtn = {0};
...
tbbtn.iString = 0; // index
The index value is the index from the string list you set to the toolbar control using the TB_ADDSTRING message. The string list can be from 2 sources, either from:
a character array with one or more null-terminated strings:
LPCTSTR szTexts[] = {_T("Text1"), _T("Text2\0") };
::SendMessage(hToolbar, TB_ADDSTRING, NULL, (LPARAM)szTexts);
or from a string in the executable resource:
::SendMessage(hToolBar,
TB_ADDSTRING,
hResource,
IDS_TBSEARCH); // string id from module's resource
but in the latter case the resource string needs to be in a special form. Its first character will be used by the Common controls API as string separator, so
IDS_TBSEARCH can look like this: |Text1|Text2||
In the first case, you can use TB_ADDSTRING at a time, to add each string to the control internal list.
::SendMessage(hToolbar, TB_ADDSTRING, NULL, (LPARAM)_T("Text1\0"));
::SendMessage(hToolbar, TB_ADDSTRING, NULL, (LPARAM)_T("Text2\0"));
Another way to set a toolbar button text is using the TB_SETBUTTONINFO message but you are limited to pass only a string buffer.
const int iIndex = ::SendMessage(
hToolBar,
TB_ADDSTRING,
hResource,
IDS_TBSEARCH); // string id from module's resource
iIndex is returned -1 (indicating an error).
Now, I looked at my MSDN from disk and then looked at the online version. Nada. But the old friend Mr. Google saved the day (he always does). I got this page:
The problem with TB_ADDSTRING or the AddString method that is simply
a wrapper to this message is that the first character in the string
resource is used as separator and is replaced with a NULL character.
So your string resource must be something similar to: "|Button text||"
where the pipe character is replaced with '\0'.
This is because the string you pass with TB_ADDSTRING message must
have a double null terminator. You can also use the TB_ADDSTRINGS
message with which you can set all the button texts using a string
like this: "|Button 1|Button 2|Button 3|Button 4||".
This is not well documented or not documented at all, I don't
remember. Anyway keep in mind that the text added to the button with
TB_ADDSTRING is not retrieved using TB_GETBUTTONINFO, but only using
TB_GETBUTTONTEXT, this is why if you use this method instead of using
TB_SETBUTTONINFO, the chevron menu does not display the text of the
menu items
No, it's not documented at all.
Something was fishy from the start though.
When you create the toolbar using the CreateToolbarEx API, you pass an array of TBBUTTON structs. TBBUTTON has its last field either an zero-based index or a pointer to string buffers (the last string needed to be terminated by an extra null to indicate the end of the list).
Zero-based index of the button string, or a pointer to a string buffer that contains text for the button.
But the question is: In the case when you specified in the TB_ADDSTRING a resource string id, what's the zero-based index from TBBUTTON for ?
The problem is that the documentation is incomplete due to what the API tried to achieve. It wanted to provide several ways to add strings but the documention failed to explain exactly.
In toolbar controls you can set buttons text in at least 2 ways:
One way is when you create the toolbar using CreateToolbarEx API.
In each TBBUTTON structure you can put either a straight text buffer or a zero-based index of the button string:
TBBUTTON tbbtn = {0};
...
tbbtn.iString = _T("Text");
or
TBBUTTON tbbtn = {0};
...
tbbtn.iString = 0; // index
The index value is the index from the string list you set to the toolbar control using the TB_ADDSTRING message. The string list can be from 2 sources, either from:
a character array with one or more null-terminated strings:
LPCTSTR szTexts[] = {_T("Text1"), _T("Text2\0") };
::SendMessage(hToolbar, TB_ADDSTRING, NULL, (LPARAM)szTexts);
or from a string in the executable resource:
::SendMessage(hToolBar,
TB_ADDSTRING,
hResource,
IDS_TBSEARCH); // string id from module's resource
but in the latter case the resource string needs to be in a special form. Its first character will be used by the Common controls API as string separator, so
IDS_TBSEARCH can look like this: |Text1|Text2||
In the first case, you can use TB_ADDSTRING at a time, to add each string to the control internal list.
::SendMessage(hToolbar, TB_ADDSTRING, NULL, (LPARAM)_T("Text1\0"));
::SendMessage(hToolbar, TB_ADDSTRING, NULL, (LPARAM)_T("Text2\0"));
Another way to set a toolbar button text is using the TB_SETBUTTONINFO message but you are limited to pass only a string buffer.
Thursday, September 27, 2007
MAKEINTRESOURCE macro
Many Win32 API functions use a LPCTSTR parameter as resource name or type.
For example, a trivial one:
HICON LoadIcon(HINSTANCE hInstance, LPCTSTR lpIconName);
And the documentation for the lpIconName states that:
Pointer to a null-terminated string that contains the name of the icon resource to be loaded. Alternatively, this parameter can contain the resource identifier in the low-order word and zero in the high-order word. Use the MAKEINTRESOURCE macro to create this value.
Now, how the LoadIcon code knows to diferentiate between a resource string name and a predefined constant like IDI_ASTERISK which is defined as:
#define IDI_ASTERISK MAKEINTRESOURCE(32516)
We are used to the wizard generated resource IDs in our applications but a resource can have any null terminated string as name and type too. Look to the FindResource function for example.
All resource Win32API functions use the macro IS_INTRESOURCE(id)
BOOL IS_INTRESOURCE(
WORD wInteger
);
#define IS_INTRESOURCE(_r) (((ULONG_PTR)(_r) >> 16) == 0)
If the macro is TRUE then the id "specifies the integer identifier of the name or type of the given resource. Otherwise, those parameters are long pointers to null-terminated strings"
Practically, the memory address of a name or type passed as input, is checked to see if its within the WORD boundary, or else said if the memory address is between 0 and unsigned short (65536), because:
typedef unsigned short WORD;
So, for the win32 API functions, if the pointer value has an 'invalid address' it's clear that it's rather an id (a integer, an unsisgned short in fact) then an actual memory address containing a null terminated string from the application.
For example, a trivial one:
HICON LoadIcon(HINSTANCE hInstance, LPCTSTR lpIconName);
And the documentation for the lpIconName states that:
Pointer to a null-terminated string that contains the name of the icon resource to be loaded. Alternatively, this parameter can contain the resource identifier in the low-order word and zero in the high-order word. Use the MAKEINTRESOURCE macro to create this value.
Now, how the LoadIcon code knows to diferentiate between a resource string name and a predefined constant like IDI_ASTERISK which is defined as:
#define IDI_ASTERISK MAKEINTRESOURCE(32516)
We are used to the wizard generated resource IDs in our applications but a resource can have any null terminated string as name and type too. Look to the FindResource function for example.
All resource Win32API functions use the macro IS_INTRESOURCE(id)
BOOL IS_INTRESOURCE(
WORD wInteger
);
#define IS_INTRESOURCE(_r) (((ULONG_PTR)(_r) >> 16) == 0)
If the macro is TRUE then the id "specifies the integer identifier of the name or type of the given resource. Otherwise, those parameters are long pointers to null-terminated strings"
Practically, the memory address of a name or type passed as input, is checked to see if its within the WORD boundary, or else said if the memory address is between 0 and unsigned short (65536), because:
typedef unsigned short WORD;
So, for the win32 API functions, if the pointer value has an 'invalid address' it's clear that it's rather an id (a integer, an unsisgned short in fact) then an actual memory address containing a null terminated string from the application.
Wednesday, September 26, 2007
Navigating to an embedded resource in .NET
This task is very common but since i did not find a clear and simple solution i decided to write it.
I have a simple C# application which has a webbrowser control and i want to use the res:// protocol to navigate to an embedded resource, a html file from the assembly, like this:
webbrowser.Navigate("res://myApp.exe/Help.htm");
since managed resources are not native ones, adding an html file to the project and select "embedded resource" wont help.
if you open the assembly with a resource editor you won;t see the html resource.
hence, the res:// protocol wont work.
so i wrote this simple console application which accepts as parameters the resource file you want to add.
Usage:
AddResourceManaged.exe ExePath ResFile ResName [ResType]
adds a new resource to an exe file
ExePath - path of the exe
ResFile - path of the file to be added as a resource
ResName - name of the resource
ResType - (optional) type of the resource; if omitted, the application will try to use one of the default resource types
based on the ResFile file extension
Example:
AddResourceManaged.exe myManagedApp.exe Help.htm help.htm 23
it is equivalent to:
AddResourceManaged.exe myManagedApp.exe Help.htm help.htm
because the value of MAKEINTRESOURCE(RT_HTML) is 23
I have a simple C# application which has a webbrowser control and i want to use the res:// protocol to navigate to an embedded resource, a html file from the assembly, like this:
webbrowser.Navigate("res://myApp.exe/Help.htm");
since managed resources are not native ones, adding an html file to the project and select "embedded resource" wont help.
if you open the assembly with a resource editor you won;t see the html resource.
hence, the res:// protocol wont work.
so i wrote this simple console application which accepts as parameters the resource file you want to add.
Usage:
AddResourceManaged.exe ExePath ResFile ResName [ResType]
adds a new resource to an exe file
ExePath - path of the exe
ResFile - path of the file to be added as a resource
ResName - name of the resource
ResType - (optional) type of the resource; if omitted, the application will try to use one of the default resource types
based on the ResFile file extension
Example:
AddResourceManaged.exe myManagedApp.exe Help.htm help.htm 23
it is equivalent to:
AddResourceManaged.exe myManagedApp.exe Help.htm help.htm
because the value of MAKEINTRESOURCE(RT_HTML) is 23
Wednesday, July 25, 2007
AIAB (Asynchronous Invocation Application Block)
i'll edit this post from time to time to update it untill it's finished.
Worker Thread and Service Agent execution.
If an exception is thrown from the SA, the exceeption is logged (using the Exception management mechanism). The default exception publisher logs to System Event log.
This is because the worker thread has a try-catch inside the thread proc. And this makes that the while loop inside the thread procedure to continue.
The Worker thread has a serviceAgentRequest memeber variable which is set \ cleared to null on SA creation execution \ SA execution returns.
Worker thred checks for this variable at the loop begin and resubmits the request (it wont look at the requests queue).
So, when an exception is thrown from a SA during execution, the SA will be resubmitted indefinetely until the SAMonitor aborts the worker thread.
Regarding the SAMonitor: Note that in the case of the exeception thrown from SA, the SAMonitor is called to add the worker thread at each resubmission. But SAMonitor checks if it has that worker thread already in its thread table and removes it and adds it again.
So this means that if a SA exception appears you can decide to either rethrow the exception so the request is resubmit or not rethrow it and maybe just return from execution (and the worker thread will mark the request as completed).
Worker Thread and Service Agent execution.
If an exception is thrown from the SA, the exceeption is logged (using the Exception management mechanism). The default exception publisher logs to System Event log.
This is because the worker thread has a try-catch inside the thread proc. And this makes that the while loop inside the thread procedure to continue.
The Worker thread has a serviceAgentRequest memeber variable which is set \ cleared to null on SA creation execution \ SA execution returns.
Worker thred checks for this variable at the loop begin and resubmits the request (it wont look at the requests queue).
So, when an exception is thrown from a SA during execution, the SA will be resubmitted indefinetely until the SAMonitor aborts the worker thread.
Regarding the SAMonitor: Note that in the case of the exeception thrown from SA, the SAMonitor is called to add the worker thread at each resubmission. But SAMonitor checks if it has that worker thread already in its thread table and removes it and adds it again.
So this means that if a SA exception appears you can decide to either rethrow the exception so the request is resubmit or not rethrow it and maybe just return from execution (and the worker thread will mark the request as completed).
Subscribe to:
Posts (Atom)