JEDI JVCL TJvProgramVersionCheck How to use HTTP - delphi

I'm wondering if someone has an example on how can be used the TJvProgramVersionCheck component performing the check via HTTP.
The example in the JVCL examples dir doesn't show how to use HTTP
thank you

The demo included in your $(JVCL)\Examples\JvProgramVersionCheck folder seems to be able to do so. Edit the properties of the JVProgramVersionHTTPLocation, and add the URL to it's VersionInfoLocation list (a TStrings). You can also set up any username, password, proxy, and port settings if needed.
You also need to add an OnLoadFileFromRemote event handler. I don't see anything in the demo that addresses that requirement, but the source code says:
{ Simple HTTP location class with no http logic.
The logic must be implemented manually in the OnLoadFileFromRemote event }
It appears from the parameters that event receives that you do your checking there:
function TJvProgramVersionFTPLocation.LoadFileFromRemoteInt(
const ARemotePath, ARemoteFileName, ALocalPath, ALocalFileName: string;
ABaseThread: TJvBaseThread): string;
So you'll need to add an event handler for this event, and then change the TJVProgramVersionCheck.LocationType property to pvltHTTP and run the demo. After testing, it seems you're provided the server and filename for the remote version, and a local path and temp filename for the file you download. The event handler's Result should be the full path and filename of the newly downloaded file. Your event handler should take care of the actual retrieval of the file.
There are a couple of additional types defined in JvProgramVersionCheck.pas, (TJvProgramVersionHTTPLocationICS and TJvProgramVersionHTTPLocationIndy, both protected by compiler defines so they don't exist in the default compilation. However, setting the ICS related define resulted in lots of compilation errors (it apparently was written against an old version of ICS), and setting the Indy define (and then setting it again to use Indy10 instead) allowed it to compile but didn't change any of the behavior. I'm going to look more into this later today.
Also, make sure that the VersionInfoLocation entry is only the URL (without the filename); the filename itself goes in the VersionInfoFileName property. If you put it in the URL, it gets repeated (as in http://localhost/Remote/ProjectVersions_http.iniProjectVersions_http.ini, and will fail anyway. (I found this while tracing through the debugger trying to solve the issue.)
Finally...
The solution is slightly (but not drastically) complicated. Here's what I did:
Copy JvProgramVersionCheck.pas to the demo folder. (It needs to be recompiled because of the next step.)
Go to Project->Options->Directories and Conditionals, and add the following line to the DEFINES entry:
USE_3RDPARTY_INDY10;USE_THIRDPARTY_INDY;
Delete the JvProgramVersionHTTPLocation component from the demo form.
Add a new private section to the form declaration:
private
HTTPLocation: TJvProgramVersionHTTPLocationIndy;
In the FormCreate event, add the following code:
procedure TForm1.FormCreate(Sender: TObject);
const
RemoteFileURL = 'http://localhost/';
RemoteFileName = 'ProjectVersions_http.ini';
begin
HTTPLocation := TJvProgramVersionHTTPLocationIndy.Create(Self); // Self means we don't free
HTTPLocation.VersionInfoLocationPathList.Add(RemoteFileURL);
HTTPLocation.VersionInfoFileName := RemoteFileName;
ProgramVersionCheck.LocationHTTP := HTTPLocation;
ProgramVersionCheck.LocationType := pvltHTTP;
VersionCheck; // This line is already there
end;
In the ProgramVersionCheck component properties, expand the VersionInfoFileOptions property, and change the FileFormat from hffXML to hffIni.
Delete or rename the versioninfolocal.ini from the demo's folder. (If you've run the app once, it stores the http location info, and the changes above are overwritten. This took a while to track down.)
Make sure your local http server is running, and the ProjectVersions_http.ini file is in the web root folder. You should then be able to run the demo. Once the form appears, click on the Edit History button to see the information retrieved from the remote version info file. You'll also have a new copy of the versioninfolocal.ini that has the saved configuration info you entered above.

Related

How can I prevent Chromium from writing to the console?

I'm making a very simple Delphi console application ({$APPTYPE CONSOLE}) with a single TChromiumWindow on the main form. The purpose of the application is to retrieve a webpage, process the HTML and output some JSON to the console. This can not be done using plain HTTP requests due to the nature of the webpage, which requires running some javascript as well.
Everything works as expected, except for one problem. The chromium components output some error messages to the console as well, which makes my JSON invalid! For example, I always get the following two error messages on startup:
[0529/133941.811:ERROR:gpu_process_transport_factory.cc(990)] Lost UI shared context.
[0529/133941.832:ERROR:url_request_context_getter_impl.cc(130)] Cannot use V8 Proxy resolver in single process mode.
Of course the best solution would be to not get any error messages in the first place, but for several reasons (which mostly have to do with company legacy code) I can't for example disable single process mode.
So the next best thing would be to keep these error messages from being printed to the console. I've tried setting
GlobalCEFApp.LogSeverity := LOGSEVERITY_DISABLE;
but that didn't help. Specifying a logfile using GlobalCEFApp.LogFile doesn't help either.
So how can I prevent the Chromium components from writing to the console at all?
The TChromium component provides an OnConsoleMessage event with signature :
TOnConsoleMessage = procedure(Sender: TObject; const browser: ICefBrowser;
const message, source: ustring; line: Integer;
out Result: Boolean) of object;
If you handle this event and set the Result variable to true the message output to the console is suppressed.
Set LogSeverity to LogSeverity.Fatal or n other desired.
var settings = new CefSettings()
{
//By default CefSharp will use an in-memory cache, you need to specify a Cache Folder to persist data
CachePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "CefSharp\\Cache"),
//Set log severity to showup only fatal errors.
LogSeverity = LogSeverity.Fatal,
};
//Autoshutdown when closing
CefSharpSettings.ShutdownOnExit = true;
//Perform dependency check to make sure all relevant resources are in our output directory.
Cef.Initialize(settings, performDependencyCheck: true, browserProcessHandler: null);

Why is COMMON_APPDATA returned as a null string on Windows XP

One of my users at a large university (with, I imagine, the aggressive security settings that university IT departments general have on their computers) is getting an empty string returned by Windows XP for CSIDL_COMMON_APPDATA or CSIDL_PERSONAL. (I'm not sure which of these is returning the empty string, because I haven't yet examined his computer to see how he's installed the software, but I'm pretty sure it's the COMMON_APPDATA...)
Has anyone encountered this or have suggestions on how to deal with this?
Here's the Delphi code I'm using to retrieve the value:
Function GetSpecialFolder( FolderID: Integer):String;
var
PIDL: PItemIDList;
Path: array[0..MAX_PATH] of Char;
begin
SHGetSpecialFolderLocation(Application.Handle, FolderID, PIDL);
SHGetPathFromIDList(PIDL, Path);
Result := Path;
end; { GetSpecialFolder }
ShowMessage(GetSpecialFolder(CSIDL_COMMON_APPDATA)); <--- This is an empty string
Edit:
Figuring out this API made me feel like I was chasing my tail - I went in circles trying to find the right call. This method and others similar to it are said to be deprecated by Microsoft (as well as by a earlier poster to this question (#TLama?) who subsequently deleted the post.) But, it seems like most of us, including me, regularly and safely ignore that status.
In my searches, I found a good answer here on SO from some time ago, including sample code for the non-deprecated way of doing this: what causes this error 'Unable to write to application file.ini'.
If you want to find out why an API call is failing you need to check the return values. That's what is missing in this code.
You need to treat each function on its own merits. Read the documentation on MSDN. In the case of SHGetSpecialFolderLocation, the return value is an HRESULT. For SHGetPathFromIDList you get back a BOOL. If that is FALSE then the call failed.
The likely culprit here is SHGetSpecialFolderLocation, the code that receives the CSIDL, but you must check for errors whenever you call Windows API functions.
Taking a look at the documentation for CSIDL we see this:
CSIDL_COMMON_APPDATA
Version 5.0. The file system directory that contains application data for all users. A typical path is C:\Documents and Settings\All
Users\Application Data. This folder is used for application data that
is not user specific. For example, an application can store a
spell-check dictionary, a database of clip art, or a log file in the
CSIDL_COMMON_APPDATA folder. This information will not roam and is
available to anyone using the computer.
If the machine has a shell version lower than 5.0, then this CSIDL value is not supported. That's the only documented failure mode for this CSIDL value. I don't think that applies to your situation, so you'll just have to see what the HRESULT status code has to say.

Programmatically downloading a file from a filehoster

I have some files uploaded at a filehoster which I want to download programmatically, using Delphi. They don't require any captchas or the like, normally you simply press a button and you get the file. Let's take this as an example.
Now I thought I could simply take the URL the Download Now - Button is pointing at, use an TIdHTTP.Get request and save it with a MemoryStream / Filestream / whatever. Copying the link address leads to this site, which, when entered into my browser pops up the download prompt.
var
MemStream: TMemoryStream;
code: string; // added for solution
number: integer; // added for solution
begin
with TIdHTTP.Create(nil) do
try
HandleRedirects := true;
System.Delete(code,1,AnsiPos('var n =',code)+7); // added
number := StrToInt(AnsiLeftStr(code,AnsiPos(' ',code)-1)) + 1; // added
MemStream := TMemoryStream.Create;
try
// Get('http://www56.zippyshare.com/d/5862319/604061/bgAvgTable.png', MemStream);
Get(TIdURI.URLEncode('http://www56.zippyshare.com/d/5862319/' + IntToStr(number)
+ '/bgAvgTable.png'), MemStream); // added for solution
MemStream.SaveToFile('test.png');
finally
MemStream.Free;
end;
finally
Free;
end;
end;
However, using a checking tool I found that it contains a 302 redirect to the original site, thus when performing the GET-request I have to set HandleRedirects to avoid error messages and I get the HTML code of the original site rather than the file I had suspected.
So, I am kind of confused about how
1) I somehow get the file from my browser though the URL only contains a 302 redirect to the previous page and
2) I can achieve the same from within my code. Any chance someone of you might educate me a little there ? ;)
EDIT
Thanks to your input I could find the issue, turns out that the address I have to use gets generated using a random number, which is to be found in the original source. So posting a request to get the number first does the trick. I have edited the code accordingly.
File hosting sites make different tricks to ensure you was not hotlinking and show you advertisement and perhaps counter. There can be
simple analysis of HTTP Referrer field in the request
setting and checking session-unique cookies
having HTTP Forms with hidden one-time values, and Download button would be not the link but the form's Submit action.
generating one-time hashed URL, and encoding different parameters like your IP and your browser name into it
maybe more
Tools like USDownloader and JDownloader makes a lot of attempts to circumvent it.
While zippyshare seems to be more liberal, it still cannot afford hotlinking and should implement at least some measures of self-defense.
When analysing traffic - start with absolutely fresh browser loading zippyshare page for the 1st time in its life and check it all.
As i re-load the page few times i see that the number "604061" is different and link keep changing time and again after each reload. You probably have to load the page, parse the link, set the HTTP referer and only then download the file.
You do not show the HTTP traffic logs so it is hard to tell for sure.
The server may be checking for some trace to avoid the file to be downloaded programmatically.
It may be anything the hostmaster wants to check, from a wide range of possibilities, but the most typical check is the referrer.
When you navigate in a web browser from one page to another using an link, the browser adds the first page as a referrer to the second page in the request header.
Indy have support for you to add a referrer:
IdHTTP1.Request.Referer := 'http://www.any.other.page';
If the check fails, the server script just redirects the input to the donwload page. This is done to show advertising or to filfull other goals of the file hosting service.

Using OgProtectExe from tponguard

Can anyone show me a simple example how this component is used.Thanks
In your application (the one you want to protect by embedding a CRC), drop an OgProtectExe component. Use the Object Inspector to add a handler for it's single event (OnChecked, if I remember correctly). The handler should contain something like this:
procedure TForm1.OgProtectExe1Checked(Sender: TObject; Status: TExeStatus);
begin
if (Status <> exeSuccess) then // CRC changed
// Handle modified executable
end;
Possible TExeStatus values are:
exeSuccess - CRC is OK
exeSizeError - File size has changed
exeIntegrityError - CRC doesn't match
exeNotStamped - Executable not stamped
Build your application as usual. Use StampExe (from the OnGuard examples\Delphi folder) to stamp your executable with the CRC (or write your own app that calls the OgProExe unit's ProtectExe function to stamp it).
ProtectExe takes two parameters - the full path and filename of the executable to protect, and a boolean that indicates whether or not it should remove it's special marker after protecting. You should pass True unless you want to have the ability to unprotect the executable afterwards.
uses
OgProExe;
...
if ProtectExe(YourExeName, EraseMarker) then // executable stamped

TWordApplication and Word collision

I am using TWordApplication in Delphi. My app opens new instance of word and make something on its document. Problem is when i first run my app and next open real word exe. Word exe didnt open new word instance but it link to my app instance. So when my app write to its document all text appears on exe word visible to user.
WordApp := TWordApplication.Create(nil);
WordApp.ConnectKind := ckNewInstance;
(WordApp.Documents.Add(EmptyParam,EmptyParam,EmptyParam, varFalse ));
Then the user opens Word manually.
WordApp.Selection.Text := 'test test test';
And user see 'test test test' in manually opened Word.
If i first opens Word manually and starts my app all is ok.
This is default behaviour of Word, it uses a running instance. What you have to do is store a reference to the document you want to modify. So don't use ActiveDocument, but use the Document you stored. Because there is no guarantee that ActiveDocument is the document you think it is.
//starting Word
var
App: TWordApplication;
Doc: WordDocument;
begin
App := TWordApplication.Create(nil);
Doc := App.Documents.AddOld(EmptyVar, EmptyVar); //open new document
<..somewhere else..>
//modifying Word
Doc.DoWhateverIWant; // <--see? no ActiveDocument, so you are not
// modifying the users doc
Make sure you use
WordApp.ConnectKind := ckNewInstance;
to open your word application. Either do it in code (as above) or set the property at design time. This ensures that you are always running a new instance of Word and that it remains hidden unless you explicitely make it visible. Any user opening Word will then always get a different instance of Word and will not see what you have put on the document (unless you have saved it and they open the saved document).
From the doc:
Set ConnectKind to indicate how the
ConnectKind component establishes a
connection. ConnectKind establishes
the connection when the application is
run (if AutoConnect is True true) or
when the application calls the Connect
(or ConnectTo) method.
The following table lists the possible values:
//Value Meaning
//ckRunningOrNew Attach to a running server or create a new instance of the server.
//ckNewInstance Always create a new instance of the server.
//ckRunningInstance Only attach to a running instance of the server.
//ckRemote Bind to a remote instance of the server. When using this option,
// you must supply a value for RemoteMachineName.
//ckAttachToInterface Don't bind to the server. Instead, the application supplies an
// interface using the ConnectTo method, which is introduced in
// descendant classes. This option can't be used with the AutoConnect
// property.
Update
Actually, opening Word may have opened a different instance (that's how I remember it for D5/Word97), but at the moment Word does indeed re-use the instance opened by the application. So to avoid "scratching all over a word document manually opened by a user" you really do need to avoid using ActiveDocument as per The_Fox's answer.
According to http://support.microsoft.com/kb/210565 there are several command line switches that will cause Word to start a new instance. The one I have used with Word2003 is /x

Resources