Mark Williams
Members-
Content Count
282 -
Joined
-
Last visited
Everything posted by Mark Williams
-
Batch Reading Emails from Windows Explorer
Mark Williams replied to Mark Williams's topic in General Help
OK. Thanks for the info. It didn't throw up an exception when trying to open the .msg file. I have decided that possibly the best way to proceed is to use TIDMessage to open and read any emails in the right format and Outlook for any emails TIDMessage can't handle by adding the latter to a temp folder in Outlook, processing and then removing the temp folder (subject to me working out exactly how I do that!). If you or anyone else has any alternative suggestions before I start coding this, they would be really appreciated. -
Batch Reading Emails from Windows Explorer
Mark Williams replied to Mark Williams's topic in General Help
My Malware bytes is preventing access to this site as it believes there is a trojan. Just thought @mitzi would want to know! -
Batch Reading Emails from Windows Explorer
Mark Williams replied to Mark Williams's topic in General Help
I was hoping to find a one-size fits all solution. Off hand do you happen to know if most other email types are also structured storage files? Requiring Outlook wouldn't be an issue, but I think it's MAPI that's needed. I've had a look at the Microsoft docs this returns me an IMessage on top of the storage doc. I have also had a look for some examples of how to deploy OpenIMsgonIStg. Haven't found much on it yet and suspect it will take me some time (form me) to master. Do you (or anyone) happen to know know if the IMessage interface will work with other email types or just Outlook? Before I go down that route, I would really appreciate any feedback on whether TIDMessage can handle MSG files and, if so, how. @Remy Lebeau -
Batch Reading Emails from Windows Explorer
Mark Williams replied to Mark Williams's topic in General Help
Sorry if I didn't make my requirements clear. My app needs to be able to read emails in (hopefully) almost any format that have been dropped into an explorer folder. In my case I dropped them from Outlook so they were saved as .msg. Could be .eml format etc. I don't have control over the email source from which my users will dropping emails into Explorer. Basically, I wish to read the various properties of the email (From, To, CC, BCC, Subject, body, MessageID etc) and add them to a database. But to do that I need to be able to load the emails from a file rather than outlook. And that's where I am getting stuck. By way of update I have just saved an email from GMail to disk (so eml format) and that was read ok by TIDMessage save for the body part, which I assume requires me to read the messageParts. So with TIDMessage the issue seems to be with msg files. As I originally said, I am not particularly bothered how I do this as long as whichever method I adopt it can reliably open and read most email formats. -
I want to use the IPropertyStore interface to read media file tags. I couldn't find any Delphi examples of how to do this, but I did find a C++ example: C++ Example I am not familiar with C++, but using this example I have got working Delphi code: var Store: IPropertyStore; v: PropVariant; begin if openDialog1.Execute then begin CoInitialize(Nil); SHGetPropertyStoreFromParsingName(pWideChar(openDialog1.FileName), Nil, GPS_READWRITE, IPropertyStore, store); store.GetValue(PKEY_Music_AlbumTitle, v); Showmessage(v.bstrVal); Couninitialize; end; end; This works without complaint (needs the following added to the uses clause WinApi.ShlOBj, WinAPI.ActiveX, WinAPI.PropKey, WinAPI.PropSys;). The C++ example says there is a very important undocumented function: store->release(). The IPropertyStore does not have this function. Does anyone know what if anything I need to do to clean up the IPropertyStore in Delphi after use?
-
MS Office BuiltInDocumentProperties and CustomDocumentProperties
Mark Williams posted a topic in General Help
I am using late binding to open Word/Excel documents etc to try to read the built in and custom properties if documents/workbooks. var MSApp, Props, item:Variant; i, int:integer; d:TDateTime; f:Double; s:string; begin MSApp := CreateOLEObject('Word.Application'); MSApp.Documents.OpenNoRepairDialog(filename, false, true, false, password, password, False, password, password, emptyParam, emptyParam, false, true); MSApp.ActiveDocument.Repaginate; Props := MSAPP.ActiveDocument.BuiltInDocumentProperties; for i := 0 to Props.Count do begin item := Props.item[i]; memo1.Lines.Add('Name='+item.Name); case varType(item.value) of varSmallInt, varShortInt, VarInteger, varSingle, varByte, varWord, varLongWord, varInt64: begin int:=item.value; Memo1.Lines.Add('value as int='+int.ToString); end; varDate: begin d:=item.value; Memo1.Lines.Add('value as date='+DateTimeToStr(d)); end; varDouble, varCurrency: begin f:=item.value; Memo1.Lines.Add('value as float='+FloatToStr(f)); end; varBoolean: begin b:=item.value; Memo1.Lines.Add('value as boolean='+ord(b).ToString); end; varOleStr, varStrArg, varString: begin s:=item.value; Memo1.Lines.Add('value as string='+s); end else try s:=item.value; Memo1.Lines.Add('value as somethign else='+s); except end; end; end; I am getting errors and other issues with a number of the properties: DateTime There are three date time properties in the built in properties: Last print date Creation date Last save time My function fails with the print date and the last save time. I get an EOLEExecption "unspecified error" when querying the VarType of the item value (case varType(item.value) ). I don't get an error for Creation date, however, the creation date is always read as the current date time, regardless of what the properties of the document actually says. The format of all three dates appears identical in the document properties. Pages, word count ,number of characters etc Pages always returns 1 regardless of number of pages. I thought calling Repaginate first might resolve this, but it didn't. Word & character count always return 0 (which is incorrect). Any help with the above appreciated. Thanks. -
MS Office BuiltInDocumentProperties and CustomDocumentProperties
Mark Williams replied to Mark Williams's topic in General Help
Thanks, but no need for apology! -
MS Office BuiltInDocumentProperties and CustomDocumentProperties
Mark Williams replied to Mark Williams's topic in General Help
I though I stated that I was trying to read the BuiltInDocumentProperties of office documents and that I was having difficulties reading certain of those properties. I also provided the code and stated what were the problems I was experiencing. I'm not sure what further info was relevant. I wish to proceed, if at all possible (and I don't see why it shouldn't be) with OLE late binding to open the office documents (I need to save them in pdf format) and then read their BuiltInDocumentProperties whilst I have the document open. I appreciate your recommendation of OOXML, but I don't really have time to delve into this at the moment and would prefer to use BuiltInDocumentProperties, particularly as I have to open the document via its relevant MS app in any event. -
Problems changing database with pooled connection
Mark Williams replied to Mark Williams's topic in Databases
That's a good suggestion although debugging an isapi dll is a bit tricky. I suppose I could monitor the connection by way of a flat file and see what that tells me. -
AFAIK you can't change the colour or event the font colour of a standard TButton. You can derive your own button with a canvas (which I have done in the past), but its quite a lot of work and won't work decently with themes. Francois' suggestion of a timer is sound, but I would suggest internittently enabling/disabling the button to provide a flash effect/
-
Problems changing database with pooled connection
Mark Williams replied to Mark Williams's topic in Databases
My dll calls the function shown in my original post to change the database as required (it needs to have access to 3 databases on the same server). If I call my dll using the external ip (eg https://52.132.222.67/MyDLLLocation/MyDLLName/MyDLLFunction) all works okay. The moment I substitute the external ip with an internal ip (192.168.0.12) I get the cannot change definition error. I really want to avoid re-establishing a connection almost every time the dll is called. There are a number of things I do not understand and would like to understand: Why is this happening at all? Is it because the pooled connection somehow thinks that its server property is being changed? Why does the pooled connection think there is an associated connection in one instance (ie where local ip used), but doesn't when an external ip is used? Is this to do with localhost being the server and, if so, how do I get around this? If my only solution is as @Dmitry Arefievsuggests to use the "full object" can someone please tell me how to do that? Also, does it avoid the problem of re-establishing the connection each call? But I would still like to understand why I am getting this different behaviour for external and internal ips. -
Problems changing database with pooled connection
Mark Williams replied to Mark Williams's topic in Databases
Sorry I don't understand what you mean -
Problems changing database with pooled connection
Mark Williams replied to Mark Williams's topic in Databases
Thanks again. But by using either active := false or CloseConnectionDef am I having to reconnect all over again with all the necessary overhead? I am not trying to switch server, merely which table I wish to query. I am obviously keen to avoid any additional overhead. I also still don't understand why I only get this error when I connect with an internal ip not with a local ip. It seems to me that if there is an error with the one there should also be an error with the other. -
Problems changing database with pooled connection
Mark Williams replied to Mark Williams's topic in Databases
Thanks. Does setting active to false lose the connection? Ie does it have to reconnect all over again? Also, any idea why I don't get the same problem using the external ip address? -
I use a Firedac query with a PostgreSQL database I start with an empty query (only option changed from default is CachedUpdates to true) and append records to it as required. To get the required table structure I query the database table using a select statement that I know will return no records (docid=0) Then I add the new records using Append. Following this I add an index: if indexes.FindIndex('doc_id')<>Nil then exit; Index := Indexes.Add; Index.Name := 'doc_id'; Index.Fields := 'docid'; Index.Active := true; IndexesActive := true; IndexName := 'doc_id'; When I try to call FindKey with different docid values it returns true but never changes the actual record. I have the same problem if I use Locate. If I iterate using First, Next I can retrieve the correct records. I should also add that I tried to assign this table's data to a FDMemTable, but the assignment failed. I can only assume there is something wrong with my table structure, but I have been unable to discover what. It also seems odd that both FindKey and Locate report true, but do not move to the correct record which I know exists. I can make this work by looping through the query record by record, but as it as to do this a potentially large number of times there will be a big performance hit. Does anyone have any ideas why FindKey does not work in the above scenario?
-
Trying to turn CKNewInstance into CKUniqueInstance for ms word
Mark Williams posted a topic in Windows API
I am automating Word via TWordApplication. I would like to create the word document in its own unique instance of Word. It is possible to open the document in its own instance by setting the ConnectKind to ckNewInstance, but Word is then a bit of a bandit and if the user open a new word document other than via my app my word instance gets hijacked by the new Word document. It doesn't sound like such a big problem, but these are some of the consequences: If the pirate document enters modal mode so does my document and my app can't do anything with it. Of course, if the user puts my document into modal mode I have the same issue and I have to respond to OLEExceptions for rejected calls. But in he case of my own document I can pop up it up from my app for the user to deal with the modal dialog before proceeding. If it is a pirate document (and there could well be more than one) it is a bit yuk to tell the user to either find it themselves and kill the modal dialog or to pop up all possible docs that might be modal. Even worse than this is that if the user opens a blank document via my exe and then open an existing word document not via my app Word commits high sea piracy and hijacks my blank document completely so that unless I monitor this my app is dealing with the wrong document altogether. There is a registry hack to force Word to open in a new instance every time, but I don't want to force that on users, plus I think it requires admin privileges. In the absence of a ckUniqueInstance constant I have been trying to replicate that effect by monitoring the onDocumentChange event of the Word application and checking if the application's documents count goes above 1. If so I close the pirate document and re-open it in its own instance. I appreciate this is not perhaps the most elegant solution and I also suspect I am going to be told not to interfere with the user experience in this way. I having quite made my mind up as yet. But it almost works, subject to two issues (so far) one major, one minor and I am curious to see if I can get it to work. First, when my app opens a new blank document I add a custom variable to the document simply so that it is flagged as having content. This stops Word trying to hijack the document when an existing document is subsequently opened. Doc.Variables.Add('AnyOldRubbish', 1); //this flags the document as having content And then in the DocumentChange event: procedure TForm.DocumentChange(ASender: TObject); var i : integer; WordAppT : TWordApplication; Template, FileName : OleVariant; begin if wordApp.Documents.Count>1 then begin for i:=WordApp.Documents.Count downTo 1 do begin Template := EmptyParam; if WordApp.Documents.Item(i) <> Doc then begin FileName := WordApp.Documents.Item(i).FullName; if not FileExists(FileName) then FileName := ''; Template := WordApp.Documents.Item(i).Get_AttachedTemplate; if Uppercase(Template) = 'NORMAL.DOTM' then Template := EmptyParam; //Close the offending document! WordApp.Documents.Item(i).Close(false, emptyParam, emptyParam); {PROBLEM CODE - see below} //Reopen it in a separate instance of Word WordAppT := TWordApplication.Create(Nil); WordAppT.ConnectKind := ckNewInstance; if FileName<>'' then WordAppT.Documents.open(FileName, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam) else WordAppT.Documents.Add(EmptyParam, EmptyParam, EmptyParam, True); WordAppT.Visible := true; WordAPPT.ActiveWindow.Activate; end; end; end; end; The two problems: By adding the variable I am marking blank documents as having been changed when they should be marked as unchanged. Not a major problem. Would be good not to have to do this, but if I can solve problem 2 I can deal with this issue by checking the word count property of the document to see if there has been any substantive change, Not perfec, but good enough for my purposes. The above code works neatly when the user tries to open a blank pirate document, but not when they try to open an existing document. In the latter case, whilst the pirate document does get opened in its own new instance, the closing it in the first place causes my original document to become unresponsive and I can't for the life of me work out why. If I break the process down into separate chunks it works fine: Open my document. Open an offending existing document outside of my app. Add a button to close the offending document. Add another button to reopen the offending document. This works leading me to believe it might be a timing issue. So I tried adding a sleep before and after the call to close the pirate document, but made no difference. -
Trying to turn CKNewInstance into CKUniqueInstance for ms word
Mark Williams replied to Mark Williams's topic in Windows API
In case any one is interested there is a partial solution to this. Create a dummy document and close it before opening your actual document. Word apparently only latches on to the first instance opened. So: //Open fake doc dummyDoc:=WordApp.Documents.Add(EmptyParam, EmptyParam, EmptyParam, True); dummyDoc.Close(false, emptyParam, emptyParam); //Open actual document WordApp.Documents.Add(EmptyParam, EmptyParam, EmptyParam, True); RealDoc:=WordApp.ActiveDocument; Thanks to the guys at Add-In Express for this suggestion. However, it has some limitations: It doesn't seem to work if you have a Word window hung somewhere in the background. It doesn't catch new word windows opened within your instance of Word. So if your users open a new or existing word document from eg the backstage of your word instance, it will open in your instance. To overcome both the problems I have implemented the following: Disable all functionality within my word instance for opening existing or new documents. Use the DocumentChange event to catch any documents that may still be loaded in your instance of Word, but do the following to overcome the issues mentioned in the previous post: In the documentChange event check if there is more than one document in your word instance. If there is, fire a timer event (interval of 100 ms seems to be sufficient) and add similar code to that shown in the first post to the timer event. Still a clunky last resort, but I really need to stop word documents being opened in my instance. -
Has anyone had problems installing 10.3.3 via the web installer. It keeps failing on me some way through the process saying it can't unzip a file. I have a partial installation, but only 32bit. I also have no idea what's missing. I have tried installing both 32 bit and 64 bit windows versions. I have also tried installing just 64 bit, but that also fails during process.
-
I have converted an existing 64bit app into an out of process com server by adding an automation object, but now I am completely lost as to how to register it and how to import the type library. RegSvr32 fails with "DLLRegisterServer" not found. I have tried running with "/regserver" switch. In non admin mode it crashes and in admin mode it asks if you want to make changes etc, but then doesn't seem to run the app (no sign of it in processes) although no error appears. I tried using tlibimp.exe to import the type library, but that reported an error loading the type library. I assume that is because there is no type library to import on account of the failure to register the server in the first place!
-
64bit Out of Process Server
Mark Williams replied to Mark Williams's topic in RTL and Delphi Object Pascal
OK. That's good to know. Thanks. -
64bit Out of Process Server
Mark Williams replied to Mark Williams's topic in RTL and Delphi Object Pascal
Yes. But it didn't create the Delphi wrapper that you get via the type library functions in the IDE -
64bit Out of Process Server
Mark Williams replied to Mark Williams's topic in RTL and Delphi Object Pascal
There is a registry hack to get around this: https://www.codeproject.com/questions/267099/how-do-i-use-32-bit-dll-in-64-bit-app -
64bit Out of Process Server
Mark Williams replied to Mark Williams's topic in RTL and Delphi Object Pascal
Apologies duplicate post. -
64bit Out of Process Server
Mark Williams replied to Mark Williams's topic in RTL and Delphi Object Pascal
I have worked out how to import the type library for a 64bit out of process server using tlibimp.exe. I was originally pointing tlibimp at the exe file and hoping it would magically produce something for me. It needs to be pointed at the tlb file. -
64bit Out of Process Server
Mark Williams replied to Mark Williams's topic in RTL and Delphi Object Pascal
I guess so.