home *** CD-ROM | disk | FTP | other *** search
-
- {*******************************************************}
- { }
- { Delphi Visual Component Library }
- { }
- { Copyright (c) 1995,96 Borland International }
- { }
- {*******************************************************}
-
- unit Report;
-
- {$Z+,R-}
-
- interface
-
- uses SysUtils, Windows, Classes, Controls, Forms,
- DDEMan, DB, Dsgnintf, Messages, BDE;
-
- const
- ctDBase = 2;
- ctExcel = 3;
- ctParadox = 4;
- ctAscii = 5;
- ctSqlServer = 6;
- ctOracle = 7;
- ctDB2 = 8;
- ctNetSQL = 9;
- ctSybase = 10;
- ctBtrieve = 11;
- ctGupta = 12;
- ctIngres = 13;
- ctWatcom = 14;
- ctOcelot = 15;
- ctTeraData = 16;
- ctDB2Gupta = 17;
- ctAS400 = 18;
- ctUnify = 19;
- ctQry = 20;
- ctMinNative = 2;
- ctMaxNative = 20;
- ctODBCDBase = 40;
- ctODBCExcel = 41;
- ctODBCParadox = 42;
- ctODBCSqlServer = 43;
- ctODBCOracle = 44;
- ctODBCDB2 = 45;
- ctODBCNetSql = 46;
- ctODBCSybase = 47;
- ctODBCBtrieve = 48;
- ctODBCGupta = 49;
- ctODBCIngres = 50;
- ctODBCDB2Gupta = 51;
- ctODBCTeraData = 52;
- ctODBCAS400 = 53;
- ctODBCDWatcom = 54;
- ctODBCDefault = 55;
- ctODBCUnify = 56;
- ctMinODBC = 40;
- ctMaxODBC = 56;
- ctIDAPIStandard = 60;
- ctIDAPIParadox = 61;
- ctIDAPIDBase = 62;
- ctIDAPIAscii = 63;
- ctIDAPIOracle = 64;
- ctIDAPISybase = 65;
- ctIDAPINovSql = 66;
- ctIDAPIInterbase = 67;
- ctIDAPIIBMEE = 68;
- ctIDAPIDB2 = 69;
- ctIDAPIInformix = 70;
- ctMinIDAPI = 60;
- ctMaxIDAPI = 70;
-
- type
- EReportError = class(Exception);
- TReportManager = class;
- TLaunchType = (ltDefault, ltRunTime, ltDesignTime);
-
- TReport = class(TComponent)
- private
- FOwner: TReportManager;
- FReportName: string;
- FReportDir: string;
- FNumCopies: Word;
- FStartPage: Word;
- FEndPage: Word;
- FMaxRecords: Word;
- FRunTime: Boolean;
- FStartedApp: Boolean;
- FAutoUnload: Boolean;
- FInitialValues: TStrings;
- FLoaded: Boolean;
- FVersionMajor: Integer;
- FVersionMinor: Integer;
- FReportHandle: HWND;
- FPreview: Boolean;
- FLaunchType: TLaunchType;
- function GetBusy: Boolean;
- function GetInitialValues: TStrings;
- function GetReportHandle: HWND;
- procedure RunApp;
- function StartApplication: Boolean;
- function ReportActive: Boolean;
- function RunReport: Integer;
- procedure SetInitialValues(Value: TStrings);
- function UseRunTime: Boolean;
- protected
- procedure Notification(AComponent: TComponent;
- Operation: TOperation); override;
- public
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
- function CloseApplication(ShowDialogs: Boolean): Integer;
- function CloseReport(ShowDialogs: Boolean): Integer;
- function Connect(ServerType: Word; const ServerName,
- UserName, Password, DatabaseName: string): Integer;
- function Print: Integer;
- function RecalcReport: Integer;
- function Run: Integer;
- function RunMacro(const Macro: string): Integer;
- function SetVariable(const Name, Value: string): Integer;
- function SetVariableLines(const Name: string; Value: TStrings): Integer;
- property ReportHandle: HWND read FReportHandle;
- property Busy: Boolean read GetBusy;
- property VersionMajor: Integer read FVersionMajor;
- property VersionMinor: Integer read FVersionMinor;
- published
- property ReportName: string read FReportName write FReportName;
- property ReportDir: string read FReportDir write FReportDir;
- property PrintCopies: Word read FNumCopies write FNumCopies default 1;
- property StartPage: Word read FStartPage write FStartPage default 1;
- property EndPage: Word read FEndPage write FEndPage default 9999;
- property MaxRecords: Word read FMaxRecords write FMaxRecords default 0;
- property AutoUnload: Boolean read FAutoUnload write FAutoUnload default False;
- property InitialValues: TStrings read GetInitialValues write SetInitialValues;
- property Preview: Boolean read FPreview write FPreview default False;
- property LaunchType: TLaunchType read FLaunchType write FLaunchType default ltDefault;
- end;
-
- { TReportManager }
-
- TCallType = (ctNone, ctDesignId, ctExecuteSQL, ctEndSQL,
- ctGetError, ctGetTableList, ctGetColumnList, ctGetNext, ctGetMemo);
-
- PCallInfo = ^TCallInfo;
- TCallInfo = record
- ProcessId: THandle;
- CallType: TCallType;
- ErrorCode: Bool;
- Data: record end;
- end;
-
- PRSDateTime= ^TRSDateTime;
- TRSDateTime = record
- Year: Word;
- Month: Word;
- Day: Word;
- Hour: Word;
- Min: Word;
- Sec: Word;
- MSec: Word;
- end;
-
- PDataElement = ^TDataElement;
- TDataElement = packed record
- FieldType: Integer;
- ColumnName: array[0..DBIMAXNAMELEN] of char;
- FieldLength: Word;
- Null: Bool;
- Data: record end;
- end;
-
- PExecInfo = ^TExecInfo;
- TExecInfo = record
- DataSet: TDataSet;
- MoreRecords: Bool;
- NumCols: Word;
- end;
-
- PStartExecInfo = ^TStartExecInfo;
- TStartExecInfo = record
- StmtIndex: Integer;
- StmtName: array[0..19] of char;
- MemoName: array[0..19] of char;
- TableName: array[0..63] of char;
- end;
-
- PMemoStruct = ^TMemoStruct;
- TMemoStruct = record
- DataSet: TDataSet;
- Index: Integer;
- ColumnName: array[0..DBIMAXNAMELEN] of char;
- Pos: Integer;
- end;
-
- PSQLStruct = ^TSQLStruct;
- TSQLStruct = record
- DataSet: TDataSet;
- Index: Integer;
- end;
-
- TReportManager = class(TComponent)
- private
- FReports: TList;
- FDataSets: TList;
- FHandle: HWND;
- FLastError: string;
- FUpdated: Boolean;
- procedure ServerProc(Value: PCallInfo);
- procedure WndProc(var Message: TMessage);
- public
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
- procedure Add(Value: TReport);
- procedure AddDataSet(Root: TComponent);
- procedure Clear;
- function EndSQL(SQLStruct: PSQLStruct): Bool;
- function ExecuteSQL(ExecInfo: PExecInfo;
- StartExecInfo: PStartExecInfo): Bool;
- function GetColumnList(Buffer: PChar): Bool;
- function GetData(DataSet: TDataSet; pStmtMem: PDataElement): Bool;
- function GetDataSet(Index: Integer): TDataSet;
- function GetDataSetByName(Value: string): TDataSet;
- function GetDataSets: TList;
- function GetMemo(MemoStruct: PMemoStruct): Bool;
- function GetNext(SQLStruct: PSQLStruct; var MoreData: Bool): Bool;
- function GetReport(Index: Integer): TReport;
- procedure GetTableList(Buffer: PChar);
- procedure Remove(Value: TReport);
- procedure UpdateDataSets;
- function ValidDataType(Value: TFieldType): Boolean;
- property DataSets: TList read GetDataSets;
- property Reports: TList read FReports;
- property DataSet[Index: Integer]: TDataSet read GetDataSet;
- property Handle: HWND read FHandle;
- property Report[Index: Integer]: TReport read GetReport;
- property Updated: Boolean read FUpdated;
- end;
-
- TReportEditor = class(TComponentEditor)
- private
- procedure Edit; override;
- procedure ExecuteVerb(Index: Integer); override;
- function GetVerb(Index: Integer): string; override;
- function GetVerbCount: Integer; override;
- end;
-
- TReportDirProperty = class(TPropertyEditor)
- public
- function GetValue: string; override;
- procedure SetValue(const Value: string); override;
- function GetAttributes: TPropertyAttributes; override;
- procedure Edit; override;
- end;
-
- TReportNameProperty = class(TPropertyEditor)
- public
- function GetValue: string; override;
- procedure SetValue(const Value: string); override;
- function GetAttributes: TPropertyAttributes; override;
- procedure Edit; override;
- end;
-
- implementation
-
- uses DBConsts, FileCtrl, Dialogs, IniFiles, Registry, LibHelp;
-
- const
- RSAPI = 'rs_api.dll';
- RS_SUCCESS = 0;
- RS_BUSY = 1;
- DesignName = 'ReportSmith';
- RunName = 'RS_RUNTIME';
- TopicName = 'Command';
- ReportClassName: string = 'OwlWindow';
- DesignExeName = 'RptSmith.EXE';
- RunExeName = 'RS_Run.EXE';
- StatementBuffer = $FFFF;
- MemoBuffer = $8000;
-
- type
- TServerProc = function(var Data: Integer): Bool stdcall;
- TStmtStruct = record
- StmtHandle: THandle;
- StmtMem: Pointer;
- MemoHandle: THandle;
- MemoMem: Pointer;
- end;
-
- var
- StartEvent: THandle;
- SyncEvent: THandle;
- SharedMem: Pointer;
- ProcessId: Integer;
- ReportManager: TReportManager;
- StmtHandles: array[0..9] of TStmtStruct;
- DriverHandle: THandle;
- APIDriverHandle: THandle;
- InitObjects: function(var StartEvent: THandle; var SyncEvent: THandle;
- var SharedMem: Pointer; ThreadFunc: TThreadStartRoutine):Bool stdcall;
- GetThread: function: THandle stdcall;
- RS_PrintReport: function(StartingPage, EndingPage: Integer; Device, Port, Driver: PChar;
- Copies: Integer): Integer; stdcall;
- RS_SetRepVar: function(Name, Value: PChar): Integer; stdcall;
- RS_Recalc: function: Integer; stdcall;
- RS_CloseReport: function(Close: Integer): Integer; stdcall;
- RS_CloseRS: function(Close: Integer): Integer; stdcall;
- RS_SetRecordLimit: function(Limit: Integer): Integer; stdcall;
- RS_LoadReport: function(FileName, Arguments: PChar; DraftMode,
- RunReport: Bool): Integer; stdcall;
- RS_ByteVersion: function(var Major, Minor: Integer): Word; stdcall;
- RS_Connect: function(ServerType: Integer; const Server, UserId, Password,
- Database: PChar): Integer; stdcall;
- RS_IsBusy: function: Bool; stdcall;
- RS_RunMacro: function(Macro: PChar): Integer; stdcall;
- RS_IsReportSmithPresent: function: Bool; stdcall;
- RS_Initiate: function(RunTime: Bool): Integer; stdcall;
- RS_RegisterCallBack: function(Value: Pointer): Integer; stdcall;
-
- function AsyncCallback: Boolean;
- var
- Msg: TMsg;
- begin
- if PeekMessage(Msg, 0, 0, 0, PM_NOREMOVE) then
- begin
- with Application do
- begin
- HandleMessage;
- Result := Terminated;
- end;
- end else
- Result := False
- end;
-
- function GetRootDir(RunTime: Boolean): string;
- var
- Key: string;
- Value: string;
- begin
- Key := LoadStr(SRptKey);
- if RunTime then
- Value := LoadStr(SRptRunTimeValue) else
- Value := LoadStr(SRptDesignTimeValue);
- with TRegistry.Create do
- try
- RootKey := HKEY_LOCAL_MACHINE;
- OpenKey(Key, True);
- Result := ReadString(Value);
- finally
- Free;
- end;
- end;
-
- function APIDriverLoaded: Boolean;
- begin
- Result := APIDriverHandle >= HINSTANCE_ERROR;
- end;
-
- function InitAPIDriver: Boolean;
- var
- OldError: Word;
- Path: string;
- begin
- OldError := SetErrorMode(SEM_NOOPENFILEERRORBOX);
- try
- Path := GetRootDir(False);
- if Path = '' then
- Path := GetRootDir(True);
- if (Path <> '') and (Path[Length(Path)] <> '\') then
- Path := Path + '\';
- Path := Path + RSAPI;
- APIDriverHandle := LoadLibrary(PChar(Path));
- if APIDriverLoaded then
- begin
- @RS_PrintReport := GetProcAddress(APIDriverHandle, 'RS_PrintReport');
- @RS_SetRepVar := GetProcAddress(APIDriverHandle, 'RS_SetRepVar');
- @RS_Recalc := GetProcAddress(APIDriverHandle, 'RS_Recalc');
- @RS_CloseReport := GetProcAddress(APIDriverHandle, 'RS_CloseReport');
- @RS_CloseRS := GetProcAddress(APIDriverHandle, 'RS_CloseRS');
- @RS_SetRecordLimit := GetProcAddress(APIDriverHandle, 'RS_SetRecordLimit');
- @RS_LoadReport := GetProcAddress(APIDriverHandle, 'RS_LoadReport');
- @RS_ByteVersion := GetProcAddress(APIDriverHandle, 'RS_ByteVersion');
- @RS_Connect := GetProcAddress(APIDriverHandle, 'RS_Connect');
- @RS_IsBusy := GetProcAddress(APIDriverHandle, 'RS_IsAPIBusy');
- @RS_RunMacro := GetProcAddress(APIDriverHandle, 'RS_RunMacroCode');
- @RS_IsReportSmithPresent := GetProcAddress(APIDriverHandle, 'RS_IsReportSmithPresent');
- @RS_Initiate := GetProcAddress(APIDriverHandle, 'RS_InitiateAPI');
- @RS_RegisterCallBack := GetProcAddress(APIDriverHandle, 'RS_RegisterWaitLoopCallback');
- end
- else APIDriverHandle := 1;
- finally
- SetErrorMode(OldError);
- end;
- Result := APIDriverLoaded;
- end;
-
- function DriverLoaded: Boolean;
- begin
- Result := DriverHandle >= HINSTANCE_ERROR;
- end;
-
- function InitDriver: Boolean;
- const
- RSDriverName = 'RS_DELPH.DLL';
- var
- OldError: Word;
- Path: string;
- begin
- OldError := SetErrorMode(SEM_NOOPENFILEERRORBOX);
- try
- Path := GetRootDir(False);
- if Path = '' then
- Path := GetRootDir(True);
- if (Path <> '') and (Path[Length(Path)] <> '\') then
- Path := Path + '\';
- Path := Path + RSDriverName;
- DriverHandle := LoadLibrary(PChar(Path));
- if DriverLoaded then
- begin
- @InitObjects := GetProcAddress(DriverHandle, 'InitObjects');
- @GetThread := GetProcAddress(DriverHandle, 'GetThread');
- end
- else DriverHandle := 1;
- finally
- SetErrorMode(OldError);
- end;
- Result := DriverLoaded;
- end;
-
- procedure RaiseError(const Message: string);
- begin
- raise EReportError.Create(Message);
- end;
-
- procedure GetDecodedDate(Date: TDateTime; var Value: TRSDateTime);
- begin
- FillChar(Value, 0, SizeOf(TRSDateTime));
- with Value do
- DecodeDate(Date, Year, Month, Day);
- end;
-
- procedure GetDecodedTime(Time: TDateTime; var Value: TRSDateTime);
- begin
- FillChar(Value, 0, SizeOf(TRSDateTime));
- with Value do
- DecodeTime(Time, Hour, Min, Sec, MSec);
- end;
-
- procedure GetDecodedDateTime(DateTime: TDateTime; var Value: TRSDateTime);
- begin
- with Value do
- begin
- DecodeDate(DateTime, Year, Month, Day);
- DecodeTime(DateTime, Hour, Min, Sec, MSec);
- end;
- end;
-
- procedure CleanUpStmt(Value: TStmtStruct);
- begin
- with Value do
- begin
- if StmtMem <> nil then UnmapViewOfFile(StmtMem);
- StmtMem := nil;
- CloseHandle(StmtHandle);
- StmtHandle := 0;
- if MemoMem <> nil then UnmapViewOfFile(MemoMem);
- MemoMem := nil;
- CloseHandle(MemoHandle);
- MemoHandle := 0;
- end;
- end;
-
- { TReport }
-
- constructor TReport.Create(AOwner: TComponent);
- begin
- inherited Create(AOwner);
- ReportManager.Add(Self);
- PrintCopies := 1;
- StartPage := 1;
- EndPage := 9999;
- MaxRecords := 0;
- FInitialValues := TStringList.Create;
- LaunchType := ltDefault;
- end;
-
- destructor TReport.Destroy;
- begin
- ReportManager.Remove(Self);
- if FRunTime and FStartedApp then CloseApplication(True);
- FInitialValues.Free;
- inherited Destroy;
- end;
-
- procedure TReport.SetInitialValues(Value: TStrings);
- begin
- FInitialValues.Assign(Value);
- end;
-
- function TReport.GetInitialValues: TStrings;
- begin
- Result := FInitialValues;
- end;
-
- function TReport.SetVariable(const Name, Value: string): Integer;
- begin
- if not Busy then
- begin
- Result := RS_SetRepVar(PChar(Name), PChar(Value));
- end else
- Result := RS_BUSY;
- end;
-
- function TReport.SetVariableLines(const Name: string; Value: TStrings): Integer;
- var
- Buffer, StrEnd: PChar;
- BufLen: Word;
- I, L, Count: Integer;
- Temp: array[0..255] of Char;
- S: string;
- begin
- if not Busy then
- begin
- BufLen := 3;
- for I := 0 to Value.Count - 1 do
- begin
- L := Length(Value[I]) + 2;
- if L > 65520 - BufLen then Break;
- Inc(BufLen, L);
- end;
- Buffer := AllocMem(BufLen);
- try
- StrEnd := StrECopy(Buffer, '"');
- Count := Value.Count - 1;
- for I := 0 to Count do
- begin
- StrCopy(Temp, PChar(Value[I]));
- StrEnd := StrECopy(StrEnd, Temp);
- if I <> Count then StrEnd := StrECopy(StrEnd, ' ');
- end;
- Buffer[StrLen(Buffer)] := '"';
- S := Buffer;
- Result := RS_SetRepVar(PChar(S), nil);
- finally
- FreeMem(Buffer, BufLen);
- end;
- end else
- Result := RS_BUSY;
- end;
-
- function TReport.RecalcReport: Integer;
- begin
- if not Busy then
- Result := RS_Recalc else
- Result := RS_BUSY;
- end;
-
- function TReport.ReportActive: Boolean;
- begin
- Result := (ReportHandle <> 0) and (@RS_IsReportSmithPresent <> nil) and
- RS_IsReportSmithPresent;
- end;
-
- function TReport.UseRunTime: Boolean;
- begin
- Result := (LaunchType = ltRunTime) or
- ((LaunchType = ltDefault) and not (csDesigning in ComponentState));
- end;
-
- function TReport.Print: Integer;
- begin
- if not Busy then
- Result := RS_PrintReport(StartPage, EndPage, nil, nil, nil, PrintCopies) else
- Result := RS_BUSY;
- end;
-
- function TReport.StartApplication: Boolean;
- var
- ExeName: string;
- ExePath: string;
- StartupInfo: TStartupInfo;
- ProcessInfo: TProcessInformation;
- begin
- ExePath := GetRootDir(FRunTime);
- if FRunTime then
- ExeName := RunExeName else
- ExeName := DesignExeName;
- if (ExePath <> '') and (ExePath[Length(ExePath)] <> '\') then
- ExePath := ExePath + '\';
- ExeName := ExePath + ExeName;
- FillChar(StartupInfo, SizeOf(TStartupInfo), 0);
- with StartupInfo do
- begin
- cb := SizeOf(TStartupInfo);
- dwFlags := STARTF_USESHOWWINDOW or STARTF_FORCEONFEEDBACK;
- if not FRunTime or Preview then wShowWindow := SW_SHOWNORMAL
- else wShowWindow := SW_SHOWMINNOACTIVE;
- end;
- Result := CreateProcess(PChar(ExeName), nil, nil, nil, False,
- NORMAL_PRIORITY_CLASS, nil, nil, StartupInfo, ProcessInfo);
- if Result then
- with ProcessInfo do
- begin
- WaitForInputIdle(hProcess, INFINITE);
- CloseHandle(hThread);
- CloseHandle(hProcess);
- FReportHandle := GetReportHandle;
- end;
- FStartedApp := Result;
- end;
-
- function TReport.CloseReport(ShowDialogs: Boolean): Integer;
- begin
- if not RS_IsBusy then
- begin
- if ReportActive then
- Result := RS_CloseReport(Ord(ShowDialogs))
- else Result := RS_SUCCESS;
- end else
- Result := RS_BUSY;
- end;
-
- function TReport.Connect(ServerType: Word; const ServerName,
- UserName, Password, DatabaseName: string): Integer;
- begin
- if not Busy then
- begin
- if ((ServerType >= ctMinNative) and (ServerType <= ctMaxNative)) or
- ((ServerType >= ctMinODBC) and (ServerType <= ctMaxODBC)) or
- ((ServerType >= ctMinIDAPI) and (ServerType <= ctMaxIDAPI)) then
- Result := RS_Connect(ServerType, PChar(ServerName), PChar(UserName),
- PChar(Password), PChar(DatabaseName))
- else RaiseError(LoadStr(SInvalidServer));
- end else
- Result := RS_BUSY;
- end;
-
- function TReport.CloseApplication(ShowDialogs: Boolean): Integer;
- begin
- if not RS_IsBusy then
- begin
- if ReportActive then
- begin
- Result := RS_CloseRS(Ord(ShowDialogs));
- if Result = RS_SUCCESS then
- begin
- FStartedApp := False;
- FReportHandle := 0;
- end;
- end
- else Result := RS_SUCCESS;
- end else
- Result := RS_BUSY;
- end;
-
- function TReport.GetReportHandle: HWND;
- var
- S: string;
- begin
- if FRunTime then S := RunName
- else S := DesignName;
- Result := FindWindow(PChar(ReportClassName), PChar(S));
- end;
-
- function TReport.GetBusy: Boolean;
- begin
- if not ReportActive then RunApp;
- Result := RS_IsBusy;
- end;
-
- function TReport.RunMacro(const Macro: string): Integer;
- begin
- if not Busy then
- begin
- if Macro <> '' then
- Result := RS_RunMacro(PChar(Macro)) else
- Result := RS_SUCCESS;
- end else
- Result := RS_BUSY;
- end;
-
- procedure TReport.RunApp;
- var
- AppName: string;
- begin
- if not APIDriverLoaded then
- raise Exception.Create(FmtLoadStr(SUnableToLoadAPIDLL, [RSAPI]));
- if not ReportActive and not RS_IsBusy then
- begin
- FRunTime := UseRunTime;
- FReportHandle := GetReportHandle;
- if ReportHandle = 0 then
- if not StartApplication then
- begin
- if FRunTime then raise Exception.Create(LoadStr(SRunLoadFailed))
- else raise Exception.Create(LoadStr(SDesignLoadFailed));
- end;
- RS_Initiate(FRunTime);
- if FRunTime then AppName := RunName
- else AppName := DesignName;
- if RS_ByteVersion(FVersionMajor, FVersionMinor) <> RS_SUCCESS then
- raise Exception.CreateFmt(LoadStr(SCannotGetVersionInfo), [AppName]);
- if VersionMajor = 0 then
- begin
- if FStartedApp then CloseApplication(False);
- raise Exception.Create(LoadStr(SIncorrectVersion));
- end;
- end;
- end;
-
- function TReport.Run: Integer;
- begin
- Result := RunReport;
- if FRunTime and FStartedApp and
- AutoUnload and not Preview then CloseApplication(True);
- end;
-
- function TReport.RunReport: Integer;
- var
- Path, FileName: string;
- Temp: array[0..255] of Char;
- Buffer, StrEnd: PChar;
- BufLen: Word;
- I, L, Count: Integer;
- S: string;
- begin
- if not Busy then
- begin
- Result := RS_SetRecordLimit(MaxRecords);
- if Result = RS_SUCCESS then
- begin
- Path := ReportDir;
- if (Path <> EmptyStr) and (Path[Length(Path)] <> '\') then
- Path := Path + '\';
- FileName := ReportName;
- if (FileName <> '') and (Pos('.', FileName) = 0) then
- FileName := FileName + '.rpt';
- if FileName <> '' then
- begin
- FileName := Path + FileName;
- if not FileExists(FileName) then
- raise Exception.Create(FmtLoadStr(SNoFile, [FileName]));
- BufLen := 3;
- for I := 0 to FInitialValues.Count - 1 do
- begin
- L := Length(FInitialValues[I]) + 2;
- if L > 65520 - BufLen then Break;
- Inc(BufLen, L);
- end;
- Buffer := AllocMem(BufLen);
- try
- StrEnd := StrECopy(Buffer, '"');
- Count := FInitialValues.Count - 1;
- for I := 0 to Count do
- begin
- StrCopy(Temp, PChar(FInitialValues[I]));
- StrEnd := StrECopy(StrEnd, Temp);
- if (I <> Count) and (Pos('>', FInitialValues[I]) > 0) then
- StrEnd := StrECopy(StrEnd, ', ');
- end;
- Buffer[StrLen(Buffer)] := '"';
- S := Buffer;
- FmtStr(S, '%s,"#%x"', [S, ProcessId]);
- Result := RS_LoadReport(PChar(FileName), PChar(S), False, True);
- finally
- FreeMem(Buffer, BufLen);
- end;
- if (Result = RS_SUCCESS) and FRunTime and not Preview then
- Result := Print;
- end;
- end;
- end else
- Result := RS_BUSY;
- end;
-
- { TReportEditor }
-
- procedure TReportEditor.Edit;
- begin
- TReport(Component).Run;
- end;
-
- procedure TReportEditor.ExecuteVerb(Index: Integer);
- begin
- if Index = 0 then Edit;
- end;
-
- function TReportEditor.GetVerb(Index: Integer): string;
- begin
- Result := LoadStr(SReportVerb);
- end;
-
- function TReportEditor.GetVerbCount: Integer;
- begin
- Result := 1;
- end;
-
- { TReportDirProperty }
-
- function TReportDirProperty.GetValue: string;
- begin
- Result := (GetComponent(0) as TReport).ReportDir;
- end;
-
- procedure TReportDirProperty.SetValue(const Value: string);
- begin
- (GetComponent(0) as TReport).ReportDir := Value;
- Modified;
- end;
-
- function TReportDirProperty.GetAttributes: TPropertyAttributes;
- begin
- Result := [paDialog, paMultiSelect];
- end;
-
- procedure TReportDirProperty.Edit;
- var
- FilePath: TFileName;
- begin
- FilePath := '';
- if SelectDirectory(FilePath, [], hcDSelectReportDir) then
- begin
- if FilePath[Length(FilePath)] <> '\' then FilePath := FilePath + '\';
- SetValue(FilePath);
- end;
- end;
-
- { TReportNameProperty }
-
- function TReportNameProperty.GetValue: string;
- begin
- Result := (GetComponent(0) as TReport).ReportName;
- end;
-
- procedure TReportNameProperty.SetValue(const Value: string);
- begin
- (GetComponent(0) as TReport).ReportName := Value;
- Modified;
- end;
-
- function TReportNameProperty.GetAttributes: TPropertyAttributes;
- begin
- Result := [paDialog, paMultiSelect];
- end;
-
- procedure TReportNameProperty.Edit;
- var
- Dialog: TOpenDialog;
- FilePath: string;
- begin
- Dialog := TOpenDialog.Create(nil);
- try
- with Dialog do
- begin
- DefaultExt := 'rpt';
- Filter := LoadStr(SReportFilter);
- if Execute then
- with GetComponent(0) as TReport do
- begin
- FileName := FileName;
- FilePath := ExtractFilePath(FileName);
- ReportDir := FilePath;
- ReportName := ExtractFileName(FileName);
- Modified;
- end;
- end;
- finally
- Dialog.Free;
- end;
- end;
-
- procedure TReport.Notification(AComponent: TComponent;
- Operation: TOperation);
- begin
- inherited Notification(AComponent, Operation);
- if AComponent is TDataSet then ReportManager.FUpdated := False;
- end;
-
- { TReportManager }
-
- constructor TReportManager.Create(AOwner: TComponent);
- begin
- inherited Create(AOwner);
- FReports := TList.Create;
- FDataSets := TList.Create;
- FHandle := AllocateHWnd(WndProc);
- end;
-
- destructor TReportManager.Destroy;
- begin
- Clear;
- Reports.Free;
- FDataSets.Free;
- DeallocateHWnd(FHandle);
- inherited Destroy;
- end;
-
- procedure TReportManager.Clear;
- begin
- while Reports.Count > 0 do TReport(Reports.Last).Free;
- end;
-
- procedure TReportManager.WndProc(var Message: TMessage);
- begin
- if Message.Msg = $7F00 then
- begin
- ServerProc(PCallInfo(SharedMem));
- end
- else with Message do
- Result := DefWindowProc(FHandle, Msg, WParam, LParam);
- end;
-
- procedure TReportManager.ServerProc(Value: PCallInfo);
- var
- pData: Pointer;
- begin
- pData := @Value^.Data;
- with Value^ do
- begin
- ErrorCode := False;
- case CallType of
- ctExecuteSQL: ErrorCode := not ExecuteSQL(PExecInfo(pData),
- PStartExecInfo(pData));
- ctEndSQL: ErrorCode := not EndSQL(PSQLStruct(pData));
- ctGetTableList: GetTableList(PChar(pData));
- ctGetColumnList: ErrorCode := not GetColumnList(PChar(pData));
- ctGetNext: ErrorCode := not GetNext(PSQLStruct(pData), Bool(pData^));
- ctGetMemo: ErrorCode := not GetMemo(PMemoStruct(pData));
- ctGetError: StrCopy(PChar(pData), PChar(FLastError));
- end;
- end;
- end;
-
- procedure TReportManager.Add(Value: TReport);
- begin
- Reports.Add(Value);
- Value.FOwner := Self;
- FUpdated := False;
- end;
-
- procedure TReportManager.Remove(Value: TReport);
- begin
- with Reports do Delete(IndexOf(Value));
- Value.FOwner := nil;
- FUpdated := False;
- end;
-
- procedure TReportManager.AddDataSet(Root: TComponent);
- var
- I: Integer;
- begin
- if Root is TDataSet then FDataSets.Add(Root);
- for I := 0 to Root.ComponentCount - 1 do
- AddDataSet(Root.Components[I]);
- end;
-
- function TReportManager.GetDataSet(Index: Integer): TDataSet;
- begin
- Result := DataSets[Index];
- end;
-
- function TReportManager.GetReport(Index: Integer): TReport;
- begin
- Result := FReports[Index];
- end;
-
- procedure TReportManager.UpdateDataSets;
- var
- I, J: Integer;
- Matched: Boolean;
- begin
- FDataSets.Clear;
- for I := 0 to Reports.Count - 1 do
- begin
- Matched := False;
- for J := I + 1 to Reports.Count - 1 do
- if Report[I].Owner = Report[J].Owner then
- begin
- Matched := True;
- Break;
- end;
- if not Matched then AddDataSet(Report[I].Owner);
- end;
- FUpdated := True;
- end;
-
- function TReportManager.ExecuteSQL(ExecInfo: PExecInfo;
- StartExecInfo: PStartExecInfo): Bool;
- var
- I, Size: Integer;
- S: string;
- DataElement: PDataElement;
- pStmtMem, pMemoMem: Pointer;
-
- function GetDataSize(Value: TField): Integer;
- begin
- case Value.DataType of
- ftString: Result := Value.Size + 1;
- ftSmallint, ftInteger, ftWord, ftBoolean, ftAutoInc:
- Result := SizeOf(Integer);
- ftFloat, ftCurrency, ftBCD:
- Result := SizeOf(Double);
- ftDate, ftTime, ftDateTime:
- Result := SizeOf(TRSDateTime);
- else Result := 0;
- end;
- end;
-
- begin
- Result := False;
- S := StartExecInfo^.TableName;
- with StmtHandles[StartExecInfo^.StmtIndex] do
- begin
- StmtHandle := OpenFileMapping(FILE_MAP_WRITE, False, StartExecInfo^.StmtName);
- if StmtHandle <> 0 then
- pStmtMem := MapViewOfFile(StmtHandle, FILE_MAP_WRITE, 0, 0, 0) else
- pStmtMem := nil;
- StmtMem := pStmtMem;
- MemoHandle := OpenFileMapping(FILE_MAP_WRITE, False, StartExecInfo^.MemoName);
- if MemoHandle <> 0 then
- pMemoMem := MapViewOfFile(MemoHandle, FILE_MAP_WRITE, 0, 0, 0) else
- pMemoMem := nil;
- MemoMem := pMemoMem;
- end;
- if (StmtHandles[StartExecInfo^.StmtIndex].StmtHandle <> 0) and
- (StmtHandles[StartExecInfo^.StmtIndex].MemoHandle <> 0) then
- with ExecInfo^ do
- begin
- DataSet := GetDataSetByName(S);
- if DataSet <> nil then
- try
- if DataSet.Active then DataSet.First
- else DataSet.Open;
- MoreRecords := not DataSet.EOF;
- NumCols := 0;
- DataElement := PDataElement(pStmtMem);
- Size := 0;
- for I := 0 to DataSet.FieldCount - 1 do
- Inc(Size, GetDataSize(DataSet.Fields[I]) + SizeOf(TDataElement));
- if Size < StatementBuffer then
- begin
- for I := 0 to DataSet.FieldCount - 1 do
- with DataSet.Fields[I], DataElement^ do
- if ValidDataType(DataType) then
- begin
- StrLCopy(ColumnName, PChar(FieldName), SizeOf(ColumnName) - 1);
- FieldType := Ord(DataType);
- FieldLength := GetDataSize(DataSet.Fields[I]);
- Inc(Integer(DataElement), SizeOf(TDataElement) + FieldLength);
- Inc(NumCols);
- end;
- Result := GetData(DataSet, pStmtMem);
- end
- else FLastError := LoadStr(SRptBindBuffer);
- except
- on E: Exception do
- FLastError := E.Message;
- end
- else FLastError := LoadStr(SRptDataSetNotAvailable);
- end
- else FLastError := LoadStr(SRptSharedMemoryError);
- end;
-
- function TReportManager.GetData(DataSet: TDataSet; pStmtMem: PDataElement): Bool;
- var
- I: Integer;
- DataValue: Pointer;
- DataElement: PDataElement;
- begin
- Result := True;
- try
- DataElement := pStmtMem;
- for I := 0 to DataSet.FieldCount - 1 do
- with DataSet.Fields[I], DataElement^ do
- if ValidDataType(DataType) then
- begin
- DataValue := Pointer(@DataElement^.Data);
- Null := IsNull;
- if not Null then
- begin
- case DataType of
- ftString, ftVarBytes:
- StrCopy(PChar(DataValue), PChar(AsString));
- ftBoolean: Bool(DataValue^) := AsBoolean;
- ftSmallint, ftInteger, ftWord, ftAutoInc:
- Integer(DataValue^) := AsInteger;
- ftFloat, ftCurrency, ftBCD:
- Double(DataValue^) := AsFloat;
- ftDate, ftTime, ftDateTime:
- GetDecodedDateTime(AsDateTime, TRSDateTime(DataValue^));
- end;
- end;
- Inc(Integer(DataElement), SizeOf(TDataElement) + FieldLength);
- end;
- except
- on E: Exception do
- begin
- FLastError := E.Message;
- Result := False;
- end;
- end;
- end;
-
- function TReportManager.GetNext(SQLStruct: PSQLStruct; var MoreData: Bool): Bool;
- var
- pStmtMem: Pointer;
- DataSet: TDataSet;
- begin
- Result := False;
- pStmtMem := StmtHandles[SQLStruct^.Index].StmtMem;
- DataSet := SQLStruct^.DataSet;
- if DataSet <> nil then
- try
- DataSet.Next;
- Result := GetData(DataSet, pStmtMem);
- MoreData := not DataSet.EOF;
- except
- on E: Exception do
- FLastError := E.Message;
- end
- else FLastError := LoadStr(SRptNoDataSetAvailable);
- end;
-
- function TReportManager.GetMemo(MemoStruct: PMemoStruct): Bool;
- var
- MemoMem: Pointer;
- DataSet: TDataSet;
- S: string;
- begin
- Result := False;
- MemoMem := StmtHandles[MemoStruct^.Index].MemoMem;
- PChar(MemoMem)^ := #0;
- DataSet := MemoStruct^.DataSet;
- if DataSet <> nil then
- try
- S := DataSet.FieldByName(MemoStruct^.ColumnName).AsString;
- if Length(S) >= MemoStruct^.Pos then
- StrLCopy(MemoMem, @S[MemoStruct^.Pos + 1], MemoBuffer - 1);
- Result := True;
- except
- on E: Exception do
- FLastError := E.Message;
- end
- else FLastError := LoadStr(SRptNoDataSetAvailable);
- end;
-
- function TReportManager.ValidDataType(Value: TFieldType): Boolean;
- begin
- Result := not (Value in [ftUnknown, ftBytes, ftVarBytes,
- ftGraphic, ftFmtMemo, ftParadoxOle, ftDBaseOle, ftTypedBinary])
- end;
-
- function TReportManager.EndSQL(SQLStruct: PSQLStruct): Bool;
- begin
- Result := True;
- if SQLStruct^.DataSet <> nil then
- try
- SQLStruct^.DataSet.Close;
- CleanUpStmt(StmtHandles[SQLStruct^.Index]);
- except
- on E: Exception do
- begin
- FLastError := E.Message;
- Result := False;
- end;
- end
- end;
-
- function TReportManager.GetDataSets: TList;
- begin
- if not Updated then UpdateDataSets;
- Result := FDataSets;
- end;
-
- procedure TReportManager.GetTableList(Buffer: PChar);
- var
- S: string;
- I: Integer;
- begin
- Buffer^ := #0;
- for I := 0 to DataSets.Count - 1 do
- begin
- S := DataSet[I].Name;
- StrCopy(Buffer, PChar(S));
- Inc(Integer(Buffer), Length(S) + 1);
- end;
- Buffer^ := #0;
- end;
-
- function TReportManager.GetDataSetByName(Value: string): TDataSet;
- var
- I: Integer;
- begin
- Result := nil;
- for I := 0 to DataSets.Count - 1 do
- if DataSet[I].Name = Value then
- begin
- Result := DataSet[I];
- Break;
- end;
- end;
-
- function TReportManager.GetColumnList(Buffer: PChar): Bool;
- var
- S: string;
- DataSet: TDataSet;
-
- procedure GetNamesByField;
- var
- I: Integer;
- begin
- for I := 0 to DataSet.FieldCount - 1 do
- if ValidDataType(DataSet.Fields[I].DataType) then
- begin
- S := DataSet.Fields[I].FieldName;
- StrCopy(Buffer, PChar(S));
- Inc(Integer(Buffer), Length(S) + 1);
- end;
- end;
-
- procedure GetNamesByFieldDef;
- var
- I: Integer;
- begin
- for I := 0 to DataSet.FieldDefs.Count - 1 do
- if ValidDataType(DataSet.FieldDefs[I].DataType) then
- begin
- S := DataSet.FieldDefs[I].Name;
- StrCopy(Buffer, PChar(S));
- Inc(Integer(Buffer), Length(S) + 1);
- end;
- end;
-
- begin
- Result := True;
- S := Buffer;
- Buffer^ := #0;
- DataSet := GetDataSetByName(S);
- if DataSet <> nil then
- with DataSet do
- try
- FieldDefs.Update;
- if FieldCount <> 0 then
- GetNamesByField else
- GetNamesByFieldDef;
- except
- on E: Exception do
- begin
- FLastError := E.Message;
- Result := False;
- end;
- end
- else begin
- FLastError := LoadStr(SRptNoDataSetAvailable);
- Result := False;
- end;
- Buffer^ := #0;
- end;
-
- procedure ProcessRequest;
- var
- pData: Pointer;
- CallRec: PCallInfo;
- begin
- CallRec := PCallInfo(SharedMem);
- pData := @CallRec^.Data;
- if (CallRec^.CallType = ctDesignId) and
- (ReportManager.Reports.Count > 0) and
- (csDesigning in ReportManager.Report[0].ComponentState) then
- begin
- CallRec^.ErrorCode := False;
- DWORD(pData^) := ProcessId;
- end
- else if CallRec^.ProcessId = ProcessId then
- SendMessage(ReportManager.Handle, $7F00, 0, 0);
- ResetEvent(StartEvent);
- SetEvent(SyncEvent);
- end;
-
- function WaitForRequest(pData: Pointer): Integer; stdcall;
- begin
- while True do
- begin
- Result := WaitForSingleObject(StartEvent, INFINITE);
- if Result = WAIT_OBJECT_0 then ProcessRequest
- else break;
- end;
- end;
-
- procedure Initialize;
- begin
- ReportManager := TReportManager.Create(nil);
- ProcessId := GetCurrentProcessId;
- if InitDriver then
- InitObjects(StartEvent, SyncEvent, SharedMem, @WaitForRequest);
- if InitAPIDriver then
- RS_RegisterCallBack(@AsyncCallback);
- end;
-
- procedure Finalize;
- var
- Thread: THandle;
- I: Integer;
- begin
- for I := Low(StmtHandles) to High(StmtHandles) do
- CleanUpStmt(StmtHandles[I]);
- if @GetThread <> nil then
- begin
- Thread := GetThread;
- if Thread <> 0 then TerminateThread(Thread, 0);
- end;
- ReportManager.Free;
- if DriverLoaded then FreeLibrary(DriverHandle);
- if APIDriverLoaded then FreeLibrary(APIDriverHandle);
- end;
-
- initialization
- Initialize;
- finalization
- Finalize;
- end.
-