COM+的部署一直是个非常头疼的事情,往往在同一台电脑上开发客户端和应用层端时,运行的好好的,可是一旦把客户端部署到其它电脑上去,麻烦就接踵而来。
今天要讨论的只是其中的一个经常会遇到的麻烦:“拒绝访问”,通常发生这种问题的原因是客户端调用者的身份不符合服务器端的要求,因此需要修改服务器端的验证规则,或者在客户端提交连接时,将服务器端的帐户提供给服务器,而不是想往常那样简单的置为NULL。
{重载连接方法}
unit uJZCom;
interface
uses windows, comobj, activex;
type
pUnShort = ^Word;
pCoAuthIdentity = ^_CoAuthIdentity;
_CoAuthIdentity = record
user: PWideChar;
UserLength: ULONG;
Domain: PWideChar;
DomainLength: ULONG;
password: PWideChar;
PasswordLength: ULONG;
Flags: ULONG;
end;
_CoAuthInfo = record
dwAuthnSvc: DWORD;
dwAuthzSvc: DWORD;
pwszServerPrincName: PWideChar;
dwAuthnLevel: DWORD;
dwImpersonationLevel: DWORD;
pAuthIdentityData: pCoAuthIdentity;
dwCapabilities: DWORD;
end;
Function MySetBlanket(var itf: IUnknown; const vCai: _CoAuthInfo): HRESULT;
function DoConnect(const Class_IID, itf_iid: PIID;
computer, username, psw: WideString): IUnknown;
implementation
uses
SysUtils,
StrUtils;
Function MySetBlanket(var itf: IUnknown; const vCai: _CoAuthInfo): HRESULT;
begin
with vCai do
begin
result := CoSetProxyBlanket(itf, dwAuthnSvc, dwAuthzSvc,
PWideChar(pAuthIdentityData^.Domain), dwAuthnLevel,
dwImpersonationLevel, pAuthIdentityData, dwCapabilities);
end;
end;
function DoConnect(const Class_IID, itf_iid: PIID;
computer, username, psw: WideString): IUnknown;
var
FCai: _CoAuthInfo;
FCid: _CoAuthIdentity;
FSvInfo: COSERVERINFO;
Mqi: MULTI_QI;
Size: DWORD;
LocalMachine: array [0 .. MAX_COMPUTERNAME_LENGTH] of char;
begin
result := nil;
if Length(computer) > 0 then
begin
Size := sizeof(LocalMachine);
if GetComputerName(LocalMachine, Size) and
(UpperCase(computer) <> UpperCase(LocalMachine)) then
// 电脑名称比较时大小写敏感
begin
FillMemory(@FCai, sizeof(FCai), 0);
FillMemory(@FCid, sizeof(FCid), 0);
FillMemory(@FSvInfo, sizeof(FSvInfo), 0);
with FCid do
begin
user := PWideChar(username); // pUnshort(@userName[1]);
UserLength := Length(username);
Domain := PWideChar(computer); // pUnshort(@computer[1]);
DomainLength := Length(computer);
password := PWideChar(psw); // pUnShort(@psw[1]);
PasswordLength := Length(psw);
Flags := 2; // Unicode 字符串
end;
with FCai do
begin
dwAuthnSvc := 10; // RPC_C_AUTHN_WINNT NTML认证服务
dwAuthzSvc := 0; // RPC_C_AUTHZ_NONE
dwAuthnLevel := 0; // RPC_C_AUTHN_LEVEL_DEFAULT 默认级别
dwImpersonationLevel := 3; // 身份模拟
pAuthIdentityData := @FCid;
dwCapabilities := $0800; // 静态跟踪
end;
FSvInfo.pwszName := PWideChar(computer);
FSvInfo.pAuthInfo := @FCai;
with Mqi do
begin
iid := itf_iid;
itf := nil;
hr := 0;
end;
// 以远程用户身份激活并取得接口引用
olecheck(CoCreateInstanceEx(Class_IID^, nil, CLSCTX_REMOTE_SERVER,
@FSvInfo, 1, @Mqi));
olecheck(Mqi.hr);
// 对取得的接口引用,要再次设置其安全属性为远程用户,否则返回的指针将仍然
// 使用本地用户进程的安全属性向远程发起调用,此时的结果就是"拒绝访问"
olecheck(MySetBlanket(Mqi.itf, FCai));
result := Mqi.itf;
end
else // 原文遗漏,造成客户端与服务器端在同一台电脑上时,出现AV错误
olecheck(CoCreateInstance(Class_IID^, nil,
CLSCTX_INPROC_SERVER or CLSCTX_LOCAL_SERVER, itf_iid^,
result));
end
else
olecheck(CoCreateInstance(Class_IID^, nil,
CLSCTX_INPROC_SERVER or CLSCTX_LOCAL_SERVER, itf_iid^,
result));
end;
end.
客户端调用方法:
procedure TForm1.Button1Click(Sender: TObject);
begin
svr := IBizRules(DoConnect(@class_bizrules, @iid_ibizrules, 'WINSEVEn',
'admin', '******'));
ClientDataSet1.Data := svr.GetEmployees;
ClientDataSet1.Open;
end;
2011年7月7日星期四
2011年7月3日星期日
唉,MIDAS
接着前面那篇关于三层的博客 。
那个仓库管理模型:服务器端采用Remote Transactional Datamodule +TClientDataset,客户端采用TSocketConnection连接,所有窗体共享主窗体上面的SocketConnection。当这三层全部安装在同一台电脑上面时,运行良好,但是如果将应用层和表示层分别安装在不同电脑上面时,设计期还可以通过数据绑定控件查看服务器端传来的数据,但是运行的时候,同时只能有一个活动连接,并且更离谱的是,当CDS断开再连接时,便会抛出“拒绝连接”的错误。
我在客户端所在的电脑上,用D7重写了demo程序,依然是共享一个SocketConnection,在三个不同的窗体中,演示了主从关系、Lookup查询以及向服务器提交变动等,一切正常!
那个仓库管理模型:服务器端采用Remote Transactional Datamodule +TClientDataset,客户端采用TSocketConnection连接,所有窗体共享主窗体上面的SocketConnection。当这三层全部安装在同一台电脑上面时,运行良好,但是如果将应用层和表示层分别安装在不同电脑上面时,设计期还可以通过数据绑定控件查看服务器端传来的数据,但是运行的时候,同时只能有一个活动连接,并且更离谱的是,当CDS断开再连接时,便会抛出“拒绝连接”的错误。
我在客户端所在的电脑上,用D7重写了demo程序,依然是共享一个SocketConnection,在三个不同的窗体中,演示了主从关系、Lookup查询以及向服务器提交变动等,一切正常!
2011年7月2日星期六
Arduino离我还有多远
Arduino是一款开源硬件产品,比较适合像我这类小白菜,本来打算入手一个,连东西都放进购物车了的,结帐时候才发现,数字证书已经在几个月之前失效。:(
这个是从网上找来的Arduino Mega 1280的参数
Arduino MEGA 规格参数:
1.微控制器核心:ATmega1280-16AU
2.工作电压:+5V
3.外部输入电压:+7V~+12V(建议)
4.外部输入电压(极值):+6V≤Vin≤+20V
5.数字信号I/O接口:54(其中14个PWM输出接口)
6.模拟信号输入接口:16
7.DC I/O接口电流:40 mA
7.Flash容量:128 KB (其他4K用于bootloader)
8.SRAM静态存储容量:8KB
9.EEPROM存储容量:4KB
10.时钟频率:16MHz
下面是一些值得去的网站:
www.arduino.cc
这个是从网上找来的Arduino Mega 1280的参数
Arduino MEGA 规格参数:
1.微控制器核心:ATmega1280-16AU
2.工作电压:+5V
3.外部输入电压:+7V~+12V(建议)
4.外部输入电压(极值):+6V≤Vin≤+20V
5.数字信号I/O接口:54(其中14个PWM输出接口)
6.模拟信号输入接口:16
7.DC I/O接口电流:40 mA
7.Flash容量:128 KB (其他4K用于bootloader)
8.SRAM静态存储容量:8KB
9.EEPROM存储容量:4KB
10.时钟频率:16MHz
下面是一些值得去的网站:
www.arduino.cc
仓库管理模型中采购计划的计算过程
最近帮人做了一个仓库管理的模型,本来是打算用库存表,在入库/出库操作时,同步更新库存表,考虑模型的数据量不是很大,于是采用动态计算的方式来生成采购计划。
在物料表中,有字段记录物料的最低安全库存/最高安全库存,由于时间的关系,设计时并没有真正考虑最高安全库存,特此说明一下。
数据库为MS SQL Server,在2000/2008上测试通过。
为了简化设计难度,增加脚本的可读性,计算过程分成两步,第一步进行汇总:
SELECT p.ItemRef, p.ItemName, p.Price, p.LowerQTY, p.UpperQTY, p.SecurityQTY, p.UOMs_Ref, ISNULL(o.OutQTY, 0) AS OutQTY, ISNULL(i.InQTY, 0) AS InQTY,
ISNULL(i.InQTY, 0) - ISNULL(o.OutQTY, 0) AS BalanceQTY, dbo.UOMs.UOMName
FROM dbo.Products AS p INNER JOIN
dbo.UOMs ON p.UOMs_Ref = dbo.UOMs.Ref LEFT OUTER JOIN
dbo.vInQTY AS i ON p.ItemRef = i.ItemRef LEFT OUTER JOIN
dbo.vOutQTY AS o ON p.ItemRef = o.ItemRef
第二步根据安全库存设置计算采购计划:
SELECT dbo.vStockSum.ItemRef, dbo.vStockSum.ItemName, dbo.vStockSum.LowerQTY, dbo.vStockSum.UpperQTY, dbo.vStockSum.SecurityQTY, dbo.vStockSum.BalanceQTY,
dbo.vStockSum.BalanceQTY - dbo.vStockSum.LowerQTY AS L, ABS(dbo.vStockSum.BalanceQTY - dbo.vStockSum.LowerQTY) AS PurchasePlan,
dbo.vStockSum.UOMs_Ref, dbo.UOMs.UOMName
FROM dbo.vStockSum INNER JOIN
dbo.UOMs ON dbo.vStockSum.UOMs_Ref = dbo.UOMs.Ref
WHERE (dbo.vStockSum.BalanceQTY - dbo.vStockSum.LowerQTY < 0)
在物料表中,有字段记录物料的最低安全库存/最高安全库存,由于时间的关系,设计时并没有真正考虑最高安全库存,特此说明一下。
数据库为MS SQL Server,在2000/2008上测试通过。
为了简化设计难度,增加脚本的可读性,计算过程分成两步,第一步进行汇总:
SELECT p.ItemRef, p.ItemName, p.Price, p.LowerQTY, p.UpperQTY, p.SecurityQTY, p.UOMs_Ref, ISNULL(o.OutQTY, 0) AS OutQTY, ISNULL(i.InQTY, 0) AS InQTY,
ISNULL(i.InQTY, 0) - ISNULL(o.OutQTY, 0) AS BalanceQTY, dbo.UOMs.UOMName
FROM dbo.Products AS p INNER JOIN
dbo.UOMs ON p.UOMs_Ref = dbo.UOMs.Ref LEFT OUTER JOIN
dbo.vInQTY AS i ON p.ItemRef = i.ItemRef LEFT OUTER JOIN
dbo.vOutQTY AS o ON p.ItemRef = o.ItemRef
第二步根据安全库存设置计算采购计划:
SELECT dbo.vStockSum.ItemRef, dbo.vStockSum.ItemName, dbo.vStockSum.LowerQTY, dbo.vStockSum.UpperQTY, dbo.vStockSum.SecurityQTY, dbo.vStockSum.BalanceQTY,
dbo.vStockSum.BalanceQTY - dbo.vStockSum.LowerQTY AS L, ABS(dbo.vStockSum.BalanceQTY - dbo.vStockSum.LowerQTY) AS PurchasePlan,
dbo.vStockSum.UOMs_Ref, dbo.UOMs.UOMName
FROM dbo.vStockSum INNER JOIN
dbo.UOMs ON dbo.vStockSum.UOMs_Ref = dbo.UOMs.Ref
WHERE (dbo.vStockSum.BalanceQTY - dbo.vStockSum.LowerQTY < 0)
Dephi三层de迷惑
最近用D7写了一个三层数据库模型,使用了Remote Transactional Datamodule和TClientDataset组件,连接方式是SocketConnection。本来一开始运行的好好的,可是在服务器端程序改动一点点之后,便开始不正常,客户端访问的时候,抛出致命错误,也不知道致命在哪里。下面两段源代码经过比较,没发现有什么特别的改动。
{Code 1: 能够正常运行的代码}
unit uRead;
{$WARN SYMBOL_PLATFORM OFF}
interface
uses
Windows,
{.....}
MidLayer_TLB,
DB,
ADODB,
Provider;
type
TReadFromDatabase = class(TMtsDataModule, IReadFromDatabase)
conRead: TADOConnection;
tblViewEmployees: TADOTable;
cdsEmployees: TClientDataSet;
dspEmployees: TDataSetProvider;
{...}
private
{ Private declarations }
protected
class procedure UpdateRegistry(Register: Boolean; const ClassID, ProgID:
string); override;
procedure PrepareEmployees; safecall;
procedure Login; safecall;
function SignIn(const ARef, APassword: WideString; ATimes: Integer;
var AAdmin, ALogedSuccess, ARejected: WordBool): WideString;
safecall;
public
{ Public declarations }
end;
var
ReadFromDatabase: TReadFromDatabase;
implementation
uses
IniFiles;
{$R *.DFM}
class procedure TReadFromDatabase.UpdateRegistry(Register: Boolean; const
ClassID, ProgID: string);
begin
if Register then
begin
inherited UpdateRegistry(Register, ClassID, ProgID);
EnableSocketTransport(ClassID);
EnableWebTransport(ClassID);
end
else
begin
DisableSocketTransport(ClassID);
DisableWebTransport(ClassID);
inherited UpdateRegistry(Register, ClassID, ProgID);
end;
end;
procedure TReadFromDatabase.PrepareEmployees;
begin
//reserved
end;
procedure TReadFromDatabase.Login;
begin
//reserved
end;
function TReadFromDatabase.SignIn(const ARef, APassword: WideString;
ATimes: Integer; var AAdmin, ALogedSuccess,
ARejected: WordBool): WideString;
begin
with TADOQuery.Create(nil) do
try
Connection := conRead;
SQL.Add('SELECT Admin, FullName FROM Employees WHERE EmployeeRef=' +
QuotedStr(ARef) + ' AND Password=' + QuotedStr(APassword));
Open;
if Eof then
begin
Inc(ATimes);
ALogedSuccess := False;
if ATimes = 3 then
ARejected := True
else
ARejected := False;
AAdmin := False;
Result := '';
end
else
begin
ALogedSuccess := True;
ARejected := False;
AAdmin := FieldByName('admin').AsBoolean;
Result := fieldbyname('FullName').AsString;
end;
finally
Close;
Free;
end;
end;
initialization
TComponentFactory.Create(ComServer, TReadFromDatabase,
Class_ReadFromDatabase, ciMultiInstance, tmBoth);
end.
{Code 2: 运行不了的代码}
unit uRead;
{$WARN SYMBOL_PLATFORM OFF}
interface
uses
{...}
MidLayer_TLB,
DB,
ADODB,
Provider;
type
TReadFromDatabase = class(TMtsDataModule, IReadFromDatabase)
tblViewEmployees: TADOTable;
cdsEmployees: TClientDataSet;
dspEmployees: TDataSetProvider;
{...}
conRead: TADOConnection;
procedure MtsDataModuleCreate(Sender: TObject);
private
{ Private declarations }
protected
class procedure UpdateRegistry(Register: Boolean; const ClassID, ProgID:
string); override;
procedure PrepareEmployees; safecall;
procedure Login; safecall;
function SignIn(const ARef, APassword: WideString; ATimes: Integer;
var AAdmin, ALogedSuccess, ARejected: WordBool): WideString;
safecall;
public
{ Public declarations }
constructor Create(AOwner: TComponent); override;
end;
var
ReadFromDatabase: TReadFromDatabase;
implementation
uses
IniFiles,
uLog;
{$R *.DFM}
class procedure TReadFromDatabase.UpdateRegistry(Register: Boolean; const
ClassID, ProgID: string);
begin
if Register then
begin
inherited UpdateRegistry(Register, ClassID, ProgID);
EnableSocketTransport(ClassID);
EnableWebTransport(ClassID);
end
else
begin
DisableSocketTransport(ClassID);
DisableWebTransport(ClassID);
inherited UpdateRegistry(Register, ClassID, ProgID);
end;
end;
procedure TReadFromDatabase.PrepareEmployees;
begin
//reserved
end;
procedure TReadFromDatabase.Login;
begin
//reserved
end;
function TReadFromDatabase.SignIn(const ARef, APassword: WideString;
ATimes: Integer; var AAdmin, ALogedSuccess,
ARejected: WordBool): WideString;
begin
with TADOQuery.Create(nil) do
try
if not conRead.Connected then
conRead.Open();
Connection := conRead;
SQL.Add('SELECT Admin, FullName FROM Employees WHERE EmployeeRef=' +
QuotedStr(ARef) + ' AND Password=' + QuotedStr(APassword));
Open;
if Eof then
begin
Inc(ATimes);
ALogedSuccess := False;
if ATimes = 3 then
ARejected := True
else
ARejected := False;
AAdmin := False;
Result := '';
end
else
begin
ALogedSuccess := True;
ARejected := False;
AAdmin := FieldByName('admin').AsBoolean;
Result := fieldbyname('FullName').AsString;
end;
finally
Close;
Free;
end;
end;
constructor TReadFromDatabase.Create(AOwner: TComponent);
begin
end;
procedure TReadFromDatabase.MtsDataModuleCreate(Sender: TObject);
var
Loger : TLoger;
msg : WideString;
begin
// if conRead.Connected then
// msg := FormatDateTime('mm-dd hh:nn:ss', Now) + ': Opened on ' +
// conRead.ConnectionString
// else
// msg := FormatDateTime('mm-dd hh:nn:ss', Now) + ': is closed ';
// Loger.Log(msg);
// ShowMessage(msg);
end;
initialization
TComponentFactory.Create(ComServer, TReadFromDatabase,
Class_ReadFromDatabase, ciMultiInstance, tmBoth);
end.
先记录下来,过段时间再回头研究到底是什么问题。
{Code 1: 能够正常运行的代码}
unit uRead;
{$WARN SYMBOL_PLATFORM OFF}
interface
uses
Windows,
{.....}
MidLayer_TLB,
DB,
ADODB,
Provider;
type
TReadFromDatabase = class(TMtsDataModule, IReadFromDatabase)
conRead: TADOConnection;
tblViewEmployees: TADOTable;
cdsEmployees: TClientDataSet;
dspEmployees: TDataSetProvider;
{...}
private
{ Private declarations }
protected
class procedure UpdateRegistry(Register: Boolean; const ClassID, ProgID:
string); override;
procedure PrepareEmployees; safecall;
procedure Login; safecall;
function SignIn(const ARef, APassword: WideString; ATimes: Integer;
var AAdmin, ALogedSuccess, ARejected: WordBool): WideString;
safecall;
public
{ Public declarations }
end;
var
ReadFromDatabase: TReadFromDatabase;
implementation
uses
IniFiles;
{$R *.DFM}
class procedure TReadFromDatabase.UpdateRegistry(Register: Boolean; const
ClassID, ProgID: string);
begin
if Register then
begin
inherited UpdateRegistry(Register, ClassID, ProgID);
EnableSocketTransport(ClassID);
EnableWebTransport(ClassID);
end
else
begin
DisableSocketTransport(ClassID);
DisableWebTransport(ClassID);
inherited UpdateRegistry(Register, ClassID, ProgID);
end;
end;
procedure TReadFromDatabase.PrepareEmployees;
begin
//reserved
end;
procedure TReadFromDatabase.Login;
begin
//reserved
end;
function TReadFromDatabase.SignIn(const ARef, APassword: WideString;
ATimes: Integer; var AAdmin, ALogedSuccess,
ARejected: WordBool): WideString;
begin
with TADOQuery.Create(nil) do
try
Connection := conRead;
SQL.Add('SELECT Admin, FullName FROM Employees WHERE EmployeeRef=' +
QuotedStr(ARef) + ' AND Password=' + QuotedStr(APassword));
Open;
if Eof then
begin
Inc(ATimes);
ALogedSuccess := False;
if ATimes = 3 then
ARejected := True
else
ARejected := False;
AAdmin := False;
Result := '';
end
else
begin
ALogedSuccess := True;
ARejected := False;
AAdmin := FieldByName('admin').AsBoolean;
Result := fieldbyname('FullName').AsString;
end;
finally
Close;
Free;
end;
end;
initialization
TComponentFactory.Create(ComServer, TReadFromDatabase,
Class_ReadFromDatabase, ciMultiInstance, tmBoth);
end.
{Code 2: 运行不了的代码}
unit uRead;
{$WARN SYMBOL_PLATFORM OFF}
interface
uses
{...}
MidLayer_TLB,
DB,
ADODB,
Provider;
type
TReadFromDatabase = class(TMtsDataModule, IReadFromDatabase)
tblViewEmployees: TADOTable;
cdsEmployees: TClientDataSet;
dspEmployees: TDataSetProvider;
{...}
conRead: TADOConnection;
procedure MtsDataModuleCreate(Sender: TObject);
private
{ Private declarations }
protected
class procedure UpdateRegistry(Register: Boolean; const ClassID, ProgID:
string); override;
procedure PrepareEmployees; safecall;
procedure Login; safecall;
function SignIn(const ARef, APassword: WideString; ATimes: Integer;
var AAdmin, ALogedSuccess, ARejected: WordBool): WideString;
safecall;
public
{ Public declarations }
constructor Create(AOwner: TComponent); override;
end;
var
ReadFromDatabase: TReadFromDatabase;
implementation
uses
IniFiles,
uLog;
{$R *.DFM}
class procedure TReadFromDatabase.UpdateRegistry(Register: Boolean; const
ClassID, ProgID: string);
begin
if Register then
begin
inherited UpdateRegistry(Register, ClassID, ProgID);
EnableSocketTransport(ClassID);
EnableWebTransport(ClassID);
end
else
begin
DisableSocketTransport(ClassID);
DisableWebTransport(ClassID);
inherited UpdateRegistry(Register, ClassID, ProgID);
end;
end;
procedure TReadFromDatabase.PrepareEmployees;
begin
//reserved
end;
procedure TReadFromDatabase.Login;
begin
//reserved
end;
function TReadFromDatabase.SignIn(const ARef, APassword: WideString;
ATimes: Integer; var AAdmin, ALogedSuccess,
ARejected: WordBool): WideString;
begin
with TADOQuery.Create(nil) do
try
if not conRead.Connected then
conRead.Open();
Connection := conRead;
SQL.Add('SELECT Admin, FullName FROM Employees WHERE EmployeeRef=' +
QuotedStr(ARef) + ' AND Password=' + QuotedStr(APassword));
Open;
if Eof then
begin
Inc(ATimes);
ALogedSuccess := False;
if ATimes = 3 then
ARejected := True
else
ARejected := False;
AAdmin := False;
Result := '';
end
else
begin
ALogedSuccess := True;
ARejected := False;
AAdmin := FieldByName('admin').AsBoolean;
Result := fieldbyname('FullName').AsString;
end;
finally
Close;
Free;
end;
end;
constructor TReadFromDatabase.Create(AOwner: TComponent);
begin
end;
procedure TReadFromDatabase.MtsDataModuleCreate(Sender: TObject);
var
Loger : TLoger;
msg : WideString;
begin
// if conRead.Connected then
// msg := FormatDateTime('mm-dd hh:nn:ss', Now) + ': Opened on ' +
// conRead.ConnectionString
// else
// msg := FormatDateTime('mm-dd hh:nn:ss', Now) + ': is closed ';
// Loger.Log(msg);
// ShowMessage(msg);
end;
initialization
TComponentFactory.Create(ComServer, TReadFromDatabase,
Class_ReadFromDatabase, ciMultiInstance, tmBoth);
end.
先记录下来,过段时间再回头研究到底是什么问题。
订阅:
博文 (Atom)