如何从64位客户端应用程序枚举SQL Server?

时间:2014-06-27 10:19:59

标签: sql-server delphi

我正在寻找一种从64位Delphi应用程序在本地网络上枚举MS SQL Server的方法。到目前为止,我在32位上使用的方法工作正常,但包含在32位上不能编译的汇编代码。我似乎无法找到一种从64位客户端枚举服务器的方法。

我无法编译的代码是:

function PtCreateADOObject(const ClassID: TGUID): IUnknown;
var
  Status: HResult;
  FPUControlWord: Word;
begin
  asm
    FNSTCW FPUControlWord
  end;
  Status := CoCreateInstance(
              CLASS_Recordset,
              nil,
              CLSCTX_INPROC_SERVER or CLSCTX_LOCAL_SERVER,
              IUnknown,
              Result);
  asm
    FNCLEX
    FLDCW FPUControlWord
  end;
  OleCheck(Status);
end;

2 个答案:

答案 0 :(得分:8)

导致您遇到麻烦的代码只是存储和恢复浮点控制状态。你可以用跨平台的方式编写它(好吧,支持x86和x64),如下所示:

type
  TFPControlState = {$IFDEF CPUX86}Word{$ENDIF}{$IFDEF CPUX64}UInt32{$ENDIF};

function GetFPControlState: TFPControlState;
begin
  {$IFDEF CPUX86}
  Result := Get8087CW;
  {$ENDIF}
  {$IFDEF CPUX64}
  Result := GetMXCSR;
  {$ENDIF}
end;

procedure SetFPControlState(const Value: TFPControlState);
begin
  {$IFDEF CPUX86}
  Set8087CW(Value);
  {$ENDIF}
  {$IFDEF CPUX64}
  SetMXCSR(Value);
  {$ENDIF}
end;

这抽象出32位代码使用8087单元的16位控制状态,64位代码使用SSE单元的32位控制状态。

现在您的功能变为:

function PtCreateADOObject(const ClassID: TGUID): IUnknown;
var
  Status: HResult;
  FPControlState: TFPControlState;
begin
  FPControlState := GetFPControlState;
  Status := CoCreateInstance(
    CLASS_Recordset,
    nil,
    CLSCTX_INPROC_SERVER or CLSCTX_LOCAL_SERVER,
    IUnknown,
    Result
  );
  SetFPControlState(FPControlState);
  OleCheck(Status);
end;

这种方法有很多好处。最清楚的是:

  1. 您完全避免使用汇编程序代码。
  2. 使用标准RTL单元来操作浮点控制状态。
  3. 您将条件代码限制在低级例程中。高级例程可以保持对该细节的无知,您可以在高级别的不同位置重复使用此方法,而不会使用条件进行污染。

答案 1 :(得分:0)

x64采用的here示例,现在它与x32 / x64系统兼容。

uses
  ActiveX, ComObj, OleDB, DB, ADOInt, ADODB;

function CreateADOObject(const ClassID: TGUID): IUnknown;
var
  Status: HResult;
{$IFDEF CPUX86}
  FPUControlWord: Word;
{$ENDIF CPUX86}
begin

{$IFDEF CPUX86}
  asm
    FNSTCW  FPUControlWord
  end;
{$ENDIF CPUX86}
  Status := CoCreateInstance(ClassID, nil, CLSCTX_INPROC_SERVER or
    CLSCTX_LOCAL_SERVER, IUnknown, Result);
{$IFDEF CPUX86}
  asm
    FNCLEX
    FLDCW FPUControlWord
  end;
{$ENDIF CPUX86}
  if (Status = REGDB_E_CLASSNOTREG) then
    raise Exception.Create('error')
  else
    OleCheck(Status);
end;

procedure ListAvailableSQLServers(Names: TStringList);
var
  RSCon: ADORecordsetConstruction;
  Rowset: IRowset;
  SourcesRowset: ISourcesRowset;
  SourcesRecordset: _Recordset;
  SourcesName, SourcesType: TField;
begin
  SourcesRecordset := CreateADOObject(CLASS_Recordset) as _Recordset;
  RSCon := SourcesRecordset as ADORecordsetConstruction;
   SourcesRowset := CreateComObject(ProgIDToClassID('SQLOLEDB Enumerator')) as ISourcesRowset;
   OleCheck(SourcesRowset.GetSourcesRowset(nil, IRowset, 0, nil, IUnknown(Rowset)));
   RSCon.Rowset := RowSet;
   with TADODataSet.Create(nil) do
   try
     Recordset := SourcesRecordset;
     SourcesName := FieldByName('SOURCES_NAME');
     SourcesType := FieldByName('SOURCES_TYPE');
     Names.BeginUpdate;
     Names.Clear;
     try
        while not EOF do
        begin
          if (SourcesType.AsInteger = DBSOURCETYPE_DATASOURCE) and
             (SourcesName.AsString <> '')
           then
            Names.Add(SourcesName.AsString);
          Next;
        end;
     finally
        Names.EndUpdate;
     end;
  finally
     Free;
  end;
end;

procedure GetServer;
var
  oItems: TStringList;
begin
  oItems:= TStringList.Create;
  try
    ListAvailableSQLServers(oItems);
    // To something with oItems
    ShowMessage(oItems.Text);
  finally
    oItems.Free;
  end;
end;
相关问题