Excel automation isn't working in C++Builder XE7 - excel

I am trying to use the code below to open an .xlsx file from C++Builder in RAD Studio XE7:
#include "ComObj.hpp"
Variant Excel = CreateOleObject("Excel.Application");
Variant Books = Excel.OlePropertyGet("Workbooks");
Excel.OlePropertySet("Visible", true);
// An escape character is missing but the problem remains
Books.OleProcedure("Open", L"D:\1.xlsx"); // exception here
But the last line causes exception with message:
Project2.exe raised exception class EOleException with message 'Unfortunately, we were unable to find the file TRUE.xlsx. It may have been moved, renamed or deleted?'.
Screen with place where the source breaks
The code in Delphi seems to work fine:
uses ComObj;
var
Excel, Books: Variant;
begin
Excel := CreateOleObject('Excel.Application');
Books := Excel.Workbooks;
Excel.Visible := True;
Books.Open('D:\1.xlsx'); // code passes
end;
Does anyone know the solution?
Update1:
The following code in VB also works fine:
Sub Button1_Click()
Dim xlApp As Excel.Application
Dim xlBooks As Excel.Workbooks
Set xlApp = CreateObject("Excel.Application")
Set xlBooks = xlApp.Workbooks
xlApp.Visible = True
xlBooks.Open ("D:\1.xlsx")
End Sub
Update2:
Sending a raw string literal causes the same exception.
Books.OleProcedure("Open", uR"(D:\1.xlsx)");
It also doesn't seem to be an environment problem. I tested the example at several computers with no effect.

In C++ the backslash character is the escape character in string literals and so needs itself to be escaped. Instead of
L"D:\1.xlsx"
you need to write
L"D:\\1.xlsx"
The error message is strange though. It is almost as though some conversion in the COM dispatch code interprets the 1 as a truth value and converts it to text. You could try passing the filename as a System::WideString which might side-step the issue.
System::WideString filename = L"D:\\1.xlsx";
Books.OleProcedure("Open", filename);
What you are reporting seems almost too weird to be true though! I have to confess I'm having some trouble believing it because it is so outlandish.

Just faced a similar problem with C++ Builder XE7 and thought I would share what I found. Any attempt to set or send a string literal of any type caused either a Bad Variable Type error, was set as TRUE in Excel like Dmitrii or caused a memory error.
What I eventually found was that there is an OLEVariant type in C++ Builder that contains data types compatible with OLE automation and at runtime can convert as required.
I first tried changing all my Variant variables to OLEVariant without success but then was able to use a cast on any string I sent to make it work, even older char strings. So you might try
Books.OleProcedure("Open", (OleVariant)L"D:\1.xlsx");
even without the WideString formatting it worked for what I was doing so this might work as well
Books.OleProcedure("Open", (OleVariant)"D:\1.xlsx");
As for escaping I'm not sure if you need to escape the backslash in the second case with a simple string or not.

The problem seems very specifically related to the use of C++ and thus the way that C++ compilers deal with literal strings (or at least this particular C++ compiler). Quite what the problem is in this case I cannot say - it could even be a bug in the compiler since (seemingly) correct escaping doesn't address the problem.
You could employ various strategies to eliminate the possibility that it is handling of the literal string in this case. But since this does involve a literal string and since you are using XE7 I believe you should be able to bypass this more explicitly by expressing the literal as a raw string (as per C++11, which is/should be supported by C++ Builder XE7):
Books.OleProcedure("Open", uR"(D:\1.xlsx)"); // uR indicates UTF-16 Raw string
Note that with raw string literals you specifically do not escape \ chars.

Related

How to write an Xpath expression containing an apostrophe in VBA Selenium

My question relates to coding selenium specifically in VBA.
I have a function that finds webelements based on text passed to it (in string variable 'toFind'). The relevent Xpath identification method I use is (where driver. is the selenium chromedriver):
mySearch = "//*[contains(text(),'" & toFind & "')]"
Set ret = driver.FindElementsByXPath(mySearch)
This works unless the toFind variable contains an apostrophe. For example if "Consultant's Forename" is passed then my expression evaluates to:
Set ret = driver.FindElementsByXPath("//*[contains(text(),'Consultant's Forename')]"), which causes an invalid selector run-time error.
I have researched elsewhere on the site and see a number of answers describing escaping from the single quotes using the backslash character. Based on this I have tried to use Set ret = driver.FindElementsByXPath("//*[contains(text(),\"Consultant's Forename\")]") instead. However, this will not compile in microsoft visual basic for applications as it reports a syntax error (code line is red). I have not tried using the driver.findElements(By.xpath method as opposed to driver.FindElementsByXPath as I assumed this would not make a difference to the handling of the XPath expression. I have tried the other suggestions of using the 'concat' function but this also seems not to be valid in VBA selenium.
I don't know if these methods are specficaly for platforms other than VBA or I am just getting my syntax wrong?
The only way I can work it at present is to ignore the existence of the apostrophe:
Set ret = driver.FindElementsByXPath("//*[contains(text(),'Consultant') and contains(text(),'s Forename')]")
Whilst this works it is an incomplete solution and any help on the correct syntax to deal with the xpath location in VBA for text containing an apostrophe would be much appreciated.

Delphi Embarcadero XE: tons of warnings with String and PAnsiChar

I'm trying to migrate from Delphi 2007 to Embarcadero RAD Studio XE.
I'm getting tons of warnings.
They all look like this: I have a procedure where I declare a "String":
procedure SendMail( ADestinataire,ASubject : String);
And I'm trying to call Windows API like:
Res := MAPIResolveName(Session, Application.Handle,
PAnsiChar(ADestinataire), MAPI_LOGON_UI, 0, PRecip);
So the warnings are:
W1044: Transtyping string into PAnsiChar suspect.
What am I doing wrong / how should I correct this (350 warnings...)?
Thank you very much
MAPIResolveName uses LPSTR parameter, which is PAnsiChar in Delphi. Simple MAPI does not support UTF16 strings (though it can be used with UTF8 strings), so if you adhere to simple MAPI you should use AnsiStrings, for example
procedure SendMail( ADestinataire,ASubject : AnsiString);
or better you can use
procedure SendMail( ADestinataire,ASubject : String);
and explicitly convert string parameters to AnsiStrings prior to calling MAPIResolveName
Update
The whole Simple MAPI is now deprecated; Simple MAPI can be used with UTF8 strings, but it requires some changes in code and registry.
So if the question is about quick porting old ANSI Simple MAPI to Unicode Delphi, the best is to adhere to AnsiStrings.
A more solid approach is to abandon Simple MAPI completely and use Extended MAPI instead.
Just write
Res := MAPIResolveName(Session, Application.Handle,
PChar(ADestinataire), MAPI_LOGON_UI, 0, PRecip);
If you have a string, that is, a Unicode string, that is, a pointer to a sequence of Unicode characters, you shouldn't cast it to PAnsiChar. PAnsiChar is a pointer to a sequence of non-Unicode characters. Indeed, a cast to a PSomethingChar type simply tells the compiler to interpret the thing inside the cast as a pointer of the specified type. It doesn't do any conversion. So, basically, right now you lie to the compiler: You have a Unicode string and instructs the compiler to interpret it as an ANSI (non-Unicode) string. That's bad.
Instead, you should cast it to PWideChar, a pointer to a sequence of Unicode characters. In Delphi 2009+, PChar is equivalent to PWideChar.
Of course, if you send a pointer to a sequence of Unicode characters to the function, then the function had better expect Unicode characters, but I am not sure if this is the case of the MAPIResolveName function. I suspect that it actually requires ANSI (that is, non-Unicode) characters. If this is the case, you need to convert the Unicode string to an ANSI (non-Unicode) string. This is easy, just write AnsiString(ADestinataire). Then you cast to a ANSI (non-Unicode) PAnsiChar:
Res := MAPIResolveName(Session, Application.Handle,
PANsiChar(AnsiString(ADestinataire)), MAPI_LOGON_UI, 0, PRecip);
Starting with Delphi 2009, the string data type is now implemented as a Unicode string. Previous versions implemented the string data type as an Ansi string (i.e. one byte per character).
This had significant implications when we ported our apps from 2007 to XE, and these article were very helpful:
Delphi in a Unicode World (first of three parts)
Delphi and Unicode
Delphi Unicode Migration for Mere Mortals
To step back a moment:
When using use the cast
============ ================
String PChar
AnsiString PAnsiChar
WideString PWideChar
String used to be an alias for AnsiString, which means it happened to work if you used PAnsiChar (even though you should have used PChar).
Now string is an alias for UnicodeString, which means using PAnsiChar (which was always wrong) is now really wrong.
Knowing this you can solve the question:
ADestinataire: String
PAnsiChar(ADestinataire)

Delphi - ADO query and FillChar generates errors

I have the following code:
var wqry:TAdoQuery;
...
FillChar(wSpaces,cSpacesAfter,' ');
try
wqry := TADOQuery.Create(nil);//here the error
wqry.Connection:=...
cSpacesAfter is a constant and has the value 1035. wSpaces is a local string variable. The problem is that I receive the following error when TAdoQuery is created
even it is in french, I believe you got the idea.....
If I comment the FillChar code, everything works ok. I have the usual compiler directives, nothing special. I'm using Delphi 7.
Can someone tell me what is wrong with that code?
The troublesome code is most likely this one
FillChar(wSpaces,cSpacesAfter,' ');
I'm assuming that wSpaces is of string type. A string variable is in fact nothing more than a pointer to the data structure that holds the string. You don't need to use pointer syntax because the compiler takes care of that for you.
So what this code does is overwrite the variable holding that pointer with 4 space characters and then write 1031 more spaces over the top of whatever follows the variable. In short you will completely corrupt your memory. That would explain why the FillChar works but the very next line of code dies a painful and dramatic death.
If your string indeed had space for 1035 characters your could instead write:
FillChar(wSpaces[1], cSpacesAfter, ' ');
However, if may be more idiomatic to write:
wSpaces := StringOfChar(' ', cSpacesAfter);
FillChar procedure fills out a section of storage Buffer with the same byte or character FillValue FillCount times.
It is principally used to initialise arrays of numbers. It can be used to initialise records and strings, but care should be used to avoid overwriting length fields. StringOfChar is best for filling out strings to the same character.
Are you sure wSpaces has the size enough to fit all of cSpacesAfter you write to it?

VB6 calls to ActiveX component with string parameters yield weird results

I'm currently finishing a piece of software a now gone co-worker started.
The app is coded in VB6 and uses a 3rd party ActiveX component to act upon a 3rd party system. Our solution is basically an integration between their company's software and ours.
The issue I'm having is that there's a method call that fails consistently, even though it's passed perfectly valid parameters on our side (it's a login method). However, when I look at the trace their application offers, I see that instead of the username I specify, it tells me (roughly) "User '⚠⚠⚠' can't login".
I figured it was likely to be an encoding issue as the ⚠ character replacing the characters I give it to log on seem to be there because the characters are unknown, but nothing I did could fix it.
Anyone know of an issue with VB6 communicating with ActiveX components like this? Or anyone have an idea what I could try? I'm at a loss here and if the issue is on their side, it'll be a pain to get it fixed as we don't have their source code.
Thanks in advance.
There are a couple of ways of passing strings. Aside from the obvious one of passing a string as in
DIM u As String
DIM p As String
u = "Username"
p = "Password"
Set objIRC = objRCL.Login(u, p)
there's also the possibility that .Login is expecting pointers to String, in which case code
Set objIRC = objRCL.Login(StrPtr(u), StrPtr(p))

Convert char array to UNICODE in MFC C++

I'm using the folowing code to read files from a folder in windows. However since this a MFC application I have to convert the char array to UNICODE. For example if I hard code the path as "C:\images3\test\" as shown below the code works.
WIN32_FIND_DATA FindFileData;
HANDLE hFind = INVALID_HANDLE_VALUE;
hFind = FindFirstFile(_T("C:\\images3\\test\\"), &FindFileData);
What I want is to get this working as follows:
char* pathOfFileType;
hFind = FindFirstFile(_T(pathOfFileType), &FindFileData);
Can anyone tell me how to fix this problem ?
Thanks
Thanks a lot for all your responses. I learnt a lot from those answers because I also didn't have much idea about what is happening underneath. Meanwhile I managed to get rid of the issue by simply converting to UNICODE using the following code with minimum changes to my existing code.
#include <atlconv.h>
USES_CONVERSION;
//An ANSI string
LPSTR lpsz_ANSI_String = pathOfFileType;
//ANSI string being converted to a UNICODE string
LPWSTR lpUnicodeStr = A2W( lpsz_ANSI_String );
hFind = FindFirstFile(lpUnicodeStr, &FindFileData);
You can use the MultiByteToWideChar function to convert a string from chars to UTF-16, but you'd better to get pathOfFileType directly in Unicode from the user or from wherever you take it, otherwise you may still experience problems with paths that contain characters not included in the current CP.
Your question demonstrates a confusion of several issues. First, using MFC doesn't mean you have to convert the character array to Unicode, one has nothing to do with the other. Furthermore, FindFirstFile is a Win32 API, not an MFC function. Finaly, _T("abc") is not necessarily unicode, rather _T(X) is a macro that in multi-byte builds expands to X, and in unicode builds expands to L X, creating a wide character literal. This is designed so that your code can compile in a unciode or multi-byte configuration. To achieve the same flexibility when declaring a variable, you use the TCHAR type instead of char or wchar_t. So your second snippet should look like
TCHAR* pathOfFileType;
hFind = FindFirstFile(pathOfFileType, &FindFileData);
Note no _T macro, that is only applied to string literals, not identifiers.
"since this a MFC application I have to convert the char array to UNICODE"
Not so. If you wish, you can use change to use the Multi-Byte Character Set.
In project properties, general change character set to 'Use Multi-Byte Character Set'
Now this will work
char* pathOfFileType;
hFind = FindFirstFile(pathOfFileType, &FindFileData);
Supposing you want to use UNICODE ( visual studio's name for the 2 byte encoding of UNICODE characters native to Windows ) then you have to explicitly call the MBCS version of the API
char* pathOfFileType;
hFind = FindFirstFileA(pathOfFileType, &FindFileData);

Resources