It works, but is not listed here:
https://msdn.microsoft.com/en-us/library/office/gg278535.aspx
... > Office shared > Office VBA language reference > Visual Basic conceptual topics
Returning Strings from Functions
and in the VBA editor:
(View) - Object Browser - VBA - Strings
This function is discussed in many forums, so people use it.
The answer to your question depends on what you mean by "... exist[s] in Access". If we open an Immediate window in Access' VBA development environment and run
?Replace$("I like tofu!", "tofu", "bacon")
we get
I like bacon!
so clearly the Replace$ function does "exist" in Access itself. However, if we try to use Replace$ in a query against an Access database from an external application (e.g., VBScript, .NET, ...) we'll get
Undefined function 'Replace$' in expression.
Adding to the confusion is the fact that the older "Jet" ODBC/OLEDB drivers did not support the Replace function (without the dollar sign) but the newer "ACE" ODBC/OLEDB drivers do. (Neither flavour supports Replace$.) So a query like
SELECT Replace([Name], 'Gordon ', 'Gord ') AS newName FROM ...
will work if we use the ODBC driver
Driver={Microsoft Access Driver (*.mdb, .accdb)}
but not if we use
Driver={Microsoft Access Driver (*.mdb)}
The functions in the VBA.Strings module are "special" in how they are handled internally by VBA. For most of them, there are actually 2 versions in the type library - a version that returns a String (ends with $), and a version that returns a Variant. Internally these are declared as a pair of functions - for example, Right (from the vbe7.dll TypeLib):
[entry(618), helpcontext(0x000f6ea5)]
BSTR _stdcall _B_str_Right(
[in] BSTR String,
[in] long Length);
[entry(619), helpcontext(0x000f656e)]
VARIANT _stdcall _B_var_Right(
[in] VARIANT* String,
[in] long Length);
The compiler apparently treats the $ similarly to a "type hint" internally, because (using the example above), there actually isn't a function Left$ defined in the TypeLib. In fact, there isn't a function declared as VBA.Strings.Right either. These live in a special restricted interface named _HiddenInterface:
[
odl,
uuid(1E196B20-1F3C-1069-996B-00DD010EF676)
]
interface _HiddenInterface {
...
[restricted, helpcontext(0x000f6d7c)]
void _stdcall Right();
...
};
Note that Right$ doesn't appear in the in the _HiddenInterface, nor do any of the other string returning functions. The VBA compiler uses the "function type hint" to forward the function call to either _B_str_Right or _B_var_Right.
By now you're probably wonder what this has to do with your question. The answer is that Replace actually doesn't have two different internal representations. It always returns a string, doesn't exist on the _HiddenInterface, and lives directly in the VBA.Strings module:
[entry(712), helpstring("Find and replace a substring within a string"), helpcontext(0x000f6522)]
BSTR _stdcall Replace(
[in] BSTR Expression,
[in] BSTR Find,
[in] BSTR Replace,
[in, optional, defaultvalue(1)] long Start,
[in, optional, defaultvalue(-1)] long Count,
[in, optional, defaultvalue(0),
custom(270D72B0-FFB8-11CF-A4BD-00A0C90F26EE, 1)
] VbCompareMethod Compare);
Basically, there is no Replace$ function at all. VBA is treating the $ as a type hint for the return value (which is always a String anyway). As far as the ODBC and OLE drivers are concerned, I would imagine (TBH I really haven't looked into it much) that they are restricted to the names that are exposed by the TypeLib and are not interpreted by the VBA runtime as forwards to different functions. They simply don't exist if you're doing IDispatch lookups on vbe7.dll.
Related
So I'm writing a standalone JScript file to be executed by Windows Script Host (this file is not going to be used as a web application).
My goal is to load a dll file. Just like using LoadLibrary function in a C++ application.
I tried researching the subject but I didn't come up with anything useful. I'm so lost I don't have any piece of code to share. I understand using ActiveXObject may come to my rescue. if so, any idea how to use it?
Update:
If we all agree that loading is impossible, I'll settle for validity check. Meaning, don't try to load but check if it is loaded and functional.
You can export a specific function for this purpose.
Then, from your JScript, execute rundll32.exe and check that the function ran as expected.
You might also give Gilles Laurent's DynaWrap
ocx a chance.
This kind of dll needs to be registered on the target system like regsvr32 /s DynaWrap.dll.
It is restricted to 32-bit DLLs, and this might be inconvenient for you to use, but it works on a 64bit Windows. You can't access function exported by ordinal number and you can't directly handle 64bit or greater values/pointers.
Here's a sample to call MessageBoxA from JScript:
var oDynaWrap = new ActiveXObject( "DynamicWrapper" )
// to call MessageBoxA(), first register the API function
oDynaWrap.Register( "USER32.DLL", "MessageBoxA", "I=HsSu", "f=s", "R=l" )
// now call the function
oDynaWrap.MessageBoxA( null, "MessageBoxA()", "A messagebox from JScript...", 3 )
And here from VBScript:
Option Explicit
Dim oDynaWrap
Set oDynaWrap = CreateObject( "DynamicWrapper" )
' to call MessageBoxA(), first register the API function
UserWrap.Register "USER32.DLL", "MessageBoxA", "I=HsSu", "f=s", "R=l"
' now call the function
UserWrap.MessageBoxA Null, "MessageBoxA()", "A messagebox from VBScript...", 3
To use a function you need to "register" the exported function of your DLL.
To do this you need to call the register method with a first parameter containing a string object to the complete path of the DLL, a second parameter for the exported name of the function to use, and following three paremeters describing the functions declartion in a somehow obscure syntax.
i= describes the number and data type of the functions parameters.
f= describes the type of call: _stdcall or _cdecl. Default to _stdcall.
r= describes the return values data type.
The supported data types are:
Code Variant Description
a VT_DISPATCH IDispatch*
b VT_BOOL BOOL
c VT_I4 unsigned char
d VT_R8 8 byte real
f VT_R4 4 byte real
h VT_I4 HANDLE
k VT_UNKNOWN IUnknown*
l VT_I4 LONG
p VT_PTR pointer
r VT_LPSTR string by reference
s VT_LPSTR string
t VT_I2 SHORT
u VT_UINT UINT
w VT_LPWSTR wide string
Thus the Register method call used in the examples describes MessageBoxA like this:
_stdcall LONG MessageBoxA( HANDLE, LPSTR, LPSTR, UINT );
For a explanation of MessageBoxA look at the docs on MSDN.
Please read the DynaWrap docs for more sophisticated examples... But you might need Google translate, 'cos they are written in french ;-)
To be able to use a dll as ActiveXObject, it needs to be registered as COM object. There are some restrictions on this but if you have a code for this dll, it is certainly doable.
When you register your dll as COM object, it is assigned a name. You use this name to create an object. This example from MSDN uses excel since it is already registered if you installed office.
var ExcelApp = new ActiveXObject("Excel.Application");
var ExcelSheet = new ActiveXObject("Excel.Sheet");
// Make Excel visible through the Application object.
ExcelSheet.Application.Visible = true;
// Place some text in the first cell of the sheet.
ExcelSheet.ActiveSheet.Cells(1,1).Value = "This is column A, row 1";
// Save the sheet.
ExcelSheet.SaveAs("C:\\TEST.XLS");
// Close Excel with the Quit method on the Application object.
ExcelSheet.Application.Quit();
Apart from restriction of registering dll, using dll is no different from using it as c++ or c# dll. Note that, C# (or other .NET dlls) should be ComVisible to be used from javascript this way.
EDIT: The only other way of using C/C++ dll from javascript is swig interfaces. I have not used it, therefore I can only point you in that direction.
SWIG is a software development tool that connects programs written in
C and C++ with a variety of high-level programming languages. SWIG is
used with different types of target languages including common
scripting languages such as Javascript, Perl, PHP, Python, Tcl and
Ruby.
I have been searching for this all over the web, but everything is either talking about the ErrObject class, or the constant vbError returned by the VarType function. I want to know what that type actually is, e.g. an integer is something like 4, a string is something like "hello world", etc.
For a little background, here is a link to the official MSDN page about VarType, which shows all the constants it returns and what they represent. What is Error value?
If you are familiar with COM in C/C++, you should know that from that perspective VarType() is a simple function that essentially extracts the value of the vt member from the Variant passed in as a parameter. The possible values of the vt member are documented in many places, such as here.
If you check inside of the COM system headers (e.g. WTypes.h distributed as part of in the Windows SDK), you will see that the VbError value of 10 indeed maps to the C/C++ enum value of VT_ERROR.
enum VARENUM
{ VT_EMPTY = 0,
...
VT_ERROR = 10,
...
} ;
The MSDN link above describes the meaning of a vt that equals VT_ERROR as follows:
An SCODE was specified. The type of the error is specified in scode.
Generally, operations on error values should raise an exception or propagate the error to the return value, as appropriate.
So, here's basically what it means:
A Variant obviously supports storing many types of values, and among them is the obscure possibility of storing an "Error Code". More correctly, these codes are formally called scode's, because they can indicate many types of "success" as well as "failures". Most people refer to these codes as HRESULTs.
These codes are the same kind of "Error Codes" that you get from Err.Number in VB6. So, you can tell a Variant to distinguish an "Error Code" from just a plain number.
In reality, few programs or COM components - if any - will ever put error codes in Variants. Almost everybody just issues COM Exceptions to communicate errors (this mechanism is exposed in VB6 via the Err object). Even those components that return error codes outside of the COM Exception mechanism, would likely do so in typed variables (e.g. Long's).
Therefore, the reason this possible return value exists is for completeness. You will almost never see it in real life.
(Edit: Remove bit about not being able to create an "Error" variant. #Joe proved me wrong on that. You can use CVErr() to create one)
In VB6 and VBA, error values are created by calling the CVErr function.
This function returns a Variant whose VarType is vbError, and for which the IsError function returns True.
A typical use case for this is an Excel UDF that returns a Variant: if you return an error value, it will display as #VALUE!.
I maintain a program which can be automated via COM. Generally customers use VBS to do their scripting, but we have a couple of customers who use Matlab's ActiveX support and are having trouble calling COM object methods with a NULL parameter.
They've asked how they do this in Matlab - and I've been scouring Mathworks' COM/ActiveX documentation for a day or so now and can't figure it out.
Their example code might look something like this:
function do_something()
OurAppInstance = actxserver('Foo.Application');
OurAppInstance.Method('Hello', NULL)
end
where NULL is where in another language, we'd write NULL or nil or Nothing, or, of course, pass in an object. The problem is this is optional (and these are implemented as optional parameters in most, but not all, cases) - these methods expect to get NULL quite often.
They tell me they've tried [] (which from my reading seemed the most likely) as well as '', Nothing, 'Nothing', None, Null, and 0. I have no idea how many of those are even valid Matlab keywords - certainly none work in this case.
Can anyone help? What's Matlab's syntax for a null pointer / object for use as a COM method parameter?
Update: Thanks for all the replies so far! Unfortunately, none of the answers seem to work, not even libpointer. The error is the same in all cases:
Error: Type mismatch, argument 2
This parameter in the COM type library is described in RIDL as:
HRESULT _stdcall OurMethod([in] BSTR strParamOne, [in, optional] OurCoClass* oParamTwo, [out, retval] VARIANT_BOOL* bResult);
The coclass in question implements a single interface descending from IDispatch.
I'm answering my own question here, after talking to Matlab tech support: There is no equivalent of Nothing, and Matlab does not support this.
In detail: Matlab does support optional arguments, but does not support passing in variant NULL pointers (actually, to follow exactly how VB's Nothing works, a VT_EMPTY variant, I think) whether as an optional argument or not. There is documentation about some null / pointerish types, a lot of which is mentioned in my question or in various answers, but these don't seem to be useable with their COM support.
I was given a workaround by Matlab support using a COM DLL they created and Excel to create a dummy nothing object that could be passed around in scripts. I haven't managed to get this workaround / hack working, and even if I had unfortunately I probably could not redistribute it. However, if you encounter the same problem this description might give you a starting point at least!
Edit
It is possible this Old New Thing blog post may be related. (I no longer work with access to the problematic source code, or access to Matlab, to refresh my memory or to test.)
Briefly, for IUnknown (or derived) parameters, you need a [unique] attribute for them to legally be NULL. The above declaration required Matlab create or pass in a VT_EMPTY variant, which it couldn't do. Perhaps adding [unique] may have prompted the Matlab engine to pass in a NULL pointer (or variant containing a NULL pointer), instead - assuming it was able to do that, which is guesswork.
This is all speculation since this code and the intricacies of it are several years behind me at this point. However, I hope it helps any future reader.
From the mathworks documentation, you can use the libpointer function:
p = libpointer;
and then p will be a NULL pointer. See that page for more details.
See also: more information about libpointer.
Peter's answer should work, but something you might want to try is NaN, which is what Matlab ususally uses as a NULL value.
In addition to using [] and libpointer (as suggested by Peter), you can also try {}.
The correct answer for something in VB that is expecting a Nothing argument, is to somehow get a COM/ActiveX Variant which has a variant type of VT_EMPTY. (see MSDN docs which reference marshaling behavior for Visual Basic Nothing)
MATLAB may do this with the empty array ([]), but I'm not sure.... so it may not be possible purely in MATLAB. Although someone could easily write a tiny COM library whose purpose is to create a Variant with VT_EMPTY.
But if the argument has the [optional] atttribute, and you want to leave that optional argument blank, you should not do this. See the COM/ActiveX docs on Variants which say under VT_EMPTY:
VT_EMPTY: No value was specified. If an optional argument to an Automation method is left blank, do not pass a VARIANT of type VT_EMPTY. Instead, pass a VARIANT of type VT_ERROR with a value of DISP_E_PARAMNOTFOUND.
Matlab should (but probably does not) provide methods to create these objects (a "nothing" and an "optional blank") so you can interface correctly with COM objects.
I'm confused about COM string assignments. Which of the following string assignment is correct. Why?
CComBSTR str;
.
.
Obj->str = L"" //Option1
OR should it be
Obj->str = CComBSTR(L"") //Option2
What is the reason
A real BSTR is:
temporarily allocated from the COM heap (via SysAllocString() and family)
a data structure in which the string data is preceded by its length, stored in a 32-bit value.
passed as a pointer to the fifth byte of that data structure, where the string data resides.
See the documentation:
MSDN: BSTR
Most functions which accept a BSTR will not crash when passed a BSTR created the simple assignment. This leads to confusion as people observe what seems to be working code from which they infer that a BSTR can be initialized just like any WCHAR *. That inference is incorrect.
Only real BSTRs can be passed to OLE Automation interfaces.
By using the CComBSTR() constructor, which calls SysAllocString(), your code will create a real BSTR. The CComBSTR() destructor will take care of returning the allocated storage to the system via SysFreeString().
If you pass the CComBSTR() to an API which takes ownership, be sure to call the .Detach() method to ensure the BSTR is not freed. BSTRs are not reference counted (unlike COM objects, which are), and therefore an attempt to free a BSTR more than once will crash.
If you use str = CComBSTR(L"") you use the constructor:
CComBSTR( LPCSTR pSrc );
If you use str = L"" you use the assignment operator:
CComBSTR& operator =(LPCSTR pSrc);
They both would initialize the CComBSTR object correctly.
Personally, I'd prefer option 1, because that doesn't require constructing a new CComBSTR object. (Whether their code does so behind the scenes is a different story, of course.)
Option 1 is preferred because it only does one allocation for the string where as option 2 does 2 (not withstanding the creation of a new temporary object for no particular reason). Unlike the bstr_t type in VC++ the ATL one does not do referenced counted strings so it will copy the entire string across.
How do I declare "as any" in VB.NET, or what is the equivalent?
The closest you can get is:
Dim var as Object
It's not exactly the same as VB6's as Any (which stores values in a Variant) but you can store variables of any type as Object, albeit boxed.
VB.NET does not support the as any keyword, VB.NET is a strongly typed language, you can however (with .NET 3.5) use implicit typing in VB
Dim fred = "Hello World" will implicitly type fred as a string variable. If you want to simply hold a value that you do not know the type of at design time then you can simply declare your variable as object (the mother of all objects) NOTE, this usually is a red flag for code reviewers, so make sure you have a good reason ready :-)
As Any must be referring to Windows API declarations, as it can't be used in variable declarations. You can use overloading: just repeat the declarations for each different data type you wish to pass. VB.NET picks out the one that matches the argument you pass in your call.
This is better than As Any was in VB6 because the compiler can still do type-checking.
I suppose you have problems with converting WinAPI declarations. Sometimes you can get away if you just declare your variable as string or integer because that is the real type of value returned.
You can also try marshaling:
<MarshalAsAttribute(UnmanagedType.AsAny)> ByRef buff As Object
VB.NET doesn't support the "As Any" keyword. You'll need to explicitly specify the type.