I have been googling all day and keep seeing the same 10 examples for FireMonkey, apptethering and Delphi XE6. I am new to XE6 and app tethering. I thank you for any help I can get.
MY STORY
I have Delphi XE6. I am trying to create a tethered FireMonkey application for the android platform. I have a VCL application that will run on a server. There will be many android tablets connecting to the server application at the same time.
The user pushes a button on a tablet which will cause a unique id to be sent to the server using the SendString method of the TTetheringAppProfile. The server has a TetherProfileResourceReceived event and gets the unique id from the AResource.Value. The server queries a database and gets a record. This is all good.
Now I need to send the record back to the SAME profile that sent the request. Every example I have seen uses the item index to get the TTetheringProfileInfo for send string (TetherProfile.Resources.Items[0].Value). I think I can't rely on the index because I will have multiple connections. I want to send the response string right back to the requesting profile.
MY FAILED ATTEMPT
procedure TfrmTabletServer.POSTetherProfileResourceReceived(
const Sender: TObject; const AResource: TRemoteResource);
var
RequestID : Integer;
SendRec := String;
Requester : String;
begin
Requester := AResource.Name;
if AResource.ResType = TRemoteResourceType.Data then begin
RequestID := AResource.Value.AsInteger;
SendRec := GetRecord(RequestID);
//this works but I cant rely on index name due to multiple connections
//POSTetherProfile.Resources.Items[0].Value = SendRec;
//I would prefer to use SendString to keep the requests temporary
//I can't figure out how to get the TTetheringProfileInfo from the AResource
POSTetherProfile.SendString('TTetheringProfileInfo from AResource?','Response ' + ID.AsString, SendRec);
end;
MY RESOURCE
http://docwiki.embarcadero.com/RADStudio/XE6/en/Sharing_Data_with_Remote_Applications_Using_App_Tethering
After a while trying to get the working I still couldn't find a way of obtaining profile identifier from the parameters sent to the OnResourceReceived event.
The way I have solved this is to append the profile identifier to AResource.Hint string so the Hint looks like
"{OriginalHint};{ProfileID}"
This way I can always find the profile identifier by looking at the hint string.
This is not ideal but it works until we have the profile identifier passed as part of AResource.
Related
I write this code for button click event . the first time I click the button every thing work correctly but when click for the second time on button it raise an error. whats the problem?
procedure TfrmMain.Button1Click(Sender: TObject);
var
B : Boolean;
begin
DM.tblTemp.DisableControls;
B:= DM.tblTemp.Locate('FoodName', DM.tblAsli.FieldByName('FoodName').AsString,[]) ;
if B then
begin
DM.tblTemp.Edit;
DM.tblTemp.FieldByName('Number').AsInteger:= DM.tblTemp.FieldByName('Number').AsInteger + 1;
DM.tblTemp.Post;
end
else
begin
DM.tblTemp.insert;
DM.tblTemp.FieldByName('FoodName').AsString := DM.tblAsli.FieldByName('FoodName').AsString;
DM.tblTemp.FieldByName('UnitPrice').AsInteger := DM.tblAsli.FieldByName('FoodPrice').AsInteger;
DM.tblTemp.FieldByName('Number').AsInteger := 1;
DM.tblTemp.Post;
end;
TotalPrice:= TotalPrice + DM.tblTemp.FieldByName('TotalPrice').AsInteger;
DM.tblTemp.EnableControls;
end;
the Error is
Row cannot be located for updating. Some values may have been changed
since it was last read
DM is data madual
tmbTbl is ADOTable
It is a pity you haven't said which DBMS you are using (e.g. Sql Server or MS Access,
nor told us a full list of the table's column types and indexes, if any.
The most likely answer to your q is that the variable B is set to False the first time
because the call to tblTemp.Locate fails to locate the Food name in question, so the Insert branch executes and the food's data is
added to the table but the second time the Edit branch executes and the error occurs,
though you have not said where, exactly. My guess would be on the call to .Post, because
the error message is one the ADO layer, which sits between the DBMS provider and your app,
emits when it attempts to post a change to the table but cannot identify which table row to update.
As I mentioned in a comment, the fix to this is usually at add a primary key index to the table, and I gather from your latest comment that this has succeeded for you. For the record and benefit of future readers it would be helpful if you could confirm which DBMS you are using and which Ole Driver you are using in your connection string.
Fwiw, I've tested your code using a table on MS Sql Server and MS Access and do not get the
error with either database.
Btw there is an obvious q, "How is it that tblTemp.Locate succeeds, but ADO is unable to
identify the correct record to post the update?" The answer is that tblTemp.Locate works in
a different way than identifying the relevant row to post the update.
I'm testing a quite old Delphi 6 application and would like to display the database name the TSqlConnection is actually connected to, so I can see quickly if I'm connected to the test or production database.
In sqlconnections.ini, the app has a connection named 'Vienna' to a Firebird database defined like this:
Database=192.168.1.15:ProductionDB (it's an alias)
and I've replaced that for testing purposes with
Database=192.168.1.15:TestDB.
But I've seen that just accessing the TSqlConnection's Params-List and there the value of 'Database' does not work. This value is always set the same as it is in design mode.
How can I find out which database (which Firebird alias in my case) the TSqlConnection is actually connected to?
monitoring tables were introduced into FB 2.1.x :-)
So try
select MON$DATABASE_NAME from MON$DATABASE
Or try
select MON$ATTACHMENT_NAME from MON$ATTACHMENTS
where MON$ATTACHMENT_ID = CURRENT_CONNECTION
See info at
c:\Program Files (x86)\Firebird\Firebird_2_1\doc\README.monitoring_tables.txt
http://firebirdsql.su/doku.php?id=mon_database via www.translate.ru
https://dba.stackexchange.com/questions/29919/firebird-monitoring-tables
http://www.upscene.com/documentation/fbtm2/index.html?dm_monitoringtables.htm
http://www.firebirdsql.org/file/community/conference-2014/pcisar/#1
When SQLConnection.Params property is empty Params.Values['Database'] return empty string even when BeforeConnect or AfterConnect events is fired .
You can use TSQLConnection.OnLogin event.
For example :
procedure TForm11.SQLConnection1Login(Database: TSQLConnection;
LoginParams: TWideStrings);
var
i : integer;
begin
//Show all params
for I := 0 to LoginParams.Count - 1 do
ShowMessage(LoginParams[i]);
// Show database
ShowMessage(LoginParams.Values['Database']);
end;
Use the OnLogin event to assign values to the User_Name, Password, and
Database parameters immediately before TSQLConnection attempts to
connect to the database server. OnLogin only occurs if the LoginPrompt
property is true. If LoginPrompt is true but there is no OnLogin event
handler, a default login dialog appears in which the user can enter a
user name and password. The connection fails if correct values for the
user name and password are not supplied in the dialog or by the
OnLogin event handler.
Source
Hi guys i've written a function which has to check the ID given to the function as iID and then output the name of the member of it is found, otherwise it must output that it is not found
A different table is active when this function is called so it must change the table to Members (to search the ID) and then back again afterwards (I have multiple tables)
function fCheckID(iID:integer):String;
var sTable:string;
begin
sTable:=datamoduleX.tableX.TableName;
datamoduleX.tableX.TableName:='Members';
if datamoduleX.tableX.Locate('RefNo',iID,[]) then
result:=dmRooiX.tblRooiX['Name']+' '+datamoduleX.tableX['Surname']
else
result:='ID: '+inttostr(iID)+' does not exist';
datamoduleX.tableX.TableName:=sTable;
end;
but the problem is every time I call this function I get an error that says "Cannot perform this operation on an open dataset"
if I close the dataset before I run the function I get "Cannot perform this operation on a closed dataset"
I know the error occurs when I try to access the table name or change it (the function does not give the error when those 3 lines are commented out)
I have no idea how to make this work
any help would be greatly appreciated
Example :
Table1.TableName := 'TABLE1';
Table1.Open;
Table1.TableName := 'TABLE2'; <-- Cannot perform this operation on Open data set. Because Table1 is open
Table1.Locate('ID',11,[]);
simple solution
Table1.TableName := 'TABLE1';
Table1.Open;
Table1.Close; <--Close table before change table name
Table1.TableName := 'TABLE2';
Table1.Open; <-- Open new table before do Locate
Table1.Locate('ID',11,[]);
I had a multi tier project in which i would collect data from a microsoft sql 2005 through a FDStoredProc with a function and the function would return a dataset to the client. When the server assigns the dataset to the result of the function and the function tries to send it to the client i get this error. Project etctec.exe raised exception class TDBXError with message 'TDBXTypes.BLOB value type cannot be accessed as TDBXTypes.Bytes value type'.
In another project i used the StoredProc of a different database with TFDStoredProc in exactly the same way and it works fine. Any ideas what would raise this error?
This is what i do in the server.
function TServerMethods1.getCategories(): TDataSet;
begin
FDStoredProc1.ParamByName('#val1').AsInteger:= 1;
FDStoredProc1.ParamByName('#val2').AsInteger:= 0;
FDStoredProc1.ParamByName('#val3').AsInteger:= 1;
FDStoredProc1.ParamByName('#val4').AsInteger:= 1;
FDStoredProc1.Open();
result:= FDStoredProc1;
end;
and the client calls it like this...
dataset:=ClientModule1.ServerMethods1Client.getCategories();
Problem comes from some fields that are of type NVARCHAR(max), anyone knows a workaround to this error without changing the field type?
I tried changing the dataset's field type to a string or something with no success. The only thing i can temporarily do is get these fields separately, put them in a stringlist or something like that and pass it to the client.
I think you should use some of similar methods below:
http://docwiki.embarcadero.com/Libraries/XE4/en/Data.DB.TDataSet.CreateBlobStream
http://docwiki.embarcadero.com/Libraries/XE4/en/Data.DB.TDataSet.GetBlobFieldData
You should define you field as a blob field and then put data in/out with the functions described in the links.
Here is also example how to copy data:
http://docwiki.embarcadero.com/Libraries/XE4/en/Data.DB.TDataSet.CreateBlobStream
I'm working on Word automation and to get rid of "Call was rejected by callee" / "the message filter indicated that the application is busy" errors I implemented an IMessageFilter. The messagefilter works like a charm when I automate Word directly like:
Word.Documents.Open(...)
Document.SaveAs(...)
But when I call TOleContainer.DoVerb(ovPrimary), I still get errors when Word is displaying a modal dialog. Why does the MessageFilter not work with TOleContainers DoVerb methode?
"Call was rejected by callee" is what you always get when Word is in interactive state, ie displaying a dialog. This is not restricted to Word. It also happens with Excel, for example when the user was editing a cell. And it does not have to be obvious in the user interface either. When you start editing a cell, move focus to another application and come back to Excel, the UI doesn't give you a clue but it is still in "interactive" mode and will reject automation calls with the "Call was rejected by callee" error.
So basically when you automate Word in conjunction with user interaction (and not just with Word in a background process), you should be prepared to get and handle these errors.
Edit
If you want to know whether Excel or Word is in interactive mode before calling any other COM method: just ask the COM-server whether it is "Ready":
Result := _GetActiveOleObject('Excel.Application');
try
aSharedInstance := not VarIsClear(Result);
if aSharedInstance then
Version := Result.Version; // If this produces an exception, then use a dedicated instance.
// In case checking the version does not produce an exception, but Excel still isn't
// ready, we'll check that as well.
// By the way, for some unclear reason, partial evaluation does not work on .Ready,
// so we'll do it like this:
if aSharedInstance and (StrToIntDef(StringBefore('.', Version), 0) >= EXCEL_VERSION_2002) then
aSharedInstance := Result.Ready;
except
aSharedInstance := False;
end;
if not aSharedInstance then
Result := CreateOleObject('Excel.Application');
Update
Apparently Word doesn't have a "Ready" property (whoever said Microsoft was consistent?). In that case you need to determine its readiness yourself by calling a simple (and fast) property before the actual call, and assuming that when that throws an exception, Word isn't ready. In the above example the Version is retrieved before the Ready property. If that throws an exception, we just assume that the application (Excel in this case) isn't ready and proceed accordingly.
Something along the lines of:
while Tries <= MaxTries do
try
Version := Word.Version;
Tries := MaxTries + 1; // Indicate success
Word.TheCallYouReallyWantToDo;
except
Inc(Tries);
sleep(0);
end;
Note Word.Version does not throw an exception when a dialog is open, so that is no use for figuring out whether Word is ready. :( You will have to experiment to find one that does.
IMessageFilter doesn't handle all exceptions, for example, at some points, office applications 'suspend' their object model, at which point it cannot be invoked and throws: 0x800AC472 (VBA_E_IGNORE)
In order to get around this, you have to put your call in a loop and wait for it to succeed:
while(true)
{
try
{
office_app.DoSomething();
break;
}
catch(COMException ce)
{
LOG(ce.Message);
}
}
// continue after successful call
See here for more details.