3

MSIデータベースからいくつかのテーブルを開き、これを読んで、Delphiを使用してこれにいくつかの行を配置する必要があります(私の例ではDelphi 7ですが、必要に応じて他のバージョンを許可しました).

たとえば、ORCA のようになります。Msi を開いて、編集可能なテーブルに書き込み、msi ファイルに書き込む必要があります。

デフォルトでは、Delphi は私のように MSI テーブルを開くことができませんが、JwaMsi や JwaMsiQuery などのライブラリが存在するJEDI Windows APIを見つけました。しかし、次のような関数を使用したドキュメントや例が見つかりません

function MsiOpenProduct(szProduct: LPCTSTR; var hProduct: MSIHANDLE): UINT; stdcall;
{$EXTERNALSYM MsiOpenProduct}

ちなみに、これに関する情報を検索しているときに、次のコードを見つけました。

const msilib = 'msi.dll';

type

  MSIHANDLE = DWORD;    
  TMsiHandle = MSIHANDLE;

function MsiCloseHandle(hAny: MSIHANDLE):UINT;stdcall;external msilib name 'MsiCloseHandle';
function MsiOpenProduct(szProduct:LPCSTR;var hProduct:MSIHANDLE):UINT;stdcall;external msilib name 'MsiOpenProductA';
function MsiGetProductProperty(hProduct:MSIHANDLE;szProperty:LPCSTR;lpValueBuf:LPSTR;pcchValueBuf:LPDWORD):UINT;stdcall; external msilib name 'MsiGetProductPropertyA';
function MsiSetInternalUI(dwUILevel:INSTALLUILEVEL;phWnd:LPHWND):INSTALLUILEVEL;stdcall;     external msilib name 'MsiSetInternalUI';

function GetMSIProperty(aProductCode:string):string;
var
 msi:TMSIHandle;
 t:string;

 function _getmsiproperty(_name:string):string;
 var
  txt:PChar;
  sz:DWORD;
 begin
  sz:=MAX_PATH;
  txt:=AllocMem(sz+1);
  if MsiGetProductProperty(msi,PChar(_name),txt,@sz)=ERROR_MORE_DATA then
   begin
    ReAllocMem(txt,sz+1);
    MsiGetProductProperty(msi,PChar(_name),txt,@sz);
   end;
  SetString(Result,txt,sz);
  FreeMem(txt,sz+1);
 end;

begin
 MsiSetInternalUI(2,nil); // скрываем GUI/hide GUI
 if MsiOpenProduct(PChar(aProductCode),msi)=ERROR_SUCCESS then
  begin
   t:=_getmsiproperty('ARPPRODUCTICON'); // главная иконка приложения/main program icon
       if t='' then t:=_getmsiproperty('ProductIcon');
       if t='' then t:=_getmsiproperty('CompleteSetupIcon');
       if t='' then t:=_getmsiproperty('CustomSetupIcon');
       if t='' then t:=_getmsiproperty('InfoIcon');
       if t='' then t:=_getmsiproperty('InstallerIcon');
       if t='' then t:=_getmsiproperty('RemoveIcon');
       if t='' then t:=_getmsiproperty('RepairIcon');
       Result:=t;
       MsiCloseHandle(msi);
  end;
end;

ドキュメントやサンプルはどこで参照できますか?

PS私の英語でごめんなさい

4

2 に答える 2

1

COM ベースの API を MSI に使用します。

このスレッドMSI API ドキュメントを参照してください

于 2013-01-08T13:58:51.237 に答える
0

だから、解決策が存在し、私はそれをします! それほど優雅ではありませんが、機能しています...

まず、MSDNの記事を読みます(私の場合は、MSI データベースの操作に関する記事です)。次に、何をする必要があるかを理解する必要があります。私の場合は次のとおりです。

  1. msi データベースを開く
  2. テーブル名の読み取り
  3. 必要なテーブルを読み込んで表示する
  4. 必要に応じて - 変更を加えて保存します

ここでは、最初の 3 つのステップをどのように行うかを示します。

準備


JEDI Windows APをダウンロードし (msi での作業を容易にするため)、プロジェクト JwaMsi.pas、JwaMsiDefs.pas、および JwaMsiQuery.pas に追加します (USES リストを忘れないでください)。依存関係は自動的に追加されます。次に、必要なすべてのコンポーネントをフォームに配置します。

コーディングしよう!

1 msi データベースを開き、2 テーブル名を読み取る


ハンドラーとバッファーの変数を定義する

var msi_handler_DB, msi_handler_view, msi_handler_record:TMSIHandle;
txt:PChar;
sz:DWORD;

ここで、テーブルのリスト (詳細はこちら) を取得し、ListBox に配置する必要があります。たとえば、

begin
  sz:=MAX_PATH;           //initialise
  txt:=AllocMem(sz+1);    //variables
OpenDialog1.Execute;      //select file
If MsiOpenDatabase(PChar(OpenDialog1.FileName), MSIDBOPEN_DIRECT, msi_handler_DB)=ERROR_SUCCESS //check if DB is open
  then begin                                                                                    //start reading
  Listbox1.Clear;   //prepare listbox for filling
  MsiDatabaseOpenView(msi_handler_DB, 'SELECT * FROM _Tables',msi_handler_view);  //prepare query to _Table
  MsiViewExecute(msi_handler_view, msi_handler_record);                           //execute...
  While not MsiViewFetch(msi_handler_view, msi_handler_record)=ERROR_NO_MORE_ITEMS   //and fetch it in cycle until end of table
    do begin
      MsiRecordGetString(msi_handler_record,1,txt,sz);    //read string
      ListBox1.Items.Add(txt);                            //and write to listbox
    end;                                                                          //end of fetch cycle

  MsiCloseAllHandles;                                     //close handles (we don't need they more)
  end;                                                                                          //stop reading
end;

3 必要なテーブルを読み込んで表示する


それは簡単です!

 begin
 edit1.text:=Listbox1.Items.ValueFromIndex[ListBox1.ItemIndex];
If MsiOpenDatabase(PChar(OpenDialog1.FileName), MSIDBOPEN_DIRECT, msi_handler_DB)=ERROR_SUCCESS //open database again
  then begin
  MsiDatabaseExport(msi_handler_DB, pchar(ListBox1.Items.Strings[ListBox1.ItemIndex]), 'C:\Windows\Temp', Pchar(ListBox1.Items.Strings[ListBox1.ItemIndex]+'.idt')); //export table to .idt
  MsiCloseAllHandles; //and close handler again

//...
//here must be placed code for
//parsing .idt as tabulation separated file
//with wordwrap and show it.
//for example - in StringGrid
//...

DeleteFile('C:\Windows\Temp\'+ ListBox1.Items.Strings[ListBox1.ItemIndex]+'.idt');  //do not forget delete temporary .idt file. save our planet! :)
  end;
end;

4 必要に応じて - 変更を加えて保存します


3 番目の手順を最初から最後まで繰り返すだけです。StringGrid をファイルにエクスポートし (.idt について、その構造についてはここで読むことができます: onetwothree )、プロパティ MsiDatabaseImport を使用して MSI にインポートします (前にデータベースを開いて追加することを忘れないでください)。その後、クローズ ハンドラーについても覚えておいてください)、一時的な .idt ファイルを削除します。

幸運を!

PS私の英語でごめんなさい、パート2:)

于 2013-01-10T14:46:20.647 に答える