Mark Williams
-
Content Count
274 -
Joined
-
Last visited
Posts posted by Mark Williams
-
-
Word ID is a randomly generated integer value. It's not intended to be used as a handle. Just a way of identifying which Word window my app is communicating with.
My app passes its own handle to be stored in the word document as follows:
QuoteWordApp.ActiveDocument.Variables.Add('ViewerHandle', Handle);
Within a word document the variables are stored as a wideString. To get the handle of my app from within the DLL I call this function which queries the Add In's host word window:
Result:=0; val := Doc.Variables.Item(i).Value; tryStrToInt(val, hdl); Result := Hdl;
Result is a THandle.
Debugging a 64 bit AddIn is a bit problematic. So I have not looked into it yet. However, I have written to a log file to output what is happening at the various stages. I know that the hdl being passed to SendMessage is the correct hdl of my app. Also GetLastError returns success after SendMessage (if that means anything!)
On My app I show the value of its handle on a label when it is running. Let's say its 12345678.
If I create another Delphi executable and call
WinAPI.Windows.SendMessage(12345678, WM_COPYDATA, 0, Integer(@copyDataStruct));
That works as expected.
If I do the same in my Add In DLL (ie substitute the hdl var with the Longint value of my app's handle) it doesn't work.
-
I have a Word Add-In created with Add-In Express, although that's probably irrelevant.
The Add-In can only be used for word docs created via a certain desktop app (MyAPP).
When MyAPP creates a Word document it stores a unique identifier for that Word document and also the handle of MyAPP.
I am trying to send information from the Add-In DLL back to MyApp using SendMessage and WM_COPYDATA.
I have the following types declared in both the DLL and MyAPP:
type TWordFunction = (wfNone, wfEdit, wfSave, wfLink, wfMasterWindow, wfBundle); type TWordActionRec = Record wf:TWordFunction; wordID:THandle; end;
Within the DLL:
procedure TAddInModule.DefaultButtonAction(wordFunction : TWordFunction); var WAR : TWordActionRec; copyDataStruct : TCopyDataStruct; hdl : THandle; begin hdl := GetViewerHandle; WAR.wf := WordFunction; WAR.wordID := GetWordIdentifier; copyDataStruct.dwData := 0; copyDataStruct.cbData := SizeOf(WAR); copyDataStruct.lpData := @WAR; WinAPI.Windows.SendMessage(hdl, WM_COPYDATA, 0, Integer(@copyDataStruct)); end;
The above runs without triggering any error and the hdl parameter is correct for MyAPP.
Within MyAPP:
procedure TForm1.WMCopyData(var Msg: TWMCopyData); var WAR : TWordActionRec; begin WAR := TWordActionRec(Msg.CopyDataStruct.lpData^); ListBox1.Items.Add(WAR.wordID.ToString); Case WAR.wf of wfNone: ListBox2.Items.Add('None'); wfEdit: ListBox2.Items.Add('Edit'); wfSave: ListBox2.Items.Add('Save'); wfLink: ListBox2.Items.Add('Link'); wfMasterWindow: ListBox2.Items.Add('MW'); wfBundle: ListBox2.Items.Add('Bundle'); End; end;
However, the DLL's message is never received by MyApp.
If I take the code from the DDL and add it to a standard executable it works as expected.
I have Googled to see if there is any reason why SendMessage would not work within a DLL and as far as I can see there shouldn't be.
Is there anything wrong with my code or is this perhaps an Add-IN Express specific issue?
-
Thanks to everyone for the detailed responses and suggestions.
I think my fundamental error was my assumption (long-held and it seems with little basis) that FTP is faster than HTTP for multiple file transfers.
I have already written an ISAPI dll using Delphi's TWebBroker which seems to be working well for my needs I just thought I could speed things up with FTP. From the above responses a better approach may be to more finely tune my ISAPI dll.
The comments re Real Thin Client are noted and I will certainly look into this at a later point.
In short, thanks for the feedback which it appears has saved me a potential mountain of unncessary work!
-
I am looking for some general guidance on how to go about setting up an FTP server for the first time. I have opted for TIDFTPServer for this purpose and have been through the help files and searched Google, but I have come up with very limited information.
I don't want to launch into this in my usual bull in a china shop manner only to find later that I have gone about it in the least appropriate way. So apologies in advance for the broad nature of this topic.
In essence my requirements are for a basic FTP Server. However, I want to try and strictly control what users can upload and, in particular, download.
My fledgling thoughts:
Uploads
The user requests permission for upload via my webserver and if authenticated is issued with a unique token to be stored in the database along with an expiry time which the user will need to renew periodically via the webserver and an identifier to identify which folder(s) the user is allowed to upload to.
The FTP client would submit the token to the FTP server which would retrieve the token, expiry time and folder identity prior to permitting any upload request. When the client has finished it will request the deletion of the token.
There are likely often to be large numbers of files to be uploaded so preferably the FTP Server will be able to store the token, expiry time etc rather than querying the database for every upload. The FTP client would submit the token on every upload request.
The FTP Server would check periodically whether the token has been removed.
Downloads
There may be a requirement for download of a large batch of files. So similar approach to the above. I already keep a record of every file in a database so each has a unique id and there is another table which records user rights to access a given file.
Before submitting download request user applies for token via webserver and at the same time submits a list of ids of those file he intends to download. Webserver checks which ones user has access to and populates a table with the file ids ("permitted_files").
On receiving the retrieve request the FTP does the same as for upload, but additionally queries the "permitted_files" table to see which files the user is permitted to download.
Questions
- Is this broadly a sensible approach?
- If not, how better to go about this?
- Assuming it is sensible, what do I need to implement this within the FTPServer component. My best guess at the moment is that it would be handled via CommandHandlers.
- No GUI interface is required and I don't really need a command line so how best to implement this on the Server: as a Windows service?
- As the FTP Server may be receiving numerous requests at the same time how best to manage the data relative to specific requests (ie tokens, permitted file ids etc).
-
I am trying to create a FTP server for the first time using TIDFTPServer.
I have dropped a TFTPServer onto a form and changed properties only as follows:
Active = True Bindings = < item IP = '**.**.***.***' Port = 21 end> ReuseSocket = rsTrue
If I set the IP address as the external ip address for the server I get an EIDCouldNotBindSocket error and it advises that "Address and port are already in use" when I try to start it.
I don't get this problem if I change the IP address to the server's local address (192.168.0.12).
Should the IP address always be set to the local IP?
I've noticed that IDFTP has an ExternalP property and also ServerHOST property. There is help on the ExternalIP property, but no on the ServerHOST property. Would I be right in assuming these properties are used for external connections to the FTP server rather than anything in the FTPServer's settings? With respect to the ExternalIP address this also seems to require setting of the DataPort.
I have tried connecting setting ExternalIP to the relevant IP address and DataPort to 21. Host is blank. When I try to connect I get an error message telling me a Host is required. I've tried setting Host to the external IP and also ServerHost, but either I get Host required error or time out.
-
I have just had a look at this component on your blog.
I could get it to write an encrypted zip file, which I can open manually no problem, but I cannot get it to decrypt using the component.
I downloaded the amended unit from https://www.uweraabe.de/Blog/2017/05/07/tzipfile-with-password-encryption-part-3/, but this still didn't work for me.
My code for unzipping is as follows:
Zipper:=TEncryptedZipFile.Create('Mark'); Stream:=TMemoryStream.create; try DecompressionStream:=TStream.Create; Zipper.Open(OpenDialog1.filename, zmReadWrite); Zipper.Read('File', DecompressionStream, zh); Stream.CopyFrom(DecompressionStream, DecompressionStream.size); {Crashes here} DecompressionStream.Free; Stream.Position:=0; Stream.SaveToFile(ExtractFilePath(includeTrailingPathDelimiter(openDialog1.FileName))+'File.docx'); Zipper.Close; finally zipper.Free; stream.Free; end;
I have used the same routine as I would for reading from the standard TZipFile ie using the DecompressionStream. Can't work out whether my approach is write or if there is a problem with the TEncryptedZipFile code.
Anyways my debug output is:
System.Classes TStream.ReadBuffer
System.Classes 8340 +1 TStream.ReadBuffer
uEncryptedZipFile 299 +9 TDecryptStream.Skip
uEncryptedZipFile 277 +10 TDecryptStream.Seek
System.Classes 7561 +1 TStream.SetPosition
System.ZLib 2891 +32 TZDecompressionStream.Read
System.ZLib 2939 +27 TZDecompressionStream.Seek
System.Classes 7569 +2 TStream.GetSize -
@Remy LebeauThanks for the detailed response. Are there any problems running an Indy FTP server alongside IIS? Also, do you know of any good sample FTP server projects anywhere?
-
28 minutes ago, Angus Robertson said:The usual solution to your problem is to use a long random file name that disappears once the download is completed, but you need to watch the log to see when that happens.
Is it not possible to also password protect the folder? My intention is to maintain a record of the virtual folders in a database and require the user to signal continued use of the folder and as soon as there is a failure to do so the folder would be deleted.
37 minutes ago, Angus Robertson said:The better solution is to you use a Delphi FTP server, like the ICS one I support, then you can control logins and directories yourself easily, using the ICS FTP Server sample you should have a working solution in a few hours, days faster than using WMI.
Is this the Overbyte stuff? I will have a look.
QuoteBut it needs it's own IP address and port, will not help if you have to use IIS.
So if I wanted to run IIS and ICS on the same server I would need a second static ip?
-
44 minutes ago, Angus Robertson said:You can start reading at https://www.iis.net/overview/control/powerfuladmintools
I'm just looking at WMI scripting for controlling IIS. Setting up a virtual directory through IIS admin is easy.
Thanks. I'll have a look at WMI. Which type library do you use? I've got a couple on my registered libraries.
-
25 minutes ago, Lars Fosdal said:I've never set these up, but isn't that something you would configure on ISS itself, and then refer to from Delphi?
Lots of security angles to consider
Yes it's pretty easy to set up via IIS, but the security angle is precisely why I don't wish to do it this way. I want to check the user's credentials against database and then issue that user a one-off password for a single transaction and add a temporary ftp folder, which then gets removed as soon as the user has completed the transaction. There may be better ways of doing this. As I said I'm a FTP novice!
-
Using IIS. New to FTP.
Can anyone please give me a steer as to how to programmatically add and configure virtual FTP directories in IIS via Delphi?
-
On 2/4/2020 at 11:30 PM, stijnsanders said:Ah well, IIS does its own thread management, that's right. And you have a choice: You could do all the work for a request in the HttpExtensionProc call you get and return HSE_STATUS_SUCCESS, it works really great for small responses and when it's pretty clear what the extension is supposed to do, and each request won't take too much time of a worker process. But, if you want do have more control over threads, over different kinds of things happening depending on the requests that come in, and especially keep threads free when they're waiting on other processes, the best you can do is return rightaway with HSE_STATUS_PENDING, and manage the threads to do the heavy lifting yourself.
Thanks. I'll look into this at some point. However, my ISAPI dll isn't really designed to do heavy lifting. Just user validation, configuration settings and downloading of smallish files.
There is occasionally a need to upload/download largish files and this is something I am planning to move to FTP or maybe there is a better/faster solution?
-
24 minutes ago, Lars Fosdal said:It is a joke
Must be a European thing. As of last Friday it is now unlawful for me to get such jokes!
- 1
-
1 hour ago, Der schöne Günther said:I'm not sure what you're referring to. Can't see anything on the page/website which helps.
-
2 hours ago, Lars Fosdal said:Geolocation from IP address:
I found an example of how to do it (although not tried it) here:
https://www.example-code.com/delphiDll/geolocation_ip_address.asp
-
I wouldn't have thought it was possible to do this, but when my kids sign in to my Netflix account from various locations I get an email from Netflix to let me know what country and region they have signed in from.
Does anyone have any idea how Netflix gets this info?
-
On 1/30/2020 at 3:57 PM, stijnsanders said:I would consider setting this to 0
I'll give that a try and see how it pans out.
On 1/30/2020 at 3:57 PM, stijnsanders said:I just keep queueing incoming requests in a queue the thread-pool can take from with any worker thread that happens to be available for work...
I thought this was more or less what iis does with the dll.
-
5 minutes ago, Kas Ob. said:I suggest to at least skip one line when scanning, you can go up to 32 skipped lines while still have very low probability of false positive, you can tweak and decide what will work for you, just consider the height of the image, and i want to suggest to consider the divergence too, so lets say RGB with value like 05 05 05 is some grey shade but 06 05 05 is not, but very close and human eye might see it as grey and the scanner itself will not return white as pure white , for that use some formulas,
I was going to suggest the fast simple formula with calculating average of R G B and then divide sum with it and compare it with a factor you can tweak but i think you got the idea and here an detailed answer to work with saturation
Ok. Thanks. I'll have a look at that and report back if I can improve on what I have already got.
-
24 minutes ago, pcplayer99 said:Using Scanlines is for every pixels. You donn't need check every pixels here.
Scanlines is fast enough for my purposes even when it scans every pixel. I would be reluctant to rely on random sampling.
-
7 minutes ago, pcplayer99 said:if the image file format is 32bit color bitmap, but the content is a black white picture, I think you can check it very quicklly by check pixels.
You donn't need to check every pixels of the picture, you just need check severial pixels samples in the picture.
Thanks. I've sort of come up with something along those lines using Scanlines. It seems to be tolerably effective and sufficiently fast.
-
The default MaxConnections is set at 32 for TISAPIApplication.
I have no idea how many people may be calling my dll at any given time. 32 seems to me to be too low a number. I have set it to 200.
I cannot find any advice as to best policy on setting this value. The help file simply says to test the ActiveCount and InactiveCount and adjust accordingly. That is going to be a little difficult to test.
I don't want to have exceptions flying off because of MaxConnection exceeded and likewise I don't want to degrade performance.
If, for example, I set MaxConnection to 1000 I assume that will not of itself degrade performance and that there will only be issues when actual connections are that high,
If that is right then it seems to me that slightly deteriorated performance is preferable to exceptions due to exceeding max connections.
Or am I missing something?
-
3 hours ago, Attila Kovacs said:Well, I'd clone/backup the project and start removing all 3rd party units, including firedac.
Solved at last. The DLL was hanging due to the call to AddToLog in CloseServer due to the Log thread being terminated at the same time as the call.
In the meantime, I had added a TFDMoniFlatFileClientLink component to provide a FireDAC trace to see what was happening with the FDManager as I thought that was causing the problem.
Once I'd fixed the problem with the thread log, I still had issues. By a process of starting from scratch (as you suggested) and adding units/components as I went along, I eventually managed to narrow it down to the FlatFile component. Once I removed it, all was well. Must be a threading problem or some such.
Many thanks for your help
- 1
-
I've been through all my code. As far as I can see, I am destroying everything I create in an action event within the same action event.
I have tried calling the DefaultHandlerAction and nothing else.
procedure TWebModule1.WebModule1DefaultHandlerAction(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); begin Handled:=true; try SetResponse(Response, 400, 'Bad Request', 'Default Handler - if you are getting this message it either means that your are providing the wrong PathInfo or the action has been deleted.'); except end; end;
And SetResponse:
Procedure SetResponse(var Response:TWebResponse; code:integer; statusText:String; debug:string=''); begin Response.StatusCode:=code; Response.ReasonString:=code.ToString+' '+StatusText; if debug<>'' then AddToLog('Response set: '+Response.StatusCode.ToString+' '+StatusText+' Debug Info: '+debug, leMinorError) else AddToLog('Response set: '+Response.StatusCode.ToString+' '+StatusText, leDevelopment); end;
It still hangs on shutdown.
So I tried removing my calls to StartServer and CloseServer. Edited my DefaulHandlerAction so it didn't call SetResponse. It just sets the statuscode to 400 and still a huge delay in shutdown.
My project file now looks like this:
library MWCWebServer; uses Winapi.ActiveX, System.Win.ComObj, System.SysUtils, Web.WebBroker, Web.Win.ISAPIApp, Web.Win.ISAPIThreadPool, WinAPI.Isapi2, WinAPI.Windows, System.SyncObjs, system.classes, WebModule in 'WebModule.pas' {WebModule1: TWebModule}; {$R *.res} exports GetExtensionVersion, HttpExtensionProc, TerminateExtension; begin CoInitFlags := COINIT_MULTITHREADED; Application.Initialize; Application.WebModuleClass := WebModuleClass; Application.MaxConnections:=200; //MaxConnection=32 by default; IsMultiThread:=true; Application.Run; end.
I'm not creating anything over and above the webapplication itself and still it won't shut down properly.
-
16 minutes ago, Attila Kovacs said:- I'll assume then that you are aware that in case of TFDManager.Create(SomeComponent), including placing the component on a datamodule, FDManager will be free'd if SomeComponent is free'd.
Yes. I'm sure that freeing of FDManager is not the issue.
17 minutes ago, Attila Kovacs said:- I'm sure, that you have tried commenting out the whole OnTerminate, and IIS still stalls on stop.
Yes.
17 minutes ago, Attila Kovacs said:Let me know if you find something. I'm really curious.
Will do.
- 1
SendMessage From DLL
in Windows API
Posted
It might be a UIPI issues as flagged by @David Heffernan initially and missed by me! But I don't think so. However not dealt with UIPI issues before so I may be handling it incorrectly,
Whilst Word runs at a low level I think the DLL Add-In will be at the same level as my receiving app. Also, as you pointed out if there is an UIPI error then GetLastError is set to 5. My SendMessage is reporting success.
However I have tried to incorporate ChangeWindowFilterEx within my receiving app to see if that makes any difference.
type TChangeFilterStruct = packed record cbSize: DWORD; ExtStatus: DWORD; end; PChangeFilterStruct = ^TChangeFilterStruct; function ChangeWindowMessageFilterEx(hWnd: HWND; Message: UINT; Action: DWORD; ChangeFilterStruct: PChangeFilterStruct): Bool; stdcall; external 'user32.dll'; const MSGFLT_ALLOW = 1; MSGFLT_DISALLOW = 2; MSGFLT_RESET = 0; {In formcreate:} ChangeWindowMessageFilterEx(Handle, WM_COPYDATA, MSGFLT_ALLOW, nil);
If I am implementing this correctly there doesn't appear to be a UIPI error.