Mark Williams
Members-
Content Count
282 -
Joined
-
Last visited
Everything posted by Mark Williams
-
Thanks for the responses. Hadn't though of delayed sends. What I'm trying to catch is someone setting the date on their computer to a date in the past and sending an email to make it appear as though it was sent earlier than it in fact was. I haven't come across this, but surely if the Date header is later than the Received header someone's clock someone is showing the wrong time?
-
Has anyone been able to get the BatchMove component to work correctly with PostgreSQL in AppendUpdate mode where there is an auto inc key field? My BatchMove component creation code is below. In addition, either the FDConnectionPG ExtendedMetaData param is set to true or the FDQueryPG updateOptions.AutoIncFields is set. Either way produces the same result, although I understand there is an efficiency hit with use of ExtendedMetaData. The problem is that whilst new rows get added to the table and my auto inc key field value gets set, it is running backwards ie -1, -2, -3 etc. Am I missing something or is this a bug? var FBatchMove: TFDBatchMove; FReader: TFDBatchMoveDataSetReader; FWriter: TFDBatchMoveSQLWriter; F:TField; begin FBatchMove := TFDBatchMove.Create(nil); FReader := TFDBatchMoveDataSetReader.Create(FBatchMove); FWriter := TFDBatchMoveSQLWriter.Create(FBatchMove); try FReader.DataSet:=FDQueryPG; FWriter.Connection:=FDConnectionPG; FWriter.TableName:='dummy'; FBatchMove.CommitCount:=1000; FBatchMove.Mode := dmAppendUpdate; FBatchMove.options:=FBatchmove.Options+[poIdentityInsert]; FBatchMove.Execute; finally FWriter.Free; FReader.Free; FBatchMove.Free; end;
-
FireDAC Array DML doesn't seem to be usable in any practical sense with Returning clauses FireDAC Array DML returning values from inserted records. That being so, I am trying to work out whether there is an efficient way I can retrieve data from inserted records using Array DML. The following seems a possibility and is lightening fast, but I am concerned that I may be missing something critical: Insert required no of new records using Array DML within a StartTransaction/Commit section. Check the number of affected rows to make sure that all required records have been inserted. If not, then rollback. Obtain the last insert id for the target table for the current session. Work backwards through the array of inserted data in my app and assign the id by decrementing the last id. My initial testing suggests that this works. But it depends on the following assumptions being correct (which I believe they are): The RowsAffected property of TFDQuery is wholly reliable. My DB (in this case PostgreSQL) will always reliably return the correct last insert id for my table in my current session. My DB will insert all my records in the same order as they are added to the TFDQuery params list. My DB will insert the records in an unbroken sequence of IDs. So my questions Are my assumptions sound? Am I missing something else?
-
Returning ids for records inserted with Array DML
Mark Williams replied to Mark Williams's topic in Databases
OK. I'm a little confused, but thanks for the help. -
Returning ids for records inserted with Array DML
Mark Williams replied to Mark Williams's topic in Databases
OK. As mentioned in original post if RowsAffected is less than the number of inserted rows I would expect I rollback the transaction (and handle it as an error). It is only if the RowsAffected = the number of inserted rows expected that I would use the approach in my original post. Taking that into account, do you think it is safe to do so? -
Returning ids for records inserted with Array DML
Mark Williams replied to Mark Williams's topic in Databases
But for an insert procedure would not the number of successful executions be the number of new rows inserted? That certainly seems to to be the case from the testing I have carried out so far. -
Returning ids for records inserted with Array DML
Mark Williams replied to Mark Williams's topic in Databases
Thanks for the suggestions. Just to clarify By that, do you mean that you consider the method I proposed in my original post would be reliable? If so, that would be the quickest and easiest route to go. -
Is there a simple way to check if my app is running elevated? I can see a few oldish posts on this on Google, but nothing recent. I was just wondering if newer Delphi has introduced a simple function for it, although I couldn't find anything in help or likely units. Or perhaps there is a new Windows API procedure?
-
Thanks. I'll check it out
-
I have used the following function for checking if a file is in use for many years (it is recommended on numerous sites): function IsFileInUse(FileName: String): Boolean; var HFileRes: HFILE; hdl:THandle; begin Result := False; if not FileExists(FileName) then Exit; HFileRes := CreateFile(PChar(FileName), GENERIC_READ or GENERIC_WRITE, 0, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); Result := (HFileRes = INVALID_HANDLE_VALUE); if not Result then CloseHandle(HFileRes); end; It still works fine for 32Bit, but not 64 bit. On 64 bit It always returns a valid handle even when the file is in use and thus reports that the file is not in use even when it is. Also, when run from the IDE in 64 bit it crashes with an EExternalException: "External exception C0000008" when trying to close the handle. I have found some posts on this via Google and it seems to be a known issue, the answer being to switch off some debug options. But I am not particularly troubled by the IDE bug. I am majorly concerned by the failure to detect that a file is in use! I've tried with CreateFileA and CreateFileW, but with the exactly the same result.
-
Changing properties in Object Inspector does not stick
Mark Williams posted a topic in Delphi IDE and APIs
Currently on 10.4, but have had this problem right back to 10.1. If I change a text property, caption, name, hint etc in the object inspector it does not stick when you click away on to some other IDE control such as say the form. You have to click on some other property in the object inspector and even then there is no guarantee that your change has been accepted. It's an absolute pain in the butt. Does anyone else have this issue and has anyone got any workarounds? -
No. I just need to know if user has run my app in elevated mode. My App launches Outlook and if one is running elevated and the other isn't then you get an OLE error. I just want to be able to advise user which is doing which so they can correct.
-
I am trying to retrieve the contents of my gmail using TIDPop3 as a test. I can connect, login fine. I then use CheckMessages to find out how many emails there are. It tells me there are 274. However, my Gmail Inbox believes there are over 1500 and my All Mail box tells me there are over 7500. I download the headers using following: Count:=CheckMessages; for i := 1 to count do begin IDMessage1.Clear; RetrieveHeader(i, IDMessage1); ListBox1.Items.Add(IDMessage1.From.Address+' '+IDMessage1.Subject+' '+DateTimeToStr(IDmessage1.Date)); end; It is starting (almost) with the oldest emails and then proceeding forwards to the most recent. However, the first email it retrieves is not the oldest. It skips a handful. Then it gets a small batch, skips some more and then produces a larger batch. It all seems a bit random. Are there any obvious reasons why CheckMessages is reporting only a small portion of the actual emails and also why RetrieveHeader may be skipping batches? Out if interest, I tried RetrieveHeader outside the range of CheckMessages, but well within the true email count and (unsurprisingly) it threw up a range error.
-
I'm just going to swerve it and use the IMAP component, which seems much fitter for purpose. Just grateful I hadn't invested too much time in Pop3 before hitting a snag.
-
Sorry to have to say "ignorance"! Didn't know there was an INDY IMAP client. Thanks for pointing in the right direction. Have abandoned Pop3.
-
I am trying to extract meta data from media files using the IPropertyStore interface. So far I have no problem extracting most property values, but I am having difficulties with certain variant types. Below is part of the code used. Procedure ExtractMediaTags(fileName : String); type TMetaDataTagType = (mdtString, mdtInteger, mdtLongInt, mdtDateTime); var Store : IPropertyStore; Function GetMetaDataValueAsString(Const MetaDataTag : TPropertyKey; MetaDataTagType : TMetaDataTagType) :String; var v : PropVariant; begin try OlECheck(store.GetValue(MetaDataTag, v)); try case MetaDataTagType of mdtString : if assigned(v.bstrVal) then Result := v.bstrVal; mdtInteger : Result := v.iVal.ToString; mdtLongInt : Result := v.hVal.QuadPart.ToString; mdtDateTime :Result := FileTimeToDateTimeStr(v.filetime); end; finally PropVariantClear(v); end; except end; end; var v : PropVariant; s : string; begin if SHGetPropertyStoreFromParsingName(pWideChar(FileName), Nil, GPS_READWRITE, IPropertyStore, store)<>S_OK then exit; AddMetaData(pd.DocProps, '70', GetMetaDataValueAsString(PKEY_Music_Artist, mdtString)); AddMetaData(pd.DocProps, '74', GetMetaDataValueAsString(PKEY_Music_AlbumArtist, mdtString)); end; For example, I am able to extract the value for PKEY_MUSIC_ALBUMARTIST. But not for PKEY_MUSIC_ARTIST. The MS docs describe both variant values as being of type VT_LPWSTR (here). However, more detailed remarks state that PKEY_MUSIC_AlbumArtist references a string value (here) and PKEY_Music_Artist references a "Multivalue String" (here). I have queried the TVarType of PKEY_Music_Artist and it doesn't register as any of the specified TVarTypes not even varUnknown. Although PKEY_MUSIC_AlbumArtist also doesn't register as any TVarType. Does anyone have any idea what type "Multivalue String" is and how I would read it?
-
Of course. I was just posting an example to show how to handle array data. I wasn't (at least not intentionally) suggesting that it is a good idea to repeat that code for every tag. My own code attempts to extract dozens of tag values and that is done by passing the tags into a separate function.
-
I'm extracting the Date headers and Received headers from the raw email headers. I want to save the date as the user's local datetime, and I am also trying to extract the UTC offset stored in the header and I am finding this difficult to do. For example, I extract the following string from a header: As far as I can see the following are the relevant functions in IDGlobalProtocols StrInternetToDateTime gives you the original datetime as in the header ie "8 Nov 2020 06:55:38". GmtOffsetStrToDateTime - looks just the job on first glance, but unfortunately it expects just the last part of the header string containing the offset info ie "-0800 (PST)." If it gets more it won't work. Looking at the code which deals with the extraction of the UTC portion, it seems to be an awful lot of work to get to what the function needs. It is not simply a case of calling StringPos for +/-. The two functions above would be enough for my purposes if it were simple to extract the relevant part of the header to submit to GmtOffsetStrToDateTime. GMTToLocalDateTime - returns the local date time from the header. I had an idea that I could: Call TTimeZone GetUTCOffset on the value returned by GMTToLocalDateTime Deduct the offset from the GMTToLocalDateTime value to give me UTC datetime. Deduct the original header date from the UTC datetime (or vice versa) to give me the UTC offset figure as in the header. However, there is a problem (bug?) with GMTToLocalDateTime and how it handles daylightsavings. It seems to apply daylight savings dependent on the date on the pc clock not the date in the header. If I call GMTToLocalDateTime on the above example date header with my PC Clock correctly set as today's date (UK aligned with UTC at the moment) it correctly returns the time as 14:55. If I change the date submitted to GMTToLocalDateTime from 8 Nov to 8 Jul, you would expect it to return the time as 15:55 (in July UK is one hour ahead of UTC). But it doesn't. It returns 14:55. If I change the pc date to 8 July and submit the 8 Nov date to GMTToLocalDateTime it returns 15:55 ie one hour ahead of UTC. This is correct for 8 July, but not for 8 Nov. RawStrInternetToDateTime this is used internally by both StrInternetToDateTime and GMTToLocalDateTime. RawStrInternetToDateTime takes the date string as a var parameter. GMTToLocalDateTime then uses the returned value (formatted as required) for submission to GmtOffsetStrToDateTime to get the UTC offset. Unfortunately, the date string parameter for both StrInternetToDateTime and GMTToLocalDateTime is a constant not a var. If it were a var, I could simply submit the returned value to GmtOffsetStrToDateTime. But the long and short of it is, I cannot figure out how best to get the UTC offset.
-
Awesome. Thanks so much.
-
That would certainly solve my problem.
-
Sorry I didn't see any code. Looking back at your post, do you mean the suggestion to use PropVariant's vt member to ascertain type? If so, I haven't ignored it. The simple example I posted was just to show, for anyone interested, how to extract array type info. The example doesn't pass in the MetaDataTagType parameter nor does it reference the vt member as it didn't need to given that the tagtype PKEY_Music_Artist is known to be an array type.
-
A slight but important update to the code sample in my initial entry. Original code: if SHGetPropertyStoreFromParsingName(PWideChar(fileName), nil, GPS_READWRITE, IPropertyStore, Store) <> S_OK Whilst using the GETPROPERTYSTOREFLAG of GPS_READWRITE will work fine for media files that Windows can both read and write, it will fail to open media files which Windows can only read. Obvious really, but caused me a half hour of consternation before I realised why I couldn't read the tags for certain other files for eg AVI. In such cases using the GPS_READWRITE tag will get you an "Access Denied" result. If all you want to do is read the tags then use GPS_DEFAULT.
-
It is the PKEY_MUSIC_ARTIST that I was having difficulties with. I can extract PKEY_Music_AlbumArtist) no problem via the bStrVal value. However, did you get comma delimited values from PKEY_Music_AlbumArtist? If so, it's a bit odd that MS should declare PKEY_Music_AlbumArtist as a string value instead of an array value.
-
Yes. You guess right! And for anyone interested in how to extract the info from type 4127 the following seems to do the job ok. v, v2 : PropVariant; k : cardinal; begin try OlECheck(store.GetValue(MetaDataTag, v)); if metaDataTag = PKEY_Music_Artist then begin if PropVariantGetElementCount(v) > 0 then //for some reason never exits loop unless you query this first begin for k := 0 to PropVariantGetElementCount(v) -1 do begin if PropVariantGetElem(v, k, v2) = S_OK then Result :=Result + v2.bstrVal + ',' end; end; NB the index for PropVariantGetElem is 0 based. Interestingly (and rather annoyingly) after testing I have realised that some files have properties stored as multivalue strings in an array with only one item, but with comma separated values! Really does seem to defeat the purpose of the array type, but possibly explains why @emailx45 possibly got a comma separated array of values from a single string PropVariant (see below).
-
The simple solution to my specific problem would be to change the declaration of either or both StrInternetToDateTime and GMTToLocalDateTime so that it takes a var parameter rather than a constant so that users can then submit the returned value to GmtOffsetStrToDateTime without having to work out/duplicate all the work Indy already does in parsing the header to extract the relevant portion. I appreciate that I am the only person who seems to have asked for this functionality to date, but it is such a simple change would it be possible to incorporate into an upcoming update?