使用ADO进行inno设置访问冲突

时间:2015-02-18 14:24:28

标签: inno-setup access-violation adodb

我正在开发一个inno安装程序安装程序,我需要使用' GO'来执行多个SQL-Management Studio脚本。声明。我的方法是打开数据库连接,循环遍历文件,将它们分成GO块并执行每个块。第一个文件按预期运行。在第二个文件中,当涉及到执行时,我在方法" ExecSQLMulti"中得到访问冲突。在这一行:

ADOCommand.ActiveConnection := DBConnection;

我认为问题在于DBConnection对象仍然被前一个ADOCommand对象引用。但是当我尝试这个时:

ADOCommand.ActiveConnection := null();

我收到错误:参数类型错误,超出可接受的范围,或者彼此冲突......

"主"方法,遍历sql文件:

function NKInstall_CreateDatabase: boolean;
var
  SQLScripts: TArrayOfString;
  ScriptTransactions: TStringList;
  i,ItemCount: integer;
  myDBConnection: Variant;
begin
  if not _PreconditionsOK_CreateDB() then begin
    Result := True;
    exit;
  end;

  if not ConnectToDatabase(SQLServerInstance.Text,myDBConnection) then exit;
  SQLScripts := CollectSQLScripts();
  ItemCount := GetArrayLength(SQLScripts)-1;
  ScriptTransactions := TStringList.Create();

  for I := 0 to ItemCount do begin
    SetProgressText('Datenbank wird erstellt...','Script: '+SQLScripts[i]);

    if LoadSQLScript(ExpandConstant('{app}\NKHLP\DB\'+SQLScripts[i]), ScriptTransactions) then begin
      if not ExecSQLMulti(myDBConnection,ScriptTransactions) then begin
        InstallSummary.Lines.Add('- DB Erstellung: Script '+SQLScripts[i]+' konnte nicht (komplett) ausgeführt werden.');
        //Result := False;   //*** Für Testphase des installers auskommentiert, damit Installation nicht abbricht.
        Result := True;
        gDBCreated := False;
        exit;
      end;
      ScriptTransactions.Clear();
    end
    else begin
      MsgBox('SQL Script '+ExpandConstant('{app}\NKHLP\DB\'+SQLScripts[i])+' konnte nicht eingelesen werden!',mbError,MB_OK);
      InstallSummary.Lines.Add('- DB Erstellung: Script '+SQLScripts[i]+' konnte nicht eingelesen werden.');
      //result := false;    //*** Für Testphase auskommentiert, damit Installation nicht abbricht.
      result := True;
      gDBCreated := False;
      exit;
    end;
    //Log('File done...'); //Debug
  end;
  gDBCreated := True;
  Result := True;
end;

建立与DB的连接:

function ConnectToDatabase(DataSourceName: string; var Connection: Variant): boolean;
begin
log('Connecting...');
  try
    Connection := CreateOleObject('ADODB.Connection');
    Connection.CursorLocation := 3;
    Connection.ConnectionString := 'Provider=SQLOLEDB;' +                   // provider
                                   'Data Source=' + DataSourceName +';' +   // server name
                                   'Initial Catalog=master;' +              // default database
                                   'Integrated Security=SSPI;';             // Use Windows Authentication
    Connection.Open;
    Result := True;
  except
    Connection := Null();
    MsgBox('Die Verbindung zur Instanz "'+DataSourceName+'" konnte nicht hergestellt werden: '+#10#13+#10#13+GetExceptionMessage,mbError,MB_OK);
    Result := False;
  end
end;

执行所有GO块:

function ExecSQLMulti(var DBConnection: Variant; Transactions: TStringList): boolean;
var
  ADOCommand: Variant;
  i: integer;
  CurrentQuery: string;
begin
  if Transactions.Count = 0 then begin
    Result := True;
    exit;
  end;
  try
    ADOCommand := CreateOleObject('ADODB.Command');
    ADOCommand.CommandType := adCmdText;
    ADOCommand.ActiveConnection := DBConnection;

    for i := 0 to Transactions.Count-1 do begin
      CurrentQuery := Transactions[i];
      if Length(CurrentQuery) > 0 then begin
        ADOCommand.CommandText := CurrentQuery;
        ADOCommand.Execute(NULL,NULL,adExecuteNoRecords);
      end;
    end;
    result := True;
  except
    MsgBox('Fehler beim ausführen eines SQL Befehls: '+GetExceptionMessage()+#10#13#10#13+
           'Query: '+CurrentQuery,
           mbError,
           MB_OK);
  end;
end;

2 个答案:

答案 0 :(得分:1)

似乎我的创建和传递连接对象的方法是错误的或者只是不起作用。我已经对此进行了重构:

function RunSQLScript(ScriptPath: String; SQLInstance: String; DBName: string; CommandTimeout: integer): boolean;
var
  ScriptTransactions: TStringList;
  ADOCommand: Variant;
  myDBConnection: Variant;
begin
  ScriptTransactions := TStringList.Create();

  if not ConnectToDatabase(SQLInstance, DBName, myDBConnection) then exit;

  ADOCommand := CreateOleObject('ADODB.Command');
  ADOCommand.ActiveConnection := myDBConnection;
  ADOCommand.CommandTimeout := CommandTimeout;
  ADOCommand.CommandType := adCmdText;

  if LoadSQLScript(ScriptPath, ScriptTransactions) then begin
    log('Executing Transactions...');
    if not ExecMultiTransaction(ADOCommand, ScriptTransactions) then begin
      MsgBox('SQL Script '+ScriptPath+' konnte nicht (komplett) ausgeführt werden!',mbError,MB_OK);
      Result := False;
    end
    else begin
      log('Sucessful');
      Result := True;
    end;
  end
  else begin
    MsgBox('SQL Script '+ScriptPath+' konnte nicht eingelesen werden!',mbError,MB_OK);
    result := False;
  end;
end;

function ExecMultiTransaction(var ADOCommand: Variant; Transactions: TStringList): boolean;
var
  i: integer;
  CurrentQuery: string;
begin
  if Transactions.Count = 0 then begin
    Result := True;
    exit;
  end;
  try
    for i := 0 to Transactions.Count-1 do begin
      CurrentQuery := Transactions[i];
      if Length(CurrentQuery) > 0 then begin
        ADOCommand.CommandText := CurrentQuery;
        ADOCommand.Execute(NULL,NULL,adExecuteNoRecords);
      end;
    end;
    result := True;
  except
    MsgBox('Fehler beim ausführen eines SQL Befehls: '+GetExceptionMessage()+#13#10#13#10+
           'Query: '+CurrentQuery,
           mbError,
           MB_OK);
  end;
end;

RunSQLScript函数中,我现在创建连接并仅将ADOCommand对象传递给ExecMultiTransaction函数,我在其中更改ADOCommand.CommandText属性并执行命令。这样我每个脚本就有一个连接。

答案 1 :(得分:0)

也许会很有用。创建多个连接时,还会出现错误“访问冲突”。我在主脚本中编写了一些用于使用SQL数据库的函数(可能不是最佳函数,但它们满足了我的目标):

procedure LogPrint(TlogFile, TlogText: String);
begin
  SaveStringToFile(TlogFile, GetDateTimeString('dd/mm/yyyy hh:nn:ss', '.', ':') + ' ' + TlogText + #13#10, True);
end;


//Function for write ADO errors to log
function ADOErrorPrintFunc(ADOConnection: Variant; FuncNameForLog, logFile: String): Boolean;
var
  ADOError: Variant; 
  I : Integer;  
begin
  if VarIsEmpty(ADOConnection) or (ADOConnection.Errors.Count = 0) then begin
    LogPrint(logFile, FuncNameForLog + ': ' + GetExceptionMessage);
    Result := False;
  end else begin        
    for I := 0 to ADOConnection.Errors.Count - 1 do
      begin
        ADOError := ADOConnection.Errors.Item(I);
        LogPrint(logFile, FuncNameForLog + ' (error) err#: ' + IntToStr(ADOError.Number) + '; ' + 'Source: ' + ADOError.Source + '; ' + 'Description: ' + ADOError.Description);
      end;
    Result := True;
  end;
end;


//Function for create ADODB.Connection once
//Example: SQLobj := _SQL_CreateADOConnection(logFile);
function _SQL_CreateADOConnection(logFile: String): Variant;
var
  ADOConnection: Variant;  
begin
  try    
    ADOConnection := CreateOleObject('ADODB.Connection'); //create the ADO connection object     
    LogPrint(logFile, '_SQL_CreateADOConnection (success)');
  except
    ADOErrorPrintFunc(ADOConnection, '_SQL_CreateADOConnection', logFile);
  end;
  Result := ADOConnection;
end;


//Function for open connection. Must used after _SQL_CreateADOConnection
//For example, ConnectionString for connect to SQL database: 'DRIVER=SQL Server;SERVER=localhost\SQLEXPRESS;DATABASE=master;UID=login;PWD=password;'      
//ConnectionString for connect to Interbase database:  'DRIVER={Easysoft IB6 ODBC};SERVER=localhost;DATABASE=localhost:c:\PathToDb\DBName.gdb;UID=login;PWD=password;'      
//Example: SQLobj := _SQL_OpenConnection(ADOConnection, ConnectionString, logFile);
function _SQL_OpenConnection(ADOConnection: Variant; ConnectionString, logFile: String): Boolean; 
begin
  try
    ADOConnection.ConnectionString := ConnectionString;   
    ADOConnection.Open;           
    LogPrint(logFile, '_SQL_OpenConnection (success)');
    Result := True;
  except
    ADOErrorPrintFunc(ADOConnection, '_SQL_OpenConnection', logFile);
    ADOConnection := Null();
    Result := False;
  end;
end;


//Function for create ADODB.Command object with activate previously opened connection. Use it after function _SQL_OpenConnection return True value.
//Example: SQLobjCmd := _SQL_CreateADOCommand(ADOConnection, logFile);
function _SQL_CreateADOCommand(ADOConnection: Variant; logFile: String): Variant;
var
  ADOCommand: Variant;  
begin
  try    
    ADOCommand := CreateOleObject('ADODB.Command');
    ADOCommand.ActiveConnection := ADOConnection;   
    LogPrint(logFile, '_SQL_CreateADOCommand (success)');
  except
    ADOErrorPrintFunc(ADOConnection, '_SQL_CreateADOCommand', logFile);
  end;
  Result := ADOCommand;
end;


//Function for use "select" query. Reads from the database values from the table and returns an array of column "ResultField". 
//SQLresArr[0] from example - number of elements. If SQLresArr[0] = '' or SQLresArr[0] = '-1' this means error in your SQL query.
//Example: SQLresArr := _SQL_Select(ADOConnection, ADOCommand, 'select * from RDB$ROLES', 'RDB$ROLE_NAME', logFile);
function _SQL_Select(ADOConnection, ADOCommand: Variant; SQLQuery: AnsiString; ResultField, logFile: String): TArrayOfString;
var
  ADORecordset: Variant;  
  tmpArray : TArrayOfString;
  i : Integer;  
begin
  SetArrayLength(tmpArray, 1);
  tmpArray[0] := '';
  try         
    ADOCommand.CommandText := SQLQuery;        
    ADOCommand.Execute(NULL, NULL, adCmdText or adExecuteNoRecords);         
    ADOCommand.CommandType := adCmdText;        
    ADORecordset := ADOCommand.Execute;      
    if ADORecordset.EOF then begin       
      tmpArray[0] := '0';
    end else begin
      i := 0;
      while not ADORecordset.eof do begin
        i := i + 1;
        tmpArray[0] := IntToStr(i);
        SetArrayLength(tmpArray, i + 1);
        tmpArray[i] := ADORecordset.Fields(ResultField).Value;
        ADORecordset.MoveNext;
      end;
    end;
    LogPrint(logFile, '_SQL_Select (success): ' + SQLQuery);
  except
    ADOErrorPrintFunc(ADOConnection, '_SQL_Select', logFile);
    tmpArray[0] := '-1';
  end;
  Result := tmpArray;
end;


//Function for execute SQL query like "update", "alter", etc
//Example: _SQL_Query(ADOConnection, ADOCommand, 'update TABLE set FIELD1 = 1 where FIELD2 = 2', logFile)
function _SQL_Query(ADOConnection, ADOCommand: Variant; SQLQuery, logFile: String): Boolean;
var
  ADORecordset: Variant;  
begin
  try
    ADOCommand.CommandText := SQLQuery;    
    ADOCommand.Execute(NULL, NULL, adCmdText or adExecuteNoRecords);
    ADOCommand.CommandType := adCmdText;
    ADORecordset := ADOCommand.Execute;               
    LogPrint(logFile, '_SQL_Query (success): ' + SQLQuery);
    Result := True;
  except
    LogPrint(logFile, '_SQL_Query(' + SQLQuery + '): ' + GetExceptionMessage);    
    Result := False;
  end;
end;



//Function for close connection. 
//Example: _SQL_Disconnect(ADOConnection, logFile);
function _SQL_Disconnect(ADOConnection: Variant; logFile: String): Boolean;  
begin
  Result := True;
  try
    ADOConnection.Close;
    LogPrint(logFile, '_SQL_Disconnect (success)');
  except
    ADOErrorPrintFunc(ADOConnection, '_SQL_Disconnect', logFile);
    Result := False;
  end;
end;

Interbase数据库示例:

[CODE]
var
  I : Integer;
  logFile: string; 
  IBDriver, IBServer, IBDatabase, IBLogin, IBPassword, IBPasswordReserved, 
  ADO_IB_Obj_ConnectionString: string;
  ADO_IB_Obj_Connected: Boolean;  
  SQLresArr: TArrayOfString;
  ADO_IB_Obj, ADO_IB_ObjCmd: Variant;
begin
  IBDriver := '{Easysoft IB6 ODBC}'
  IBServer := 'localhost'
  IBDatabase := 'C:\Path\DbName.gdb'
  IBLogin := 'SYSDBA';
  IBPassword := 'masterkey';
  IBPasswordReserved := 'tttttttt';
  logFile := 'C:\SQLlog.txt';

  ADO_IB_Obj_Connected := False;
  ADO_IB_Obj := _SQL_CreateADOConnection(logFile);

  // try connect to database with standard password
  ADO_IB_Obj_ConnectionString := 'DRIVER=' + IBDriver + ';SERVER=' + IBServer + ';DATABASE=' + IBServer + ':' + IBDatabase + ';UID=' + IBLogin + ';PWD=' + IBPassword + ';'
  if _SQL_OpenConnection(ADO_IB_Obj, ADO_IB_Obj_ConnectionString, logFile) then begin
    ADO_IB_Obj_Connected := True;
  end else begin
    //try connect to database with other password
    ADO_IB_Obj_ConnectionString := 'DRIVER=' + IBDriver + ';SERVER=' + IBServer + ';DATABASE=' + IBServer + ':' + IBDatabase + ';UID=' + IBLogin + ';PWD=' + IBPasswordReserved + ';'
    if _SQL_OpenConnection(ADO_IB_Obj, ADO_IB_Obj_ConnectionString, logFile)  then begin
      ADO_IB_Obj_Connected := True;
    end; 
  end; 

  //working with database only if connection is opened successfully
  if ADO_IB_Obj_Connected then begin
    ADO_IB_ObjCmd :=_SQL_CreateADOCommand(ADO_IB_Obj, logFile); //create once
    _SQL_Query(ADO_IB_Obj, ADO_IB_ObjCmd, 'update TABLE1 set FIELD1 = 1 where FIELD2 = 2', logFile);
    SQLresArr := _SQL_Select(ADO_IB_Obj, ADO_IB_ObjCmd, 'select RDB$RELATION_NAME from RDB$RELATIONS', 'RDB$RELATION_NAME', logFile);
    For i := 1 to GetArrayLength(SQLresArr) - 1 do begin
      LogPrint(logFile, 'Table ' + IntToStr(i) + ' = "' + Trim(SQLresArr[i]) + '"; Tables count = ' + SQLresArr[0]);
    end;

    _SQL_Disconnect(ADO_IB_Obj, logFile);
  end;
end;