3

我需要从 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;

什么更好用,在哪里可以看到文档和/或示例?

PS对不起我的英语

4

2 回答 2

1

我会使用基于 COM 的 API 到 MSI。

请参阅此线程MSI API 文档

于 2013-01-08T13:58:51.237 回答
0

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

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

  1. 打开微星数据库
  2. 读取表名
  3. 阅读需要的表格并展示它
  4. 如果需要 - 进行更改并保存

在这里,我展示了我如何完成前三个步骤。

准备中


下载JEDI Windows AP(为了更轻松地使用 msi)并添加到项目 JwaMsi.pas、JwaMsiDefs.pas 和 JwaMsiQuery.pas(不要忘记使用列表)。将自动添加依赖项。接下来,将所有需要的组件放在表单上。

让我们编码吧!

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 如果需要 - 进行更改并保存


只需从头到尾重复第三步:将 StringGrid 导出到文件(关于 .idt 及其结构,您可以在此处阅读:),使用属性 MsiDatabaseImport 导入到 MSI(不要忘记之前打开数据库并附加它之后,还要记住关闭处理程序)并删除临时 .idt 文件。

祝你好运!

PS对不起我的英语,第二部分:)

于 2013-01-10T14:46:20.647 回答