Using Office Interop Application Object to get the PowerPoint version during installation with Inno Setup - vb.net

During the installation of our PowerPoint add-in using Inno Setup installer, I need to get the currently used version of PowerPoint by querying an Application.PowerPoint object itself – instead of relying on registry entries which can't be guaranteed to give the correct value.
I have successfully implemented this for an MSI installer written with WIX based on this answer using this code:
Imports Microsoft.Office.Interop.PowerPoint
Public Class Environment
Public Shared Function GetPowerPointVersion() As String
Dim CurVer As String
Dim thisPowerPoint As Object
thisPowerPoint = New Application()
CurVer = thisPowerPoint.Version
thisPowerPoint.Quit()
Return CurVer
End Function
End Class
I don't entirely trust this to work in all situations (maybe paranoid), so will put in try/catch blocks and use the registry method if this fails.
I haven't been able to work out how to do a similar thing with Inno Setup installer. There are some examples of using DLLs – https://jrsoftware.org/ishelp/index.php?topic=scriptdll – but I can't see how I could create a function callable from Inno Setup from this which would return the version number.

You can use CreateOleObject to call PowerPoint and return the version:
[Code]
function GetPowerPointVersion(): string;
var
MyPowerPoint: Variant;
begin
MyPowerPoint := CreateOleObject('PowerPoint.Application');
Result := MyPowerPoint.Version;
MyPowerPoint.Quit;
end;

For completeness, this is the Pascal Script code which I am now using to get the PowerPoint version – based on Matej's answer, with a fallback to checking the registry if that fails:
function PowerPointVersion(): String;
var
key: String;
versionToUse: String;
installedPowerPoint: Variant;
begin
versionToUse := '';
try
installedPowerPoint := CreateOleObject('PowerPoint.Application');
versionToUse := installedPowerPoint.Version;
installedPowerPoint.Quit;
except
versionToUse := '';
end;
if versionToUse = '' then
begin
if RegQueryStringValue(GetHKLM, 'SOFTWARE\Microsoft\Office\ClickToRun\Configuration','VersionToReport', key) then
begin
versionToUse := key;
Delete(versionToUse, Pos('.', key), Length(key));
versionToUse := versionToUse + '.0';
end;
end;
if versionToUse = '' then
begin
if RegQueryStringValue(HKCR, 'PowerPoint.Application\CurVer\','', key) then
begin
StringChangeEx(key, 'PowerPoint.Application.', '', True);
versionToUse := key;
versionToUse := versionToUse + '.0';
end;
end;
try
// Check to see if versionToUse string can convert to a float:
StrToFloat(versionToUse);
Result := versionToUse;
except
Result := '';
end;
end;

It turns out that using Microsoft.Office.Interop.PowerPoint which is a NuGet package is not a good idea as it is not supported and will be prone to failure. See this discussion.
This external C# code will work and can be set up to be called from Inno Setup. However, using CreateOleObject within Inno Setup Pascal code as described in the accepted answer is far simpler.
[SupportedOSPlatform("windows")]
public class PowerPointEnvironment
{
public static string GetPowerPointVersion()
{
string CurVer = "";
Type? PowerPointType = Type.GetTypeFromProgID("PowerPoint.Application");
if (PowerPointType != null)
{
dynamic? thisPowerPoint = Activator.CreateInstance(PowerPointType);
if (thisPowerPoint != null)
{
CurVer = thisPowerPoint.version();
}
}
return CurVer;
}
}

Related

Build a query on the selected record , position of the dataset pointer

to display the content of a geometry/spatial field I made a small component. This component does it's job when I provide all the information to query the table correctly.
tablename
fieldname of the gemoetry field
fieldname of a field to select the correct element by a query
a connection to connect the query to the correct database on my server
a datasource to display other elements of my table for reference
as my component should be similliar to all the other dbmemo, dbedit ... VCL components, I do not want to share
a) fieldname of a field to select the correct element by a query
and if possible also remove from parameter transfer to my component
b) the connection
here come my current working solution and need help on a) and b)
unit Unit_DBmemoSpatial;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, DB, ADODB ;
type
TDBmemoSpatial = class(TMemo)
private
{ Private-Deklarationen }
Fdatasource : Tdatasource ;
FConnection : TADOConnection;
FdataField : String;
procedure Setdatasource(const Value: Tdatasource);
procedure SetdataField(const Value: String);
procedure AfterSCROLL(DataSet: TDataSet);
protected
{ Protected-Deklarationen }
public
{ Public-Deklarationen }
published
{ Published-Deklarationen }
property Connection : TADOConnection read FConnection write FConnection ;
property datasoure : Tdatasource read Fdatasource write Setdatasource ;
property dataField : String read FdataField write SetdataField ;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('TOOLS', [ TDBmemoSpatial]);
end;
{ TDBmemoSpatial }
procedure TDBmemoSpatial.AfterSCROLL(DataSet: TDataSet);
var aQuery : TADOQuery ;
SQLSTR : String ;
RIndex : Integer ;
begin
/// this will only work on my table , not for others !!!!!
RIndex := Fdatasource.DataSet.FieldByName('MyrecordIndex').AsInteger;
self.Lines.Clear;
self.Lines.Add( 'DBCOMP' + TimetoStr(now) );
aQuery := TADOQuery.Create(nil);
try
aQuery.Connection := FConnection;
SQLSTR := 'SELECT ' + FdataField+'.STAsText() FROM ' + 'MyTable' + ' where MyrecordIndex=' + IntToStr(RIndex);
aQuery.SQL.Add(SQLSTR);
aQuery.Open;
self.Lines.Add( 'record affected ' + IntToStr(aQuery.RecordCount) );
self.Lines.Add( 'Text ' + (aQuery.Fields[0].asString) );
finally
aQuery.Free;
end;
end;
procedure TDBmemoSpatial.SetdataField(const Value: String);
begin
FdataField := Value;
end;
procedure TDBmemoSpatial.Setdatasource(const Value: Tdatasource);
begin
Fdatasource := Value;
Fdatasource.DataSet.AfterScroll := AfterSCROLL;
end;
end.
the calling code for my component goes like this
dbmsptl1.Connection := FCon;
dbmsptl1.datasoure := Fdatasource;
dbmsptl1.dataField :='geometry_by_Fieldname';

Delphi load image save as blob in a sql database

I'm trying to load a Image control from a image blob saved previously in a sql database.I have testd so many ways and i can't make it work. The image blob is saved as:
qry.SQL.Text := 'update tbl set pic = :blobVal where id = :idVal';
qry.Parameters.ParamByName('blobVal').LoadFromFile('c:\sample.jpg', ftBlob);
qry.Parameters.ParamByName('idVal').Value := 1;
any suggestion?
There a a lot of treads here about loading images to as database, but I did not find one with update or insert parameters.
You might simply assign a graphic object to your parameter.
If you want to store different graphic types you should add a column
keeping the Information which kind of graphic should be stored (e.g. jpeg,bmp,png).
to be able to create the needed TGraphic class descendant if you want to retrieve the picture from the database.
uses jpeg, pngimage;
type
TitTYPES=(itJPG,itPNG,itBMP);
procedure TDEMO.Button1Click(Sender: TObject);
var
jp:TJpegimage;
g:TGraphic;
begin
jp:=TJpegimage.Create;
try
ads.Close;
jp.LoadFromFile('C:\Bilder1\PIC.jpg');
ads.SQL.Text := 'Insert into IMGBlob (ID,Blob,typ) Values (:ID,:BLOB,:typ)';
ads.Parameters[0].Value := 1;
ads.Parameters[1].Assign(jp);
ads.Parameters[2].Value := itJPG;
ads.ExecSQL;
ads.SQL.Text := 'Select * from IMGBlob where ID=:ID';
ads.Parameters[0].Value := 1;
ads.Open;
try
case TitTYPES(ads.FieldByName('typ').AsInteger) of
itJPG: g:=TJpegimage.Create;
itPNG: g:=TPNGImage.Create;
itBMP: g:=TBitmap.Create;
end;
g.Assign(ads.FieldByName('Blob'));
Image1.Picture.Assign(g);
finally
g.Free;
end;
finally
jp.Free;
end;
end;
To load a BLOB field into an image, you need to use TDataSet.CreateBlobStream.
var
Stream: TStream;
JPG: TJpegImage;
begin
JPG := TJpegImage.Create;
try
Stream := Qry.CreateBlobStream(Qry.FieldByName('BLOBVAL'), bmRead);
try
JPG.LoadFromStream(Stream);
finally
Stream.Free; // edited
end;
finally
JPG.Free;
end;
end;
To store the image back, you'll need to do the reverse:
var
Stream: TBlobStream;
Jpg: TJpegImage;
begin
Jpg := TJpegImage.Create;
try
Jpg.Assign(Image1.Picture.Graphic);
// Assign other query parameters here
Stream := Qry.CreateBlobStream(Qry.FieldByName('BLOBVAL'), bmWrite);
try
Jpg.SaveToStream(Stream);
Qry.ExecSQL;
finally
Stream.Free;
end;
finally
Jpg.Free;
end;
end;
TDBImage is only designed to work with bitmaps (when the field is ftGraphic), so it won't work with JPEG images directly. The easiest thing to do is to load the blob as a JPEG, and assign it to a standard TImage.Picture.Graphic in an event handler for the dataset (such as it's AfterScroll event).
save to db:
var
ms:tmemorystream;
Begin
ms:=tmemorystream.create;
ms.position:=0;
image1.picture.bitmap.savetostream(ms);
ms.position:=0;
with yourfield as tblobfield do
loadfromstream(ms);
freeandnil(ms);
end;
Load from db:
var
ms:tmemorystream;
Begin
ms:=tmemorystream.create;
ms.position:=0;
with yourfield as tblobfield do
savetostream(ms);
ms.position:=0;
image1.picture.bitmap.loadfromstream(ms);
freeandnil(ms);
end;
It does not work with all graphic types like PNG etc.
This one does work on PNG as well:
var mBitmap : TGraphic;
var mStream : TStream;
var mClass : TGraphicClass;
begin
mStream := Query.CreateBlobStream(Query.FieldByName('yourBlobfield'), bmRead);
mClass := GetGraphicClassForFileExtension(mStream.ReadAnsiString);
mBitmap := mClass.Create;
mBitmap.LoadFromStream(mStream);
Image4.Picture.Assign(mBitmap);
end;

Oracle 11g - Find Records in a CLOB with Carriage Return Line Feed

I am on Oracle 11g. I am trying to do a Find and Replace functionality on a CLOB field (using REPLACE).
Now the data in my CLOB has CRLFs in them, the replace works just fine until I want to find a string that contains CRLFs. Perhaps this would be best explained by example:
So say the text in my field is:
----------------------------------
Hi there this is some text
that has CRLFS in it.
Some other text that
is there also.
Have a nice day
Now what I want to do is replace all occurrences of this INCLUDING the CRLFs:
Search Text
--------------------------------------------------------------------------------
Some other text that
is there also.
With this text INCLUING the CRLFs:
Replace Text
------------------------------------
Some other text that
has some new text that is there also.
So That could come out to be:
----------------------------------
Hi there this is some text
that has CRLFS in it.
Some other text that
has some new text that is there also.
Have a nice day
Now I am doing this in a stored procedure and the Search Text and Replace Text come in as variables, but When I try and say where like % || ReplaceText || '%' it returns 0 rows.
Does anyone have an idea how to do this?
Here is my stored procedure (iOldResponsibilities is the Search Text, iNewResponsibilities is the replace text:
PROCEDURE FindReplaceResponsibilities (
iOldResponsibilities IN JP_JOB_FAMILIES.RESPONSIBILITIES%TYPE,
iNewResponsibilities IN JP_JOB_FAMILIES.RESPONSIBILITIES%TYPE,
oNumRowsUpdated OUT INTEGER
)
IS
BEGIN
oNumRowsUpdated := 0;
SAVEPOINT sp_jf_findrepresp;
-- If there is no old text to search for then,
-- append the new text to the end of every row.
-- Else replace all occurrences of the old text with the new text
IF iOldResponsibilities IS NULL THEN
UPDATE JP_JOB_FAMILIES
SET RESPONSIBILITIES = RESPONSIBILITIES || iNewResponsibilities;
oNumRowsUpdated := SQL%ROWCOUNT;
ELSE
UPDATE JP_JOB_FAMILIES
SET RESPONSIBILITIES = REPLACE(RESPONSIBILITIES, iOldResponsibilities, iNewResponsibilities)
WHERE RESPONSIBILITIES like '%' || iOldResponsibilities || '%';
-- I have also tried this:
--WHERE dbms_lob.instr(RESPONSIBILITIES, TO_CLOB(iOldResponsibilities)) > 0; -- This is a LIKE for CLOBS
oNumRowsUpdated := SQL%ROWCOUNT;
END IF;
RETURN;
EXCEPTION
WHEN OTHERS THEN
BEGIN
oNumRowsUpdated := -1;
ROLLBACK TO sp_jf_findrepresp;
dbms_output.put_line('error: ' || sqlerrm);
RETURN;
END;
END FindReplaceResponsibilities;
The Text is coming from an asp.net application (c#) as String values:
public int FindReplaceJobFamilyResponsibilities(String oldResponsibilities, String newResponsibilities, IDbTransaction transaction = null)
{
using (IDbCommand cmd = this._dataHelper.GetStoredProcedure(_connectionString,
"JP_JOBFAM_PKG.FindReplaceResponsibilities", true))
{
_dataHelper.SetParameterValue(cmd, "iOldResponsibilities", oldResponsibilities);
_dataHelper.SetParameterValue(cmd, "iNewResponsibilities", newResponsibilities);
DataHelperBase.VerifyParameters(cmd.Parameters, false);
base.SetExecuteConnection(cmd, transaction);
_dataHelper.ExecuteNonQuery(cmd);
return Convert.ToInt32(_dataHelper.GetParameterValue(cmd, "oNumRowsUpdated"));
}
}
Turns out to be a case of bad data. The data in my Test DB was corrupted and only had LFs instead of CRLFs.
GIGO :-)
Thanks for all your help
Oh and by the way In my code example I went with the INSTR function instead of the like function. If the user entered a % in the text to search through that might have messed up the like statement. (Can't filter those out because % might be a valid character in my data)
Here is the Final Code:
PROCEDURE FindReplaceResponsibilities (
iOldResponsibilities IN JP_JOB_FAMILIES.RESPONSIBILITIES%TYPE,
iNewResponsibilities IN JP_JOB_FAMILIES.RESPONSIBILITIES%TYPE,
oNumRowsUpdated OUT INTEGER
)
IS
BEGIN
oNumRowsUpdated := 0;
SAVEPOINT sp_jf_findrepresp;
-- If there is no old text to search for then,
-- append the new text to the end of every row.
-- Else replace all occurrences of the old text with the new text
IF iOldResponsibilities IS NULL THEN
UPDATE JP_JOB_FAMILIES
SET RESPONSIBILITIES = RESPONSIBILITIES || iNewResponsibilities;
oNumRowsUpdated := SQL%ROWCOUNT;
ELSE
UPDATE JP_JOB_FAMILIES
SET RESPONSIBILITIES = REPLACE(RESPONSIBILITIES, iOldResponsibilities, iNewResponsibilities)
WHERE dbms_lob.instr(RESPONSIBILITIES, iOldResponsibilities) > 0;
oNumRowsUpdated := SQL%ROWCOUNT;
END IF;
RETURN;
EXCEPTION
WHEN OTHERS THEN
BEGIN
oNumRowsUpdated := -1;
ROLLBACK TO sp_jf_findrepresp;
dbms_output.put_line('error: ' || sqlerrm);
RETURN;
END;
END FindReplaceResponsibilities;
The code from my application was fine:
public int FindReplaceJobFamilyResponsibilities(String oldResponsibilities, String newResponsibilities, IDbTransaction transaction = null)
{
using (IDbCommand cmd = this._dataHelper.GetStoredProcedure(_connectionString,
"JP_JOBFAM_PKG.FindReplaceResponsibilities", true))
{
_dataHelper.SetParameterValue(cmd, "iOldResponsibilities", oldResponsibilities);
_dataHelper.SetParameterValue(cmd, "iNewResponsibilities", newResponsibilities);
DataHelperBase.VerifyParameters(cmd.Parameters, false);
base.SetExecuteConnection(cmd, transaction);
_dataHelper.ExecuteNonQuery(cmd);
return Convert.ToInt32(_dataHelper.GetParameterValue(cmd, "oNumRowsUpdated"));
}
}

How to get cursor from Oracle using Groovy?

I'm using a Groovy script in Mule ESB to get output parameters from Oracle stored procedure (including cursor) and getting an exception.
Minimal example:
import groovy.sql.Sql
import oracle.jdbc.pool.OracleDataSource
import oracle.jdbc.driver.OracleTypes
def ds = new OracleDataSource()
// setting data source parameters here
def sql = new Sql(ds)
def data = []
sql.call("""declare
result_table sys_refcursor;
begin
open result_table for select 1 as a from dual;
insert into CURSOR_TEST (ID) values (1);
commit;
${Sql.resultSet OracleTypes.CURSOR} := result_table;
insert into CURSOR_TEST (ID) values (2);
commit;
end;
"""
){ table ->
throw new RuntimeException("Never getting this exception.")
table.eachRow {
data << it.toRowResult()
}
}
sql.close()
return data
Error:
Message : java.sql.SQLException: Closed Statement (javax.script.ScriptException)
Code : MULE_ERROR--2
--------------------------------------------------------------------------------
Exception stack is:
1. Closed Statement(SQL Code: 17009, SQL State: + 99999) (java.sql.SQLException)
oracle.jdbc.driver.SQLStateMapping:70 (null)
2. java.sql.SQLException: Closed Statement (javax.script.ScriptException)
org.codehaus.groovy.jsr223.GroovyScriptEngineImpl:323 (http://java.sun.com/j2ee/sdk_1.3/techdocs/api/javax/script/ScriptException.html)
3. java.sql.SQLException: Closed Statement (javax.script.ScriptException)
(org.mule.api.transformer.TransformerException)
org.mule.module.scripting.transformer.ScriptTransformer:39 (http://www.mulesoft.org/docs/site/current3/apidocs/org/mule/api/transformer/TransformerException.html)
--------------------------------------------------------------------------------
Root Exception stack trace:
java.sql.SQLException: Closed Statement
at oracle.jdbc.driver.SQLStateMapping.newSQLException(SQLStateMapping.java:70)
at oracle.jdbc.driver.DatabaseError.newSQLException(DatabaseError.java:133)
at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:199)
+ 3 more (set debug level logging or '-Dmule.verbose.exceptions=true' for everything)
********************************************************************************
Select from CURSOR_TEST returns 1 and 2.
Oracle server version: Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production.
Mule version: 3.5.0.
I'm using jdbc\lib\ojdbc6.jar from oracle client version 11.1.0.7.0.
What am I doing wrong?
The following code can help you get variable of SYS_REFCURSOR from Oracle anonymous block.
We should focus on a few key details:
Class groovy.sql.Sql doesn't have corresponding OutParameter and we make it manually as CURSOR_PARAMETER and pass it to sql.call method
Consider that the block starts with {call DECLARE and ends with END } without semicolon after END. Otherwise we can get a poorly recognizable SQLException in the face.
The question marks ? inside the sqlString are places for parameter bindings. Bindings are made in the natural order. In this example:
the first ? binds with the first element in parametersList: "abc", treating the value as IN parameter ;
the second ? binds with CURSOR_PARAMETER treating the value as OUT parameter of passed type;
There is only one enter into closure after sql.call and ResultSet rs provide rows of cursor my_cur declared in anonymous block.
import groovy.sql.OutParameter
import groovy.sql.Sql
import oracle.jdbc.OracleTypes
import java.sql.ResultSet
def driver = 'oracle.jdbc.driver.OracleDriver'
def sql = Sql.newInstance('jdbc:oracle:thin:#MY-SERVER:1521:XXX', 'usr', 'psw', driver)
// special OutParameter for cursor type
OutParameter CURSOR_PARAMETER = new OutParameter() {
public int getType() {
return OracleTypes.CURSOR;
}
};
// look at some ceremonial wrappers around anonymous block
String sqlString = """{call
DECLARE
my_cur SYS_REFCURSOR;
x VARCHAR2(32767) := ?;
BEGIN
OPEN my_cur
FOR
SELECT x || level AS my_column FROM dual CONNECT BY level < 10;
? := my_cur;
END
}
""";
// the order of elements matches the order of bindings
def parametersList = ["abc", CURSOR_PARAMETER];
// rs contains the result set of cursor my_cur
sql.call(sqlString, parametersList) { ResultSet rs ->
while (rs.next()) {
println rs.getString("my_column")
}
};

Inno Setup: How to create password wizard page only if component "X" selected

Can anyone help me to protect a selection group or component.
For examples
If ('Readme.txt').selected or ('compact').selected = True then
begin "Password wizard page";
else
result := true;
end;
Something like that to this working script :P
function CheckPassword(Password: String): Boolean;
begin
result := false;
if (Password='component') or (Password='type') then
result := true;
end;
I'm not sure I completely understood your question, but maybe this helps. Here are a few functions you only need to add to the [code] section of the Components.iss sample, and one of the components ("help") can only be installed when the user enters the correct password.
Since you need the password later in the installation, and not always, you can not use the standard setup password page. You will instead create your own page and insert it after the components selection page:
[Code]
var
PasswordPage: TInputQueryWizardPage;
procedure InitializeWizard();
begin
PasswordPage := CreateInputQueryPage(wpSelectComponents,
'Your caption goes here',
'Your description goes here',
'Your subcaption goes here');
PasswordPage.Add(SetupMessage(msgPasswordEditLabel), True);
end;
Note that this uses the translated password caption, you may need to make the other three strings translatable as well.
Next you will need to hide that page if the user has not selected the component for installation:
function ShouldSkipPage(PageID: Integer): Boolean;
begin
Result := False;
if PageID = PasswordPage.ID then begin
// show password page only if help file is selected for installation
Result := not IsComponentSelected('help');
end;
end;
Finally you need to check the password, and prevent the user from going to the next page if the password is wrong:
function NextButtonClick(CurPageID: Integer): Boolean;
begin
Result := True;
if CurPageID = PasswordPage.ID then begin
// stay on this page if password is wrong
if PasswordPage.Edits[0].Text <> 'my-secret-password' then begin
MsgBox(SetupMessage(msgIncorrectPassword), mbError, MB_OK);
Result := False;
end;
end;
end;