VC++.NET中添加由C#编译的DLL引用的方法
http://blog.youkuaiyun.com/much0726/article/details/4986627关于VC.NET的资料网络上确实非常少,除了一些MSDN上的资料和英文资料就没有了,查找引用DLL文件的
方法都找不到,DLL文件是有C#编译的,是托管模块,即.netModule ,如果敢兴趣可以查查。添加托管
的DLL文件和C#中的方法是一样的:
1.打开项目,选择项目右键菜单中的引用。
2.通用属性->框架和引用,添加新引用就可以了。
注意,在项目中不要使用:
#using "dll的绝对路径"
using namespace 命名空间
这样会引起引用冲突,而且这样的做法也是不对的,在调用DLL的方法中会提示为导入的DLL引用模块等
错误提示信息。
小结
VC.net比较也是托管语言,使用CLR公共语言库编译,因此也是需要FRAMEWORK支持的,但是本身有是支
持系统库文件的调用USER32.DLL等,因此熟悉C#的人想开发类似MFC或者WIN32程序的时候VC.Net倒是挺
适合的。毕竟C#调用API挺麻烦的。
========
VC++和VC++.NET中与图像处理有关的几个概念、结构和类
http://blog.youkuaiyun.com/okaimee/article/details/5559064最近一直在看VC++有关图像处理方面的书,终于把以前一直混淆的几个概念、结构和类弄清楚了,
特整理如下。如有错误,请大家批评指正,不胜感激。下一步想好好学习学习OpenCV,希望也能总结点
东西。
一、DDB与DIB位图
一个Windows的位图实际上是一些和显示像素相对应的位阵列,它有两种类型:一种称之为GDI
(Graphic Device Interface)位图,另一种是DIB位图(Device-Independent Bitmap)。
GDI位图包含了一种和Windows的GDI模块有关的Windows数据结构,该数据结构是与设备有关的,故
此位图又称为DDB位图(Device-Dependent Bitmap)。当用户的程序取得位图数据信息时,其位图显示方
式视显示卡而定。由于GDI位图的这种设备依赖性,当位图通过网络传送到另一台PC,很可能就会出现问
题。
DIB比GDI位图有很多编程优势,例如它自带颜色信息,从而使调色板管理更加容易。且任何运行
Windows的机器都可以处理DIB,并通常以后缀为.BMP的文件形式被保存在磁盘中或作为资源存在于程序
的EXE或DLL文件中。
二、CBitmap类、BITMAP结构
CBitmap类继承自CGdiObject,是封装了图形设备接口(GDI)的位图,提供成员函数来操纵位图。
要使用一个CBitmap对象,构造该对象,用初始化成员函数之一把一个位图句柄连接到该对象,然后调用
该对象的成员函数。
CBitmap类主要用于处理DDB位图,封装了与DDB位图操作函数相关的数据结构和操作函数。结构体
BITMAP定义了DDB位图的类型、宽度、高度、颜色和像素值,其定义如下:
typedef struct _tagBITMAP
{
LONG bmType ; // set to 0
LONG bmWidth ; // width in pixels
LONG bmHeight ; // height in pixels
LONG bmWidthBytes ; // width of row in bytes
WORD bmPlanes ; // number of color planes
WORD bmBitsPixel ; // number of bits per pixel
LPVOID bmBits ; // pointer to pixel bits
}
BITMAP, * PBITMAP ;
而CBitmap的LoadBitmap、CreateCompatibleBitmap、SetBitmapBits、GetBitmap等成员函数则定义
了对DDB位图的装载、创建、设定位值和属性查询等操作。
创建或装入内存的位图必须用CDC::SelectObject函数来将其选入设备上下文中,然后用CDC的
BitBlt或StretchBlt函数显示出来,这两个函数的原型如下:
BOOL BitBlt(int x, int y, int nWith, int nHeight, CDC* pSrcDC, int xSrc, int ySrc, DWORD
dwRop);
该函数把源设备上下文中的位图复制到本身的设备上下文中,两个设备下下文可以是内存设备下下
文,也可以是同一个设备上下文。
三、Image类和Bitmap类
在VC++.NET中,GDI+的Image类封装了对BMP、GIF、JPEG、PNG、TIFF、WMF(Windows元文件)和EMF(
增强WMF)图像文件的调入、显示、格式转换以及简单处理(如缩放、旋转、拉伸等)的功能。而Bitmap类
(注意不是结构BITMAP)是从Image类继承的一个图像(另一个从Image继承的类是Metafile类),它封
装了Windows位图操作的常用功能。例如,Bitmap::SetPixel和Bitmap::GetPixel分别用来对位图进行读
写像素操作,从而可以为图像的柔化和锐化处理提供一种可能。这些功能和MFC的新类CImage功能基本一
样,如果仅用于图像的读取与显示,用Bitmap类或Image类是不错的选择,如果是做图像处理,则CImage
可能更符合MFC程序员的编程习惯。
四、CImage类
CImage类是VC++.NET中MFC和ATL共享的新类,它能从外部磁盘中调入一个JPEG、GIF、BMP和PNG格式
的图像文件加以显示,而且这些文件格式可以相互转换。
CImage既能处理DIB位图也能处理非DIB位图,但你可以仅用DIB位图来Create或CImage::Load。你也
可以使用Attach把一个非DIB位图连接给CImage对象,但你不能使用下列方法,这些方法仅支持DIB位图
:GetBits、GetColorTable、GetMaxColorTable、Entries、GetPitch、GetPixelAddress、IsIndexed、
SetColorTable。要确定一个连接的位图是否是一个DIB位图,调用IsDibSection。
由于CImage在不同的Windows操作系统中其某些性能是不一样的,因此在使用时要特别注意。例如,
CImage::PlgBlt和CImage::MaskBlt只能在 Windows NT 4.0 或更高版本中使用,但不能运行在Windows
95/98 应用程序中。CImage::AlphaBlend和CImage::TransparentBlt也只能在 Windows 2000/98或其更
高版本中使用。即使在Windows 2000运行程序还必须将stdafx.h文件中的WINVER和_WIN32_WINNT的预定
义修改成0x0500才能正常使用。 CImage可以在MFC或ATL中使用。当使用CImage创建一个项目时,必须包
含atlimage.h文件。
CImage封装了DIB(设备无关位图)的功能,因而可以让我们能够处理每个位图像素。
CImage提供了HBITMAP操作符,因此HBITMAP为参数的地方,都可以用CImage来替代。
五、传统的VC++中的图像处理方法与VC++.NET中的图像处理方法
由于DIB图不依赖于具体设备,因此可以用来永久性地保存图象。DIB一般是以*.BMP文件的形式保存
在磁盘中的,有时也会保存在*.DIB文件中。运行在不同输出设备下的应用程序可以通过DIB来交换图象
。因而在数字图象处理中会经常用到DIB位图。DIB还可以用一种RLE算法来压缩图像数据,但一般来说
DIB是不压缩的。
在VC++.net之前MFC未提供现成的类来封装DIB,这给MFC用户带来很多不便。因为用户要想使用DIB
,首先应该了解DIB的结构。
在内存中,一个完整的DIB由两部分组成:一个BITMAPINFO结构和一个存储像素阵列的数组。
BITMAPINFO描述了位图的大小,颜色模式和调色板等各种属性,其定义为
typedef struct tagBITMAPINFO {
BITMAPINFOHEADER bmiHeader;
RGBQUAD bmiColors[1]; //颜色表
} BITMAPINFO;
RGBQUAD结构用来描述颜色,其定义为
typedef struct tagRGBQUAD {
BYTE rgbBlue; //蓝色的强度
BYTE rgbGreen; //绿色的强度
BYTE rgbRed; //红色的强度
BYTE rgbReserved; //保留字节,为0
} RGBQUAD;
注意,RGBQUAD结构中的颜色顺序是BGR,而不是平常的RGB。
BITMAPINFOHEADER结构包含了DIB的各种信息,其定义为
typedef struct tagBITMAPINFOHEADER{
DWORD biSize; //该结构的大小
LONG biWidth; //位图的宽度(以像素为单位)
LONG biHeight; //位图的高度(以像素为单位)
WORD biPlanes; //必须为1
WORD biBitCount //每个像素的位数(1、4、8、16、24或32)
DWORD biCompression; //压缩方式,一般为0或BI_RGB (未压缩)
DWORD biSizeImage; //以字节为单位的图象大小(仅用于压缩位图)
LONG biXPelsPerMeter; //以目标设备每米的像素数来说明位图的水平分辨率
LONG biYPelsPerMeter; //以目标设备每米的像素数来说明位图的垂直分辨率
DWORD biClrUsed; /*颜色表的颜色数,若为0则位图使用由biBitCount指定的最大颜色数*/
DWORD biClrImportant; //重要颜色的数目,若该值为0则所有颜色都重要
} BITMAPINFOHEADER;
与DDB不同,DIB的字节数组是从图象的最下面一行开始的逐行向上存储的,也即等于把图象倒过来
然后在逐行扫描。另外,字节数组中每个扫描行的字节数必需是4的倍数,如果不足要用0补齐。
DIB可以存储在*.BMP或*.DIB文件中。DIB文件是以BITMAPFILEHEADER结构开头的,该结构的定义为
typedef struct tagBITMAPFILEHEADER {
WORD bfType; //文件类型,必须为“BM”
DWORD bfSize; //文件的大小
WORD bfReserved1; //为0
WORD bfReserved2; //为0
DWORD bfOffBits; //存储的像素阵列相对于文件头的偏移量
} BITMAPFILEHEADER;
紧随该结构的是一个BITMAPINFOHEADER结构,然后是RGBQUAD结构组成的颜色表(如果有的话),文件
最后存储的是DIB的像素阵列。
DIB的颜色信息储存在自己的颜色表中,程序一般要根据颜色表为DIB创建逻辑调色板。在输出一幅
DIB之前,程序应该将其逻辑调色板选入到相关的设备上下文中并实现到系统调色板中,然后再调用相关
的GDI函数(如::SetDIBitsToDevice或::StretchDIBits)输出DIB。在输出过程中,GDI函数会把DIB转换
成DDB,这项工作主要包括以下两步:将DIB的颜色格式转换成与输出设备相同的颜色格式;将DIB像素的
逻辑颜色索引转换成系统调色板索引。
看到这么多结构,你是不是已经眼花缭乱了,然而更不幸的还在后面。以上说的DIB是Windows中的
DIB,还有一种DIB是OS/2 DIB,它和Windows DIB的主要区别是位图信息结构(信息头和颜色表结构)。
它们的图像数据的存储方式是完全一样的。在OS/2 DIB中,与WIndows DIB的BITMAPFILEHEADER、
BITMAPINFOHEADER、RGBQUAD相对应的结构分别是BITMAPCOREHEADER、BITMAPCOREINFO、和RGBTRIPLE。
它们的定义分别如下:
typedef struct tagBITMAPCOREHEADER // bmch
{
DWORD bcSize ; // size of the structure = 12
WORD bcWidth ; // width of image in pixels
WORD bcHeight ; // height of image in pixels
WORD bcPlanes ; // = 1
WORD bcBitCount ; // bits per pixel (1, 4, 8, or 24)
}
BITMAPCOREHEADER, * PBITMAPCOREHEADER ;
typedef struct tagBITMAPCOREINFO // bmci
{
BITMAPCOREHEADER bmciHeader ; // core-header structure
RGBTRIPLE bmciColors[1] ; // color table array
}
BITMAPCOREINFO, * PBITMAPCOREINFO ;
typedef struct tagRGBTRIPLE // rgbt
{
BYTE rgbtBlue ; // blue level
BYTE rgbtGreen ; // green level
BYTE rgbtRed ; // red level
}
RGBTRIPLE;
因此再操作之前你应该先根据BITMAPINFOHEADER的大小来判断是哪一种DIB,然后再进行操作。
由于MFC未提供一个封装好的易用的DIB类,用户在使用DIB时将面临繁重的Windows API编程任务。
不信你看看Microsoft提供的MFC的DibLook例程提就知道了。所以传统的图像处理方法一般都会把这些
Win32 SDK中的操作DIB位图的APIs做一个封装,作为一个通用的类来使用,以减少后续算法编写中的编
程负担。
VC++.NET提供了多种常用图像文件格式(如BMP、TIF、GIF、JPEG与PNG等)的输入/输出模块,ATL中
的CImage类极大地简化了图像数据的操作,因此VC++.NET中的图像处理应以CImage为基础。
现在市面上有一些冠名教用VC++.NET做图像处理的书,但是编程方法还是老的一套,没有跳出设备相
关位图(DDB)与设备无关位图(DIB)概念的条框,编程方法比较复杂、繁琐、低效。用VC++.NET环境做图像
处理,应该以CImage类为基础,完全跳出DDB、DIB概念的条框,才能使得处理方法返朴归真,大为简化。
转载自:http://conner-wang.spaces.live.com
========
实例解析C++/CLI中的接口与泛型
http://dev.yesky.com/msdn/285/3039285.shtml接口
某些时候,让不相关的类分享一组公有成员,以便产生相同的行为,是非常有用的。一个最基本的
方法可能是通过一个公共的基类来定义它们,但这种方法太受局限,因为它要求这些类通过继承而互相
关联,另外,它们也许还有着各自的基类,且CLI类型只支持单一类继承。
C++/CLI提供了一种方法,可利用多个类实现一组通用的功能,这就是我们通称的"接口",而一个接
口则是一组成员函数的声明。要注意,这些函数只是声明,没有定义,也就是说,一个接口定义了一个
由抽象函数组成的类型--这些函数实际上是纯虚函数,且在适当的时候,这些函数由客户类来实现。一
个接口可允许不相关的类用同一名称和类型,实现同一功能,而无须要求这些类分享公共基类。在例1中
演示了怎样定义一个接口。
例1:
using namespace System;
public interface class ICollection
{
void Put(Object^ o); //隐式public abstract
Object^ Get(); //隐式public abstract
};
一个接口的定义看上去非常像一个类,除了用interface取代了ref或value,所有的函数都没有函数
体,且均隐式为public和abstract。按照通常的约定,一个接口名带有起始字母I,后再接一个大写字母
。(接口类与接口结构是等价的。)与类相似,一个接口也能有public或private访问可见性。
一个接口能有一个或多个"基接口",在这种情况下,它将继承这些接口中的所有抽象函数,例如,
在例2中,接口I2显式继承自I1,而I3显式继承自I1与I2,并通过I2隐式继承自I1。
例2:
interface class I1 { /* ... */ };
interface class I2 : I1 { /* ... */ };
interface class I3 : I1, I2 { /* ... */ };
一个类可像从基类继承时那样,来实现一个接口,见例3。
例3:
public ref class List : ICollection
{
public:
void Put(Object^ o)
{
// ...
}
Object^ Get()
{
// ...
}
// ...
};
一个类能实现一个以上的接口,在这种情况下,必须使用逗号来分隔接口列表,顺序倒不是很重要
。当然,一个类在实现一个或多个接口时,也能显式地带有一个基类,在这种情况下,基类通常(但不
是必须)写在最前面。
如果一个类实现了一个接口,但没有定义接口中所有的函数,这个类就必须声明为abstract。当然
了,任何从抽象类继承而来的类也是抽象类,除非定义了之前的这些抽象函数。
========
实例解析C++/CLI之代理与事件
http://dev.yesky.com/msdn/77/2661077.shtml在C++/CLI中,代理是对函数进行包装的对象;而事件是一种为客户程序提供通知的类机制。
在前几篇文章中,已经多次演示了如果让一个句柄在不同的时间,被引用至不同的对象,从而以更
抽象的方法来解决程序中的问题,但是,也能使用代理通过函数来达到同样的效果;代理是包装了函数
的一个对象,且对实例函数而言,也能通过特定的实例,与这些函数发生联系。一旦一个代理包装了一
个或多个函数,你就能通过代理来调用这些函数,而无须事先了解包装了哪些函数。
请看例1中的代码,在标号1中,定义一个代理类型Del,由于使用了上下文关键字delegate,所以有
点像函数的声明,但与函数声明不同的是,此处声明的是一个代理类型Del的实例,其可包装进任意接受
一个int类型作为参数并返回一个int值类型的函数(任意有效的参数列表及返回类型组合都是允许的)
。一旦定义了某种代理类型,它只能被用于包装具有同样类型的函数;代理类型可被定义在源文件中或
命名空间的范围内,也能定义在类中,并可有public或private访问控制属性。
例1:
using namespace System;
ref struct A
{
static int Square(int i)
{
return i * i;
}
};
ref struct B
{
int Cube(int i)
{
return i * i * i;
}
};
/*1*/
delegate int Del(int value);
int main()
{
/*2*/ Del^ d = gcnew Del(&A::Square);
/*3*/ Console::WriteLine("d(10) result = {0}", d(10));
/*4*/ B^ b = gcnew B;
/*5*/ d = gcnew Del(b, &B::Cube);
/*6*/ Console::WriteLine("d(10) result = {0}", d(10));
}
静态函数A::Square与实例函数B::Cube对Del来说,都具有相同的参数类型及返回类型,因此它们能
被包装进同类型的代理中。注意,即使两个函数均为public,当考虑它们与Del的兼容性时,它们的可访
问性也是不相关的,这样的函数也能被定义在相同或不同的类中,主要由程序员来选择。
一旦定义了某种代理类型,就可创建此类型实例的句柄,并进行初始化或赋值操作,如标号2中所示
的静态函数A::Square,及标号5中所示的实例函数B::Cube。(此处只是出于演示的目的,否则把Cube做
成实例函数没有任何好处。)
创建一个代理实例涉及到调用一个构造函数,如果是在包装一个静态函数,只需传递进一个指向成
员函数的指针;而对实例函数而言,必须传递两个参数:一个实例的句柄及指向实例成员函数的指针。
在初始化代理实例之后,就能间接地调用它们包装的函数了,用法与直接调用原函数一样,只不过
现在用的是代理实例名,如标号3与6,由包装函数返回的值也是像直接调用函数时那样获得。如果一个
代理实例的值为nullptr,此时再试图调用被包装的函数,会导致System::NullReferenceException类型
异常。
以下是输出:
d(10) result = 100
d(10) result = 1000
传递与返回代理
有时,把包装好的函数传递给另一个函数,会非常有用,接受一方的函数并不知道会传递过来哪个
函数,并且它也无须关心,只需简单地通过包装好的代理,间接调用此函数就行了。
下面以集合中元素排序来说明,大多数时候,集合中元素排序所依据的规则,只在对某对元素进行
比较的方法上存在区别。如果在运行时提供进行比较的函数,一个排序过程就能用相应定义的比较函数
排出任意的顺序,请看例2。
例2:
using namespace System;
ref struct StrCompare
{
static int CompareExact(String^ s1, String^ s2)
{
Console::WriteLine("Comparing {0} and {1} " "using CompareExact", s1, s2);
// ...
return 0;
}
static int CompareIgnoreCase(String^ s1, String^ s2)
{
Console::WriteLine("Comparing {0} and {1}" "using CompareIgnoreCase", s1, s2);
// ...
return 0;
}
};
delegate int Compare(String^ s1, String^ s2);
/*1*/
Compare^ FindComparisonMethod()
{
// ...
}
void Sort(Compare^ compare)
{
int result;
/*3*/ result = compare("Hello", "Hello");
/*4*/ result = compare("Hello", "HELLO");
/*5*/ result = compare("Hello", "Hell");
}
int main()
{
/*6*/ Sort(gcnew Compare(&StrCompare::CompareIgnoreCase));
/*7*/ Sort(FindComparisonMethod());
/*8*/ FindComparisonMethod()("Red", "RED");
}
Compare代理类型可对任意接受两个String^参数并返回一个int结果的函数进行包装,在此,有两个
函数为StrCompare::CompareExact和StrCompare::CompareIgnoreCase。
在标号6中,创建了一个Compare代理类型的实例,用它来包装StrCompare::CompareIgnoreCase,并
把此代理句柄传递给Sort函数,其将会利用比较函数进一步进行处理。
正如大家所看到的,Sort可接受一个代理类型的参数--而此参数可像其他函数参数一样,可为传值
、传址、传引用。
在标号7中,调用了FindComparisonMethod函数,其返回一个Del代理类型,接着在标号7及8中调用
了包装过的函数。此处要重点说一下标号8:首先,FindComparisonMethod函数是被调用来获取代理实例
--其常用于调用底层函数;其次,这两个函数的调用操作符都有同等的优先级,所以它们从左至右调用
。
FindComparisonMethod函数中也用了一些逻辑用于确定到底需要包装哪个函数,此处就未作详细说
明了。
========
VC++.NET中使用GDI+创建特效字体
http://dev.yesky.com/msdn/338/2037838.shtml来自于微软.NET技术的C++托管扩展所包含的GDI+技术功能十分强大,本文将介绍如何使用GDI+的画
刷来绘制文本。
一、使用画刷绘制文本的基本技术
本文所带的例子程序允许用户定义所要显示的文本、字体尺寸、显示文本所用的画刷(网格画刷或
渐变画刷)以及绘制文本的颜色等。例子代码下载:GDIPlusTextWithBrushes.zip
下面是GDI+中使用渐变画刷或网格画刷绘制文本的基本步骤:
1、 在控件的绘制(Paint)事件中添加一个事件处理函数。
在这个处理函数中进行绘制文本的相关处理,这样控件才能正确地进行重绘。
2、 获取一个图形(Graphics)对象。
正如我们所熟悉的设备上下文一样,图形对象是NET封装的一个绘制平面,例如,当在一个
PictureBox控件上进行绘制时,可以调用PictureBox::CreateGraphics方法来获取一个Graphics对象,
并在控件上绘图时使用这个图形(Graphics)对象。互联网上有很多例子都是这么做的,但是,有一个
问题是,这样得到的Graphics对象不是永久对象,如果用户从当前程序转到另一个应用程序并再次返回
时,这个控件将无法正确地进行重绘。所以 ,要得当图形对象应当使用传递给控件Paint方法的
PaintEventArgs对象中的Graphics对象,代码如下所示:
private: System::Void picText_Paint(System::Object * sender,
System::Windows::Forms::PaintEventArgs * e)
{
...
Graphics* g = e->Graphics;
3、实例化一个字体对象
在字体类的13个构造函数中,最基本的一个构造函数只需要你提供字体名及字体的大小。在下面的
例子中,创建了一个20点,"Times New Roman"类的常规字体:
using namespace System::Drawing;
...
Font* font = new Font(S"Times new Roman", 20, FontStyle::Regular);
4、测量将被绘制的文本尺寸
为了绘制文本需要使用Graphics::MeasureString方法来测量文本尺寸。可以使用
Graphics::MeasureString方法来完成这个任务。这个方法需要提供被测量的文本及字体对象,并返回
SizeF结构对象,这个结构包含了将要进行绘制文本的尺寸。
SizeF textSize = g->MeasureString(S"My Sample Text", font);
5、实例画刷对象
可以使用各种各样的画刷进行绘制文本,包括网格画刷、线性渐变画刷、路径渐变画刷、实体画刷
及纹理画刷等,只是在创建各个不同的实例画刷时传递的参数有一些小小的不同而已。对各种画刷进行
探讨不是本文的内容,在本文的实例中只使用两种画刷(网格画刷及线性渐变画刷)。
// HatchBrush example
Brush* brush = new HatchBrush(HatchStyle::Cross,
Color::Black, Color::Blue);
// LinearGradientBrush example
RectangleF* rect = __nogc new RectangleF(PointF(0, 0), textSize);
brush= new LinearGradientBrush(*rect, Color::Black, Color::Blue,
LinearGradientMode::ForwardDiagonal);
6、(选项)填充背景
为了使应用程序有特色,可以在绘制文本前对背景进行颜色填充,这有两个标准的方法。较简单的
方法是调用Graphics::Clear方法并定义将使用的颜色;但是有时需要更高级的控制,这时候需要使用
Graphics::FillRectange方法。
Graphics::FillRectange方法允许开发人员规定所选择的画刷对象并定义确切的矩形坐标位置。关
于画刷对象,可以使用实例化的自定义画刷或者是系统画刷SystemBrushes,系统画刷定义了若干属性成
员,它们是实心画刷,各自用来表现窗口的不同的元素,包括激活的边框及标题条等。
// Use the Windows-defined color for controls
// and explicitly state the rectangle coordinates
g->FillRectangle(SystemBrushes::Control, picText->Left, picText->Top,
picText->Right - picText->Left, picText->Bottom - picText->Top);
// Color the entire drawing surface using White
g->Clear(Color::White);
7、绘制文本
一旦将所有的GDI+对象实例化后,下面所需要做的事就是调用Graphics::DrawString方法。下面的
例子使用了这个方法,在这个方法里规定了需要显示的文本、画刷和字体及显示文本的位置。
// Center the text on the drawing surface
g->DrawString(txtToDisplay->Text, font, brush,
(picText->Width - textSize.Width) / 2,
(picText->Height - textSize.Height) / 2);
========