I am relatively new to Delphi and I want to make a quick application the uses the ShellExecute command.
I want to use string values in edit boxes to add to the command line for doing the processing job outside of the application.
Everything works fine, but I get the error :
"Incompatible types: String and PAnsiChar"
I have tried to convert it using:
Variable := PAnsiChar(AnsiString(editbox.Text), but to no avail.
Can anyone assist me with this problem please.
In Delphi 7, it's a simple typecast to PChar, which is already a PAnsiChar:
PChar(YourStringVariable);
or
PChar('Some text here'); // Cast not needed; demonstration only
PChar('C:\' + AFileName); // Cast needed because of variable use
Using it with ShellExecute:
AFile := 'C:\MyDir\Readme.txt';
Res := ShellExecute(0, 'open', PChar(AFile),
nil, nil, SW_NORMAL )
How could it work fine when you cannot compile it?
You have posted too little code to be sure what is wrong, but you definitively have one typecast too much. AnsiChar is type that can store only single character and it makes no sense here.
If Variable is PAnsiChar then you should be using:
Variable := PAnsiChar(editbox.Text)
Related
Related to Inno setup: how to replace a string in XML file?
As suggested in the answer I'm using a template xml, let's say "app.xml". I would like to replace a string in that file: "Dsn=Serverxxx" with the result of a function "DsnName" {code:DsnName} I use elsewhere in the script.
[Code]
procedure WriteAppPath;
var
FileData: String;
begin
LoadStringFromFile(ExpandConstant('{app}\app.xml'), FileData);
StringChange(FileData, 'XXXXXMARKERXXXXX', ExpandConstant('{app}'));
SaveStringToFile(ExpandConstant('{app}\app.xml'), FileData, False);
end;
I cant seem to get this to work. How would the code above look acomplishing this? And, how would it look using StringChangeEx?
I know this will probably be marked as duplicate, but I just can't figure it out.
Thanks.
I think if you look at the Inno Setup help it explains it:
http://www.jrsoftware.org/ishelp/index.php?topic=isxfunc_stringchangeex
Description:
Changes all occurrences in S of FromStr to ToStr. If SupportDBCS is True (recommended unless you require binary safety), double-byte character sequences in S are recognized and handled properly. Otherwise, the function behaves in a binary-safe manner. Returns the number of times FromStr was matched and changed.
So what you are doing is:
Reading in your text file as one long text string.
Changing all instances of Xxx with Yyy in the string.
Saving the string back to the file.
But looking here it does states:
Loads the specified binary or non Unicode text file into the specified string. Returns True if successful, False otherwise.
So this function might not be ok for you because XML files are usually Unicode.
The answer here explains.
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.
When passing filename parameters to procedures/functions, should I use TFilename or String.
If there is a difference, what is it, and what are then potential ramifications if using a String?
e.g.
procedure TForm1.OpenFile(const AFilename : String);
begin
//Open the file if it exists
...
end;
I think TFilename should be used when developing components because that way IDE can show it's property editor (TOpenDialog will be shown when clicked on ellipsis in the property inspector).
Other than this there is basically no difference which one to use. Remember that if you use TFilename you must add SysUtils to your uses clause.
The only practical difference between string and TFileName types in a plain code is in passing an argument by reference; the following code
procedure GetFileName(var FileName: TFileName);
begin
FileName:= 'abcd.abc';
end;
procedure TForm1.Button2Click(Sender: TObject);
var
S: string;
begin
GetFileName(S);
end;
does not compile with error
[DCC Error] E2033 Types of actual and formal var parameters must be identical
Maybe this is a bit too obvious, but using the string type doesn't communicate anything about the intended usage of a variable. But when you encounter a variable declared as a TFileName, there's a lot more detail communicated right there.
The same principle applys to other basic types like Integer, Cardinal, Double etc. Instead you might want to consider using aliases for these like TCustomerID, THashValue, TInterestRate, etc. as these communicate much clearer what the intended usage of these variables is.
This improves readablility, and also allows for changing the base-type when needed, without having to touch any code using the type... just a recompile and you're done (but do be carefull with binary compatibility ofcourse).
Hmm, My strong preference is for const AFilename: String;
For the reason that especially for larger projects, if you ever need to add source code from another coder, if they have used lots of custom types like TCustomerID, THashValue, TInterestRate, instead of Integer, Cardinal, Double, then you have lots of the above mentioned E2033 to resolve.
Even lots of delphi built in source code doesn't use TFileName, like:
function MatchesMask(const Filename, Mask: string): Boolean;
Furthermore if I have a variable defined like AFileName: TFileName; then its obvious its a filename & the named type doesn't add any readability for me, if anything in some cases it makes code less readable, because you have to click through to check what actual variable its derived from.
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?
Is there a way to get local date time stamp in Inno Setup ?
The answer depends on when you need it.
Needed at the time the setup is built
Needed at the time of install.
Needed at the time the setup is built.
You will need to use ISPP which is part of the Quick Start pack.
You can use the str GetDateTimeString(str, str, str) function.
Example: #define MyDateTimeString GetDateTimeString('dd/mm/yyyy hh:nn:ss', '-', ':');
The help menu in ISTool (Also a part of the Quick Start Pack) has a good help file for ISPP functions including this one where there is a page of information on this function.
Needed at the time of install.
Although a different source, the function is also called GetDateTimeString
Then it must be in a pascal coding block.
Example:
function DateTime : String;
begin
result := GetDateTimeString('dd/mm/yyyy hh:nn:ss', '-', ':');
end;
The details of how to use it are found in the Help File.
Although both functions have the same name, the context of when they are used is important to understanding why you would get one value over another.