I'm using Delphi 2010 and Rave Reports (comes built in, v. 7.7.0).
I have been using this couple for 5 months without any problem. In my company i use two languages, first i use our primary language (Turkish) and when people wants to use another language i change the specific text and memo values according to their tag value.
This approach worked fine till last week. Last week changing the values at runtime stopped working. I don't know why, everything seems ok with the code, i also tried to check changed values, the values seemed to changed but when i execute reports all the values changed the their defaults.
Here is my code for changing :
procedure ProcessRaveReport( APageName : string ); // 'rp411.rp411Page'
var
myPage : TRavePage;
myText : TRaveText;
i, iTag : Integer;
begin
dm.Rave.Open;
with dm.Rave.ProjMan do
begin
myPage := FindRaveComponent(APageName,nil) as TRavePage;
if myPage = nil then Exit;
for i:= 0 to myPage.ComponentCount-1 do
begin
if myPage.Components[i] is TComponent then
iTag := (myPage.Components[i] as TComponent).Tag;
if (iTag > 0) then
begin
if myPage.Components[i] is TRaveText then
begin
//ShowMessage((myPage.Components[i] as TRaveText).Text);
//ShowMessage(getLangIDS((myPage.Components[i] as TRaveText).Tag));
(myPage.Components[i] as TRaveText).Text := getLangIDS((myPage.Components[i] as TRaveText).Tag);
//ShowMessage('Sonuc : '+(myPage.Components[i] as TRaveText).Text);
end
else if myPage.Components[i] is TRaveMemo then
(myPage.Components[i] as TRaveMemo).Text := getLangIDS((myPage.Components[i] as TRaveMemo).Tag);
end;
//iTag := 0;
end;
end;
dm.Rave.Close;
end;
You can see my showmessage calls, this messages prove that value changed to new language but at the end i always see the default values.
Is there anyone knows any solution that problem?
Denizhan
I miss the .execute of the RvProject-component "Rave" ... on a quick check it looks good but you only change the instance of the RvProject and not the file itself.
Related
I have some problems to create an instance of the StarOffice Desktop object.
I used the standard construct below but whenever it comes to the line: StarDesktop := StarOffice.CreateInstance('com.sun.star.frame.Desktop');
My StarDesktop Variant stays unassigned. I am pretty sure that the code is ok until there but perhaps something with the OpenOffice installation is messed up.
Is there a way to check the com objects or did somebody had the same problem and could solve it...
uses
ComObj;
procedure OpenOfficeDocument;
var
StarOffice: Variant;
StarDesktop: Variant;
begin
StarOffice := CreateOleObject('com.sun.star.ServiceManager');
StarDesktop := StarOffice.CreateInstance('com.sun.star.frame.Desktop');
// StarDesktop is always "unassigned"
....
Yes, I know. I should have stated more clearly that I am too 100% sure that it would work normally in a correct environment.
But my question is what could be the cause why it doesn't work. Why the 'com.sun.star.frame.Desktop' instance is unassigned. I have no option/way to debug it...
And it is a bit unfair to vote me down, I researched for one hour without finding something to explain why it could not work.
Or how and where to check if something is wrong with the Office installation (I uninstalled and reinstalled it twice already"
Again, I know this will work for others and normally would work for me, but something is wrong at my system and I would like to know some help to point me in the direction what could be wrong in the system (and not in the code example...)
is OpenOffice installed on client?
doesn't throw any exception?
I'm using Bernard Marcelly's Delphi 7 OOo tool and as can you see his code like that;
var
OpenOffice, StarDesktop: Variant;
...
OpenOffice:= CreateOleObject('com.sun.star.ServiceManager');
if isNullEmpty(OpenOffice) then Raise Exception.Create('OpenOffice connection is impossible');
StarDesktop:= OpenOffice.createInstance('com.sun.star.frame.Desktop');
if isNullEmpty(Result) then Raise Exception.Create(Format('Impossible to create service : %s', ['com.sun.star.frame.Desktop']));
...
'some constants converted to string'
So, if StarDesktop is null, possible can not access Oo Desktop service. If OpenOffice installed properly some features may be missing, options have to set.
This works for me (in my application):
class procedure TOpenOffice.Connect;
begin
if IsConnected then
Exit;
try
FServiceManager := CreateOleObject('com.sun.star.ServiceManager');
except
FServiceManager := Null;
end;
if VarIsNull(FServiceManager) then
raise EOpenOfficeException.Create(StrConnectionFailed);
FDesktop := CreateService('com.sun.star.frame.Desktop');
FDispatchHelper := CreateService('com.sun.star.frame.DispatchHelper');
FIntrospection := CreateService('com.sun.star.beans.Introspection');
FReflection := CreateService('com.sun.star.reflection.CoreReflection');
end;
and:
class function TOpenOffice.CreateService(const ServiceName: string): Variant;
begin
Result := FServiceManager.createInstance(ServiceName);
if VarIsNull(Result) then
raise EOpenOfficeException.CreateFmt(StrCouldNotCreateService,
[ServiceName]);
end;
I have this simple code to check if a record exists in a table, but it always returns a runtime error :
Arguments are of the wrong type, are out of acceptable range, or are
in conflict with one another.
my code is this :
function TDataModuleMain.BarCodeExists(barCode: string): boolean;
begin
if ADOQuerySql.Active then
ADOQuerySql.Close;
ADOQuerySql.SQL.Clear;
ADOQuerySql.SQL.Text := 'select count(1) from Card where BarCode = (:TestBarcode)';
ADOQuerySql.Parameters.ParamByName('TestBarcode').Value := barCode;
ADOQuerySql.Open; // HERE THE RUNTIME ERROR APPEARS
Result := ADOQuerySql.Fields[0].AsInteger = 1;
ADOQuerySql.Close;
ADOQuerySql.Parameters.Clear;
end;
The field BarCode in table Card is of type nvarchar(100)
In debug I see that the parameter is created, and gets populated with the correct value.
Running the query in sql server management studio also works.
I also found this How to pass string parameters to an TADOQuery? and checked my code with the code in the answer but I don't see any problems here.
Also this AdoQuery Error using parameters did not help me.
It will no doubt be something very simple that I have missed but I just dont see it now.
EDIT : things I tried from suggestions in the comments:
.ParamCheck := True (default)
.Parameters.ParamByName('TestBarcode').DataType := ftString
.Parameters.ParamByName('TestBarcode').DataType := ftWideString
None of these worked however.
What did help was using a non-shared AdoQuery for this, and that one did the job without any errors. I am using that now as the solution but I am still looking at the shared AdoQuery out of curiousity what the exact problem is.
EDIT: the source of the problem is found.
I used the function provided by MartinA to examine both the dynamic created query and the shared AdoQuery and I found one difference.
The shared AdoQuery had the this property filled :
ExecuteOption := [eoExecuteNoRecords]
and the dynamic created query does not.
Since this property is not set in designtime I did not see it.
After clearing the property to [] the shared AdoQuery worked again.
I am going to switch to using non shared AdoQuery for this kind of work as been suggested.
Thanks everyone for your assistance.
The following isn't intended to be a complete answer to your q, but to follow up my comment that "all you have to do is to inspect your form's DFM and compare the properties of your original ADoQuery with the unshared one. The answer should lie in the difference(s)" and your reply that the unshared query is created dynamically.
There is no "voodoo" involved in the difference in behaviour between your two ADOQuerys. It's just a question of capturing what the differences actually are.
So, what you need, to debug the problem yourself, is some code to compare the properties of two components, even if one or both of them is created dynamically. Using the following routine on both components will enable you to do exactly that:
function TForm1.ComponentToString(AComponent : TComponent) : String;
var
SS : TStringStream;
MS : TMemoryStream;
Writer : TWriter;
begin
// Note: There may be a more direct way of doing the following, without
// needing the intermediary TMemoryStream, MS
SS := TStringStream.Create('');
MS := TMemoryStream.Create;
Writer := TWriter.Create(MS, 4096);
try
Writer.Root := Self;
Writer.WriteSignature;
Writer.WriteComponent(AComponent);
Writer.FlushBuffer;
MS.Position := 0;
ObjectBinaryToText(MS, SS);
Result := SS.DataString;
finally
Writer.Free;
MS.Free;
SS.Free;
end;
end;
Over to you ...
I've got a Delphi 7.0 application that throws a memory access exception / message box every time it writeln's an empty string from the string list associated with a combo box:
csvstrlst := combobox1.Items;
csvstrlst.clear;
csvstrlst.add(''); //problem
csvstrlst.add('a'); //no problem
csvstrlst.add(''); //problem
csvstrlst.add('b'); //no problem
//throws memory access messages (I think the writeln writes a line though)
for n := 1 to csvstrlst.Count do begin
writeln(out_file,csvstrlst.strings[n-1])
end;
//throws memory access messages (writeln does write a comma text string though)
writeln(out_file,csvstrlst.commatext);
Running on Windows 7 or XP. As application or in D7 IDE. Combobox with empty string items also causes the same error if the parent of the form it is on is changed.
Has anyone else ever seen or heard of this problem? Any other information available at all?
This is a known and solved bug described in QC:
TCombobox gives AV when selecting empty item from dropdown
Although this is a bug, you should not reuse parts from controls to perform some data tasks as described in your question.
You will not save anything doing so, but getting most the time unwanted sideeffects (controls get repainted and/or fire events)
If you want to have a TStringList then create an instance.
csvstrlst := TStringList.Create;
try
// csvstrlst.Clear;
csvstrlst.Add( '' );
csvstrlst.Add( 'a' );
csvstrlst.Add( '' );
csvstrlst.Add( 'b' );
for n := 0 to csvstrlst.Count - 1 do
begin
WriteLn( out_file, csvstrlst[n] )
end;
WriteLn( out_file, csvstrlst.CommaText );
finally
csvstrlst.Free;
end;
As Sir Rufo has discovered the issue is a VCL bug introduced in Delphi 7 as described in QC#2246. According to that report the bug is resolved in a build with major version number 7 so you may be able to fix the problem by applying the latest Delphi 7 updates.
If not then you can fix the problem from the outside. I don't actually have a Delphi 7 installation to test this on, but I believe that this interposer class will work.
type
TFixedComboBoxStrings = class(TComboBoxStrings)
protected
function Get(Index: Integer): string; override;
end;
TComboBox = class(StdCtrls.TComboBox)
protected
function GetItemsClass: TCustomComboBoxStringsClass; override;
end;
function TFixedComboBoxStrings.Get(Index: Integer): string;
var
Len: Integer;
begin
Len := SendMessage(ComboBox.Handle, CB_GETLBTEXTLEN, Index, 0);
if (Len <> CB_ERR) and (Len > 0) then
begin
SetLength(Result, Len);
SendMessage(ComboBox.Handle, CB_GETLBTEXT, Index, Longint(PChar(Result)));
end
else
SetLength(Result, 0);
end;
function TComboBox.GetItemsClass: TCustomComboBoxStringsClass;
begin
Result := TFixedComboBoxStrings;
end;
The bug that was introduced in Delphi 7 is simply that the if statement reads:
if Len <> CB_ERR then
So, when Len is zero, that is when the item is the empty string, the True branch of the if is chosen. Then, the SendMessage becomes:
SendMessage(ComboBox.Handle, CB_GETLBTEXT, Index, Longint(PChar('')));
Now, PChar('') has special treatment and evaluates to a pointer to read only memory containing a zero character. And so when the combo box window procedure attempts to write to that memory, an access violation occurs because the memory is read only.
I'm writing an app I'd like to be backwardly compatible to some extent on XP, or at the very least windows vista.
EDIT FOR CLARITY: I need to be able to do what the first code snippet below does, but in XP. "Does anybody know the best approach to take under XP, given the functions aren't available in USER32.DLL.?"
My initial prototype code on windows 7 just called CreateProcess to start up displayswitch.exe, which is deployed with windows 7.
if you are not familiar with it, it's a handy little utility that is what gets invoked when you press the windows key and the letter P. you can read more about it here.
while this was adequate, i subsequently needed to sense the current state (eg internal vs external or extend vs clone), so i have now coded up a winapi solution that works well on windows 7 (and i presume 8). it involves making calls to SetDisplayConfig and QueryDisplayConfig in User32.DLL
The pertinent section of it is here (minus the many, many structures i had to hand craft in pascal code from the original klingon).
function getTopology : DISPLAYCONFIG_TOPOLOGY_ID ;
var NumPathArrayElements,
NumModeInfoArrayElements : UINT32;
var PathArrayElements_Size,
ModeInfoArrayElements_Size : UINT32;
error : Longint;
paths : PDISPLAYCONFIG_PATH_INFO_array;
info : PDISPLAYCONFIG_MODE_INFO_array;
begin
NumModeInfoArrayElements := 0;
Result := DISPLAYCONFIG_TOPOLOGY_EXTERNAL;
inc(result);
error := GetDisplayConfigBufferSizes(QDC_DATABASE_CURRENT,NumPathArrayElements,NumModeInfoArrayElements);
case error of
ERROR_SUCCESS :
begin
PathArrayElements_Size := sizeof(DISPLAYCONFIG_PATH_INFO) * NumPathArrayElements ;
ModeInfoArrayElements_Size := sizeof(DISPLAYCONFIG_MODE_INFO) * NumModeInfoArrayElements;
GetMem(paths,PathArrayElements_Size);
try
GetMem(info,ModeInfoArrayElements_Size );
try
error := QueryDisplayConfig(QDC_DATABASE_CURRENT,NumPathArrayElements, paths,NumModeInfoArrayElements, info,result);
case error of
ERROR_SUCCESS :;
else
Result := DISPLAYCONFIG_TOPOLOGY_EXTERNAL;
inc(result);
end;
finally
FreeMem(info,ModeInfoArrayElements_Size );
end;
finally
FreeMem(paths,PathArrayElements_Size);
end;
end;
end;
end;
function setTopology ( top : DISPLAYCONFIG_TOPOLOGY_ID) : boolean;
var flags : dword;
begin
result := false;
flags := DecodeDISPLAYCONFIG_TOPOLOGY_ID_SDC(top);
if flags <> 0 then
begin
result := SetDisplayConfig(0,nil,0,nil,SDC_APPLY or flags) = ERROR_SUCCESS;
end;
end;
Since these functions don't exist in XP (as far as I know), I am looking for a stable way of achieving a similar thing in XP. whilst i am coding in Delphi, it's not necessary that the solution be presented as such. i am quite happy to just look at how it's done, or read a description of the appropriate steps, and implement it myself.
(removed full listing as it was confusing the issue as it did not appear like a question)
I'm using FastReport 4.7.31 in Turbo Delphi Pro.
The following procedure processes the data stored in several dated files depending on user input.
procedure TfrmMain.MyReportPrint;
var MDate : Tdate;
S, myfile : string;
firstone: boolean;
// Date1, Date2 & ShowPreview are global variables set via a dialog box
begin
firstone := true;
MDate := Date1;
while MDate < IncDay(Date2, 1) do
begin
DateTimeToString(S,'yyyymmdd',MDate);
myfile := 'm' + S + '.dbf';
if FileExists(DataPath + '\' + myfile) then
begin
tblPS.Close;
tblPS.TableName := myfile;
frxMyReport.PrepareReport(firstone);
firstone := false;
end;
MDate := IncDay(MDate, 1);
end;
if ShowPreview then frxMyReport.ShowReport else frxMyReport.Print;
end;
frxMyReport.Print prints all the pages.
frxMyReport.ShowReport shows only the last page prepared.
The ShowReport method takes an optional parameter ClearLastReport, and its default value is true. Whether it's true or false, ShowReport prepares the report before displaying it, so in your code, you're discarding everything you've already prepared and then re-preparing the report using the most recently assigned table settings. If the only change you were to make to your code would be to pass False to ShowReport, then you'd find that the preview showed all your pages, but repeated the last page.
In contrast to ShowReport, the Print method does not prepare the report. It only prints what has already been prepared. You want ShowPreparedReport for your preview, not ShowReport. See section 1.9 of the FastReport Programmer's Manual.