2008年06月06日

1.Windows系统下:
>>> import os
>>> os.environ.keys(  )
['WINBOOTDIR', 'PATH', 'USER', 'PP2HOME', 'CMDLINE', 'PYTHONPATH', 'BLASTER', 
'X', 'TEMP', 'COMSPEC', 'PROMPT', 'WINDIR', 'TMP']

>>> os.environ['PYTHONPATH']
‘C:\\PP2ndEd\\examples\\Part3;C:\\PP2ndEd\\examples\\Part2;C:\\PP2ndEd\\
examples\\Part2\\Gui;C:\\PP2ndEd\\examples’

>>>
>>> import string
>>> for dir in string.split(os.environ['PYTHONPATH'], os.pathsep):
…   print dir

C:\PP2ndEd\examples\Part3
C:\PP2ndEd\examples\Part2
C:\PP2ndEd\examples\Part2\Gui
C:\PP2ndEd\examples

2.Unbuntu系统下:

>>>os.environ.keys()
['GNOME_DESKTOP_SESSION_ID', 'QT_IM_MODULE', 'LOGNAME', 'USER', 'HOME', 'PATH', 'DISPLAY', 'LANG', 'SHELL', 'XDG_SESSION_COOKIE', 'SESSION_MANAGER', 'XDG_DATA_DIRS', 'WINDOWPATH', 'XMODIFIERS', 'GPG_AGENT_INFO', 'USERNAME', 'GDM_XSERVER_LOCATION', 'SSH_AUTH_SOCK', 'DESKTOP_SESSION', 'GDMSESSION', 'DBUS_SESSION_BUS_ADDRESS', 'XAUTHORITY', 'GTK_IM_MODULE', 'GNOME_KEYRING_SOCKET', 'e4nokde', 'GNOME_KEYRING_PID', 'GDM_LANG', 'PWD', 'DESKTOP_STARTUP_ID', 'GTK_RC_FILES']

>>> os.environ['PATH']
‘/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games’

>>> for dir in string.split(os.environ['PATH'],os.pathsep):
…     print dir

/usr/local/sbin
/usr/local/bin
/usr/sbin
/usr/bin
/sbin
/bin
/usr/games

注:OS不同,他们的环境变量的key也不同

2007年12月09日
C#调用MATLAB的COM组件 [原创 2006-08-03 16:30:53 ] 发表者: cyymissyou   
字体变小 字体变大

一.步骤

1.编写M函数

2..制作COM组件

a.在MATLAB工作空间中输入comtool依照步骤输入组件名及类名.

(注:本人在6.5下做COM组件未成功.7.0下成功)

b.编译.其中组件名_1.0.dll便是作好的组件.

3.调用COM组件

a.新建C#项目

b.添加引用

右击右边的解决方案管理器中的引用—-选择COM,找到自己制作的 组件名称_1.0.dll,选择确定,即可.

c.调用组件

编写调用代码

二.实例

1.无参数

a.m函数 rs_jpg

function rs_jpg

I=inread(‘c:jpg’);

Imshow(I);

b.COM组件

组件名称comtest,类名testclass

c.调用代码

comtest.testclassClass testclass1=new comtest.testclassClass();

testclass1.rs_jpg();

 2.有参数

a.m函数 split2rgb.m

split2rgb.m的源代码
%%//   测试文件
function [m_nHeight,m_nWidth,mOrigR,mOrigG,mOrigB]=Split2RGB(FileName)

%%//  读入一个Jpg文件,
mOrigData=imread(FileName);
%mDestData=imresize(mOrigData,0.5);
imwrite(mOrigData,’c:\2.jpg’);

%%//  用三个变量保存其R,G,B分量
mOrigR=mOrigData(:,:,1);
mOrigG=mOrigData(:,:,2);
mOrigB=mOrigData(:,:,3);

%%//  获得图象的高度,宽度
[m_nHeight,m_nWidth]=size(mOrigR);

figure(1);
set(gcf,’MenuBar’,'none’)
imshow(mOrigData);
title(['Orginal Image:',FileName],’Color’,'b’,'FontSize’,14);
xlabel(['Height: ',num2str(m_nHeight),'    Width :',num2str(m_nWidth)],’Color’
,’b',’FontSize’,12);

%%//  写param文件
paraFName=[FileName(1:length(FileName)-4),'.param'];
fid=fopen(paraFName,’w');
fwrite(fid,m_nHeight,’uint32′);
fwrite(fid,m_nWidth,’uint32′);
fclose(fid);

%%//   写 R 分量文件
RFName=[FileName(1:length(FileName)-4),'_R.rot'];
fid=fopen(RFName,’w');
fwrite(fid,mOrigR,’uint8′);
fclose(fid);

%%//   写 G 分量文件
GFName=[FileName(1:length(FileName)-4),'_G.rot'];
fid=fopen(GFName,’w');
fwrite(fid,mOrigG,’uint8′);
fclose(fid);

%%//   写 B 分量文件
BFName=[FileName(1:length(FileName)-4),'_B.rot'];
fid=fopen(BFName,’w');
fwrite(fid,mOrigB,’uint8′);
fclose(fid);

b.Com组件

组件名称comtest类名:sglttest

c.c#调用代码

comtest.sgltestClass st=new comtest.sgltestClass();                       
    object h=null,w=null,r=null,g=null,b=null;
    object filename="c:\\1.jpg";
    st.split2rgb(5,ref h,ref w,ref r,ref g,ref b,filename);

2007年05月29日
解决了Matlab和C#集成的问题

  作者:我轻轻地来了 提交日期:2005-5-23 16:57:00  
今天解决了Matlab和C#集成的问题。其实,这个问题在Visual Studio.Net和Matlab R14环境下要简单得多,只要用后者生成一个com组件就可以了,而以前要调用数学库,或者利用Matlab引擎,复杂一些。现在这些东西都被更简单的组件技术取代了,但是,这是一个新东西,自己摸索了好几天。
主要就是一个object的问题,C#中这是一个最基本的对象,从Matlab的com传过来的就是这样一个object,它可以表示任何一个类型,怎么样把它还原回矩阵(或者说数组)?强制类型转换就行了,用(double[,]),就这么简单。

在Matlab做如下设置后

mbuild -setup
Please choose your compiler for building standalone MATLAB applications:
 
Would you like mbuild to locate installed compilers [y]/n? y
 
Select a compiler:
[1] Lcc C version 2.4.1 in E:\APPLICATIONS\MATLAB71\sys\lcc
[2] Microsoft Visual C/C++ version 7.1 in C:\Program Files\Microsoft Visual Studio .NET 2003
 
[0] None
 
Compiler: 2
 
Please verify your choices:
 
Compiler: Microsoft Visual C/C++ 7.1
Location: C:\Program Files\Microsoft Visual Studio .NET 2003
 
Are these correct?([y]/n): y
 
Try to update options file: C:\Documents and Settings\WangGang\Application Data\MathWorks\MATLAB\R14SP3\compopts.bat
From template:              E:\APPLICATIONS\MATLAB71\BIN\win32\mbuildopts\msvc71compp.bat
 
Done . . .
 
 
–> ""E:\Applications\MATLAB71\bin\win32\mwregsvr" "E:\Applications\MATLAB71\bin\win32\mwcomutil.dll""
 
DllRegisterServer in E:\Applications\MATLAB71\bin\win32\mwcomutil.dll succeeded
 
–> ""E:\Applications\MATLAB71\bin\win32\mwregsvr" "E:\Applications\MATLAB71\bin\win32\mwcommgr.dll""
 
DllRegisterServer in E:\Applications\MATLAB71\bin\win32\mwcommgr.dll succeeded

键入:comtool

却不能正常编译.

提示找不到"iostream"

我又重新添加了C++的类库

在mbuild -setup

按上面的步骤设置

再用comtool编译时 成功的编译出来并在C#环境中成功的调用

下面是具体的例子:

主要步骤如下:
1.建一个m文件(test.m)
例如:
function test(h,D)
for x=-10:0.1:10,
  if x>D
      y=h;
      hold on;
      plot(x,y)
  elseif x<-D
      y=-h; 
      hold on;
      plot(x,y)
  else
      y=h/(D*x);
      hold on;
      plot(x,y)
  end
end

2.在matlab中输入comtool命令,就打开了matlab com builder,

点击file-new project,新建一个工程,

在component name里输入名字 点击ok就行了。

然后点击project–Add files,将test.m添加入工程,

然后点Build-Com Object,就会在comtest\distrib\文件夹下生成一个test1_0.dll(它就
是做好的com组件),

Build时matlab已经自动将此dll在注册表中注册

我们还需做一个准备工作,进入<matlabroot>/ bin/win32目录下(matlabroot为你机器上
matlab安装的路径),打regsvr32 mwcomutil.dll ,即对mwcomutil.dll进行注册(这个dll
是matlab下作的任何com组件都要用到的dll),下面 我们在其他编译器下调用时就可以用了

3.c#下实现调用
 
    新建一个c#项目(我采用的是vs.net2003),选中右边的解决方案资源管理器中的引用
,点鼠标右键,选添加引用,在弹出来的窗口中选com,然后也找到test_1_0 .dll,点选
择,然后确定就可,此时此com组件也添加到工程里面去了,同样我们可以选择 然后编写程序

    test.testclassClass st=new test.testclassClass();
            object m=1,n=2;
            st.test(m,n);
即可
然后在c#中就出现了我们所想得到的图形,呵呵

引言

Mat lab是一种以矩阵和阵列为基本编程单元的科学工程计算语言,它提供了功能齐全的数学函数和各种工具箱,在矩阵运算、数值计算、数值信号处理、系统识别、神经网络、图像语音处理等许多方面得到了广泛的应用。Matlab的计算功能非常强大,即使是复杂的计算程序,开发效率也很高,其主要缺点是执行速度比较慢。在解决实际问题时,往往利用matlab实现矩阵运算等计算过程,其他语言工具再通过与matlab的接口调用计算过程,返回结果。这样,就可以将其他语言灵活高效的编程能力和matlab强大的计算能力有机结合起来。

1  Matlab接口的实现方法介绍

其他语言与Matlab接口的实现有多种方法,下面介绍四种常见的方法。

1.1利用文件传递数据

   利用文件传递数据是一种简单、方便的方法。实际使用中,可以使用文本文件、dat文件等文件来传递数据。如果用二进制数据文件传递数据,要注意Matlab与其他语言中二进制数据所占用的字节数以及输入输出所对应得格式要相匹配。

   这种方法可以把M文件编译成可独立执行的EXE文件,这个EXE文件通过读入其他语言中用于传递数据的文件的数据,经过计算后,将结果放到文件中,然后其他语言读出文件中的数据。这样,就可以避免在其他语言中进行复杂的数值计算。

1,2利用ActiveX技术

   ActiveX是由Microsoft制定的一种独立于编程语言的组件集成协议,它不受开发环境的限制。ActiveX空间能够在不同的环境中使用,包括VCDelphic++ builder等。可以利用自动化服务器和自动化控制器技术,实现matlab和其他语言的接口。

1.3利用Mideva平台

MidevaMathtools公司推出的一种matlab编译开发软件平台,是一个强大而完备的M文件解释和开发环境,它通过应用Matcom和实时编译技术而达到快捷的速度。该软件平台有为Borland c++Visual basicDelphi等编程语言开发的不同版本。Mediva具有编译转换功能,能够将matlab函数或编写的matlab程序转换为c++形式的DLL,然后在Delphi中调用动态库函数,这样就可能实现对matlab各种工具箱的调用。同时,Mediva提供了上千个基本功能函数,包括基本操作、命令等,通过必要的设置,就可以直接实现与其他语言的混合编程,而不必依赖matlab环境,前提是必须要有两个动态链接库mdv4300.dllago4300.dll

1.4利用com组件技术

comcomponent object module的简称,它是一种通用的对象接口,任何语言只要按照这种接口标准,就可以实现调用。Matlab6.5新推出的com builder就是把matlab下的程序做成com组件,供其他语言调用。

matlab com builder在编译生成com组件的时候需要借助于外部的编译器。因为并不是所有的编译器都能生成和microsoft相兼容的com组件。com builder目前支持下列编译器:Borland c++ builderMicrosoft Visual Studio 6.0, Microsoft Visual Studio .net等。设置matlab com builder所使用的外部编译器,可以通过在matlabcommandwindow中输入命令MBuilder-setup来实现编译器的设置,具体步骤只要按照matlab提示进行即可。

2  matlabcom builder 使用方法

我们先将一个图片放入c盘下,命名为1.jpg,再准备一个测试文件test.m,如下:

%读入并且显示图片

function rs_jpg

I=imread(‘c:.jpg);

Imshow(I);

然后,在matlabworkspace下输入comtool,就可以启动matlab com builder环境。点击工具栏上的File菜单下的new project新建一个工程,在component name栏里填上comtest,将classes栏里自动生成的comtest类删除,class name里填上一个testclasscompile code incc++都可以,不过因为c性能较好,推荐选择c。如果使用matlab里的图形库,需要在compiler options中选中Use Handle Graphics Library,我们使用了图形功能,故选中该项。选中Build debug version会生成调试版本的com组件,在调试的时候,调试版本的com组件如果真有错误发生,就会有相应得错误提示信息,可以方便地定位错误发生的地方。选中Show ……可以在编译生成com组件的时候输出详细的过程信息。在工程创建制后,仍然可以通过菜单Project->setting…打开此窗口进行修改。

Matlab下可以给com组件中的类添加方法。具体操作如下:

使用菜单Project->Add File或者点击按钮Add File添加已经存在的m函数文件就可以了。在这里,我们点击Project->Add files,将test.m添加如工程。方法(函数)添加完后,就可以使用菜单Builde->Com Object或者按钮build进行编译。编译完后就会在matlab工作目录下的comtest目录下生成两个文件夹:一个是srv,里面存放一些中间文件;另一个是distrib,里面就是动态链接库形式的com组件文件等。在这里,编译后distrib\文件夹下生成一个comtest_1_0.dll,这就是做好的com组件。

3 调用com组件的方法

3.1 vb下实现调用

   打开或者新建一个vb工程,点击project->reference,在弹出的窗口中找到comtest 1.0 Type Library,将左边的复选框选中,点击ok,此时便将此com组件添加到工程里了。

   示意调用代码如下:

   Dim testclass As testclass

   Set testclass1=New testclass

   Call testclass1.rs_jpg

3.2 c#.net下实现调用

   打开或者新建一个c#项目(这里采用vs.net),选中右边的解决方案资源管理器中的引用,点鼠标右键,选添加引用,在弹出窗口中选com,然后也找到comtest_1_0.dll,选择并确定即可。

   示意调用代码如下:

   omtest.testclasstestclass1=newcomtest.testclass();

testclass1.rs_jpg();

3.3 vc下实现调用

   点击vc下的tools->ole/com objectviewer打开oleviewer工具,在oleviewer工具的右边选择type libraries,找到comtest 1.0 type library,选中并且右键,选择view,点save按钮,分别保存为comtest_1_0.hcomtest_1_0.c(也可以存为comtest_1_0.idl接口文件),我们就可以根据这两个文件实现对comtest_1_0.dll调用。

新建或打开一个vc工程,将comtest_1_0.hcomtest_1_0.c加入工程,并复制一个comtest_1_0.dll到工程目录下,由于comtest_1_0.dll还要用到mwcomutil.dll,所以将<matlabroot>

/extern/include/下的mwcomutil.hmwcomtypes.h也加入工程中。

示意调用代码如下:

#import “mwcomutil.dll”

#import “comtest_1_0.dll”

#include “mwcomutil.h”

#include “comtest_1_0.h”

//初始化调用com

if (FAILED(CoInitialize(NULL)))

  {

    AfxMessageBox(“unable to

initialize COM”)

}

testclass *testclass1=NULL;

HRESULT hr=CoCreateInstance(CLSID_testclass,    NULL,CLSCTX_ALL,IID_ testclass,(void**)&st);

if (SUCCEEDED(hr))

  {

    st->rs_jpg();

    AfxMessageBox(“succeed”);

    st->release();

}

else

  {

   AfxMessageBox(“unsucceed”);

}

3.4 delphi下实现调用

   点击delphiproject->import type library…,出现图形窗口,找到testclass 1.0 type library,然后点击”install…”按钮,就在testclass控件页上安装了testclass控件。

   打开或建立一个工程,在form上放一个testclass控件和一个按钮控件,然后在按钮相应事件中添加如下代码:

   Procedure TForm1.Button1Click(SenderTObject);

   Begin

     Testclass1.rs_jpg;

   End;

4 打包

  matlab下的workspace里输入comtool,选择菜单flile->open project打开我们建好的工程文件comtest.cbl,再选择菜单component->package component就实现了打包。此时,可以在comtest\distrib文件夹里看,生成的comtest.exe就是打包后的解压程序,双击就会解压出一些文件,再点击解压出来的install.bat就可以实现安装。

5 结束语

通过应用编程接口,可以把其他高级语言灵活多变的编程能力和matlab的强大计算能力结合起来。matlabcom builder工具可以方便地把matlab程序做成com组件,为系统开发人员提供了简单、易用地应用程序接口。和其他接口方法相比,将matlab程序做成com组件,使其他语言工具和matlab的混合编程更加简单。

参考文献

[1]韩守红等.DelphiMatlab接口的实现方法研究.微计算机信息.2001

[2]胡劲松等.基于COMMATLABDelphi混合编程研究.计算机应用研究.2005

  Matlab是Mathworks公司推出的数学软件,它将数值分析、矩阵计算、信号处理和图形显示结合在一起,包含大量高度集成的函数可供调用,命令语句功能十分强大,为科学研究、工程设计及众多学科领域提供了一种简洁、高效的编程工具。但是Matlab使用的是解释性语言,大大限制了它的执行速度;源代码的公开不利于算法和数据的保密;局限于Matlab运行环境而不能用于开发商用软件。Visual C++是Windows平台下主要的应用程序开发环境之一,它能方便实现软件开发,开发的系统具有界面友好、执行速度快、易维护和升级等优点。但是在工程计算方面,和Matlab相比编程显得复杂的多。因此实现VC与Matlab混合编程,使两者结合起来,协同工作,必将提高软件开发效率,使所开发的软件具有更高的性能,更大的应用范围,也可以为科学研究和工程技术提供更强的技术支持。

  VC与Matlab混合编程的实现方案

  在VC环境中调用Matlab程序主要有以下几种方法:

  (1)用Matlab引擎,采用客户机/服务器(Client/Server)的计算模式。在VC中设计程序框架,作为前端客户机,通过调用Matlab引擎与后台Matlab服务器建立连接,实现命令和数据信息的传递。这种方式需要Matlab在后台运行,离不开Matlab环境。不利于软件的开发,但是它可以充分利用Matlab的功能,包括调用工具箱函数和图形函数。

  (2)用Matlab的编译器将.m源文件转化为c、c++等各种不同类型的源代码,并在此基础上根据应用需要生成MEX文件、独立可执行应用程序等文件类型,大大提高程序的运行速度,提高代码的执行效率。主要是使用mcc命令实现文件的转化。但是这种方法不支持图形函数,不能编译一些Matlab的内建函数,并且转换的代码可读性不太好。

  (3)使用Matcom编译器可以将Matlab源代码译成同等功能的C++代码,既保持了Matlab的优良算法,又提高了执行速度。它还支持一定的图形显示,生成代码的可读性很好。因其简单便捷、功能强大、应用灵活,本文主要探讨这种实现方案。

  实现VC与Matcom的接口步骤

  Matcom是Mathworks公司推出的第一个由Matlab到C++的编译开发软件平台,它的可视化界面,方便丰富的调试功能和对数学库的强大支持受到广大技术人员的重视。现在的最高版本为Matcom4.5。

  我们可以通过在Matcom中file菜单下的compile to exe or dll调用.m文件编译,在Matcom的debug目录下找到生成的cpp、exe、dll文件。把需要的文件加到VC开发环境的工程中,再包含头文件就可以实现调用。也可以直接在VC环境下使用科学运算库,即Matcom的Matrix<LIB>,它是按照Matcom的语法在VC中实现类似于Matlab函数的方法。但是这里还有一种将Matcom集成到VC环境的方法,即安装Visual Matcom的方式,这种方式操作更加简便易行,只需要熟悉Matlab编程,经过简单的步骤就可以轻松实现VC环境中调用Matlab。下面介绍Visual Matcom开发环境的安装。

  (1)拷贝<matcom45>\bin\usertype.dat文件(<matcom45>指Matcom的安装路径)到<visual c++>\Common\MSDev98\Bin目录(<visual c++>指VC的安装路径)下。

  (2)运行Visual C++,从菜单条中选择Tools/Customize/Add-ins and Macro Files,选择Browse,改变文件类型为Add-ins(.dll),选定<matcom45>\bin\mvcide.dll文件,确定。

  (3)这样,可以在Visual C++的开发环境中看到一个如图1所示的Visual Matcom工具栏,表明安装成功。


图1 Visual Matcom工具栏

下面以一个线性方程组的求解过程为例,介绍通过Matcom实现VC对Matlab程序的调用,运行环境:Win2000、Matlab6.1、Matcom4.5、Visual C++6.0

  (1)Matlab运行环境中编写程序equation.m,其代码如下:

%equation 求解线性方程组的解
%线性方程组形如:A*X = B
function X = equation(A, B)
X = A\B;

  (2)这里以一个简单的控制台程序为例,其它程序基本相同。

 

在VC环境中建立一个名为Test的Win32 Console Application工程。

  (3)点击Visual Matcom工具栏上的m++图标,选择保存过的Matlab文件equation.m进行转化。如果看到的转化信息提示没有错误就可以观察到此时在FileView标签中多了m-files,C++files created from m-files,Matrix<lib>等文件。并且该工程目录下增加了equation.h,equation.cpp,equation.mak,equation.r等的4个文件。这时会在VC中出现一个转换完毕的文件,文件中如果报告有错误就要考虑是否程序有问题,可以双击C++files文件夹下的equation.m进行修改,再重新转化直到没有错误报告为止。

  (4)在Test工程下建立一个文件test.cpp调用转化的Matlab函数,代码为:

#include "stdio.h"
#include "matlib.h" //提供转化后C++代码中使用的数据类型,函//数原型及常数
#include "equation.h"
void main()
{
 /*解线性方程组:
 X =
 */
 initM(MATCOM_VERSION); //初始化matlib库
 Mm a,b,x; //使用矩阵类Mm构造矩阵a,b,x.
 a = (BR(1),2,3,semi,4,5,6,semi,7,8,1);
 //给矩阵a赋值,BR是Matrix<LIB>库的一个
 //宏,用于定义一个矩阵的开始;semi是库的//一个常量,用于分隔不同行的矩阵元素
 b = zeros(3,1); //初始化矩阵b为零矩阵3行1列
 b(1,1) = 37; b(2,1) = 85; b(3,1) = 69; //给矩阵b赋值
 x = equation(a,b); //调用转化的函数,求解线性方程组的解
 for (int i = 1; i <= x.rows(); i++) //把解矩阵X的元素显示出来
 {
  for (int j=1;j<=x.cols();j++)
   printf("x(%d,%d)=%f\n",i,j,x.r(i,j));
 }
 exitM(); //结束对matlib库的调用
 return;
}

  注意:程序中涉及到了两个成员函数.rows()和.cols(),它们分别返回矩阵的行数和列数;x.r(i,j)代表矩阵x的第i行第j列的元素。

  (5)编译运行后结果为x(1,1) = 3.000000 x(2,1) = 5.000000 x(3,1) = 8.000000与实际结果一致。

  总结:如果需要转换的.m文件不是一个函数,只是一些Matlab命令的集合,则要在工程目录下找到转换文件的.cpp文件,将其中的C代码拷贝到需要调用它的函数里面。Matcom克服了mcc命令只能编译一个独立.m文件的缺点,当所编译的.m文件依赖于其他.m文件时,只要把被调用的.m文件与要编译的.m文件放在同一目录下,生成的被调用文件的.h和.cpp文件插入到VC开发的工程中就可以了。

  上面只是一个简单的控制台工程的例子,我们也可以建立其它类型的工程,只要在需要调用转换后函数的程序中包含matlib.h并且在响应函数中初始化matlib库initM(MATCOM_VERSION);结束调用后做结束工作exitM();就可以了。

  结束语

  本文讨论了VC与Matlab的混合编程,主要集中讨论在VC中调用Matlab的实现方案。通过一个Matcom编译实现的例子我们看到,利用Visual Matcom转换代码非常方便,生成的代码可读性好,而且还支持多数图形函数,基本上使Matlab和VC能够得到充分的发挥。作者在海洋遥感图像处理中使用这种方法来提高程序的执行速度,取得了非常理想的效果。它不仅解决了Matlab的解释执行方式带来的执行速度过慢的缺陷,更重要的是它完全脱离了Matlab系统,当代码被编译成可执行程序后,又可大大提高程序的安全性。

.net是微软的发展方向,以后我们开发软件的时候不免会调用一些matlab语言

今晚一直在***图用c#调用matlab,终于成功。

主要思想就是首先把matlab的m函数做成com组件,然后在c#中应用此com组件,即可。

主要步骤如下:
1.建一个m文件(test.m)
例如:
function test(h,D)
for x=-10:0.1:10,
   if x>D
       y=h;
       hold on;
       plot(x,y)
   elseif x<-D
       y=-h;  
       hold on;
       plot(x,y)
   else
       y=h/(D*x);
       hold on;
       plot(x,y)
   end
end

2.在matlab中输入comtool命令,就打开了matlab com builder,

点击file-new project,新建一个工程,

在component name里输入名字 点击ok就行了。

然后点击project–Add files,将test.m添加入工程,

然后点Build-Com Object,就会在comtest\distrib\文件夹下生成一个test1_0.dll(它就
是做好的com组件),

Build时matlab已经自动将此dll在注册表中注册

我们还需做一个准备工作,进入<matlabroot>/ bin/win32目录下(matlabroot为你机器上
matlab安装的路径),打regsvr32 mwcomutil.dll ,即对mwcomutil.dll进行注册(这个dll
是matlab下作的任何com组件都要用到的dll),下面 我们在其他编译器下调用时就可以用了

3.c#下实现调用

     新建一个c#项目(我采用的是vs.net2003),选中右边的解决方案资源管理器中的引用
,点鼠标右键,选添加引用,在弹出来的窗口中选com,然后也找到test_1_0 .dll,点选
择,然后确定就可,此时此com组件也添加到工程里面去了,同样我们可以选择

然后编写程序

     test.testclassClass st=new test.testclassClass();
             object m=1,n=2;
             st.test(m,n);
即可

然后在c#中就出现了我们所想得到的图形,呵呵

看到这个,挺***兴
原来在c#中实现调用matlab没有我们所想象的困难呀,呵呵
希望大家有想法或者好的建议多多给出,

2007年03月15日

http://www.1to2.us/C-a168126.htm

2006年11月16日

指针的概念

  指针是一个特殊的变量,它里面存储的数值被解释成为内存里的一个地址。 要搞清一个指针需要搞清指针的四方面的内容:指针的类型,指针所指向的 类型,指针的值或者叫指针所指向的内存区,还有指针本身所占据的内存区。让我们分别说明。

  先声明几个指针放着做例子:

  例一:

  (1)int*ptr;

  (2)char*ptr;

  (3)int**ptr;

  (4)int(*ptr)[3];

  (5)int*(*ptr)[4];

  如果看不懂后几个例子的话,请参阅我前段时间贴出的文章<<如何理解c和c ++的复杂类型声明>>。

  指针的类型

  从语法的角度看,你只要把指针声明语句里的指针名字去掉,剩下的部分就是这个指针的类型。这是指针本身所具有的类型。让我们看看例一中各个指针的类型:

  (1)int*ptr;//指针的类型是int*

  (2)char*ptr;//指针的类型是char*

  (3)int**ptr;//指针的类型是int**

  (4)int(*ptr)[3];//指针的类型是int(*)[3]

  (5)int*(*ptr)[4];//指针的类型是int*(*)[4]

  怎么样?找出指针的类型的方法是不是很简单?

  指针所指向的类型

  当你通过指针来访问指针所指向的内存区时,指针所指向的类型决定了编译器将把那片内存区里的内容当做什么来看待。

  从语法上看,你只须把指针声明语句中的指针名字和名字左边的指针声明符*去掉,剩下的就是指针所指向的类型。例如:

  (1)int*ptr;//指针所指向的类型是int

  (2)char*ptr;//指针所指向的的类型是char

  (3)int**ptr;//指针所指向的的类型是int*

  (4)int(*ptr)[3];//指针所指向的的类型是int()[3]

  (5)int*(*ptr)[4];//指针所指向的的类型是int*()[4]

  在指针的算术运算中,指针所指向的类型有很大的作用。

   指针的类型(即指针本身的类型)和指针所指向的类型是两个概念。当你对C越来越熟悉时,你会发现,把与指针搅和在一起的"类型"这个概念分成"指针的类 型"和"指针所指向的类型"两个概念,是精通指针的关键点之一。我看了不少书,发现有些写得差的书中,就把指针的这两个概念搅在一起了,所以看起书来前后 矛盾,越看越糊涂。

  指针的值,或者叫指针所指向的内存区或地址

  指针的值是指针本身存储的数值,这个值将被编译器 当作一个地址,而不是一个一般的数值。在32位程序里,所有类型的指针的值都是一个32位整数,因为32位程序里内存地址全都是32位长。 指针所指向的内存区就是从指针的值所代表的那个内存地址开始,长度为si zeof(指针所指向的类型)的一片内存区。以后,我们说一个指针的值是XX,就相当于说该指针指向了以XX为首地址的一片内存区域;我们说一个指针指向 了某块内存区域,就相当于说该指针的值是这块内存区域的首地址。

  指针所指向的内存区和指针所指向的类型是两个完全不同的概念。在例一中,指针所指向的类型已经有了,但由于指针还未初始化,所以它所指向的内存区是不存在的,或者说是无意义的。

  以后,每遇到一个指针,都应该问问:这个指针的类型是什么?指针指的类型是什么?该指针指向了哪里?

  指针本身所占据的内存区

  指针本身占了多大的内存?你只要用函数sizeof(指针的类型)测一下就知道了。在32位平台里,指针本身占据了4个字节的长度。

  指针本身占据的内存这个概念在判断一个指针表达式是否是左值时很有用。
  指针的算术运算

  指针可以加上或减去一个整数。指针的这种运算的意义和通常的数值的加减运算的意义是不一样的。例如:

  例二:

  1、chara[20];

  2、int*ptr=a;

  …

  …

  3、ptr++;

   在上例中,指针ptr的类型是int*,它指向的类型是int,它被初始化为指向整形变量a。接下来的第3句中,指针ptr被加了1,编译器是这样处理 的:它把指针ptr的值加上了sizeof(int),在32位程序中,是被加上了4。由于地址是用字节做单位的,故ptr所指向的地址由原来的变量a的 地址向高地址方向增加了4个字节。
由于char类型的长度是一个字节,所以,原来ptr是指向数组a的第0号单元开始的四个字节,此时指向了数组a中从第4号单元开始的四个字节。

  我们可以用一个指针和一个循环来遍历一个数组,看例子:

  例三:

intarray[20];
int*ptr=array;

//此处略去为整型数组赋值的代码。

for(i=0;i<20;i++)
{
 (*ptr)++;
 ptr++;
} 

  这个例子将整型数组中各个单元的值加1。由于每次循环都将指针ptr加1,所以每次循环都能访问数组的下一个单元。

  再看例子:

  例四:

  1、chara[20];

  2、int*ptr=a;

  …
  …

  3、ptr+=5;

   在这个例子中,ptr被加上了5,编译器是这样处理的:将指针ptr的值加上5乘sizeof(int),在32位程序中就是加上了5乘4=20。由于 地址的单位是字节,故现在的ptr所指向的地址比起加5后的ptr所指向的地址来说,向高地址方向移动了20个字节。在这个例子中,没加5前的ptr指向 数组a的第0号单元开始的四个字节,加5后,ptr已经指向了数组a的合法范围之外了。虽然这种情况在应用上会出问题,但在语法上却是可以的。这也体现出 了指针的灵活性。

  如果上例中,ptr是被减去5,那么处理过程大同小异,只不过ptr的值是被减去5乘sizeof(int),新的ptr指向的地址将比原来的ptr所指向的地址向低地址方向移动了20个字节。

   总结一下,一个指针ptrold加上一个整数n后,结果是一个新的指针ptrnew,ptrnew的类型和ptrold的类型相同,ptrnew所指向 的类型和ptrold所指向的类型也相同。ptrnew的值将比ptrold的值增加了n乘sizeof(ptrold所指向的类型)个字节。就是说, ptrnew所指向的内存区将比ptrold所指向的内存区向高地址方向移动了n乘sizeof(ptrold所指向的类型)个字节。

   一个指针ptrold减去一个整数n后,结果是一个新的指针ptrnew,ptrnew的类型和ptrold的类型相同,ptrnew所指向的类型和 ptrold所指向的类型也相同。ptrnew的值将比ptrold的值减少了n乘sizeof(ptrold所指向的类型)个字节,就是说, ptrnew所指向的内存区将比ptrold所指向的内存区向低地址方向移动了n乘sizeof(ptrold所指向的类型)个字节。

  运算符&和*

  这里&是取地址运算符,*是…书上叫做"间接运算符"。

  &a的运算结果是一个指针,指针的类型是a的类型加个*,指针所指向的类型是a的类型,指针所指向的地址嘛,那就是a的地址。

  *p的运算结果就五花八门了。总之*p的结果是p所指向的东西,这个东西有这些特点:它的类型是p指向的类型,它所占用的地址是p所指向的地址。

  例五:

inta=12;
intb;
int*p;
int**ptr;
p=&a;
//&a的结果是一个指针,类型是int*,指向的类型是int,指向的地址是a的地址。
*p=24;
//*p的结果,在这里它的类型是int,它所占用的地址是p所指向的地址,显然,*p就是变量a。
ptr=&p;
//&p的结果是个指针,该指针的类型是p的类型加个*,在这里是int **。该指针所指向的类型是p的类型,这里是int*。该指针所指向的地址就是指针p自己的地址。
*ptr=&b;
//*ptr是个指针,&b的结果也是个指针,且这两个指针的类型和所指向的类型是一样的,所以用&b来给*ptr赋值就是毫无问题的了。
**ptr=34;
//*ptr的结果是ptr所指向的东西,在这里是一个指针,对这个指针再做一次*运算,结果就是一个int类型的变量。 

  指针表达式

  一个表达式的最后结果如果是一个指针,那么这个表达式就叫指针表式。

  下面是一些指针表达式的例子:

  例六:

inta,b;
intarray[10];
int*pa;
pa=&a;//&a是一个指针表达式。
int**ptr=&pa;//&pa也是一个指针表达式。
*ptr=&b;//*ptr和&b都是指针表达式。
pa=array;
pa++;//这也是指针表达式。 

  例七:

char*arr[20];
char**parr=arr;//如果把arr看作指针的话,arr也是指针表达式
char*str;
str=*parr;//*parr是指针表达式
str=*(parr+1);//*(parr+1)是指针表达式
str=*(parr+2);//*(parr+2)是指针表达式 

  由于指针表达式的结果是一个指针,所以指针表达式也具有指针所具有的四个要素:指针的类型,指针所指向的类型,指针指向的内存区,指针自身占据的内存。

  好了,当一个指针表达式的结果指针已经明确地具有了指针自身占据的内存的话,这个指针表达式就是一个左值,否则就不是一个左值。

  在例七中,&a不是一个左值,因为它还没有占据明确的内存。*ptr是一个左值,因为*ptr这个指针已经占据了内存,其实*ptr就是指针pa,既然pa已经在内存中有了自己的位置,那么*ptr当然也有了自己的位置。

  数组和指针的关系

  如果对声明数组的语句不太明白的话,请参阅我前段时间贴出的文章<<如何理解c和c++的复杂类型声明>>。

  数组的数组名其实可以看作一个指针。看下例:

  例八:

intarray[10]={0,1,2,3,4,5,6,7,8,9},value;


value=array[0];//也可写成:value=*array;
value=array[3];//也可写成:value=*(array+3);
value=array[4];//也可写成:value=*(array+4); 

   上例中,一般而言数组名array代表数组本身,类型是int[10],但如果把array看做指针的话,它指向数组的第0个单元,类型是int*,所 指向的类型是数组单元的类型即int。因此*array等于0就一点也不奇怪了。同理,array+3是一个指向数组第3个单元的指针,所以* (array+3)等于3。其它依此类推。

  例九:

char*str[3]={
 "Hello,thisisasample!",
 "Hi,goodmorning.",
 "Helloworld"
};
chars[80];
strcpy(s,str[0]);//也可写成strcpy(s,*str);
strcpy(s,str[1]);//也可写成strcpy(s,*(str+1));
strcpy(s,str[2]);//也可写成strcpy(s,*(str+2)); 

  上例中,str是一个三单元的数组,该数组的每个单元都是一个指针,这些指针各指向一个字符串。把指针数组名str当作一个指针的话,它指向数组的第0号单元,它的类型是char**,它指向的类型是char*。
*str 也是一个指针,它的类型是char*,它所指向的类型是char,它指向的地址是字符串"Hello,thisisasample!"的第一个字符的地 址,即’H'的地址。 str+1也是一个指针,它指向数组的第1号单元,它的类型是char**,它指向的类型是char*。

  *(str+1)也是一个指针,它的类型是char*,它所指向的类型是char,它指向 "Hi,goodmorning."的第一个字符’H',等等。

   下面总结一下数组的数组名的问题。声明了一个数组TYPEarray[n],则数组名称array就有了两重含义:第一,它代表整个数组,它的类型是 TYPE[n];第二 ,它是一个指针,该指针的类型是TYPE*,该指针指向的类型是TYPE,也就是数组单元的类型,该指针指向的内存区就是数组第0号单元,该指针自己占有 单独的内存区,注意它和数组第0号单元占据的内存区是不同的。该指针的值是不能修改的,即类似array++的表达式是错误的。

  在不同的表达式中数组名array可以扮演不同的角色。

  在表达式sizeof(array)中,数组名array代表数组本身,故这时sizeof函数测出的是整个数组的大小。
在表达式*array中,array扮演的是指针,因此这个表达式的结果就是数组第0号单元的值。sizeof(*array)测出的是数组单元的大小。

  表达式array+n(其中n=0,1,2,….。)中,array扮演的是指针,故array+n的结果是一个指针,它的类型是TYPE*,它指向的类型是TYPE,它指向数组第n号单元。故sizeof(array+n)测出的是指针类型的大小。

  例十:

intarray[10];
int(*ptr)[10];
ptr=&array; 

  上例中ptr是一个指针,它的类型是int(*)[10],他指向的类型是int[10] ,我们用整个数组的首地址来初始化它。在语句ptr=&array中,array代表数组本身。

  本节中提到了函数sizeof(),那么我来问一问,sizeof(指针名称)测出的究竟是指针自身类型的大小呢还是指针所指向的类型的大小?答案是前者。例如:

int(*ptr)[10];

  则在32位程序中,有:

sizeof(int(*)[10])==4
sizeof(int[10])==40
sizeof(ptr)==4 

  实际上,sizeof(对象)测出的都是对象自身的类型的大小,而不是别的什么类型的大小。

  指针和结构类型的关系

  可以声明一个指向结构类型对象的指针。

  例十一:

structMyStruct
{
 inta;
 intb;
 intc;
}
MyStructss={20,30,40};
//声明了结构对象ss,并把ss的三个成员初始化为20,30和40。
MyStruct*ptr=&ss;
//声明了一个指向结构对象ss的指针。它的类型是MyStruct*,它指向的类型是MyStruct。
int*pstr=(int*)&ss;
//声明了一个指向结构对象ss的指针。但是它的类型和它指向的类型和ptr是不同的。

  请问怎样通过指针ptr来访问ss的三个成员变量?

  答案:

ptr->a;
ptr->b;
ptr->c;

  又请问怎样通过指针pstr来访问ss的三个成员变量?

  答案:

*pstr;//访问了ss的成员a。
*(pstr+1);//访问了ss的成员b。
*(pstr+2)//访问了ss的成员c。

  虽然我在我的MSVC++6.0上调式过上述代码,但是要知道,这样使用pstr来访问结构成员是不正规的,为了说明为什么不正规,让我们看看怎样通过指针来访问数组的各个单元:

  例十二:

intarray[3]={35,56,37};
int*pa=array;

  通过指针pa访问数组array的三个单元的方法是:

*pa;//访问了第0号单元
*(pa+1);//访问了第1号单元
*(pa+2);//访问了第2号单元

  从格式上看倒是与通过指针访问结构成员的不正规方法的格式一样。

   所有的C/C++编译器在排列数组的单元时,总是把各个数组单元存放在连续的存储区里,单元和单元之间没有空隙。但在存放结构对象的各个成员时,在某种 编译环境下,可能会需要字对齐或双字对齐或者是别的什么对齐,需要在相邻两个成员之间加若干个"填充字节",这就导致各个成员之间可能会有若干个字节的空 隙。

  所以,在例十二中,即使*pstr访问到了结构对象ss的第一个成员变量a,也不能保证*(pstr+1)就一定能访问到结构成 员b。因为成员a和成员b之间可能会有若干填充字节,说不定*(pstr+1)就正好访问到了这些填充字节呢。这也证明了指针的灵活性。要是你的目的就是 想看看各个结构成员之间到底有没有填充字节,嘿,这倒是个不错的方法。

  通过指针访问结构成员的正确方法应该是象例十二中使用指针ptr的方法。

  指针和函数的关系

  可以把一个指针声明成为一个指向函数的指针。

intfun1(char*,int);
int(*pfun1)(char*,int);
pfun1=fun1;
….
….
inta=(*pfun1)("abcdefg",7);//通过函数指针调用函数。 

  可以把指针作为函数的形参。在函数调用语句中,可以用指针表达式来作为实参。

  例十三:

intfun(char*);
inta;
charstr[]="abcdefghijklmn";
a=fun(str);


intfun(char*s)
{
intnum=0;
for(inti=0;i{
num+=*s;s++;
}
returnnum;
} 

   这个例子中的函数fun统计一个字符串中各个字符的ASCII码值之和。前面说了,数组的名字也是一个指针。在函数调用中,当把str作为实参传递给形 参s后,实际是把str的值传递给了s,s所指向的地址就和str所指向的地址一致,但是str和s各自占用各自的存储空间。在函数体内对s进行自加1运 算,并不意味着同时对str进行了自加1运算。

  指针类型转换

  当我们初始化一个指针或给一个指针赋值时,赋值号的左边是一个指针,赋值号的右边是一个指针表达式。在我们前面所举的例子中,绝大多数情况下,指针的类型和指针表达式的类型是一样的,指针所指向的类型和指针表达式所指向的类型是一样的。

  例十四:

  1、floatf=12.3;

  2、float*fptr=&f;

  3、int*p;
 
  在上面的例子中,假如我们想让指针p指向实数f,应该怎么搞?是用下面的语句吗?

  p=&f;

   不对。因为指针p的类型是int*,它指向的类型是int。表达式&f的结果是一个指针,指针的类型是float*,它指向的类型是 float。两者不一致,直接赋值的方法是不行的。至少在我的MSVC++6.0上,对指针的赋值语句要求赋值号两边的类型一致,所指向的类型也一致,其 它的编译器上我没试过,大家可以试试。为了实现我们的目的,需要进行"强制类型转换":

p=(int*)&f; 

  如果有一个指针p,我们需要把它的类型和所指向的类型改为TYEP*TYPE, 那么语法格式是:

  (TYPE*)p;

  这样强制类型转换的结果是一个新指针,该新指针的类型是TYPE*,它指向的类型是TYPE,它指向的地址就是原指针指向的地址。而原来的指针p的一切属性都没有被修改。

  一个函数如果使用了指针作为形参,那么在函数调用语句的实参和形参的结合过程中,也会发生指针类型的转换。

  例十五:

voidfun(char*);
inta=125,b;
fun((char*)&a);


voidfun(char*s)
{
charc;
c=*(s+3);*(s+3)=*(s+0);*(s+0)=c;
c=*(s+2);*(s+2)=*(s+1);*(s+1)=c;
}
} 

   注意这是一个32位程序,故int类型占了四个字节,char类型占一个字节。函数fun的作用是把一个整数的四个字节的顺序来个颠倒。注意到了吗?在 函数调用语句中,实参&a的结果是一个指针,它的类型是int*,它指向的类型是int。形参这个指针的类型是char*,它指向的类型是 char。这样,在实参和形参的结合过程中,我们必须进行一次从int*类型到char*类型的转换。结合这个例子,我们可以这样来想象编译器进行转换的 过程:编译器先构造一个临时指针char*temp, 然后执行temp=(char*)&a,最后再把temp的值传递给s。所以最后的结果是:s的类型是char*,它指向的类型是char,它指 向的地址就是a的首地址。

  我们已经知道,指针的值就是指针指向的地址,在32位程序中,指针的值其实是一个32位整数。那可不可以把一个整数当作指针的值直接赋给指针呢?就象下面的语句:

unsignedinta;
TYPE*ptr;//TYPE是int,char或结构类型等等类型。


a=20345686;
ptr=20345686;//我们的目的是要使指针ptr指向地址20345686(十进制

ptr=a;//我们的目的是要使指针ptr指向地址20345686(十进制) 

  编译一下吧。结果发现后面两条语句全是错的。那么我们的目的就不能达到了吗?不,还有办法:

unsignedinta;
TYPE*ptr;//TYPE是int,char或结构类型等等类型。


a=某个数,这个数必须代表一个合法的地址;
ptr=(TYPE*)a;//呵呵,这就可以了。 

  严格说来这里的(TYPE*)和指针类型转换中的(TYPE*)还不一样。这里的(TYPE*)的意思是把无符号整数a的值当作一个地址来看待。上面强调了a的值必须代表一个合法的地址,否则的话,在你使用ptr的时候,就会出现非法操作错误。

  想想能不能反过来,把指针指向的地址即指针的值当作一个整数取出来。完 全可以。下面的例子演示了把一个指针的值当作一个整数取出来,然后再把这个整数当作一个地址赋给一个指针:

  例十六:

inta=123,b;
int*ptr=&a;
char*str;
b=(int)ptr;//把指针ptr的值当作一个整数取出来。
str=(char*)b;//把这个整数的值当作一个地址赋给指针str。 

  现在我们已经知道了,可以把指针的值当作一个整数取出来,也可以把一个整数值当作地址赋给一个指针。

  指针的安全问题

  看下面的例子:

  例十七:

chars=’a';
int*ptr;
ptr=(int*)&s;
*ptr=1298; 

   指针ptr是一个int*类型的指针,它指向的类型是int。它指向的地址就是s的首地址。在32位程序中,s占一个字节,int类型占四个字节。最后 一条语句不但改变了s所占的一个字节,还把和s相临的高地址方向的三个字节也改变了。这三个字节是干什么的?只有编译程序知道,而写程序的人是不太可能知 道的。也许这三个字节里存储了非常重要的数据,也许这三个字节里正好是程序的一条代码,而由于你对指针的马虎应用,这三个字节的值被改变了!这会造成崩溃 性的错误。

  让我们再来看一例:

  例十八:

  1、chara;

  2、int*ptr=&a;

  …
  …

  3、ptr++;

  4、*ptr=115;

   该例子完全可以通过编译,并能执行。但是看到没有?第3句对指针ptr进行自加1运算后,ptr指向了和整形变量a相邻的高地址方向的一块存储区。这块 存储区里是什么?我们不知道。有可能它是一个非常重要的数据,甚至可能是一条代码。而第4句竟然往这片存储区里写入一个数据!这是严重的错误。所以在使用 指针时,程序员心里必须非常清楚:我的指针究竟指向了哪里。在用指针访问数组的时候,也要注意不要超出数组的低端和高端界限,否则也会造成类似的错误。

   在指针的强制类型转换:ptr1=(TYPE*)ptr2中,如果sizeof(ptr2的类型)大于sizeof(ptr1的类型),那么在使用指针 ptr1来访问ptr2所指向的存储区时是安全的。如果sizeof(ptr2的类型)小于sizeof(ptr1的类型),那么在使用指针ptr1来访 问ptr2所指向的存储区时是不安全的。至于为什么,读者结合例十七来想一想,应该会明白的。


1. 关于Glade的一般信息

1.1 Glade有哪些文档?
1.2 有没有范例代码?
1.3 Glade生成的C代码必须使用特定的许可协议吗?

2. 在Glade创建用户界面

2.1 向一个窗口加入一个控件后, 它充满了整个窗口,不能再加入任何其它控件.
2.2 如何更改控件颜色, 比如让一个标签变成红色?
2.3 如何在按钮上增加一幅位图?
2.4 如何一次性加入几个相同的控件?(这句不太好译)
2.5 使用滚动窗口时出现以下警告信息:
Gtk-WARNING **: gtk_scrolled_window_add(): cannot add non scrollable
widget use gtk_scrolled_window_add_with_viewport() instead
2.6 Glade支持哪些图像格式?
2.7 如何加入一个回调函数(signal handler)?

3. 联编(Building) Glade生成的C代码

3.1 如何联编Glade生成的代码?

3.2 出现错误信息:
aclocal: configure.in: 10: macro `AM_PATH_GTK’ not found in library

3.3 出现错误信息:
** CRITICAL **: file glade_gnome.c: line 939
(glade_gnome_write_menu_item_source):
assertion `source_buffer != NULL’ failed.

4. 使用Glade生成的C代码

4.1 Glade生成了哪些文件?
4.2 哪些文件可以被安全地修改,哪些会被程序覆盖?
4.3 如何向工程中加入自己的资源文件?
4.4 如何向工程中加入库?
4.5 在回调函数中如何获得一个控件的指针?
4.6 如何获得另一个窗口中的控件的指针?
4.7 如何获得GtkOptionMenu的值?
4.8 当GtkOptionMenu值改变时如何调用一个函数?
4.9 如何连接到GtkAdjustment的信号?
4.10 窗口显示之前如何向其中的GtkCList控件中加入一行?

==============================================================================

1. 关于Glade的一般信息
==================================

1.1 Glade有哪些文档?

在GNOME版本的Glade中,帮助菜单中有快速入门教材(Quick-Start Guide)、手册和FAQ。但这些并没有涉及到使用Glade的各方面。

网络上还有一些其它文件:

o 西班牙语入门教材 – http://tigre.aragon.unam.mx/m3d/links_es.htm
o 意大利语入门教材 – http://digilander.iol.it/robang/glade

我并没有看到过专门介绍Glade的书, 在Wrox出版社为Linux开发者出版的一本书中其中的一章介绍到了Glade.

当有其它相关资料时,我会在http://glade.gnome.org上更新。

1.2 没有范例代码?

在Glade的examples/editor目录下,有一个简单的文件编辑器例子。
如果你的Glade是个二进制包 (比如一个RPM包), 这些文件可能被
安装在/usr/doc/glade-X.X.X目录下. 如果你找不到
从http://glade.gnome.org.下载Glade的tar包。

这个网站有一些用Glade创建的程序的连接.
其中可能有一些有用的代码. 请浏览http://glade.gnome.org上的Application页面.

1.3 Glade生成的C代码必须使用特定的许可协议吗?

不。Glade生成的C代码可以使用任何许可协议。

不过基于开源软件的精神我们建议你使用GPL或LGPL许可协议。

==============================================================================

2. 在Glade创建用户界面
=====================================

2.1 向一个窗口加入一个控件后, 它充满了整个窗口,不能再加入任何其它控件.

这并不是Glade的一个Bug! 在GTK+中使用容器放置控件。
经常使用的容器在调色板上主页面的下部。
试着向窗口中加入一个纵向盒状容器,再向盒状容器中加入一个表格。
知道如何做了吧?

如果你想把控件放置在一个特定的位置上,
可以试一下Fixed容器。不过不推荐使用这种方法,原因是当窗口或对话框改变大小
时就显得不好看,而且当把标签和按钮上的文字翻译成其它语言后无法匹配(fit)。

2.2 如何更改控件颜色, 比如让一个标签变成红色?

可以使用GTK+的 rc文件来设置控件的颜色和字体。
如果打开了Glade中 ‘Set Widget Names’工程选项,可以使用控件的名字更简单地指向控件。
请查看http://developer.gnome.org/doc/API上的GTK+资源文件文档。

还可以在代码中调用来gtk_widget_modify_style()改变控件的样式,比如:

GdkColor red = { 0, 65535, 0, 0 };
GtkRcStyle *rc_style = gtk_rc_style_new ();
rc_style->fg[GTK_STATE_NORMAL] = red;
rc_style->color_flags[GTK_STATE_NORMAL] |= GTK_RC_FG;
gtk_widget_modify_style (widget, rc_style);
gtk_rc_style_unref (rc_style);

2.3 如何在按钮上增加一幅位图?

创建一个按钮,并在弹出菜单上选择 ‘Remove Label’ (去掉标签)。
你现在可以向按钮中加入任何控件,比如一个内有一幅位图和一个标签的横向盒状容器。
将来Glade会有更简单的方法来创建这种按钮。

2.4 如何一次性加入几个相同的控件?

在调色板中选择控件的同时按下 ‘Control’ 键。这个控件就会一直处于选择状态,
直到你选择了另一个控件或选择器。

2.5 使用滚动窗口时出现以下警告信息:

Gtk-WARNING **: gtk_scrolled_window_add(): cannot add non scrollable
widget use gtk_scrolled_window_add_with_viewport() instead

可以忽略这个警告。这个信息是在提醒人们更新他们的代码,
因为从GTK+ 1.0到GTK+ 1.2滚动窗口发生很大的变化,不过Glade代码是正确的。
这个警告不会出你的最终程序中。

2.6 Glade支持哪些图像格式?

如果是创建GTK+程序,图像必须是XPM格式。
(GTK+ 2.0包含了gdk-pixbuf库,可以支持很多种图像格式。)

如果是创建GNOME程序,可以使用大多数图像格式。
不过大多数图像比如图标建议选择PNG格式。

注意: 可以用GIMP程序或ImageMagick工具箱中的’convert’功能来转换图像格式。

2.7 如何加入一个回调函数(signal handler)?

按以下步骤:

o 选择要加入回调函数的控件。
o 选择属性窗口中的 ‘Signals’页。
o 点击’Signal:’域右边的’…’按钮,会弹出一个关于这个控件的信号列表。
o 选择准备用来连接函数的信号。
o Glade自动在’Handler:’域创建这个回调函数的名字,你可以更改这个名字。
o 按 ‘Add’按钮把这个回调函数加入此控件的回调函数列表中。

当产生C代码后,在callbacks.c文件中会有一个空的回调函数,你可以在其中
加入自己的代码。

==============================================================================

3. 联编(Building) Glade生成的C代码
=========================================

3.1 如何联编Glade生成的代码?

联编生成的C代码需要automake版本 >= 1.4以及autoconf版本 >= 2.13。
如果打开gettext支持,还需要gettext版本 >= 0.10.35。
README文件中’Requirements’节有相关的链接。

在工程的根目录下运行’./autogen.sh’来启动
automake、autoconf和相关的程序来产生Makefiles文件。configure的任何
选项都可以直接写在./autogen.sh后面,比如:

./autogen.sh –prefix /usr/local/gnome

接下来运行’make’来创建你自己的程序。

注意: 如果是GNOME程序,必须再运行’make install’,这样位图文件可以被正确地安装。
不然,程序可以运行但看不到位图。

3.2 出现错误信息:

aclocal: configure.in: 10: macro `AM_PATH_GTK’ not found in library

这条错误信息的意思是找不到gtk.m4文件。(gtk.m4文件是一套m4的宏,
GTK+的一部分,用来创建GTK+程序。)
aclocal程序(automake程序的一部分)查找宏并加到程序根目录下的aclocal.m4文件中。

想知道GTK+安装在哪里,运行 ‘gtk-config –prefix’即可。
gtk.m4文件应该在’share/aclocal’子目录下。
想知道aclocal程序使用的目录,可以运行 ‘aclocal –print-ac-dir’。

应该把GTK+ m4文件的安装目录加入到ACLOCAL_FLAGS环境变量中,
比如:如果GTK+ m4文件在目录/usr/local/share/aclocal中,
在$HOME/.profile文件中加入以下内容:

export ACLOCAL_FLAGS="-I /usr/local/share/aclocal/"

3.3 出现错误信息:

** CRITICAL **: file glade_gnome.c: line 939
(glade_gnome_write_menu_item_source):
assertion `source_buffer != NULL’ failed.

在仅仅是GTK+程序中使用GNOME系统菜单项。
编辑所有的菜单确保每个菜单项的"Stock"属性设为"None"。

==============================================================================

4.使用Glade生成的C代码
======================================

4.1 Glade生成了哪些文件?

以下是一些缺省的输出文件,如果你改变了工程选项,文件名称可能有所不同。

autogen.sh - 按正确次序运行automake, autoconf和其它相关程序的脚本文件,
使创建程序更简单。可以把给configure参数直接传递给它。运行
后可以键入 ‘make’来创建程序。
configure.in - 传递给autoconf的标准脚本,用来产生configure脚本。

Makefile.am - 传递给automake的标准make规则,用来产生Makefile.in文件。
Makefile.in可以被configure脚本转换为Makefile文件。

acconfig.h - 包含一些宏,这些宏在configure脚本中被设置,并加入到
config.h头文件中(在所有源文件中头文件应该是最先被包含进来的)。
gettext支持需要使用到大多数宏(ENABLE_NLS, HAVE_CATGETS,
HAVE_GETTEXT, HAVE_LC_MESSAGES, HAVE_STPCPY),
GNOME则需要HAVE_LIBSM宏 (but it doesn’t hurt a GTK+ app),
另一些是由Glade加入的(PACKAGE_LOCALE_DIR, PACKAGE_DATA_DIR,
PACKAGE_SOURCE_DIR).
stamp-h.in - automake用来做为时间戳,以便重新创建出一些已生成的文件。

AUTHORS - 这些文件全是空文件, 以符合GNU的规定。
ChangeLog
NEWS
README

src/Makefile.am - 标准automake文件。
src/main.c - 包含main()主函数,主函数可以创建每一个窗口/对话框。

src/interface.h – 函数声明,调用这些函数可以生成Gladed中创建的窗口和对话框。

src/interface.c – 生成窗口、对话框及其它控件的代码。

src/callbacks.h – 信号及所写回调函数的声明。

src/callbacks.c – 信号处理及回调函数的代码。
src/support.h – 一些支持函数的声明,包括lookup_widget()函数,可以通过这个
函数得到控件的指针。
src/support.c – 支持函数的代码。

如果开启gettext支持, 会创建po子目录,POTFILES.in文件以及一个单独的ChangeLog
文件。POTFILES.in文件列出了包含有可翻译字符串的资源文件,你写的任何资源文件
也要加在里面。

对于GNOME程序,会有macros目录,包含所有用来创建工程的m4宏。(这些宏做为
GNOME的一部分应该已经安装好,但不幸的是在GNOME 1.0.X中并没有这样做。希望在
在GNOME今后的版本中会加以改正,这样就不需要这个目录了)。

*注意*: 如果在创建工程后改变了 ‘GNOME Support’或 ‘Gettext Support’工程选项,
则需要更新文件比如configure.in, Makefile.am。最好的解决方法就是在’Project
Options’对话框里改变工程目录,重新创建工程。不过得把加在回调函数的所有代码
拷贝出来。另一个方法是删除autogen.sh, configure.in, Makefile.am, src/Makefile.am,
和src/main.c文件,用Glade重新生成这些文件。不过如果你改变过这些文件,需要再把这些
更改加进去。(希望将来Glade能够改得更好一些。)

4.2 哪些文件可以被安全地修改,哪些会被程序覆盖?

Glade不会覆盖大多数文件。如果联编文件(build files)不存在,它将会重建
这些文件 (相应的工程选项要被设置)。

Glade会覆盖的文件有:

interface.h
interface.c
support.h
support.c

(如果你在工程对话框中更改了这些文件的名称,可能与以上名称不同)

这些文件最顶部会有’DO NOT EDIT’(不要编辑)的信息。

如果你加入或更新了任何信号处理,它们会加入在callbacks.h和callbacks.c文件中。
因此你加入的任何回调函数代码完全安全。如果你改变了一个回调函数的名称,则
需要把删除旧版本并将代码拷到新函数中。

4.3 如何向工程中加入自己的资源文件?

把资源文件(和任何头文件)加入到src/Makefile.am(project1_SOURCES变量的值)中
(假定’project1′是你的工程名称)。

如果使用gettext, 应该同时把资源文件加入到topo/POTFILES.in中,以便能够翻译这些
字符串。

4.4 如何向工程中加入库?

你需要为在工程的configure.in文件中的库加入一个测试,
确认CPPFLAGS变量和LIBS变量已更新为库的说明。(这句不太懂)
(CPPFLAGS变量包含了所有传递给C预处理程序的 -I 标记,
LIBS变量包含了传递给连接器的-l 和 -L 选项)。

autoconf程序提供宏如 AC_CHECK_HEADER和AC_CHECK_LIB,可以
用来检查普通的头文件和库。

许多GTK+和Gnome库提供了配置脚本比如gtk-config,可以输出
所需要的CPPFLAGS和LIBS标记。

例如,libxml提供了一个xml配置脚本,可以象下面那样使用:

dnl Get libxml flags & libs
AC_PATH_PROG(xml_config, xml-config)
if test "x$xml_config" = "x"; then
AC_MSG_ERROR([*** xml-config not found.])
fi

XML_CFLAGS=`$xml_config –cflags 2>/dev/null`
XML_LIBS=`$xml_config –libs 2>/dev/null`
CPPFLAGS="$CPPFLAGS $XML_CFLAGS"
LIBS="$LIBS $XML_LIBS"

注意: 确保把你的configure.in测试放在调用AC_OUTPUT前面。

4.5 在回调函数中如何获得一个控件的指针?

如果有窗口内任一控件的指针,你可以通过调用Glade提供的lookup_widget()函数来获得
窗口内其它控件的指针(在support.c文件中)。

传入参数为窗口内任一控件的指针和想要得到指针的控件名称。通常在信号处理函数中可以
用它的第一个参数做为lookup_widget()函数的第一个参数,例如:

void
on_button1_clicked (GtkButton *button,
gpointer user_data)
{
GtkWidget *entry1;

entry1 = lookup_widget (GTK_WIDGET (button), "entry1");


}

注意如果使用libglade,以上代码将不会起作用。相对应的代码应该是:

void
on_button1_clicked (GtkButton *button,
gpointer user_data)
{
GladeXML* xml;
GtkWidget* entry1;

xml = glade_get_widget_tree (GTK_WIDGET (button1));
entry1 = glade_xml_get_widget (xml, "entry1");


}

4.6 如何获得另一个窗口中的控件的指针?

需要跟踪所有上层(toplevel)窗口的指针。对于简单的程序,可以用全局变量来存储
这些指针。

对于大多数复杂的程序可以使用gtk_object_set_data()及相关函数来存储在此窗口中
另一个窗口的指针。例如,如果你想创建一个对话框并希望对主窗口中的控件进行操作,
可以这样做:

dialog = create_dialog1 (); /* Call the function generated by Glade. */
gtk_object_set_data (GTK_OBJECT (dialog), "main_window", main_window);

当在对话框代码需要对主窗口进行操作时,可以这样做:

main_window = gtk_object_get_data (GTK_OBJECT (dialog), "main_window");

注意: 必须要很小心地确保这个指针有效。如果这个指针所指向的窗口被销毁,确保再
也不使用这个指针,否则你的程序将会崩溃。

4.7 如何获得GtkOptionMenu的值?

调用gtk_menu_get_active()并传入GtkOptionMenu控件的menu做为参数,可以获得当前
所选菜单项。可以用g_list_index()函数查找到它在菜单中的索引号:

void
on_button1_clicked (GtkButton *button,
gpointer user_data)
{
GtkWidget *option_menu, *menu, *active_item;
gint active_index;

option_menu = lookup_widget (GTK_WIDGET (button), "optionmenu1");
menu = GTK_OPTION_MENU (option_menu)->menu;
active_item = gtk_menu_get_active (GTK_MENU (menu));
active_index = g_list_index (GTK_MENU_SHELL (menu)->children, active_item);

g_print ("Active index: %i\n", active_index);
}

4.8 当GtkOptionMenu值改变时如何调用一个函数?
Glade目前并不支持, 不过你可以手工设置。

创建一个窗口后,获得option menu控件指针,并连接此控件菜单(menu)的"deactivate":

window1 = create_window1 ();
option_menu = lookup_widget (window1, "optionmenu1");
gtk_signal_connect (GTK_OBJECT (GTK_OPTION_MENU (option_menu)->menu),
"deactivate", GTK_SIGNAL_FUNC (on_option_selected),
NULL);

向callbacks.c中加入一个信号处理. 你可以象上一个问题那样获得所选项的索引号(index):

static void
on_option_selected (GtkMenuShell *menu_shell,
gpointer data)
{
GtkWidget *active_item;
gint item_index;

active_item = gtk_menu_get_active (GTK_MENU (menu_shell));
item_index = g_list_index (menu_shell->children, active_item);

g_print ("In on_option_selected active: %i\n", item_index);
}

4.9 如何连接到GtkAdjustment的信号?
Glade目前并不支持,不过你可以象问题3.6(应为4.Cool中的一样手工来设置。

创建窗口后,获得包含有adjustment的容器的指针,并连接到
"changed"或"value_changed"信号:

window1 = create_window1 ();
hscale = lookup_widget (window1, "hscale1");
gtk_signal_connect (GTK_OBJECT (GTK_RANGE (hscale)->adjustment),
"changed", GTK_SIGNAL_FUNC (on_adjustment_changed),
NULL);

4.10 窗口显示之前如何向其中的GtkCList控件中加入一行?

用Glade生成的’create’ 函数创建一个窗口后,可以使用
lookup_widget()函数获得GtkCList控件指针,并按需要加入行,例如:

GtkWidget *window, *clist;
gchar *row[2]; /* Our GtkCList only has 2 columns. */

window = create_window1 ();
clist = lookup_widget (window, "clist1");

row[0] = "Hello";
row[1] = "World";
gtk_clist_append (GTK_CLIST (clist), row);

row[0] = "Second";
row[1] = "Row";
gtk_clist_append (GTK_CLIST (clist), row);

gtk_widget_show (window1);