Jump to content

Recommended Posts

Posted
2 hours ago, argumentum said:

Nice !.
The $tagIStreamErrorTable is in French. I don't mind because am a polyglot ( via google ) but in English would be better :) 

Shall we do a copy-paste? 😁
 
; #INTERNAL_USE_ONLY# ===========================================================================================================
; Name ..........: $tagIStreamErrorTable
; Description ...: 2D array containing possible HRESULT error codes for the IStream interface, along with their symbolic names and descriptions.
; Fields ........: HRESULT (hex) | Symbolic name | Description | Affected methods
; Author ........: Numeric1
; Modified ......: 2025-07-27
; Remarks .......: HRESULT codes starting with 0x8003 are specific to storage errors (STG_E_*). Use this array to translate HRESULT codes returned by IStream methods.
; Related .......: IStream UDF
; ===============================================================================================================================
Global Const $tagIStreamErrorTable = [ _
        ["0x80030001", "STG_E_INVALIDFUNCTION", "Function invalid for this stream type (e.g., Revert on a non-transactional stream).", "Revert, Commit, LockRegion, UnlockRegion"], _
        ["0x80030002", "STG_E_FILENOTFOUND", "The specified file or stream does not exist.", "Stat"], _
        ["0x80030008", "STG_E_TOOMANYOPENFILES", "Too many files or streams are open.", "Open, Create"], _
        ["0x8003001D", "STG_E_INVALIDPOINTER", "Invalid pointer passed to the method.", "Read, Write, Seek, SetSize, CopyTo, Stat, Clone"], _
        ["0x80030020", "STG_E_LOCKVIOLATION", "Lock violation (e.g., accessing a locked region).", "Read, Write, LockRegion, UnlockRegion"], _
        ["0x80030021", "STG_E_FILEALREADYEXISTS", "The stream or file already exists.", "Create"], _
        ["0x80030050", "STG_E_INVALIDNAME", "Invalid stream name.", "Stat"], _
        ["0x80030057", "STG_E_INVALIDPARAMETER", "Invalid parameter passed to the method.", "Seek, CopyTo, Commit, LockRegion, UnlockRegion"], _
        ["0x80030070", "STG_E_MEDIUMFULL", "The storage medium is full.", "Write, SetSize, CopyTo"], _
        ["0x800300F0", "STG_E_READFAULT", "Error reading from the stream.", "Read, CopyTo"], _
        ["0x800300FA", "STG_E_WRITEFAULT", "Error writing to the stream.", "Write, CopyTo, SetSize"], _
        ["0x80030102", "STG_E_REVERTED", "The stream was reverted and is no longer valid.", "Read, Write, Seek"], _
        ["0x80030103", "STG_E_NOTCURRENT", "The stream has been modified since the last operation.", "Commit, Revert"], _
        ["0x80030104", "STG_E_CANTSAVE", "Unable to save changes to the stream.", "Commit"], _
        ["0x8007000E", "E_OUTOFMEMORY", "Insufficient memory to perform the operation.", "Read, Write, SetSize, CopyTo, Clone"], _
        ["0x80070057", "E_INVALIDARG", "One or more arguments are invalid.", "Read, Write, Seek, SetSize, CopyTo, Commit, LockRegion, UnlockRegion, Stat, Clone"], _
        ["0x80004001", "E_NOTIMPL", "The method is not implemented for this stream type (e.g., LockRegion on a memory stream).", "LockRegion, UnlockRegion"], _
        ["0x80004005", "E_FAIL", "Unspecified operation failure.", "All methods"], _
        ["0x80030006", "STG_E_INSUFFICIENTMEMORY", "Insufficient memory for the operation (similar to E_OUTOFMEMORY).", "Read, Write, SetSize, CopyTo, Clone"] _
        ]

thanks

From 0 and 1 to the stars, it all starts with binary, Numeric1
 
Posted

🔧 Proposal: COM Interface Validation via QueryInterface (_IsObjType, _IsStream)

Hi everyone 👋,

In the IStream.au3 UDF I recently shared, some internal functions rely on IsObj() to validate the COM objects passed as parameters. While convenient, this check does not guarantee that the object truly implements the expected COM interface (e.g., IStream).

I’m suggesting a more robust alternative that directly uses the COM mechanism QueryInterface() to verify if a given object supports a specific interface via its Interface Identifier (IID).

Goal

- Ensure a COM object actually implements the required interface (IStream, IMarshal, IPersistStream, etc.).
- Avoid runtime crashes when calling methods on unsupported interfaces.
- Follow COM best practices: validate interface compatibility through QueryInterface() rather than relying only on IsObj().

 

🔍 Difference: IsObj() vs _IsObjType()

; Comparison Table: IsObj() vs _IsObjType()

; +----------------+-------------------+--------------+---------------------------------------------+
; |   Check Type   |   Reliability     | Performance  |                   Comment                   |
; +----------------+-------------------+--------------+---------------------------------------------+
; |   IsObj()      | Basic (any object)|   Fast       | Does NOT verify interface type              |
; | _IsObjType()   | Strong (uses QI)  |   Slower     | Uses QueryInterface for real type checking  |
; +----------------+-------------------+--------------+---------------------------------------------+


 

⚠️ Performance in loops

Using QueryInterface() internally, _IsObjType() is naturally more expensive than IsObj():
- It converts the IID to a binary structure
- Calls the COM method QueryInterface
- Releases the interface pointer safely to avoid memory leaks

This performance impact is usually negligible in typical use.  
However, within loops (e.g. enumerating files or objects via IEnumSTATSTG, IEnumUnknown), repeatedly calling _IsObjType() could slow down execution.

💡 In such cases:
- Perform the check once before the loop
- Or cache the result locally

 

🔧 Main Function: _IsObjType()

Func _IsObjType(ByRef $oObject, $sIID, $blTag = True)
    If Not IsObj($oObject) Then Return SetError(1, 0, 0)
    If Not IsString($sIID) Or $sIID = "" Then Return SetError(2, 0, 0)

    Local $tRIID = _WinAPI_GUIDFromString($sIID)
    If Not IsDllStruct($tRIID) Then Return SetError(3, 0, 0)

    Local $aCall = ($blTag) ? _
        $oObject.QueryInterface("hresult", "ptr", DllStructGetPtr($tRIID), "ptr*", 0) : _
        $oObject.QueryInterface(DllStructGetPtr($tRIID), 0)

    If @error Then
        If Not IsArray($aCall) Then Return SetError(4, @error, 0)
        If $aCall[0] <> 0 Then Return SetError(5, $aCall[0], 0)
    EndIf

    Local $pInterface = $aCall[2]
    If $pInterface Then
        DllCall("ole32.dll", "ulong", "Release", "ptr", $pInterface)
        Return 1
    EndIf
    Return SetError(6, 0, 0)
EndFunc

 

🔁 _IsStream() simplified example

Func _IsStream(ByRef $oObject)
    Return _IsObjType($oObject, "{0000000C-0000-0000-C000-000000000046}")
EndFunc

---

🔗 Suggested Integration in UDF

I recommend using these functions instead of IsObj() in your COM wrappers (especially those expecting IStream or other specific interfaces).  
For example, replace:

If Not IsObj($oStream) Then Return SetError(1, 0, 0)


with:
 

If Not _IsStream($oStream) Then Return SetError(1, 0, 0)

 

📎 References
- IUnknown::QueryInterface (MSDN)
- IStream interface (MSDN)

 

What do you think?  
Should I integrate these improvements into the next version of the IStream.au3 UDF?  
All feedback and suggestions are welcome! 🙏
 

From 0 and 1 to the stars, it all starts with binary, Numeric1
 

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...