While I'm thinking of it, one feature that I think would be a very good thing for FF3 is to improve the intrinsic support for manifests. You already create a manifest if they select the option for the 6.0 common controls (the "Windows XP theme" controls), but there's more to them with Vista and Windows 7. Specifically, you should also allow developers to specify a trustInfo section that tells Windows if the program should execute with elevated privileges. If that trustInfo section is missing from the manifest, Vista and Win7 considers the program to be a "legacy" application.
While allowing developers to provide their own manifest to merge in with the stock manifest would be ideal, you could start with the simple addition of a checkbox in addition to the "Windows XP Theme" checkbox that would say something like "Application requires administrator privileges". If that checkbox is checked, then you'd want to add a section to the manifest that looks like:
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
</requestedPrivileges>
</security>
</trustInfo>
Otherwise, if the program doesn't require elevated privileges, then you would use:
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
</requestedPrivileges>
</security>
</trustInfo>
Note the only difference is the level attribute being either "requireAdministrator" or "asInvoker".
On a related note that's more of a minor bug, the manifest that you create is not entirely correct. You're just adding a stock manifest that looks like:
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
version="1.0.0.0"
processorArchitecture="X86"
name="PlanetSquires.FireFly.Visual_Designer"
type="win32" />
<description>WinXP Manifest For FireFly Visual Designer</description>
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="X86"
publicKeyToken="6595b64144ccf1df"
language="*" />
</dependentAssembly>
</dependency>
</assembly>
The problem is that you're identifying every application built with Firefly as being version 1.0, and of being Firefly itself. You really should be using the executable name and version information provided by the developer, and have some way for them to provide an optional description. Not a huge deal, but technically what you're doing there is incorrect.
Thanks Mike, I have very little knowledge of the intrinisic benefits of manifests so everything you mentioned in your post certainly helped.
The stock manifest that I used worked so it became a case of "if it ain't broke..." :)
I will implement youf checkbox option suggestion of allowing the elevated privledges to be inserted into the manifest.
For reference, Wikipedia has a decent article on UAC and how elevation is handled with manifests at http://en.wikipedia.org/wiki/User_Account_Control (http://en.wikipedia.org/wiki/User_Account_Control)
The changes suggested by Mike in this thread have been implemented. You will see it in the 3.03 update. A checkbox has been added to the Project Properties called "Application requires administrator privileges". The manifest is also updated to correctly show the application's version number, product name and description.
I'm completely in the dark on manifests, although more and more I seem to be coming across that term. There is this from Mike's Wikipedia reference...
Quote
A program can request elevation in a number of different ways. One way for program developers is to add a requestedPrivileges section to an XML document, known as the manifest, that is then embedded into the application.
The phrase 'that is then embedded into the application' suggests to me that these XML strings somehow go into an rc file that is then compiled into the exe? Where would one go to find out how to do this if that is indeed how it works? I followed the link in Mike's post, which then provided another link on manifests, but the whole context of that was .NET. Somehow these things relate to non-.NET development like we all do, but I don't know how.
Quote from: Fred Harris on November 12, 2009, 12:10:57 PM
The phrase 'that is then embedded into the application' suggests to me that these XML strings somehow go into an rc file that is then compiled into the exe? Where would one go to find out how to do this if that is indeed how it works?
You're right that it's a resource that's compiled into the executable. Here's the steps for a traditional PowerBasic program:
1. Create the XML manifest and name it something like MyProgram.manifest. Here's a complete, example manifest that we use for one of our PowerBasic examples in SocketTools:
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
version="6.0.6010.1194"
processorArchitecture="X86"
name="SocketTools.Example.Download"
type="win32" />
<description>SocketTools Example</description>
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="X86"
publicKeyToken="6595b64144ccf1df"
language="*" />
</dependentAssembly>
</dependency>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
</requestedPrivileges>
</security>
</trustInfo>
</assembly>
2. Next, add the manifest file to the program's resource file, typically named something like MyProgram.rc; if the resource file doesn't exist, you should create one with version information. Near the top of the file, right after the #include "resource.h" line, add:
#define RT_MANIFEST 24
#define CREATEPROCESS_MANIFEST_RESOURCE_ID 1
CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "MyProgram.manifest"
Of course, if you prefer to be terse, you could just add the single line:
1 24 "MyProgram.manifest"
All that does is tell the resource compiler that you want to add a resource with an ID of 1 and type of 24 (which is used for manifests) to the resources for the executable.
3. Compile the resource file (in the PowerBasic IDE, you'd do this by opening the resource file in the editor, and selecting Run | Compile from the menu), and that will create the .pbr and .res files. Then in your program source, add the following:
#RESOURCE "MyProgram.pbr"
That's it. Of course with Firefly, you don't need to worry about any of that. With the option that Paul's adding, you just check a couple of checkboxes and it will do all of this for you so that you don't need to worry about the manifest at all.
Thanks a lot Mike. I really appreciate your helpfulness.
Mike,
Is-it normal that after the tag <description> there is not quotes for the description ?
I've some issues with version FF 3.03 when the description contains some accented characters like é è ê a... then I cannot run the executable file.
Jean-Pierre
Jean-Pierre,
Does the executable work if you manually insert the quotes and then manually recompile (not using FF)?
Quote from: Jean-Pierre Leroy on November 13, 2009, 05:17:19 PM
Is-it normal that after the tag <description> there is not quotes for the description ?
As far as I know quotes aren't required, however non-ASCII characters may need to be escaped using an entity code (i.e.: an & followed by #233; etc.) rather than be included as-is.
Edit: The description is completely optional by the way, it's not required to be in the manifest. So if it saves Paul some headaches in this regard, he could just remove it.
David and Mike thank for your help.
For my FF3 project, the field "File Description" in the "Project Proporties" contains the string "évaluation".
In the CODEGEN*.Exe.Manifest the original line was
<description>évaluation</description>
in that case, I was not able to run the executable.
With quotes ...
<description>"évaluation"</description>
I get the same results, I was not able to run the executable.
But with
<description>#233valuation</description>
I'm able to run the executable ===> Correction: the description is displayed but the exe doesn't run
Not sure that it will be easy to fix.
Jean-Pierre
I can change the code generation when writing the description to the manifest file to simply REPLACE xxx WITH xxx IN xxxx
All I need are the characters that need to be replaced and the #numeric equivalent to replace it with.
I also noticed that Jean-Pierre just used the # sign followed by the number rather than also using the & like Mike suggested.
XML entity codes (and HTML) begin with ampersands; basically what you could do is look at any characters that aren't strictly in the ASCII printable range and replace them with &#nnn;
Paul, Mike I'm sorry for the confusion but I made 3 other tests without any success with theses lines:
<description>#233valuation</description>
or
<description>évaluation</description>
or
<description>évaluation</description>
Seems to be more complex that I thought initially.
Jean-Pierre
I did a little experimenting. The manifest XML expects the document to be UTF-8 encoded (as per the encoding), and the character "é" isn't valid. Apparently, Microsoft also won't accept anything other than UTF-8, I tried different encoding types like ISO-8859-1 and it wouldn't work. If you look in the event log viewer, you can see where the loader is complaining about invalid XML syntax.
So, the solution is that the manifest must be UTF-8 encoded. Now, conveniently, all printable ASCII characters are UTF-8 "encoded" because ASCII and Unicode values in that range are the same; however once you start getting into the extended ANSI characters, the representation changes. For example, the letter "é" is encoded as a two-byte sequence "é" using UTF-8.
Fortunately, Windows makes this pretty easy with the MultiByteToWideChar and WideCharToMultiByte functions. Basically just convert the string to UTF-16 (Unicode) using MultiByteToWideChar, and then from there, convert it from UTF-16 to UTF-8 using WideCharToMultiByte and specify CP_UTF8 as the codepage parameter.
For Paul and anyone else interested, I've attached functions that will convert between ANSI and UTF-8 strings. I'm making the presumption that FF3 was written in PowerBasic, and thought this might help.
Edit: Fixed a minor issue where more memory was being allocated for the ANSI string than was really necessary.
See also: UTF-8 (8-bit Unicode Transformation Format)
http://www.jose.it-berater.org/smfforum/index.php?topic=74.0
' ========================================================================================
' Converts an Ansi string to an UTF-8 encoded string.
' ========================================================================================
FUNCTION AnsiToUtf8 (BYVAL strAnsi AS STRING) AS STRING
LOCAL i AS LONG ' // Loop counter
LOCAL strUtf8 AS STRING ' // UTF-8 encoded string
LOCAL idx AS LONG ' // Position in the string
LOCAL c AS LONG ' // ASCII code
LOCAL b2 AS LONG ' // Second byte
IF LEN(strAnsi) = 0 THEN EXIT FUNCTION
' // The maximum length of the translated string will be
' // twice the length of the original string.
' // We are pre-allocating the buffer for faster operation
' // than concatenating each character one by one.
strUtf8 = SPACE$(LEN(strAnsi) * 2)
' // Intialize index position in the string buffer
' // used to store the UTF-8 encoded string
idx = 1
' // Examine the contents of each character in the Ascii string
FOR i = 1 TO LEN(strAnsi)
' // Get the Ascii code of the character
c = ASC(MID$(strAnsi, i, 1))
' // If it is betwen 0 and 127...
IF c < 128 THEN
' // ...we simply copy it to the string buffer...
MID$(strUtf8, idx, 1) = MID$(strAnsi, i, 1)
' // ...and increase the position by 1.
idx = idx + 1
ELSE
' // We need to split the character into two characters.
' // For the second byte, we only need the lower six bits of the character,
' // and to ensure that the two upper bits will be 10 (in binary),
' // i.e. (00111111 AND xxxxxxxx) OR 10000000
b2 = (c AND &H3F) OR &H80
' // For the first byte, we need only the upper two bits from the character,
' // and to ensure that the three upper bits will be 110 (in binary).
SHIFT RIGHT c, 6
c = c OR &HC0
' // Copy the bytes to the buffer string and increase the index position by 2.
MID$(strUtf8, idx, 2) = CHR$(c, b2)
idx = idx + 2
END IF
NEXT
' // Return the encoded string
FUNCTION = LEFT$(strUtf8, idx - 1)
END FUNCTION
' ========================================================================================
' ========================================================================================
' Converts an UTF-8 encoded string to an Ansi string.
' ========================================================================================
FUNCTION Utf8ToAnsi (BYVAL strUtf8 AS STRING) AS STRING
LOCAL i AS LONG ' // Loop counter
LOCAL strAnsi AS STRING ' // Ascii string
LOCAL idx AS LONG ' // Position in the string
LOCAL c AS LONG ' // ASCII code
LOCAL b2 AS LONG ' // Second byte
LOCAL fSkipChar AS LONG ' // Flag
IF LEN(strUtf8) = 0 THEN EXIT FUNCTION
' // The maximum length of the translated string will be
' // the same as the length of the original string.
' // We are pre-allocating the buffer for faster operation
' // than concatenating each character one by one.
strAnsi = SPACE$(LEN(strUtf8))
' // Intialize index position in the string buffer
' // used to store the converted Ascii string
idx = 1
' // Examine the contents of each character in the UTF-8 encoded string
FOR i = 1 TO LEN(strUtf8)
' // If fSkipChar is set we have to skip this character
IF fSkipChar THEN
fSkipChar = 0
ITERATE FOR
END IF
' // Get the Ascii code of the character
c = ASC(MID$(strUtf8, i, 1))
' // If it is betwen 0 and 127...
IF c < 128 THEN
' // ...we simply copy it to the string buffer...
MID$(strAnsi, idx, 1) = MID$(strUtf8, i, 1)
' // ...and increase the position by 1.
idx = idx + 1
ELSEIF c < 224 THEN
' // We need to join this byte and the next byte.
b2 = ASC(MID$(strUtf8, i + 1, 1))
IF b2 > 127 THEN
c = (c - 192) * 64 + (b2 - 128)
MID$(strAnsi, idx, 1) = CHR$(c)
' // Set the flag to skip the next character
fSkipChar = %TRUE
' // Increase the position by 1.
idx = idx + 1
END IF
END IF
NEXT
' // Return the encoded string
FUNCTION = LEFT$(strAnsi, idx - 1)
END FUNCTION
' ========================================================================================
I just heard back from Jean-Pierre. The manifest problem is all fixed now. The utf-8 function to convert the ansi strings fixed the problem. Thanks guys.