Open WaynePhillipsEA opened 3 years ago
Good idea. I would even simplify EmbeddedResources
into just Resources
.
Because resources are always embedded, or not?
So an enumeration could be
Dim Res As Resource
For Each Res In Resources
If Res.Type = vbResBitmap Then
...
End If
Next Res
And/or alternatively (can be in addition to above scenario which enums technically all resources)
Dim Res As Resource
For Each Res In Resources.Bitmaps
...
Next Res
Addition:
The Resources.Bitmaps
could be an collection which can be accessed by an index (one-based in order it is displayed in the VS code folder in IDE) or key (= MyFile.bmp)
It might also be worth supporting BCP47 language tags (e.g. en-GB and en-US) as well as the current raw LCID_xxxx format for localization support.
I have a possibly ignorant question. For graphic resources, it seems to me that VB* and C++ is very much based on raster images, which make it a poor fit for designing responsive forms or handling high DPI.
We obviously have to support raster images such as bitmaps for backward compatibility. However, I'm thinking that we should have support for vector images to allow seamless scaling, or at least make it easy to provide a larger raster image that can scale down reasonably well. The point here is we want to encourage moving away from fixed-size resources which won't work with large range of scale.
One major shortcoming with VBx is that it does not make it easy to create & maintain a resource table that's more complicated than just a bunch of strings. We already have the ability to create string tables. However, I think we can do better than that.
It's not just a string. We usually have those properties:
Number
Source
Description
HelpFile
HelpContext
Not all of those need to be populated; some of them (e.g. Source
or HelpFile
might be either hard-coded or calculated by an expression. For example, CurrentComponentName & "." & CurrentProcedureName
might be a good candidate for the Source
. Likewise, Number
might be calculated as vbObjectError + N
.
Because of the above, it can get awkward trying to formulate a Err.Raise
. In some case, we may have something that looks like this:
Public Enum MyErrorCodes
SomeComplicatedErrorCode = vbObjectError + 1
...
End Enum
Public Sub ReportSomeComplicatedError( _
SomeData As String, _
ComponentName As String = CurrentComponentName, _
ProcedureName As String = CurrentProcedureName) _
)
Dim AdditionalInfo As String
AdditionalInfo = GetMoreInfo()
Err.Raise SomeComplicatedErrorCode, ComponentName & "." & ProcedureName, "Yikes, something went wrong with the " & SomeData & "..." & AdditionalInfo
End Sub
This is a maintenance nightmare because we now have an enum of error codes, then we have a procedure that is specialized to build an error for this one error. We'd need to write other procedures -- that can get very tedious. Then we have to call the specialized procedure and pass in the arguments to provide the context needed from the procedure where the error should be raised. Way too painful.
Messages are another example where it's not just a string. For example, we may want to use a certain title or a certain icons/buttons associated with a message. We might want to be able to do something like this:
If vbYes = PromptUserForConfirm() Then
Normally, we'd have to write a procedure, maybe something like this:
Public Function PromptUserForConfirm() As vbMsgBoxResult
Return MsgBox("Are you sure?", vbExclamation Or vbYesNo, "Confirm")
End Function
Note that we have 2 strings and a enum to govern the formatting of the messagebox. With a string table, we'd need to create 2 entries for the message text and the title, which is more maintenance.
In .NET, when a resource is created, we can have a early-bound & intellisense aware of resources (e.g. Resources.MyCustomMessage
. That does help a lot with finding and tracking the usages of resources in the codebase. Therefore, the message box example above could be something like:
MsgBox(Resource.ConfirmUser_Text, vbExclamation Or vbYesNo, Resources.ConfirmUser_Title)
This is better now that we no longer have literals in the code and we can at least find the 2 related resources.
But can we do better? I think yes. I can see 2 ways:
Instead of an ordinary string, we can allow for a structure to act as a record. Something like this as an example:
ErrorData = {
Number: Long,
Description: String
},
[
{
Id = "ErrorOne",
Number = 1,
Description = "Error One"
},
{
Id = "ErrorTwo",
Number = 2,
Description = "Error Two"
}
]
We can then make a generic helper function:
Public Sub ReportError( _
Data As ErrorData, _
ComponentName As String = CurrentComponentName, _
ProcedureName As String = CurrentProcedureName, _
)
Err.Raise Data.Number + vbObjectError, ComponentName & "." & ProcedureName, Data.Description
End Sub
The calling code would then be something like:
ReportError ErrorDataResources.ErrorOne
Note that the ErrorDataResources
would be an automatically generated class based on the structure, returning a ErrorData
structure and creating a member for each Id
entry. No literals anywhere!
You can get similar idea for the custom messaging and other possible uses, I think.
However, there's one more way we can improve!
In the above example, we were still using literals which we need to then call into a helper function to construct everything we need. That also means that if we need a different format, we'd need to add branching in the helper function or create a different helper functions. That can get messy...
What if we could do this instead:
ErrorData = {
Parameters: {
ComponentName = CurrentComponentName,
ProcedureName = CurrentProcedureName
},
BaseNumber: Long,
ErrorNumber: '=BaseNumber + vbObjectError'
Source: '=ComponentName & "." & ProcedureName'
Description: String
},
[
{
Id = "ErrorOne",
BaseNumber = 1,
Description = "Error One: Welp...."
},
{
Id = "ErrorTwo",
Parameters = {
AdditionalData: String
},
Number = 2,
Description = "Error Two: {FormatAdditionalErrorInformation(AdditionalData)}"
}
]
The calling codes for each would then be like so:
ReportError ErrorDataResources.ErrorOne
ReportError ErrorDataResources.ErrorTwo(Data)
This way, we do not need to customize ReportError
with multiple versions and compiler can verify that the ErrorTwo
has the required parameter AdditionalData
provided by the calling code, separate from the ErrorOne
which has no required parameters.
Because we can write expressions, I think that yields for a more readable and easier to understand system than if we had to write several overloaded functions and remembering to supply the right parameters for right error/message/whatever it is and hope that we did call the right function...
Is your feature request related to a problem? Please describe.
VB6 doesn't offer a particularly nice way of working with embedded resources. We have LoadResData (etc), but these functions take a simple string or ordinal identifier that is not verified until runtime.
Describe the solution you'd like
I'd like resources to be accessible directly via the object model (e.g.
EmbeddedResources.BITMAP.MyFile
, and for resource types to be enumerable (e.g.For Each Resource In EmbeddedResources.BITMAP
). This would allow for compile time verification of resource use (and later allowing for rename refactoring etc).Additional context
We'd need to accommodate dots inside the resource names (e.g.
EmbeddedResources.BITMAP.[MyFile.bmp]
) and resources that are identified by ordinals.The object returned for each resource should provide appropriate methods. e.g. resources in the BITMAP folder would offer a ToPicture() method that maps to the legacy LoadResPicture function.