简体   繁体   English

如何在Delphi中打开MSI表?

[英]How to open MSI table in Delphi?

I need to open some tables from MSI database, read this and place some rows in this with using Delphi (in my example is Delphi 7 but allowed other versions if it needed). 我需要从MSI数据库中打开一些表,阅读本文并使用Delphi放置一些行(在我的例子中是Delphi 7,但如果需要则允许其他版本)。

For example it would look like ORCA. 例如,它看起来像ORCA。 Msi must be open, written to the table where it's can be edited and written to the msi file. Msi必须是开放的,写入表格,可以编辑并写入msi文件。

By default, Delphi can't open MSI tables as I thing but I found a JEDI Windows API where exists libraris like JwaMsi and JwaMsiQuery. 默认情况下,Delphi无法打开MSI表,但我找到了一个JEDI Windows API ,其中存在像JwaMsi和JwaMsiQuery这样的库。 But I can't find documentations or examples of using functions like 但是我找不到文档或使用函数的例子

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

By the way, while I search information about this I found this code: 顺便说一句,当我搜索有关此信息时,我发现此代码:

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;

What is better to use and where I can see documentation and/or examples? 什么是更好的使用,我可以看到文档和/或示例?

PS Sorry for my English PS抱歉我的英文

I would use the COM-based API to MSI. 我会使用基于COM的API到MSI。

See this thread and the MSI API documentation 请参阅此主题MSI API文档

So, solution exist and I do it! 所以,解决方案存在,我做到了! It's not so gracefully but it's working... 这不是那么优雅,但它的工作......

At first, read articles from MSDN (in my case is articles about work with MSI database). 首先,阅读MSDN上的文章(在我的例子中是关于使用MSI数据库的文章)。 At second, you must understand what you need to do. 第二,你必须明白你需要做什么。 In my case it is: 在我的情况下它是:

  1. Open msi database 打开msi数据库
  2. Read table names 读表名
  3. Read needed table and show it 阅读所需的表并显示它
  4. If it needed - make changes and save it 如果需要 - 进行更改并保存

Here I show how I do the three first steps. 在这里,我将展示如何完成前三个步骤。

Preparing 准备


Download JEDI Windows AP (for easer work with msi) and add to project JwaMsi.pas, JwaMsiDefs.pas and JwaMsiQuery.pas (do not forget about USES list). 下载JEDI Windows AP (轻松使用msi)并添加到项目JwaMsi.pas,JwaMsiDefs.pas和JwaMsiQuery.pas(不要忘记USES列表)。 Dependencies will be added automatically. 将自动添加依赖项。 Next, place on form all needed components. 接下来,在表单上放置所有需要的组件。

Let's code! 我们的代码!

1 Open msi database and 2 Read table names 1打开msi数据库和2个读取表名


Define variables for handlers and for buffers 为处理程序和缓冲区定义变量

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

Now we need take a list of tables ( more info here ) and place it in ListBox, for example 现在我们需要获取一个表列表( 这里有更多信息 )并将其放在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 Read needed table and show it 3阅读所需的表并显示它


It's easy! 这很容易!

 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 If it needed - make changes and save it 4如果需要 - 进行更改并保存


Just repeat third step from the end to beginning: export StringGrid to file (about .idt and it's structure you can read here: one , two , three ), import in to MSI with property MsiDatabaseImport (do not forget open database before and append it after, also remember about close handlers) and delete temporary .idt file. 只需重复从头到尾的第三步:将StringGrid导出到文件(关于.idt及其结构,你可以在这里阅读: ),导入到具有属性MsiDatabaseImport的MSI(不要忘记之前打开数据库并附加它之后,还记得关闭处理程序)并删除临时.idt文件。

Good luck! 祝好运!

PS Sorry for my English, part two :) PS抱歉我的英文,第二部分:)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM