如何在Delphi中打开MSI表?

时间:2013-01-08 09:41:09

标签: windows delphi windows-installer

我需要从MSI数据库中打开一些表,阅读本文并使用Delphi放置一些行(在我的例子中是Delphi 7,但如果需要则允许其他版本)。

例如它看起来像ORCA。 Msi必须是开放的,写入表格,可以编辑并写入msi文件。

默认情况下,Delphi无法打开MSI表,但我找到了一个JEDI Windows API,其中存在像JwaMsi和JwaMsiQuery这样的库。但我找不到使用

等功能的文档或示例
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;

什么是更好的使用和我可以看到文档和/或示例?

P.S。对不起我的英文

2 个答案:

答案 0 :(得分:1)

我会将基于COM的API用于MSI。

请参阅this threadMSI API documentation

答案 1 :(得分:0)

所以,解决方案存在,我做到了!它不是那么优雅,但它正在发挥作用......

首先,阅读MSDN中的文章(在我的例子中是关于使用MSI数据库的文章)。 第二,你必须明白你需要做什么。就我而言,它是:

  1. 打开msi数据库
  2. 阅读表格名称
  3. 阅读所需的表并显示
  4. 如果需要 - 进行更改并保存
  5. 这里我将展示如何完成前三个步骤。

    准备


    下载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;
    

    现在我们需要获取表格列表(more info here)并将其放在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如果需要 - 进行更改并保存


    从头到尾重复第三步:将StringGrid导出到文件(关于.idt及其结构,你可以在这里阅读:onetwothree),导入到具有属性MsiDatabaseImport的MSI(不要忘记之前打开数据库并追加它,还要记住关闭处理程序)并删除临时.idt文件。

    祝你好运!

    P.S。对不起,我的英文,第二部分:)