I have an installer that needs to install some visual c++ redistributables before anything else gets installed. This is because an ActiveX COM object wont register without it being installed first.
I have the code that properly installs the visual c++, but the program seems to hang for a minute (to the user) while it is installing since they have no clue what is happening. Is there any way to inform the user what i am doing without popping up an actual messagebox? Like how you can set the status in the [Run] block.
// This function will be called after the last "Next" button is pressed, but before any files get installed
procedure DoPreInstall();
var
ErrorCode: Integer;
begin
Exec(ExpandConstant('vcredist_x86_2008.exe'), '/q', '', SW_SHOW, ewWaitUntilTerminated, ErrorCode)
end;
end;
I haven't tried it but I found this: http://news.jrsoftware.org/news/innosetup.code/msg21747.html.
It might help. Another is to run the vcredist in a non-silent mode.
Related
I'm trying to play around with some Delphi ActiveX library for MS-RDP (mstscax.dll), so I imported the library into my project, and started looking for some codes snippets on the web. On a first look, it's pretty obvious, but the lack of examples makes it a little complex.
First the library gives an error on Delphi Seattle, on this line:
property ConnectWithEndpoint: POleVariant1 write Set_ConnectWithEndpoint;
Ok, I commented this line out (not the best solution, I know), but it compiled. Later I tried to change POleVariant1 to OleVariant only, and still compiling.
Ok, after compiled, I tried this code:
var
Form1: TForm1;
RDP: TMsRdpClient8NotSafeForScripting;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
RDP:= TMsRdpClient8NotSafeForScripting.Create(Nil);
RDP.Server:= 'xxxx';
RDP.AdvancedSettings8.RDPPort:= 3389;
RDP.UserName:= 'terminal';
RDP.AdvancedSettings8.ClearTextPassword:= '123456';
RDP.Connect;
if RDP.Connected.ToBoolean = true then
ShowMessage('connected')
else
ShowMessage('error');
end;
I tried some different types for the var RDP, like TMsRdpClient8 only, but still the same problem:
It don't even try to connect! While looking on the sniffer, no tcp connections are made, just nothing happens and the "error" message appears. Any idea about how to work with this guy?
This question intrigued me so I tried to import that ActiveX control and try it myself. It seems to work for me, so I'll explain what I did.
I imported the mstscax.dll ActiveX control then added it to a new package in order to install components onto the tool palette. I immediately ran into the error you did with the ConnectWithEndpoint property. I changed the type in the declaration to OleVariant because the Set_ConnectWithEnpoint property setter function takes an OleVariant. There is clearly something about the type information that our ActiveX importer code is getting confused by. Either way, that change got the file to compile and install the component package. There are now a bunch of TMsRdpClientXXXX components.
Created a new VCL Forms project, then dropped the TMsRdpClient9 component into the form. Added a TButton and then added this code into the button's OnClick handler:
MsRdpClient91.Server := '<some remote server>';
MsRdpClient91.Domain := 'embarcadero.com';
MsRdpClient91.UserName := 'abauer';
MsRdpClient91.Connect;
Once I ran the app, and pressed the button, it connected and the content of the ActiveX control showed the remote server login screen just fine.
I'm running Windows 10, build 10565.
Here's what I'm seeing on my little app I wrote:
I wonder whether in Delphi calling
Query1.Unprepare;
implicitly closes Query1, if it was previously active. Such that e.g. calling Next on it will fail.
You might say, just go ahead and try but I did on a 64-bit Windows 7 system and had all sort of problems with it until finally my BDE Administrator seems to be completely broken. So I decided to just ask this questions before I start to find out, how I can get BDE running on my system ;-)
You can not use Prepare/Unprepare on an open dataset. you need to close it first.
unit DBTables;
...
procedure TQuery.SetPrepared(Value: Boolean);
begin
if Handle <> nil then DatabaseError(SDataSetOpen, Self);
...
// SDataSetOpen = 'Cannot perform this operation on an open dataset';
I use Delphi 2007 and I try to find out how to ask Windows (XP, Server 2003 or 2008) if a named MSMQ queue is installed. I have found this but it is in C++ so it is not easy to use from Delphi. Example, I have an installed queue named '.\private$\nctsinqueue'. It works fine to use it by:
var
QueueInfo : IMSMQQueueInfo2;
begin
QueueInfo := CoMSMQQueueInfo.Create;
The problem is that in some installations of Windows where my application is installed this queue does not exists. It depend of the preferences if a queue is needed. So I want to ask Windows if a named queue is installed and in that case I can go on with the code above.
EDIT:
Tried this code
function Test: Boolean;
var
QueueInfo : IMSMQQueueInfo2;
begin
Result := True;
QueueInfo := CoMSMQQueueInfo.Create;
QueueInfo.PathName := '.\private$\nonexistingqueue';
FQueue := QueueInfo.Open(MQ_RECEIVE_ACCESS, MQ_DENY_NONE);
end;
And it raise an exception on the last line. I can of course have a try/except here and return False in that case but I don't like to have exceptionhandling for this. I want to ask WinApi or something if the queue exists. Queue.IsOpen that kobik suggest only says if an existing queue is opened. It must of course exist before it can be opened.
Edit2:
I take a more practical approach to this, so I solved it with ini-files for my application.
It tries to open only if the queue is present in the ini-file.
Disadvantage is of course that the ini-file must be in sync with the queues in the system, but that part is rather static.
I have just started working with RemObjects Pascal Script. and have been trying to follow the remobjects tutorial.
http://devcenter.remobjects.com/articles/?id={2FFC1EE9-F18D-4B11-9DE4-1BA0A79D0D04}
all was going fine up to the part you run
begin
ShowNewMessage('Show This !');
end.
where it claimed it does not know of it.
but i have it here
procedure Tmainwindow.ceCompile(Sender: TPSScript);
begin
Sender.AddMethod(Self, #Tmainwindow.ShowNewMessage,
'procedure ShowNewMessage(const Message: string);');
end;
procedure ShowNewMessage(const Message: string);
procedure Tmainwindow.ShowNewMessage(const Message: string);
begin
//ShowMessage('ShowNewMessage invoked:'#13#10+Message);
end;
added on the compile event as instructed... it all compiles in delphi but when i run the code from within my executable it says it dont exist.
secondly if i add any plugins to improve the function calls of the script i get this..
please help i realise i may be doing something silly here im new to rem objects.
Well, I tried building the example as shown on that page, and it compiled and ran correctly for me. Try using the example shown at the top of the page, under "The following code will compile and...". Just make sure to leave out the line that replaces the script text.
As for the plugins, it can't register your event types because they refer to object classes that haven't been registered yet. Unfortunately, the PS Plugin system doesn't have any way of automatically resolving dependencies, and the compiler's error message doesn't tell you which type it couldn't find. You'll need the debugger to help you resolve this. But a lot of the basics, including TObject (yes, you have to import it explicitly) are found in TPSImport_Classes.
i have the same Problem. That has nothing to do with the syntax, only with the inclusion of the Forms-Unit template for the script compiler.
Sry, i do not have a solution for that problem, because it even occurs when removing the OnMenuDrawItem and OnMenuAdvancedDrawItem events (which both make Problems).
I use BDS 2006, that might be the problem as it uses advanced Forms source code in comparison to what D7 used (which was the version RO PS was actually made for).
So, remove the Forms unit plugin for the compiler, which also includes the menus unit and try it again, that should "solve" your problem.
I am trying to extend a 3rd party application so that it can be invoked via command line in addition to using the windows form GUI (mixed mode is desired). It's a fairly simple program which basically loads a file and then you click a button it starts sending UDP network packets.
I need to invoke the application from another and would like to pass in an argument and need to be able to return the ExitCode to the calling app. From what i've read, in order to do so you need to add the compiler directive {APPTYPE CONSOLE}.
I did this and my application worked as I wanted it to except sending the network packets slowed down to a crawl. I found that whenever I moved my mouse around on the form. That the network transfer rate increased significantly. I suspect there is some type of Windows Message queue problem and moving mouse is causing interrupts which in turn is causing the message queue to be processed?
I have googled around and tried calling Application.ProcessMessages and PeekMessages in a Timer with a 1ms interval and that didn't help at all. I found in this user manual for some other application it says that Indy 10 is supported in both APPTYPE CONSOLE and GUI types. Quite frankly this just confuses me as I would have assumed that all network library would work in both modes... but like I said I'm not familiar with Delphi.
I am positive that the issue is isolated to a single line in my application and that is whether or not {APPTYPE CONSOLE} is included or not.
Anyone have any ideas?
Version Info:
Delphi 7 Personal (Build 4.453)
Indy 9.0.4
If you add {APPTYPE CONSOLE} to your application even though you desire mixed mode execution, then you will have to live with a console even when the application is in GUI mode. You can of course close the console, but this will cause some flicker and feels a bit hackish to me.
You should be able to do what you want without a console program. A small test program proves that the exit code can be read from a GUI program:
procedure TForm1.Timer1Timer(Sender: TObject);
begin
Close;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
ExitCode := 42;
Timer1.Interval := 1000;
Timer1.Enabled := TRUE;
end;
If this is executed with the following cmd file:
#echo off
start /WAIT project1.exe
echo %ERRORLEVEL%
the program shows its main form for 1 second, closes, and the script prints 42 to the console window.
Now for capturing the output - doing this from a GUI program is actually easier than doing it from a console program, if you allow for the use of a temporary file. You need to start the program with a command line parameter anyway, so why not give it the name of a temporary file, wait for the application to finish, read in the file and delete it afterwards?
If you want an application to return an "error" code there is no need to make it a console application. You only need to set the ExitCode, e.g.
ExitCode := 10;
in a batch file
#Echo off
project1
echo %errorlevel%
Will display the application, then display 10 when.
Note: It is also possible to create a console window dynamically from the windows API using AllocConsole or to attach using AttachConsole.
I created an object wrapper for this once, but no longer have the code available. From memory it didn't support redirection (because I didn't need it).
If I understand you correctly, then you want your app to have two modes:
If no argument is passed, run in GUI mode
Run in non-GUI mode otherwise
The easiest is if you can centralize your logic so it can be called from one method (CoreLogic in my example).
The below app then should work fine.
Two tricks:
Application.ShowMainForm := False; that will not make the MainForm show at all.
ExitCode := 327; which will set your return code (like mghie and Gerry already mentioned).
A few notes:
because the CoreLogic does not process any windows messages, anything in your application that depends on Windows messages being processed will stall.
if you need windows message processing, then just all Application.ProcessMessages() inside your CoreLogic
if you need your form to be visible, then you change the logic inside your MainForm to test for the commandline parameters, and exit when it's work as been done (by calling Application.Terminate()). The best place to put that logic in is the event method for the MainForm.OnShow event.
Hope this helps :-)
program VCLAppThatDoesNotShowMainForm;
uses
Forms,
MainFormUnit in 'MainFormUnit.pas' {MainForm},
Windows;
{$R *.res}
procedure CoreLogic;
begin
Sleep(1000);
ExitCode := 327;
end;
procedure TestParams;
begin
if ParamCount > 0 then
begin
MessageBox(0, CmdLine, PChar(Application.Title), MB_ICONINFORMATION or MB_OK);
CoreLogic();
Application.ShowMainForm := False;
end;
end;
begin
Application.Initialize();
Application.MainFormOnTaskbar := True;
TestParams();
Application.CreateForm(TMainForm, MainForm);
Application.Run();
end.
A timer with 1ms will only fire about every 40 ms (due to Windows limitations), so it won't help. I have seen effects like you describe with mixed console and GUI apps, another is that they don't minimize properly.
Instead of enabling the console in the project, you could probably use the CreateConsole API call (Not sure whether the name is correct) to create one after the programm was started. I have seen no adverse effects in the one (!) program I have done this.
But this is only necessary if you want to write to the console. If you only want to process command line parameters and return an exit code, you do not need a console. Just evaluate the ParamCount/ParamStr functions for the parameters and set ExitCode for the return value.
If some threads in your console application call Synchronize (and I guess the Indy stuff is actually doing that), you have to make some preparations:
Assign a method to the WakeMainThread variable. This method must have the signature of TNotifyEvent.
Inside this method call CheckSynchronize.
For additional information see the Delphi help for these two items.