Jump to content

Mark Williams

Members
  • Content Count

    274
  • Joined

  • Last visited

Everything posted by Mark Williams

  1. Mark Williams

    IsElevated

    Thanks. I'll check it out
  2. Mark Williams

    isFileInUSe

    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.
  3. 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?
  4. Mark Williams

    IsElevated

    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.
  5. Mark Williams

    Retrieving gmail email with TIDPop3

    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.
  6. Mark Williams

    Retrieving gmail email with TIDPop3

    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.
  7. Mark Williams

    Retrieving gmail email with TIDPop3

    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.
  8. Mark Williams

    IPropertyStore MultiValue Strings

    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?
  9. Mark Williams

    IPropertyStore MultiValue Strings

    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.
  10. Mark Williams

    IDGlobalProtocols GmtOffsetStrToDateTime

    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.
  11. Mark Williams

    IDGlobalProtocols GmtOffsetStrToDateTime

    Awesome. Thanks so much.
  12. Mark Williams

    IDGlobalProtocols GmtOffsetStrToDateTime

    That would certainly solve my problem.
  13. Mark Williams

    IPropertyStore MultiValue Strings

    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.
  14. Mark Williams

    IPropertyStore MultiValue Strings

    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.
  15. Mark Williams

    IPropertyStore MultiValue Strings

    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.
  16. Mark Williams

    IPropertyStore MultiValue Strings

    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).
  17. Mark Williams

    IDGlobalProtocols GmtOffsetStrToDateTime

    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?
  18. Mark Williams

    IDGlobalProtocols GmtOffsetStrToDateTime

    The TTimeZone unit is probably fine for my purposes. All I need to be able to do is get the UTC time zone from the end of the email header.
  19. Mark Williams

    TIDMessage Terminator Sequence

    The above is a quote from the Indy help file re TIDMessage LoadFromFile. I have tested loading .eml files with TIDMessage none of which contain the specified terminator sequence "CRLF.CRLF". They load without problem. I can see online that there are quite a few posts dealing with this, but I am wondering if it is still an issue for the latest versions of INDY. If so, how do you deal with it. I have tried adding the terminator sequence to emails before reading them and IDMessage fails to read the data. I have tried with the following code: var strStream : TStringStream; begin strStream:=TStringStream.Create; try strStream.LoadFromFile(FileName); strStream.seek(0, soEnd); strStream.writeString(sLineBreak+'.'+sLineBreak); strStream.position := 0; IDMessage1.LoadFromStream(strStream); finally strStream.Free; end; The email loads as expected, but my questions are: (1) is this is actually necessary and, if so, (2) is this the right way to go about it? I am dubious because if this was needed in every case to ensure that the email opens then I would have thought it would have been incorporated in TIDmessage's Load methods.
  20. Mark Williams

    TIDMessage Terminator Sequence

    I've had a look at the TIDMessageHelper unit. I've been testing with EML files saved to file from Gmail. Reading your comments in the Github issue you have mentioned, I would have thought these files should not be using dot transparency. However, I can read them fine from file with TIDMessage using LoadFromFile and without the helper unit. But if I am dealing with email files coming from possibly any email client presumably there is no way of knowing whether the email uses dot transparency before loading it. If that is correct then is my best option: Load the file into stream Try IDMessage.LoadFromStream - if it doesn't load... Try the helper unit Although I recall reading somewhere that if you try and read an email without dot transparency via IDMessage it takes a while and eventually triggers a time out error. Is that correct and is there any way around it? That statement should have been deleted. I had tested the sample code included in my original post without positioning the stream back to 0 so IDMessage was processing from the end of the stream. I realised my mistake and corrected it and I thought I had deleted this statement.
  21. Mark Williams

    TIDMessage extract RFT body

    I can't find any examples of how to do this online. Plenty of how to send rtf, but not read it. I can't seem to find any specific unit that deals with it. What mime types do I need to check for just 'text/rtf' or also 'text/enriched' and 'text/richtext'? How do I extract the raw rtf? And how are inline images handled? Are they accessible via the attachments list or in some other way? Thanks.
  22. Mark Williams

    TIDMessage extract RFT body

    Awesome. Thank you so much for your help.
  23. Mark Williams

    TIDMessage extract RFT body

    I have had a look at the functions in IDCoderTNef. I will be working with a TIDAttachment object. A couple of questions: With a TNef attachment does the entire email get wrapped in the .dat file (including any attachments) or just the rtf? In other words. is it safe to parse out the TNef attachment into a new IDMessage object and just ignore the remainder of the original IDMessage? I can see there is a function for IsFileNameTNef which basically checks to see if the file is in the form mentioned by you above. But if the 'application/ms-tnef' is it possible it could be in any other form and, if so what?
  24. Mark Williams

    TIDMessage extract RFT body

    I have mistakenly thought that the Body would contain plain text only. Presumably, it's also therefore possible for an html body to be contained in the body itself if the ContentType is text/html? I have yet to come across any examples of this although I can now see it would be possible. I would prefer to deal with an html body if it exists, if not then a rtf body and finally plain text. With that in mind can you see any problems with the following approach: Check the IDMessage ContentType. If it is text/???? then that is the body type I have to work with. If the ContentType is multipart/???? iterate through the attachments and find the one that best suits my purpose. Is it possible for the ContentType to be multipart and the body to be stored in TIDMessage body rather than in the message parts? I thought they were just alternate names for RTF. Having looked at the specification(s), I see not. What a pain! I have not come across either of these formats and from what I can see online they're not used that much, but I would still wish to be able to handle them if my app comes across them. I suppose the options are to display the body text as plaint text with tags and all or write a converted to convert to html and handle in same way as I handle html body. I have checked online to see if anyone has already written anything to handle the conversion and drew a blank. However, it doesn't look like it would be too difficult to write some code to convert the existing tags to their html equivalents at least the most common ones. AFAIK RTF usually inserts the raw image data into the RTF document (at least that is what WordPad does). I have created RTF emails with Outlook for text purposes. However, when I send these to a Google account they are getting converted to html along the way. I don't know if this is done at the Outlook or Google end. Outlook retains the original email as rtf and when I used Outlook's MailItem object to read it, the body is provided in RTF format and any images are handled as OLE attachments and incorporated by referenced in the rtf. But this may just be how Outlook handles them internally. Without any examples to play with I will work on the basis that the image data is embedded in the raw rtf.
  25. Mark Williams

    Byte Array to String

    Outlook's MailItem gives access to the RTFBody property for RTF formatted emails. This is described in the MS docs I need to get the RTFBody as a string that I can then write to file. I can't work out how to do this in Delphi 10.4. If found the following example from a post 8 years ago: System.Text.Encoding.UTF8.GetString(mailItem.RTFBody); This doesn't compile in 10.4. Can anyone please advise what function should be used with 10.4?
×