CWindow Release Candidate 18
Incorporates all the previously discussed changes.
The most notable recent feature is the implementation of an OLE Container.
The attached example demonstrates how to host the WebBrowser Control, connect to the events fired by it and use the IDocHostUIHandler interface to customize the control, in this case to make it DPI aware.
GDI+ Examples
These are the examples that I have used to test the CGdiPlus classes.
Around 300 small examples demostrating the use of the methods.
Jose,
I was just taking a peek at your AfxOleCon.inc.
Is this line correct with the double braces at the end???
CONST OC_IID = "{12520D52-F372-47FF-A74D-5FFA862BF4BA}}" ' // Container's IID
James
You can remove it. It is not used at all.
I'm currently working in the another datatype missing in FB, VARIANTs. It is going very well.
When finished, I will be able to add support to several Windows COM technologies, such WMI and ADO.
Everything using FB code, without C++, Visual Basic or wathever.
Because the FB headers don't provide support for PropSys.dll, an essential library to work seriously with variants, I'm loading it in the CVAR class and calling the functions dynamically, e.g.
' ========================================================================================
PRIVATE FUNCTION CVar.VariantToStringArray (BYVAL pvar AS VARIANT PTR, BYVAL prgsz AS PWSTR PTR, BYVAL crgsz AS ULONG, BYVAL pcElem AS ULONG PTR) AS HRESULT
FUNCTION = E_POINTER
IF psLib = NULL THEN EXIT FUNCTION
DIM pVariantToStringArray AS FUNCTION (BYVAL pvar AS VARIANT PTR, BYVAL prgsz AS PWSTR PTR, BYVAL crgsz AS ULONG, BYVAL pcElem AS ULONG PTR) AS HRESULT
pVariantToStringArray = DyLibSymbol(psLib, "VariantToStringArray")
IF pVariantToStringArray THEN FUNCTION = pVariantToStringArray(pvar, prgsz, crgsz, pcElem)
END FUNCTION
' ========================================================================================
CVar - Variant class
A wrapper class to deal with variants.
The constructors and LET operators only admit a string, a variant or another CVAR because if, for example, you assign a value of 1, there is no way to know if the variant type of the target variant has to be byte, long, single, etc. Therefore, to assign or cast a numeric value you have to use the various vtxxx properties, e.g. vtLong, vtSingle, etc.
DIM cv AS CVAR
cv.vtLong = 12345
print cv.vtLong
With strings, you can use a constructor
DIM cv AS CVAR = "This is a test string"
print cv.Str
or the Str property
DIM cv AS CVAR
cv.Str = "This is a test string"
print cv.Str
When calling a function with an OUT variant parameter you will use the @operator
Foo @cv
When wanting to pass it by value, you will use
Foo cv
where Foo is the name of the procedure.
A default constructor for numbers could be added to allow to pass numbers by value to functions that have parameters declared as CVar, just as we can already do with strings, without having to declare first a variable as CVAR ans pass this variable. But which type to choose for the variant, double?
I have added two new constructors for numbers, one for LONGs and another for DOUBLEs, that are the ones most used in COM. For other types, dim a CVAR variable, assign the correct type and pass the variable.
There is a way but implies the use of casting, eg. CLNG(12),
Be warned that this class is still evolving. I'm going to add new methods.
If the variant is of the type VT_ARRAY, I will add methods to return the number of dimensions of the underlying safe array, the lower and upper bounds, and if the underlying safe array is an array of variants, a method to get individual elements as a CVAR.
I will also add new constructors and operators for each data type, but if you pass a number instead of a variable, you will have to cast it with CLNG, CBYTE, etc.
This is going to be another great new data type. I'm already using it to implement an associative array of variants. Such an array can be added to CWindow to store all kind of indexed data available at any time from any part of the application.
If I ever implement a safearray class (a one-dimensional is not too difficult) we will have all we need to work with COM.
Jose
QuoteBecause the FB headers don't provide support for PropSys.dll
if you are still interrested i've managed to make the libpropsys.dll.a (32bits)
as i understood, James Fuller has already done a working 64bits version,
and with your propvautil.bi file you normally have everything to use propsys.dll,
without calling dynamically each function...
even in win xp ( wich does not have the propsys.dll), i've put a vista version in my system32
and tested like that (following your proposal on other post)
#include "windows.bi"
#include once "win/ole2.bi"
#INCLIB "propsys"
extern "Windows"
DECLARE FUNCTION VariantGetElementCount (BYVAL varIn AS VARIANT PTR) AS ULONG
DECLARE FUNCTION VariantToInt16(BYVAL varIn AS VARIANT PTR, BYVAL piRet AS SHORT PTR) AS HRESULT
end extern
PRIVATE FUNCTION VariantFromInt16(BYVAL iVal AS SHORT, BYVal pvar AS VARIANT PTR) AS HRESULT
pvar->vt = VT_I2
pvar->iVal = iVal
return S_OK
END FUNCTION
'CoInitialize NULL
DIM v AS VARIANT
VariantFromInt16(-32000,@v)
DIM n AS ULONG = VariantGetElementCount(@v)
DIM m AS short
VariantToInt16(@v, @m)
'CoUninitialize
print n
print m
sleep i've also done tests with your propvautil.bi , its seems ok
here the attached file with libpropsys.dll.a and the def file used to make it
Thanks very much, but I no longer need it since I already have implemented the functions in CVar.inc. It has the advantage of not having to force the users of copying the libraries and includes to the FB lib and inc folders. Another advantage is not to have to deal with libraries, that I find annoying: one for 32 bit, another for 64 bit, one function exists in one library but not in the other...
IMO the best system is the one used by other BASICs, but with delay loading, that is what I have done. If implemented in the compiler, only declares will we needed, with a clause like "DELAY".
I have added constructors and operators for all the types, and several additional methods. Testing will tell us if more methods will be needed.
The attached file contains the current CVar.inc.
And for testing it, I have written another class, CDicObj, that implements an associative array with variants.
Usage example:
#include "Afx/CDicObj.inc"
using Afx
' // Create an instance of the CDicObj class
DIM pDic AS CDicObj
DIM cv AS CVAR
pDic.Add "a", "Athens"
pDic.Add "b", "Madrid"
pDic.Add "c", "Roma"
print pDic.Count
print pDic.Exists("a")
print pDic.Item("a").Str
print "-------------"
DIM cvItems AS CVAR = pDic.Items
FOR i AS LONG = cvItems.GetLBound TO cvItems.GetUBound
print cvItems.GetVariantElem(i).Str
NEXT
PRINT
PRINT "Press any key..."
SLEEP
As it uses variants, both the keys and items can be any type, and also can be mixed.
The code of CDicObj also demonstrates how to use CVar effectively.
Jose , sincerly your code flow is like the Amazonia River...
impressive, and as always excellent.
Jose in your CVar.inc :
QuoteNote: Requires Windows XP SP2 or superior.
I am under XP SP3 and that propsys.dll is not part of the system ( it is normally beginning with vista , I think)
if needed, has to be placed a downloaded version on system32 folder for 32bits !
is it what i've done, and seems to work ...
I have modified some comments and added the AnsiStr property to convert to ansi. The CAST () AS STRING operator returns an ansi string that contains unicode. This is a hack to workaround the problem of not being able of returning a CBSTR in an operator.
Therefore, we can use
print pDic.Item("a")
or
print pDic.Item("a").Str
that will print an unicode string,
or
print pDic.Item("a").AnsiStr
that will print an ansi string.
We can also use UTF8:
pDic.Add "a", AfxUcode("Αθήνα", CP_UTF8)
Quote from: Marc Pons on August 29, 2016, 07:14:34 AM
Jose in your CVar.inc :
QuoteNote: Requires Windows XP SP2 or superior.
I am under XP SP3 and that propsys.dll is not part of the system ( it is normally beginning with vista , I think)
if needed, has to be placed a downloaded version on system32 folder for 32bits !
is it what i've done, and seems to work ...
That is what Microsoft says...
Minimum supported client
Windows XP with SP2, Windows Vista [desktop apps only]
https://msdn.microsoft.com/en-us/library/windows/desktop/bb776584(v=vs.85).aspx
Quote from: Marc Pons on August 29, 2016, 07:02:49 AM
Jose , sincerly your code flow is like the Amazonia River...
impressive, and as always excellent.
I'm becoming old and can't wait years. Every day counts...
Each new class that I write opens new possibilities, but also the need for more classes.
I have almost finished a class for WINHTTP, but I need support for streams, so I will have to write a CStream class some day to finish it...
The new variant class will allow me to write classes for ADO, and the ADO classes will allow me to write classes for CDO.
shlwapi.dll provides API functions to work with streams, but, of course, they aren't declared in the FB headers. What a surprise! Well, it doesn't matter.
Quote from: Jose Roca on August 29, 2016, 10:21:53 AM
shlwapi.dll provides API functions to work with streams, but, of course, they aren't declared in the FB headers. What a surprise! Well, it doesn't matter.
Jose,
Just a FYI.
Peter of BaCon fame switched to dynamic loading of shared libraries quite some time ago.
With the number of linux distros you never know exactly which version of a shared library is available.
Also this way you can call the parameters just about any thing you want ala PowerBASIC. :)
Fred also did this with his TCLib.
James
And now I'm doing it with FreeBASIC. I had a lot of problems with GdiPlus. Some functions are supported in 32 bit and not in 64 bit, and viceversa. The problem is that GdiPlus has more than 600 functions...
Another test for the new CVA data type.
CWinHTTPRequest class
Microsoft Windows HTTP Services (WinHTTP) provides developers with a server-supported, high-level interface to the HTTP/1.1 Internet protocol. WinHTTP is designed to be used primarily in server-based scenarios by server applications that communicate with HTTP servers.
https://msdn.microsoft.com/en-us/library/windows/desktop/aa382925(v=vs.85).aspx
Thanks Jose for all your incredible hard work. You have driven forward FreeBasic Windows development exponentially.
Some examples:
' ========================================================================================
' The following example checks if the current platform is supported.
' ========================================================================================
#include "windows.bi"
#include "Afx/CWinHttpRequest.inc"
using Afx
IF AfxWinHttpCheckPlatform THEN
PRINT "This platform is supported by WinHTTP."
ELSE
PRINT "This platform is NOT supported by WinHTTP."
END IF
PRINT
PRINT "Press any key..."
SLEEP
' ========================================================================================
' The following example shows how to open an HTTP connection, send an HTTP request, and
' returns the "Date" header from the response.
' ========================================================================================
#include "windows.bi"
#include "Afx/CWinHttpRequest.inc"
using Afx
' // Create an instance of the CWinHttp class
DIM pWHttp AS CWinHttpRequest
' // Open an HTTP connection to an HTTP resource
pWHttp.Open "GET", "http://microsoft.com"
' // Send an HTTP request to the HTTP server
pWHttp.Send
' // Wait for response with a timeout of 5 seconds
DIM iSucceeded AS LONG = pWHttp.WaitForResponse(5)
' // Get the response headers
DIM cbsResponseHeader AS CBSTR = pWHttp.GetResponseHeader("Date")
PRINT cbsResponseHeader
PRINT
PRINT "Press any key..."
SLEEP
' ========================================================================================
' The following example shows how to open an HTTP connection, send an HTTP request, and
' returns all of the headers from the response.
' ========================================================================================
#include "windows.bi"
#include "Afx/CWinHttpRequest.inc"
using Afx
' // Create an instance of the CWinHttp class
DIM pWHttp AS CWinHttpRequest
' // Open an HTTP connection to an HTTP resource
pWHttp.Open "GET", "http://microsoft.com"
' // Send an HTTP request to the HTTP server
pWHttp.Send
' // Wait for response with a timeout of 5 seconds
DIM iSucceeded AS LONG = pWHttp.WaitForResponse(5)
' // Get the response headers
DIM cbsResponseHeaders AS CBSTR = pWHttp.GetAllResponseHeaders
PRINT cbsResponseHeaders
PRINT
PRINT "Press any key..."
SLEEP
' ========================================================================================
' The following example shows how to open an HTTP connection, send an HTTP request, and
' returns the response body.
' ========================================================================================
#include "windows.bi"
#include "Afx/CWinHttpRequest.inc"
using Afx
' // Create an instance of the CWinHttp class
DIM pWHttp AS CWinHttpRequest
' // Open an HTTP connection to an HTTP resource
pWHttp.Open "GET", "http://microsoft.com"
' // Send an HTTP request to the HTTP server
pWHttp.Send
' // Wait for response with a timeout of 5 seconds
DIM iSucceeded AS LONG = pWHttp.WaitForResponse(5)
' // Get the response body
DIM cbsResponseBody AS CBSTR = pWHttp.GetResponseBody
PRINT cbsResponseBody
PRINT
PRINT "Press any key..."
SLEEP
' ========================================================================================
' The following example shows how to open an HTTP connection, send an HTTP request, and
' returns the response body as a stream of bytes.
' ========================================================================================
#include "windows.bi"
#include "Afx/CWinHttpRequest.inc"
using Afx
' // Create an instance of the CWinHttp class
DIM pWHttp AS CWinHttpRequest
' // Open an HTTP connection to an HTTP resource
pWHttp.Open "GET", "http://www.microsoft.com/library/homepage/images/ms-banner.gif"
' // Send an HTTP request to the HTTP server
pWHttp.Send
' // Wait for response with a timeout of 5 seconds
DIM iSucceeded AS LONG = pWHttp.WaitForResponse(5)
' // Get the response body
DIM strResponseBody AS STRING = pWHttp.GetResponseStream
' // Save the buffer into a file
IF LEN(strResponseBody) THEN
DIM fn AS LONG = FREEFILE
OPEN "ms-banner.gif" FOR OUTPUT AS #fn
PUT #fn, 1, strResponseBody
CLOSE #fn
PRINT "File saved"
ELSE
PRINT "Failure"
END IF
PRINT
PRINT "Press any key..."
SLEEP
' ========================================================================================
' The following example shows how to open an HTTP connection, send an HTTP request, and
' returns the response text.
' ========================================================================================
#include "windows.bi"
#include "Afx/CWinHttpRequest.inc"
using Afx
' // Create an instance of the CWinHttp class
DIM pWHttp AS CWinHttpRequest
' // Open an HTTP connection to an HTTP resource
pWHttp.Open "GET", "http://microsoft.com"
' // Send an HTTP request to the HTTP server
pWHttp.Send
' // Wait for response with a timeout of 5 seconds
DIM iSucceeded AS LONG = pWHttp.WaitForResponse(5)
' // Get the response headers
DIM cbsResponseText AS CBSTR = pWHttp.GetResponseText
PRINT cbsResponseText
PRINT
PRINT "Press any key..."
SLEEP
Another example:
' ========================================================================================
' The following example checks if the current platform is supported.
' ========================================================================================
#include "windows.bi"
#include "Afx/CWinHttpRequest.inc"
using Afx
' // Create an instance of the CWinHttp class
DIM pWHttp AS CWinHttpRequest
' // Open an HTTP connection to an HTTP resource
pWHttp.Open "GET", "http://microsoft.com"
' // Specify the user agent
pWHttp.SetOption(WinHttpRequestOption_UserAgentString, "A WinHttpRequest Example Program")
' // Send an HTTP request to the HTTP server
pWHttp.Send
IF pWHttp.GetLastResult = S_OK THEN
' // Get user agent string.
DIM cvText AS CVAR = pWHttp.GetOption(WinHttpRequestOption_UserAgentString)
PRINT cvText
' // Get URL
cvText = pWHttp.GetOption(WinHttpRequestOption_URL)
PRINT cvText
' // Get URL Code Page.
cvText = pWHttp.GetOption(WinHttpRequestOption_URLCodePage)
PRINT cvText
' // Convert percent symbols to escape sequences.
cvText = pWHttp.GetOption(WinHttpRequestOption_EscapePercentInURL)
PRINT IIF&(cvText.vd.boolVal, "true", "false")
END IF
PRINT
PRINT "Press any key..."
SLEEP
As a demonstration of how wrappers can made things easy, see the original C++ code of the example above posted:
#include <windows.h>
#include <stdio.h>
#include <objbase.h>
#include "httprequest.h"
#pragma comment(lib, "ole32.lib")
#pragma comment(lib, "oleaut32.lib")
// IID for IWinHttpRequest.
const IID IID_IWinHttpRequest =
{
0x06f29373,
0x5c5a,
0x4b54,
{0xb0, 0x25, 0x6e, 0xf1, 0xbf, 0x8a, 0xbf, 0x0e}
};
int main()
{
// Variable for return value
HRESULT hr;
// Initialize COM
hr = CoInitialize( NULL );
IWinHttpRequest * pIWinHttpRequest = NULL;
BSTR bstrResponse = NULL;
VARIANT varFalse;
VARIANT varEmpty;
VARIANT varUserAgent;
CLSID clsid;
VariantInit(&varFalse);
V_VT(&varFalse) = VT_BOOL;
V_BOOL(&varFalse) = VARIANT_FALSE;
VariantInit(&varEmpty);
V_VT(&varEmpty) = VT_ERROR;
varUserAgent.vt = VT_BSTR;
varUserAgent.bstrVal = NULL;
hr = CLSIDFromProgID(L"WinHttp.WinHttpRequest.5.1", &clsid);
if (SUCCEEDED(hr))
{
hr = CoCreateInstance(clsid,
NULL,
CLSCTX_INPROC_SERVER,
IID_IWinHttpRequest,
(void **)&pIWinHttpRequest);
}
if (SUCCEEDED(hr))
{
// Open WinHttpRequest.
BSTR bstrMethod = SysAllocString(L"GET");
BSTR bstrUrl = SysAllocString(L"http://microsoft.com");
hr = pIWinHttpRequest->Open(bstrMethod,
bstrUrl,
varFalse);
SysFreeString(bstrMethod);
SysFreeString(bstrUrl);
}
if (SUCCEEDED(hr))
{
// Specify the user agent.
varUserAgent.vt = VT_BSTR;
varUserAgent.bstrVal =
SysAllocString(
L"A WinHttpRequest Example Program");
//varUserAgent is L"A WinHttpRequest Example Program");
hr = pIWinHttpRequest->put_Option(
WinHttpRequestOption_UserAgentString,
varUserAgent);
}
if (SUCCEEDED(hr))
{ // Send Request.
hr = pIWinHttpRequest->Send(varEmpty);
}
if (SUCCEEDED(hr))
{
// Get user agent string.
hr = pIWinHttpRequest->get_Option(
WinHttpRequestOption_UserAgentString,
&varUserAgent);
// Print response to console.
wprintf(L"%s\n\n", varUserAgent.bstrVal);
// Get URL.
hr = pIWinHttpRequest->get_Option(WinHttpRequestOption_URL,
&varUserAgent);
// Print response to console.
wprintf(L"%s\n\n", varUserAgent.bstrVal);
// Get URL Code Page.
hr = pIWinHttpRequest->get_Option(
WinHttpRequestOption_URLCodePage,
&varUserAgent);
// Print response to console.
wprintf(L"%u\n\n", varUserAgent.lVal);
// Convert percent symbols to escape sequences.
hr = pIWinHttpRequest->get_Option(
WinHttpRequestOption_EscapePercentInURL,
&varUserAgent);
// Print response to console.
wprintf(L"%s\n", varUserAgent.boolVal?L"True":L"False");
}
// Release memory.
if (pIWinHttpRequest)
pIWinHttpRequest->Release();
if (bstrResponse)
SysFreeString(bstrResponse);
if (varUserAgent.bstrVal)
SysFreeString(varUserAgent.bstrVal);
CoUninitialize();
return 0;
}
All this
// Open WinHttpRequest.
BSTR bstrMethod = SysAllocString(L"GET");
BSTR bstrUrl = SysAllocString(L"http://microsoft.com");
hr = pIWinHttpRequest->Open(bstrMethod,
bstrUrl,
varFalse);
SysFreeString(bstrMethod);
SysFreeString(bstrUrl);
becomes reduced to
pWHttp.Open "GET", "http://microsoft.com"
Quote from: TechSupport on August 29, 2016, 01:04:43 PM
Thanks Jose for all your incredible hard work. You have driven forward FreeBasic Windows development exponentially.
In a few months I have posted more code than the entire FB community in ten years :)
At the beginning I was a bit slow because I still had not mastered the language and had not the new data types. Now everything is becoming very simple.
It takes me more time to do the testing and write the documentation than to write the code.
Now I need a safe array class to complete the toolbox.
There was a mistake in the CWinHTTPRequest class and header. New file uploaded.
An example for the dictionary object.
#include "Afx/CDicObj.inc"
using Afx
' // Creates an instance of the CDicObj class
DIM pDic AS CDicObj
' // Adds some key, value pairs
pDic.Add "a", "Athens"
pDic.Add "b", "Belgrade"
pDic.Add "c", "Cairo"
print "Count: "; pDic.Count
print pDic.Exists("a")
print
' // Retrieve an item and display it
print pDic.Item("b")
print
' // Change key "b" to "m" and "Belgrade" to "México"
pDic.Key("b") = "m"
pDic.Item("m") = "México"
print pDic.Item("m")
print
' // Get all the items and display them
DIM cvItems AS CVAR = pDic.Items
FOR i AS LONG = cvItems.GetLBound TO cvItems.GetUBound
print cvItems.GetVariantElem(i)
NEXT
print
' // Get all the keys and display them
DIM cvKeys AS CVAR = pDic.Keys
FOR i AS LONG = cvKeys.GetLBound TO cvKeys.GetUBound
print cvKeys.GetVariantElem(i)
NEXT
print
' // Remove key "m"
pDic.Remove "m"
IF pDic.Exists("m") THEN PRINT "Key m exists" ELSE PRINT "Key m doesn't exists"
' // Remove all keys
pDic.RemoveAll
print "All the keys must have been deleted"
print "Count: "; pDic.Count
PRINT
PRINT "Press any key..."
SLEEP
an exemple of using the implicit cast capabilies of CVar class : in input or in output
#include once "Afx/CVar.inc"
using Afx
' ========================================================================================
' you can use implicit Casting in input/output for numeric values or string
' the casted output value is depending on the type of the variable you assign the value
'
' if case of numeric type be carefull to use a variable type accepting the range of the supposed output
' if the value exceed the range of the var , the overflow will put 0 in var or negative value
'change here with or without double quotes to see the differences
#define TESTVALUE -10001 'or test with " -10001 " or - 10001 or " + 10001 " or + 10001 or whatever...
DIM cv AS CVar = TESTVALUE 'implicit casting in input can accept literal string/numeric
DIM l1 AS long = cv 'implicit casting in output
dim s1 as string = cv 'implicit casting in output
if s1 <> "" THEN
print : print "Test value = """ ; TESTVALUE;"""" : print
else
print : print "Test value = " ; TESTVALUE : print
END IF
if l1> 0 THEN
print " l1 > 0 s1 = [" & s1 & "] l1 = " & l1
elseif l1 = 0 THEN
print " l1 = 0 s1 = [" & s1 & "] l1 = " & l1
else
print " l1 < 0 s1 = [" & s1 & "] l1 = " & l1
END IF
PRINT
PRINT "Press any key..."
SLEEP
the beauty of that : the string representation of numeric value assigned to CVar
can give the right value when assigning that Cvar to numeric var, just carefull on overflow
on the exemple : change the value on the define to see the effect
or change DIM l1 AS long = cv by different numeric type
Thanks again Jose for that huge code collection!
With all this good stuff, it's time to take a look at FB. Will FF ever catch up and include all the gadgets?
We are going to have the one datatype that was missing, safe arrays...
I already have implemented a class, CSafeArray, that works with arrays of any type and dimensions.
' // Two-dimensional array of BSTR
' // 1D: elements = 5, lower bound = 1
' // 1D: elements = 3, lower bound = 1
DIM rgsabounds(0 TO 1) AS SAFEARRAYBOUND = {(5, 1), (3, 1)}
DIM csa AS CSafeArray = CSafeArray(VT_BSTR, 2, @rgsabounds(0))
DIM cbs1 AS CBSTR = "Test string 1"
' // array index: first dimension, first element
DIM rgidx(0 TO 1) AS LONG = {1, 1}
' // Put the value
csa.PutElement(@rgidx(0), *cbs1)
' // Get the value
DIM cbsOut AS CBSTR
csa.GetElement(@rgidx(0), @cbsOut)
print cbsOut
' // array index: second dimension, first element
rgidx(0) = 2 : rgidx(0) = 1
' // Put the value
DIM cbs2 AS CBSTR = "Test string 2"
csa.PutElement(@rgidx(0), *cbs2)
' // Get the value
csa.GetElement(@rgidx(0), @cbsOut)
print cbsOut
Now I'm going to add overloaded functions to ease its use with one-dimensional arrays, like
' // One-dimensional array of VT_VARIANT
DIM csa AS CSafeArray = CSafeArray(VT_VARIANT, 1, 5)
DIM cv1 AS CVAR = "Test variant 1"
csa.PutElement(1, @cv1)
DIM cvOut AS CVAR
csa.GetElement(1, @cvOut)
print cvOut
DIM cv2 AS CVAR = "Test variant 2"
csa.PutElement(1, @cv2)
csa.GetElement(1, @cvOut)
print cvOut
And add overloaded functions to GetElement and PutElement.
In COM programming, about 90% of the safe arrays are one-dimensional. I only have seen a two-dimensional safe array in the GetRows method of ADO.
Safe vectors are not-resizable one-dimensional safe arrays allocated in contiguous memory for efficiency.
I'm not satisfied with the name of CVar for the variant class and I'm going to change it to CVariant.
I have changed CVAR to CVARIANT and have finished the SAFEARRAY class.
Tomorrow I will post a new release candidate package and an updated help file.
The safe arrays can be multidimensional and there are overloaded functions to ease the use of the one and to dimensional safe arrays.
Usage example of a one-dimensional array of variants:
' ========================================================================================
' CSafeArray test
' ========================================================================================
#include "Afx/CSafeArray.inc"
using Afx
' // One-dimensional array of VT_VARIANT
DIM csa AS CSafeArray = CSafeArray(VT_VARIANT, 1, 2)
DIM cv1 AS CVARIANT = "Test variant 1"
csa.PutElement(1, @cv1)
DIM cvOut AS CVARIANT
csa.GetElement(1, @cvOut)
print cvOut
DIM cv2 AS CVARIANT = "Test variant 2"
csa.PutElement(1, @cv2)
csa.GetElement(1, @cvOut)
print cvOut
' // Redim (preserve) the safe array
csa.Redim(1, 3)
DIM cv3 AS CVARIANT = "Test variant 3"
csa.PutElement(3, @cv3)
csa.GetElement(3, @cvOut)
print cvOut
PRINT
PRINT "Press any key..."
SLEEP
Usage example of a two-dimensional safe array of BSTRs.
' ========================================================================================
' CSafeArray test
' ========================================================================================
#include "Afx/CSafeArray.inc"
using Afx
' // Two-dimensional array of BSTR
' // 1D: elements = 5, lower bound = 1
' // 1D: elements = 3, lower bound = 1
DIM rgsabounds(0 TO 1) AS SAFEARRAYBOUND = {(5, 1), (3, 1)}
DIM csa AS CSafeArray = CSafeArray(VT_BSTR, 2, @rgsabounds(0))
' or we can use:
' DIM csa AS CSafeArray = CSafeArray(VT_BSTR, 5, 1, 3, 1)
' // array index: first element, first dimension
DIM rgidx(0 TO 1) AS LONG = {1, 1}
' // Put the value
DIM cbs1 AS CBSTR = "Test string 1.1"
csa.PutElement(@rgidx(0), *cbs1)
' // array index: second element, first dimension
rgidx(0) = 2 : rgidx(1) = 1
' // Put the value
DIM cbs2 AS CBSTR = "Test string 2.1"
csa.PutElement(@rgidx(0), *cbs2)
' // array index: first element, second dimension
rgidx(0) = 1 : rgidx(1) = 2
' // Put the value
DIM cbs3 AS CBSTR = "Test string 1.2"
csa.PutElement(@rgidx(0), *cbs3)
' // array index: second element, second dimension
rgidx(0) = 2 : rgidx(1) = 2
' // Put the value
DIM cbs4 AS CBSTR = "Test string 2.2"
csa.PutElement(@rgidx(0), *cbs4)
DIM hr AS HRESULT, cbsOut AS CBSTR
hr = csa.GetElement(1, 1, @cbsOut)
print cbsOut
hr = csa.GetElement(2, 1, @cbsOut)
print cbsOut
print
hr = csa.GetElement(1, 2, @cbsOut)
print cbsOut
hr = csa.GetElement(2, 2, @cbsOut)
print cbsOut
PRINT
PRINT "Press any key..."
SLEEP
For the two-dimensional arrays in the example above, we can use
csa.PutElement(1, 1, *cbs1)
csa.PutElement(2, 1, *cbs2)
instead of
DIM rgidx(0 TO 1) AS LONG = {1, 1}
-or-
rgidx(0) = 2 : rgidx(1) = 1