Adding nodes to xml with DOM in inno Setup - strange problems -


very strange problem: use dom edit xml file (a .exe.config file app needs interact ours), seeing have bulk-add several similar sections, made function insert whole needed block.

calling function once works perfectly. calling again different parameters afterwards gives exception (see explanation below code).

the code:

// split string array using passed delimeter procedure explode(var dest: tarrayofstring; text: string; separator: string); var   i: integer; begin   := 0;   repeat     setarraylength(dest, i+1);     if pos(separator,text) > 0     begin       dest[i] := copy(text, 1, pos(separator, text)-1);       text := copy(text, pos(separator,text) + length(separator), length(text));       := + 1;     end     else     begin       dest[i] := text;       text := '';     end;   until length(text)=0; end;  // ensures xpath exists, creating nodes if needed function ensurexpath(const xmldoc: variant; xpath: string): variant; var   pathparts: tarrayofstring;   testnode, currentnode, newnode: variant;   nodelist: variant;   i, j: integer;   found: boolean; begin   currentnode:=xmldoc.documentelement;   explode(pathparts, xpath, '/');    msgbox('array length: ' + inttostr(getarraylength(pathparts)), mbinformation, mb_ok);   := 0 getarraylength(pathparts) - 1   begin     msgbox('current path part:'#13#10 + '''' + pathparts[i] + '''', mbinformation, mb_ok);     if pathparts[i] <> ''     begin       //msgbox('current node:'#13#10 + '''' + currentnode.nodename + '''' + #13#10'current path part:'#13#10 + '''' + pathparts[i] + '''' + #13#10'list length: ' + inttostr(nodelist.length), mbinformation, mb_ok);       msgbox('current node:'#13#10 + '''' + currentnode.nodename + '''', mbinformation, mb_ok);       msgbox('current path part:'#13#10 + '''' + pathparts[i] + '''', mbinformation, mb_ok);       nodelist:= currentnode.childnodes;       msgbox('list length: ' + inttostr(nodelist.length), mbinformation, mb_ok);       found:=false;       j := 0 nodelist.length - 1       begin         testnode:=nodelist.item[j]         if (testnode.nodename = pathparts[i])         begin           currentnode:= testnode;           found:=true;         end;       end;             if (not found)       begin         newnode := xmldoc.createelement(pathparts[i]);         currentnode.appendchild(newnode);         currentnode:=currentnode.lastchild;       end;     end;   end;   result:=currentnode;   msgbox('last node:'#13#10 + '''' + currentnode.nodename + '''', mbinformation, mb_ok); end;  // seeks out node, returning node in "resultnode", , whether found result. function seeknode(const parentnode: variant; var resultnode: variant; subnodepath, attrname, attrvalue :string; isfirstcall: boolean): boolean; var   nodeslist: variant;   attrnode: variant;   attrlist: variant;   attr: variant;   pathparts, newpathparts: tarrayofstring;   i, j, truelength: integer;   currentpath, remainderpath: string;   callagain,callresult: boolean; begin   explode(pathparts, subnodepath, '/');   truelength:=getarraylength(pathparts);   i:=0 getarraylength(pathparts) -1   begin      if pathparts[i] = ''       truelength:=truelength-1;   end;   if (truelength <> getarraylength(pathparts))   begin     setarraylength(newpathparts, truelength);     truelength:=0;     i:=0 getarraylength(pathparts) -1     begin        if pathparts[i] <> ''       begin         newpathparts[truelength] := pathparts[i];         truelength:=truelength+1;       end;     end;   end   else     newpathparts:=pathparts;    callagain:=getarraylength(newpathparts)>1;   currentpath:=newpathparts[0];   remainderpath:='';   i:=1 getarraylength(newpathparts) -1   begin     if (remainderpath <> '')       remainderpath:=remainderpath + '/';     remainderpath:=remainderpath + newpathparts[i];   end;   nodeslist:=parentnode.childnodes;   //msgbox('node count ' + currentpath + ':'#13#10 + '''' + inttostr(nodeslist.length) + '''', mbinformation, mb_ok);   result:=false;   := 0 nodeslist.length - 1   begin     attrnode := nodeslist.item[i];     //msgbox('current node:'#13#10 + '''' + attrnode.nodename  + ''''#13#10'current path:'#13#10+ '''' + currentpath  + '''', mbinformation, mb_ok);     if (attrnode.nodename = currentpath)     begin       if callagain       begin         //msgbox('remainder of path:'#13#10 + '''' + remainderpath  + '''', mbinformation, mb_ok);         callresult:=seeknode(attrnode, resultnode, remainderpath, attrname, attrvalue, false);         if callresult         begin           result:=true;           if isfirstcall             resultnode:=attrnode;           exit;         end;       end       else       begin         attrlist:=attrnode.attributes;         //msgbox('node:'#13#10 + '''' + attrnode.nodename + '''' + #13#10'attributes count:'#13#10 + '''' + inttostr(attrlist.length) + '''', mbinformation, mb_ok);         j := 0 attrlist.length - 1         begin           attr:= attrlist.item[j];           //msgbox('node:'#13#10'''' + attrnode.nodename + ''''#13#10'attribute:'#13#10'''' + attr.nodename + ''''#13#10'value:'#13#10'''' + attr.nodevalue + ''''#13#10'to find:'#13#10'''' + attrvalue + '''', mbinformation, mb_ok);           if (attr.nodename = attrname)           begin             if (attr.nodevalue = attrvalue)             begin               //msgbox('attribute found.', mbinformation, mb_ok);               resultnode:=attrnode;               result:=true;               exit;             end             else             begin               result:=false;               exit;             end;           end;         end;       end;     end;   end; end;  // use of seeknode: remove node function removenode(const parentnode: variant; subnodepath, attrname, attrvalue :string): boolean; var   resultnode: variant; begin   result:=seeknode(parentnode, resultnode, subnodepath, attrname, attrvalue, true);   if (result)     parentnode.removechild(resultnode); end;  // use of seeknode: test node existence function hasnode(const parentnode: variant; subnodepath, attrname, attrvalue :string): boolean; var   resultnode: variant; begin   result:=seeknode(parentnode, resultnode, subnodepath, attrname, attrvalue, true); end;  // adds single assembly binding block xml procedure addassemblybinding(const xmldoc: variant; const parentnode: variant; ainame, aiculture, aikey, brold, brnew, cbver, cbhref: string); var   dependentassemblynode: variant;   assemblyidentitynode: variant;   bindingredirectnode: variant;   codebasenode: variant;   publisherpolicynode: variant; begin //        <assemblyidentity name="ecompas.runtime" culture="" publickeytoken="f27ad8cb97726f87" /> //        <bindingredirect oldversion="3.0.1.0 - 3.0.1.133" newversion="3.0.1.133" /> //        <codebase version="3.0.1.133" href="[targetdir]ecompas.runtime.dll" /> //        <publisherpolicy apply="no"/>    dependentassemblynode:= xmldoc.createelement('dependentassembly');    assemblyidentitynode:= xmldoc.createelement('assemblyidentity');   assemblyidentitynode.setattribute('name', ainame);   assemblyidentitynode.setattribute('culture', aiculture);   assemblyidentitynode.setattribute('publickeytoken', aikey);   dependentassemblynode.appendchild(assemblyidentitynode);    if ((brold <> '') , (brnew <> ''))   begin     bindingredirectnode:= xmldoc.createelement('bindingredirect');     bindingredirectnode.setattribute('oldversion', brold);     bindingredirectnode.setattribute('newversion', brnew);     dependentassemblynode.appendchild(bindingredirectnode);   end;    codebasenode:= xmldoc.createelement('codebase');   codebasenode.setattribute('version', cbver);   codebasenode.setattribute('href', cbhref);   dependentassemblynode.appendchild(codebasenode);    publisherpolicynode:= xmldoc.createelement('publisherpolicy');   publisherpolicynode.setattribute('apply', 'no');   dependentassemblynode.appendchild(publisherpolicynode);    // doesn't work? no idea why gives xmlns while parent has one.   //dependentassemblynode.removeattribute('xmlns');    // seems actual variables of nodes somehow lost after adding   // them parent - add them in advance!   parentnode.appendchild(dependentassemblynode); end;  function updateconfig(const afilename, appdir: string; delete:boolean): boolean; var   xmldoc: variant;   rootnode, mainnode, addnode: variant;   becompasruntime, becompasmetamodel, becompasdatabasems:  boolean;  begin   try     xmldoc := createoleobject('msxml2.domdocument');   except     raiseexception('msxml required complete post-installation process.'#13#10#13#10'(error ''' + getexceptionmessage + ''' occurred)');   end;      xmldoc.async := false;   xmldoc.resolveexternals := false;   xmldoc.load(afilename);    if xmldoc.parseerror.errorcode <> 0   begin     msgbox('xml processing error:'#13#10 + xmldoc.parseerror.reason, mbinformation, mb_ok);     result:=false;     exit;   end;   xmldoc.setproperty('selectionlanguage', 'xpath');    rootnode:=xmldoc.documentelement;   if (rootnode.nodename <> 'configuration')   begin     msgbox('xml processing error:'#13#10'root element ''configuration'' not found.', mbinformation, mb_ok);     result:=false;     exit;   end;   mainnode:=ensurexpath(xmldoc, 'runtime/assemblybinding');    becompasruntime := hasnode(mainnode,'dependentassembly/assemblyidentity','name','ecompas.runtime');   becompasmetamodel := hasnode(mainnode,'dependentassembly/assemblyidentity','name','ecompas.metamodel');   becompasdatabasems := hasnode(mainnode,'dependentassembly/assemblyidentity','name','ecompas.database.ms');    if (not delete)   begin      if not becompasruntime       addassemblybinding(xmldoc, mainnode, 'ecompas.runtime', '', 'f27ad8cb97726f87', '3.0.1.0 - 3.0.1.133', '3.0.1.133', '3.0.1.133', appdir + '\ecompas.runtime.dll');     if not becompasmetamodel       addassemblybinding(xmldoc, mainnode, 'ecompas.metamodel', '', 'f27ad8cb97726f87', '3.0.1.0 - 3.0.1.133', '3.0.1.133', '3.0.1.133', appdir + '\ecompas.metamodel.dll');     if not becompasdatabasems       addassemblybinding(xmldoc, mainnode, 'ecompas.database.ms', '', 'f27ad8cb97726f87', '3.0.1.0 - 3.0.1.133', '3.0.1.133', '3.0.1.133', appdir + '\ecompas.database.ms.dll');   end   else   begin     removenode(mainnode,'dependentassembly/assemblyidentity','name','ecompas.runtime');     removenode(mainnode,'dependentassembly/assemblyidentity','name','ecompas.metamodel');     removenode(mainnode,'dependentassembly/assemblyidentity','name','ecompas.database.ms');   end;    mainnode:=ensurexpath(xmldoc, 'appsettings');   if (not delete)   begin     //<add key="logdir" value=".\log" />     if (not hasnode(mainnode,'add','key','logdir'))     begin       addnode:= xmldoc.createelement('add');       addnode.setattribute('key', 'logdir');       addnode.setattribute('value', '.\log');       mainnode.appendchild(addnode);     end;   end   else   begin     removenode(mainnode,'add','key','logdir');   end;    xmldoc.save(afilename);    result:=true; end; 

originally, updateconfig function done this:

if (not hasnode(mainnode,'dependentassembly/assemblyidentity','name','ecompas.runtime'))   addassemblybinding(xmldoc, mainnode, 'ecompas.runtime', '', 'f27ad8cb97726f87', '3.0.1.0 - 3.0.1.133', '3.0.1.133', '3.0.1.133', appdir + '\ecompas.runtime.dll'); if (not hasnode(mainnode,'dependentassembly/assemblyidentity','name','ecompas.metamodel'))   addassemblybinding(xmldoc, mainnode, 'ecompas.metamodel', '', 'f27ad8cb97726f87', '3.0.1.0 - 3.0.1.133', '3.0.1.133', '3.0.1.133', appdir + '\ecompas.metamodel.dll'); if (not hasnode(mainnode,'dependentassembly/assemblyidentity','name','ecompas.database.ms'))   addassemblybinding(xmldoc, mainnode, 'ecompas.database.ms', '', 'f27ad8cb97726f87', '3.0.1.0 - 3.0.1.133', '3.0.1.133', '3.0.1.133', appdir + '\ecompas.database.ms.dll'); 

this code ran fine first time, second time, gave aforementioned "unknown method" error on setattribute in addassemblybinding. got more bizarre... when removed 3 lines setting attributes assemblyidentitynode, rest of code did run fine other nodes.

the thing imagine related these nodes query in hasnode function see if block exists. can dom not handle querying through unsaved changes? edited code existence checks in advance , store result in booleans, because thought maybe problem seeking nodes on modified tree. gives error trying nest node under or own child ("msxml3.dll: inserting node or ancestor under not allowed"), on dependentassemblynode.appendchild(bindingredirectnode); line. neither of these errors makes sense whatsoever.

i seem loads more it. ensurexpath, when used second time in situation had add nodes, gave illegal nesting error. feeling somehow, object mysteriously becomes null somewhere, , that null seen root node in functions handling node objects.

does have clue may causing behaviour?

the xml i'm editing typically looks this:

<?xml version="1.0" encoding="utf-8"?> <configuration>     <runtime>         <assemblybinding xmlns="urn:schemas-microsoft-com:asm.v1">             <dependentassembly>                 <assemblyidentity name="appstring1" publickeytoken="43265234666" culture="neutral"/>                 <bindingredirect oldversion="1.0.0.0-1.1.99.99" newversion="1.2.0.0"/>             </dependentassembly>             <dependentassembly>                 <assemblyidentity name="appstring2" publickeytoken="43265234666" culture="neutral"/>                 <bindingredirect oldversion="1.0.0.0-1.1.99.99" newversion="1.2.0.0"/>             </dependentassembly>         </assemblybinding>     </runtime> </configuration> 

(with more of these dependentassembly sections... hardly matters)

in end, there didn't seem way out of mess, , ended making external app xml edits. tool making xml changes extracted program folder, , set in run , uninstallrun sections correct parameters.

(the uninstallrun part needed because xml edit part of external app needed integrated our app. obviously, if you're in situation need xml edits in own program, extracting app {tmp} , running once there should enough)

if ever figures out makes com mess fail, though, please add answer. ran when making external app, though, related change in namespace halfway in xml tree.


Comments

Popular posts from this blog

c# - Operator '==' incompatible with operand types 'Guid' and 'Guid' using DynamicExpression.ParseLambda<T, bool> -