Skip to content

Commit

Permalink
Add database read functions to Msi.java
Browse files Browse the repository at this point in the history
  • Loading branch information
russellbanks committed Aug 8, 2023
1 parent e96f301 commit 5d22632
Show file tree
Hide file tree
Showing 3 changed files with 295 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Expand Up @@ -7,6 +7,7 @@ Next Release (5.14.0)

Features
--------
* [#1537](https://github.com/java-native-access/jna/pull/1537): Add MSI database access functions - [@russellbanks](https://github.com/russellbanks)

Bug Fixes
---------
Expand Down
238 changes: 238 additions & 0 deletions contrib/platform/src/com/sun/jna/platform/win32/Msi.java
Expand Up @@ -23,7 +23,10 @@
package com.sun.jna.platform.win32;

import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.WinBase.FILETIME;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.ptr.PointerByReference;
import com.sun.jna.win32.StdCallLibrary;
import com.sun.jna.win32.W32APIOptions;

Expand Down Expand Up @@ -104,6 +107,36 @@ public interface Msi extends StdCallLibrary {
*/
int INSTALLSTATE_DEFAULT = 5;

/**
* Create a new database, direct mode read/write.
*/
static final String MSIDBOPEN_CREATEDIRECT = "MSIDBOPEN_CREATEDIRECT";

/**
* Create a new database, transact mode read/write.
*/
static final String MSIDBOPEN_CREATE = "MSIDBOPEN_CREATE";

/**
* Open a database direct read/write without transaction.
*/
static final String MSIDBOPEN_DIRECT = "MSIDBOPEN_DIRECT";

/**
* Open a database read-only, no persistent changes.
*/
static final String MSIDBOPEN_READONLY = "MSIDBOPEN_READONLY";

/**
* Open a database read/write in transaction mode.
*/
static final String MSIDBOPEN_TRANSACT = "MSIDBOPEN_TRANSACT";

/**
* Add this flag to indicate a patch file.
*/
static final String MSIDBOPEN_PATCHFILE = "MSIDBOPEN_PATCHFILE";

/**
* The MsiGetComponentPath function returns the full path to an installed component. If the key path for the
* component is a registry key then the registry key is returned.
Expand Down Expand Up @@ -218,4 +251,209 @@ public interface Msi extends StdCallLibrary {
* ERROR_SUCCESS - A value was enumerated.
*/
int MsiEnumComponents(WinDef.DWORD iComponentIndex, char[] lpComponentBuf);

/**
* The MsiCloseHandle function closes an open installation handle.
*
* @param hAny
* Specifies any open installation handle.
*
* @return
* ERROR_INVALID_HANDLE - An invalid handle was passed to the function.
* ERROR_SUCCESS - The function succeeded.
*/
int MsiCloseHandle(Pointer hAny);

/**
* The MsiDatabaseOpenView function prepares a database query and creates a view object. This function
* returns a handle that should be closed using {@link #MsiCloseHandle}.
*
* @param hDatabase
* Handle to the database to which you want to open a view object.
*
* @param szQuery
* Specifies a SQL query string for querying the database.
*
* @param phView
* Pointer to a handle for the returned view.
*
* @return
* ERROR_BAD_QUERY_SYNTAX - An invalid SQL query string was passed to the function.
* ERROR_INVALID_HANDLE - An invalid or inactive handle was supplied.
* ERROR_SUCCESS - The function succeeded, and the handle is to a view object.
*/
int MsiDatabaseOpenView(Pointer hDatabase, String szQuery, PointerByReference phView);

/**
* The MsiGetSummaryInformation function obtains a handle to the _SummaryInformation stream for an installer
* database. This function returns a handle that should be closed using {@link #MsiCloseHandle}.
*
* @param hDatabase
* Handle to the database.
*
* @param szDatabasePath
* Specifies the path to the database.
*
* @param uiUpdateCount
* Specifies the maximum number of updated values.
*
* @param phSummaryInfo
* Pointer to the location from which to receive the summary information handle.
*
* @return
* ERROR_INSTALL_PACKAGE_INVALID - The installation package is invalid.
* ERROR_INVALID_HANDLE - An invalid or inactive handle was supplied.
* ERROR_INVALID_PARAMETER - An invalid parameter was passed to the function.
* ERROR_SUCCESS - The function succeeded.
*/
int MsiGetSummaryInformation(Pointer hDatabase, String szDatabasePath, int uiUpdateCount,
PointerByReference phSummaryInfo);

/**
* The MsiOpenDatabase function opens a database file for data access. This function returns a handle that should be
* closed using {@link #MsiCloseHandle}.
*
* @param szDatabasePath
* Specifies the full path or relative path to the database file.
*
* @param szPersist
* Receives the full path to the file or the persistence mode. You can use the persist parameter to direct the
* persistent output to a new file or to specify one of the following predefined persistence modes:
* {@link #MSIDBOPEN_CREATEDIRECT} - Create a new database, direct mode read/write.
* {@link #MSIDBOPEN_CREATE} - Create a new database, transact mode read/write.
* {@link #MSIDBOPEN_DIRECT} - Open a database direct read/write without transaction.
* {@link #MSIDBOPEN_READONLY} - Open a database read-only, no persistent changes.
* {@link #MSIDBOPEN_TRANSACT} - Open a database read/write in transaction mode.
* {@link #MSIDBOPEN_PATCHFILE} - Add this flag to indicate a patch file.
*
* @param phDatabase
* Pointer to the location of the returned database handle.
*
* @return
* ERROR_CREATE_FAILED - The database could not be created.
* ERROR_INVALID_PARAMETER - One of the parameters was invalid.
* ERROR_OPEN_FAILED - The database could not be opened as requested.
* ERROR_SUCCESS - The function succeeded.
*/
int MsiOpenDatabase(String szDatabasePath, String szPersist, PointerByReference phDatabase);

/**
* The MsiRecordGetString function returns the string value of a record field.
*
* @param hRecord
* Handle to the record.
*
* @param iField
* Specifies the field requested.
*
* @param szValueBuf
* Pointer to the buffer that receives the null terminated string containing the value of the record field. Do not
* attempt to determine the size of the buffer by passing in a null (value=0) for szValueBuf. You can get the size
* of the buffer by passing in an empty string (for example ""). The function then returns ERROR_MORE_DATA and
* pcchValueBuf contains the required buffer size in TCHARs, not including the terminating null character. On
* return of ERROR_SUCCESS, pcchValueBuf contains the number of TCHARs written to the buffer, not including the
* terminating null character.
*
* @param pcchValueBuf
* Pointer to the variable that specifies the size, in TCHARs, of the buffer pointed to by the variable
* szValueBuf. When the function returns ERROR_SUCCESS, this variable contains the size of the data copied to
* szValueBuf, not including the terminating null character. If szValueBuf is not large enough, the function
* returns ERROR_MORE_DATA and stores the required size, not including the terminating null character, in the
* variable pointed to by pcchValueBuf.
*
* @return
* ERROR_INVALID_HANDLE - An invalid or inactive handle was supplied.
* ERROR_INVALID_PARAMETER - An invalid parameter was supplied.
* ERROR_MORE_DATA - The provided buffer was too small to hold the entire value.
* ERROR_SUCCESS - The function succeeded.
*/
int MsiRecordGetString(Pointer hRecord, int iField, char[] szValueBuf, IntByReference pcchValueBuf);

/**
* The MsiSummaryInfoGetProperty function gets a single property from the summary information stream.
*
* @param hSummaryInfo
* Handle to summary information.
*
* @param uiProperty
* Specifies the property ID of the summary property. This parameter can be a property ID listed in the Summary
* Information Stream Property Set. This function does not return values for PID_DICTIONARY OR PID_THUMBNAIL
* property.
*
* @param puiDataType
* Receives the returned property type. This parameter can be a type listed in the Summary Information Stream
* Property Set.
*
* @param piValue
* Receives the returned integer property data.
*
* @param pftValue
* Pointer to a file value.
*
* @param szValueBuf
* Pointer to the buffer that receives the null terminated summary information property value. Do not attempt to
* determine the size of the buffer by passing in a null (value=0) for szValueBuf. You can get the size of the
* buffer by passing in an empty string (for example ""). The function then returns ERROR_MORE_DATA and
* pcchValueBuf contains the required buffer size in TCHARs, not including the terminating null character. On
* return of ERROR_SUCCESS, pcchValueBuf contains the number of TCHARs written to the buffer, not including the
* terminating null character. This parameter is an empty string if there are no errors.
*
* @param pcchValueBuf
* Pointer to the variable that specifies the size, in TCHARs, of the buffer pointed to by the variable
* szValueBuf. When the function returns ERROR_SUCCESS, this variable contains the size of the data copied to
* szValueBuf, not including the terminating null character. If szValueBuf is not large enough, the function
* returns ERROR_MORE_DATA and stores the required size, not including the terminating null character, in the
* variable pointed to by pcchValueBuf.
*
* @return
* ERROR_INVALID_HANDLE - An invalid or inactive handle was supplied.
* ERROR_INVALID_PARAMETER - An invalid parameter was passed to the function.
* ERROR_MORE_DATA - The buffer passed in was too small to hold the entire value.
* ERROR_SUCCESS - The function succeeded.
* ERROR_UNKNOWN_PROPERTY - The property is unknown.
*/
int MsiSummaryInfoGetProperty(Pointer hSummaryInfo, int uiProperty, IntByReference puiDataType,
IntByReference piValue, FILETIME pftValue, char[] szValueBuf, IntByReference pcchValueBuf);

/**
* The MsiViewExecute function executes a SQL view query and supplies any required parameters. The query uses
* the question mark token to represent parameters as described in SQL Syntax. The values of these parameters
* are passed in as the corresponding fields of a parameter record.
* <p>
* Note: In low memory situations, this function can raise a STATUS_NO_MEMORY exception.
*
* @param hView
* Handle to the view upon which to execute the query.
*
* @param hRecord
* Handle to a record that supplies the parameters. This parameter contains values to replace the parameter
* tokens in the SQL query. It is optional, so hRecord can be zero.
*
* @return
* ERROR_FUNCTION_FAILED - A view could not be executed.
* ERROR_INVALID_HANDLE - An invalid or inactive handle was supplied.
* ERROR_SUCCESS - The function succeeded.
*/
int MsiViewExecute(Pointer hView, Pointer hRecord);

/**
* The MsiViewFetch function fetches the next sequential record from the view. This function returns a handle that
* should be closed using {@link #MsiCloseHandle}.
* <p>
* Note: In low memory situations, this function can raise a STATUS_NO_MEMORY exception.
*
* @param hView
* Handle to the view to fetch from.
*
* @param phRecord
* Pointer to the handle for the fetched record.
*
* @return
* ERROR_FUNCTION_FAILED - An error occurred during fetching.
* ERROR_INVALID_HANDLE - An invalid or inactive handle was supplied.
* ERROR_INVALID_HANDLE_STATE - The handle was in an invalid state.
* ERROR_NO_MORE_ITEMS - No records remain, and a null handle is returned.
* ERROR_SUCCESS - The function succeeded, and a handle to the record is returned.
*/
int MsiViewFetch(Pointer hView, PointerByReference phRecord);
}
56 changes: 56 additions & 0 deletions contrib/platform/test/com/sun/jna/platform/win32/MsiTest.java
Expand Up @@ -24,7 +24,9 @@

import java.util.Arrays;

import com.sun.jna.platform.win32.WinBase.FILETIME;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.ptr.PointerByReference;

import junit.framework.TestCase;

Expand Down Expand Up @@ -90,4 +92,58 @@ public void testMsiGetComponentPathW() {
String path = new String(pathBuffer, 0, pathBufferSize.getValue()).trim();
assertFalse("Path is empty", path.isEmpty());
}

public void testMsiOpenDatabaseW() {
PointerByReference phDatabase = new PointerByReference();
assertEquals(WinError.ERROR_INVALID_PARAMETER, Msi.INSTANCE.MsiOpenDatabase("", Msi.MSIDBOPEN_READONLY, phDatabase));
}

public void testMsiCloseHandle() {
PointerByReference handle = new PointerByReference();
assertEquals(WinError.ERROR_INVALID_HANDLE, Msi.INSTANCE.MsiCloseHandle(handle.getPointer()));
}

public void testMsiDatabaseOpenViewW() {
PointerByReference hDatabase = new PointerByReference();
PointerByReference phView = new PointerByReference();
assertEquals(WinError.ERROR_INVALID_HANDLE, Msi.INSTANCE.MsiDatabaseOpenView(hDatabase.getPointer(), "", phView));
}

public void testMsiRecordGetStringW() {
PointerByReference hRecord = new PointerByReference();
IntByReference pcchValueBuf = new IntByReference();
char[] szValueBuf = new char[40];
pcchValueBuf.setValue(40);
assertEquals(WinError.ERROR_INVALID_HANDLE, Msi.INSTANCE.MsiRecordGetString(hRecord.getPointer(), 0, szValueBuf, pcchValueBuf));
}

public void testMsiViewFetch() {
PointerByReference hView = new PointerByReference();
PointerByReference phRecord = new PointerByReference();
assertEquals(WinError.ERROR_INVALID_HANDLE, Msi.INSTANCE.MsiViewFetch(hView.getPointer(), phRecord));
}


public void testMsiViewExecute() {
PointerByReference hView = new PointerByReference();
PointerByReference hRecord = new PointerByReference();
assertEquals(WinError.ERROR_INVALID_HANDLE, Msi.INSTANCE.MsiViewExecute(hView.getPointer(), hRecord.getPointer()));
}

public void testMsiGetSummaryInformationW() {
PointerByReference hDatabase = new PointerByReference();
PointerByReference phSummaryInfo = new PointerByReference();
assertEquals(WinError.ERROR_INVALID_HANDLE, Msi.INSTANCE.MsiGetSummaryInformation(hDatabase.getPointer(), "", 0, phSummaryInfo));
}

public void testMsiSummaryInfoGetProperty() {
PointerByReference hSummaryInfo = new PointerByReference();
IntByReference puiDataType = new IntByReference();
IntByReference piValue = new IntByReference();
FILETIME pftValue = new FILETIME();
char[] szValueBuf = new char[40];
IntByReference pcchValueBuf = new IntByReference();
pcchValueBuf.setValue(40);
assertEquals(WinError.ERROR_INVALID_HANDLE, Msi.INSTANCE.MsiSummaryInfoGetProperty(hSummaryInfo.getPointer(), 7, puiDataType, piValue, pftValue, szValueBuf, pcchValueBuf));
}
}

0 comments on commit 5d22632

Please sign in to comment.