分类目录归档:.Net

About .net framework, C#, WPF, and so on…

在Unity Android 程序中使用动态库及注意事项

很多C/C++代码以动态库的方式供第三方调用,在Unity中,这类文件(dll for windows, *.so file for Android/Linux, *.dylib for MAC OSX)叫插件。

在Unity开发的Android程序中使用动态库插件是非常方便的,曾经因为被误导而放弃使用unity,转而研究Android通过原生Java的JNI方式包装*.so文件,虽然还行,但是JNI晦涩丑陋的API实在看着不舒服。相对而言,C#也是可以直接包装*.so文件并在Android系统中直接调用的,而且Unity制作Android app跟Unity制作其他平台的app是无缝的,只是在发布时选择要发布的平台就行了,真正做到了平台无关,极大的方便了开发。

我还没仔细研究如何在Android Studio中开发NDK,这里只介绍如何用Eclipse开发NDK。

在Eclipse中开发NDK的几个必须的安装项:NDK编译环境、Android SDK直接从官网单独下载单独安装;CDT、ADT插件可以直接从Eclipse的help的插件管理中安装。

然后就可以创建项目了:

1、打开Eclipse,通过File->New Project,弹出对话框,填入项目名称:

01

2、点Next,出现Config Project窗口:

02

由于只用于编译NDK,所以把前两项的钩钩去掉,第三个勾上,标记为so项目;目录也自定义一下,因为通常情况下我只会为编译so文件配置项目,真正的代码会单独放在更顶层的目录,方便跨平台的其他编译项目使用。结果如下:

03

4、按Finish结束创建过程,目录结构如下:

04

5、添加Native代码支持。也就是JNI相关的东西:在左侧项目根目录上右键->Android Tools->Add Native Support…

05

在随后弹出的对话框中输入要生成的so文件的名字:

06

这时候会发现多了一个目录:

07

6、编译配置

在jni目录中加入Application.mk文件和Android.mk文件

Application.mk:

APP_ABI      := all
#APP_ABI       := armeabi-v7a
#APP_ABI       += armeabi

#APP_OPTIM        := release
APP_PLATFORM     := android-8
#APP_BUILD_SCRIPT := Android.mk

# GNU STL implements most C++11 features. Use either gnustl_static or gnustl_shared
# Without this your C++ code will not be able to access headers like <thread>, <mutex>
#APP_STL := stlport_static
#APP_CPPFLAGS := -std=c++11 -frtti -fexceptions 
APP_STL       := gnustl_static
APP_CPPFLAGS  := -std=gnu++11 -pthread -frtti -fexceptions -DNDEBUG #-NDEBUG -mfpu=neon -fomit-frame-pointer -DULM_BLOCKED -msse3 -mfpmath=sse

Android.mk:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := PNLib
LOCAL_SRC_FILES := PNLib.cpp

include $(BUILD_SHARED_LIBRARY)

本来Android.mk文件很简单,把所有头文件、cpp文件加入,直接编译就行。但是对于我的项目这远远不够,因为我的代码要跨平台,有各个平台的编译项目单独出去,同时使用一份src文件,所以代码被放到了顶层目录中的src目录下,我需要遍历这个目录并把它加入NDK编译系统中来,所以下面从网上找了一段遍历头文件和cpp文件的脚本来用了:

# Copyright (C) 2009 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE   := PNLib

# 锟斤拷锟斤拷锟皆硷拷锟斤拷源锟侥硷拷目录锟斤拷源锟侥硷拷锟斤拷缀锟斤拷
MY_FILES_PATH  :=  $(LOCAL_PATH)/../../src

#$(warning $(MY_FILES_PATH))

MY_FILES_SUFFIX := %.cpp %.c

# 递归遍历目录下的所有的文件
rwildcard=$(wildcard $1$2) $(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2))

# 获取相应的源文件
MY_ALL_FILES := $(foreach src_path,$(MY_FILES_PATH), $(call rwildcard,$(src_path),*.*) ) 
MY_ALL_FILES := $(MY_ALL_FILES:$(MY_CPP_PATH)/./%=$(MY_CPP_PATH)%)
MY_SRC_LIST  := $(filter $(MY_FILES_SUFFIX),$(MY_ALL_FILES)) 
MY_SRC_LIST  := $(MY_SRC_LIST:$(LOCAL_PATH)/%=%)

# 去除字串的重复单词
define uniq =
  $(eval seen :=)
  $(foreach _,$1,$(if $(filter $_,${seen}),,$(eval seen += $_)))
  ${seen}
endef

# 递归遍历获取所有目录
MY_ALL_DIRS := $(dir $(foreach src_path,$(MY_FILES_PATH), $(call rwildcard,$(src_path),*/) ) )
MY_ALL_DIRS := $(call uniq,$(MY_ALL_DIRS))

# 赋值给NDK编译系统
LOCAL_SRC_FILES  := $(MY_SRC_LIST)
LOCAL_C_INCLUDES := $(MY_ALL_DIRS)

# Add additional include directories
LOCAL_C_INCLUDES += $(LOCAL_PATH)/../../
#LOCAL_C_INCLUDES += $(LOCAL_PATH)/../../../Eigen-3.2.2   
#必须从Android.mk配置文件中拿掉对Eigen的直接包含,放到程序代码中用相对路径包含:
# #include "../../Eigen-3.2.2/Eigen"
# using namespace Eigen;

#$(warning $(LOCAL_SRC_FILES))
#$(warning $(LOCAL_C_INCLUDES))

# use log system in NDK
LOCAL_LDLIBS += -llog

include $(BUILD_SHARED_LIBRARY)

7、最后一步是为此项目配置一个NDK编译工具:

A、项目->右键->Properties,弹出项目属性对话框:

08

B、选中Builders,点击New按钮,弹出框中选中“Program”,点击OK:

09

C、出现新Builder配置对话框,随便起个名字“New_Builder_for_NDK”;

在Main标签页中,“Location”项,通过“Browse File System…“找到NDK的build文件,windows系统为ndk-build.cmd,Mac或其他类Linux系统为ndk-build;

工作目录”Working Directory“指定当前项目下的jni目录就行了。

10

D、切到Refresh页,勾选”Refresh resources upon completion“

11

E、切到”Build Options“页,勾选”Specify working set of relevant resources“,点击后面按钮”Specify Resources…“,指本项目下的jni目录。

12

最后的编译条如下:

13

点击左边锤子图标即可对项目进行编译。剩下的工作就是一步步修正跨平台代码,最后生成PNLib.so文件。

 

以上步骤有几个地方需要特别注意:

1、使用gun++11

NKD支持c++11,GCC已经支持大部分c++11特性,所以可以直接使用gun++11。实际上,如果用c++11,Android版程序可能报错:DLLNotFundException,编译出来的动态库在Android系统中无法加载,所以Application.mk文件中必须如下配置:

APP_STL       := gnustl_static
APP_CPPFLAGS  := -std=gnu++11 -pthread -frtti -fexceptions -DNDEBUG

2、x64版ndk目前还有bug

你的系统即便是x64的,也别下载x64版本的ndk,否则会报找不到make.exe的问题(make.exe不是内部命令XXXXXXXXX)

 

3、不识别vector、list等模板类的问题

Add

APP_STL := stlport_static

to their Application.mk file.

其实Android插件应该用APP_STL := gnustl_static

 

4、使用第三方库Eigen导致array等冲突的问题

原因是在NDK的Android.mk文件中把Eigen的路径也加入到里面了。

正确的做法是,不在Android.mk文件加入Eigen,而是在程序代码中直接包含Eigen头文件。

 

Returning Strings from a C++ API to C#

从C++的dll中返回字符串是一种经常用到的C#调用C++函数的方式,但是带有返回值的调用一直是说不清道不明的问题,不细细研究,的确没法用起来。

这里直接给出一个正确的做法,后面会提供一份Google出来的资料,讲的非常好。原理知道了,对于返回结构体也应该是可以按同理搞定的。

 

Code in C++ dll:

// If any error, you can call 'BRGetLastErrorMessage' to get error information
BDR_API char* BRGetLastErrorMessage()
{
    return Error::Instance()->GetLastError();

    // follow code is not platform-cross, so throw away.

#ifdef __OS_XUN__
    return Error::Instance()->GetLastError();
#else
    char* msg = Error::Instance()->GetLastError();
    int len = strlen(msg);

    char* pszReturn = (char*)::CoTaskMemAlloc(len);
    // Copy the contents of szSampleString
    // to the memory pointed to by pszReturn.
    strcpy(pszReturn, msg);
    // Return pszReturn.
    return pszReturn;
#endif
}

 

C# side code:

        // If any error, you can call 'BRGetLastErrorMessage' to get error information
        [DllImport("BVHDataReader.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
        //[return: MarshalAs(UnmanagedType.LPStr)]
        private static extern IntPtr BRGetLastErrorMessage();
        public static string strBRGetLastErrorMessage()
        {
            // Receive the pointer to Unicde character array
            // from API.
            IntPtr ptr = BRGetLastErrorMessage();
            // Construct a string from the pointer.
            string str = Marshal.PtrToStringAnsi(ptr);
            // Display the string.
            Console.WriteLine("Returned string : " + str);
            return str;
        }

 

 

下面是同名资料:

Returning Strings from a C++ API to C#

1. Introduction.

1.1 APIs that return strings are very common. However, the internal nature of such APIs, as well as the use of such APIs in managed code, require special attention. This blog will demonstrate both concerns.

1.2 I will present several techniques for returning an unmanaged string to managed code. But before that I shall first provide an in-depth explanation on the low-level activities that goes on behind the scenes. This will pave the way towards easier understanding of the codes presented later in this blog.

2. Behind the Scenes.

2.1 Let’s say we want to declare and use an API written in C++ with the following signature :

char* __stdcall StringReturnAPI01();

This API is to simply return a NULL-terminated character array (a C string).

2.2 To start with, note that a C string has no direct representation in managed code. Hence we simply cannot return a C string and expect the CLR to be able to transform it into a managed string.

2.3 The managed string is non-blittable. It can have several representations in unmanaged code : e.g. C-style strings (ANSI and Unicode-based) and BSTRs. Hence, it is important that you specify this information in the declaration of the unmanaged API, e.g. :

[DllImport("<path to DLL>", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.LPStr)]
public static extern string StringReturnAPI01();

In the above declaration, note that the following line :

[return: MarshalAs(UnmanagedType.LPStr)]

indicates that the return value from the API is to be treated as a NULL-terminated ANSI character array (i.e. a typical C-style string).

2.4 Now this unmanaged C-style string return value will then be used by the CLR to create a managed string object. This is likely achieved by using theMarshal.PtrToStringAnsi() method with the incoming string pointer treated as an IntPtr.

2.5 Now a very important concept which is part and parcel of the whole API calling operation is memory ownership. This is an important concept because it determines who is responsible for the deallocation of this memory. Now the StringReturnAPI01() API supposedly returns a string. The string should thus be considered equivalent to an “out” parameter, It is owned by the receiver of the string, i.e. the C# client code. More precisely, it is the CLR’s Interop Marshaler that is the actual receiver.

2.6 Now being the owner of the returned string, the Interop Marshaler is at liberty to free the memory associated with the string. This is precisely what will happen. When the Interop Marshaler has used the returned string to construct a managed string object, the NULL-terminated ANSI character array pointed to by the returned character pointer will be deallocated.

2.7 Hence it is very important to note the general protocol : the unmanaged code will allocate the memory for the string and the managed side will deallocate it. This is the same basic requirement of “out” parameters.

2.8 Towards this protocol, there are 2 basic ways that memory for an unmanaged string can be allocated (in unmanaged code) and then automatically deallocated by the CLR (more specifically, the interop marshaler) :

  • CoTaskMemAlloc()/Marshal.FreeCoTaskMem().
  • SysAllocString/Marshal.FreeBSTR().

Hence if the unmanaged side used CoTaskMemAlloc() to allocate the string memory, the CLR will use the Marshal.FreeCoTaskMem() method to free this memory.

The SysAllocString/Marshal.FreeBSTR() pair will only be used if the return type is specified as being a BSTR. This is not relevant to the example given in point 2.1 above. I will demonstrate a use of this pair in section 5 later.

2.9 N.B. : Note that the unmanaged side must not use the “new” keyword or the “malloc()” C function to allocate memory. The Interop Marshaler will not be able to free the memory in these situations. This is because the “new” keyword is compiler dependent and the “malloc” function is C-library dependent. CoTaskMemAlloc(), and SysAllocString() on the other hand, are Windows APIs which are standard.

Another important note is that although GlobalAlloc() is also a standard Windows API and it has a counterpart managed freeing method (i.e. Marshal.FreeHGlobal()), the Interop Marshaler will only use the Marshal.FreeCoTaskMem() method for automatic memory freeing of NULL-terminated strings allocated in unmanaged code. Hence do not use GlobalAlloc() unless you intend to free the allocated memory by hand using Marshal.FreeHGlobal() (an example of this is give in section 6 below).

3. Sample Code.

3.1 In this section, based on the principles presented in section 2, I shall present sample codes to demonstrate how to return a string from an unmanaged API and how to declare such an API in managed code.

3.2 The following is a listing of the C++ function which uses CoTaskMemAlloc() :

extern "C" __declspec(dllexport) char*  __stdcall StringReturnAPI01()
{
    char szSampleString[] = "Hello World";
    ULONG ulSize = strlen(szSampleString) + sizeof(char);
    char* pszReturn = NULL;

    pszReturn = (char*)::CoTaskMemAlloc(ulSize);
    // Copy the contents of szSampleString
    // to the memory pointed to by pszReturn.
    strcpy(pszReturn, szSampleString);
    // Return pszReturn.
    return pszReturn;
}

3.4 The C# declaration and sample call :

[DllImport("<path to DLL>", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.LPStr)]
public static extern string StringReturnAPI01();

static void CallUsingStringAsReturnValue()
{
  string strReturn01 = StringReturnAPI01();
  Console.WriteLine("Returned string : " + strReturn01);
}

3.5 Note the argument used for the MarshalAsAttribute : UnmanagedType.LPStr. This indicates to the Interop Marshaler that the return string from StringReturnAPI01() is a pointer to a NULL-terminated ANSI character array.

3.6 What happens under the covers is that the Interop Marshaler uses this pointer to construct a managed string. It likely uses the Marshal.PtrToStringAnsi() method to perform this. The Interop Marshaler will then use the Marshal.FreeCoTaskMem() method to free the character array.

4. Using a BSTR.

4.1 In this section, I shall demonstrate here how to allocate a BSTR in unmanaged code and return it in managed code together with memory deallocation.

4.2 Here is a sample C++ code listing :

extern "C" __declspec(dllexport) BSTR  __stdcall StringReturnAPI02()
{
  return ::SysAllocString((const OLECHAR*)L"Hello World");
}

4.3 And the C# declaration and usage :

[DllImport("<path to DLL>", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.BStr)]
public static extern string StringReturnAPI02();

static void CallUsingBSTRAsReturnValue()
{
  string strReturn = StringReturnAPI02();
  Console.WriteLine("Returned string : " + strReturn);
}

Note the argument used for the MarshalAsAttribute : UnmanagedType.BStr. This indicates to the Interop Marshaler that the return string from StringReturnAPI02() is a BSTR.

4.4 The Interop Marshaler then uses the returned BSTR to construct a managed string. It likely uses the Marshal.PtrToStringBSTR() method to perform this. The Interop Marshaler will then use the Marshal.FreeBSTR() method to free the BSTR.

5. Unicode Strings.

5.1 Unicode strings can be returned easily too as the following sample code will demonstrate.

5.2 Here is a sample C++ code listing :

extern "C" __declspec(dllexport) wchar_t*  __stdcall StringReturnAPI03()
{
  // Declare a sample wide character string.
  wchar_t  wszSampleString[] = L"Hello World";
  ULONG  ulSize = (wcslen(wszSampleString) * sizeof(wchar_t)) + sizeof(wchar_t);
  wchar_t* pwszReturn = NULL;

  pwszReturn = (wchar_t*)::CoTaskMemAlloc(ulSize);
  // Copy the contents of wszSampleString
  // to the memory pointed to by pwszReturn.
  wcscpy(pwszReturn, wszSampleString);
  // Return pwszReturn.
  return pwszReturn;
}

5.3 And the C# declaration and usage :

[DllImport("<path to DLL>", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.LPWStr)]
public static extern string StringReturnAPI03();

static void CallUsingWideStringAsReturnValue()
{
  string strReturn = StringReturnAPI03();
  Console.WriteLine("Returned string : " + strReturn);
}

The fact that a wide charactered string is now returned requires the use of the UnmanagedType.LPWStr argument for the MarshalAsAttribute.

5.4 The Interop Marshaler uses the returned wide-charactered string to construct a managed string. It likely uses the Marshal.PtrToStringUni() method to perform this. The Interop Marshaler will then use the Marshal.FreeCoTaskMem() method to free the wide-charactered string.

6. Low-Level Handling Sample 1.

6.1 In this section, I shall present some code that will hopefully cement the reader’s understanding of the low-level activities that had been explained in section 2 above.

6.2 Instead of using the Interop Marshaler to perform the marshaling and automatic memory deallocation, I shall demonstrate how this can be done by hand in managed code.

6.3 I shall use a new API which resembles the StringReturnAPI01() API which returns a NULL-terminated ANSI character array :

extern "C" __declspec(dllexport) char*  __stdcall PtrReturnAPI01()
{
  char   szSampleString[] = "Hello World";
  ULONG  ulSize = strlen(szSampleString) + sizeof(char);
  char*  pszReturn = NULL;

  pszReturn = (char*)::GlobalAlloc(GMEM_FIXED, ulSize);
  // Copy the contents of szSampleString
  // to the memory pointed to by pszReturn.
  strcpy(pszReturn, szSampleString);
  // Return pszReturn.
  return pszReturn;
}

6.4 And the C# declaration :

[DllImport("<path to DLL>", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr PtrReturnAPI01();

Note that this time, I have indicated that the return value is an IntPtr. There is no [return : …] declaration and so no unmarshaling will be performed by the Interop Marshaler.

6.5 And the C# low-level call :

static void CallUsingLowLevelStringManagement()
{
  // Receive the pointer to ANSI character array
  // from API.
  IntPtr pStr = PtrReturnAPI01();
  // Construct a string from the pointer.
  string str = Marshal.PtrToStringAnsi(pStr);
  // Free the memory pointed to by the pointer.
  Marshal.FreeHGlobal(pStr);
  pStr = IntPtr.Zero;
  // Display the string.
  Console.WriteLine("Returned string : " + str);
}

This code demonstrates an emulation of the Interop Marshaler in unmarshaling a NULL-terminated ANSI string. The returned pointer from PtrReturnAPI01() is used to construct a managed string. The pointer is then freed. The managed string remains intact with a copy of the returned string.

The only difference between this code and the actual one by the Interop Marshaler is that the GlobalAlloc()/Marshal.FreeHGlobal() pair is used. The Interop Marshaler always uses Marshal.FreeCoTaskMem() and expects the unmanaged code to use ::CoTaskMemAlloc().

7. Low-Level Handling Sample 2.

7.1 In this final section, I shall present one more low-level string handling technique similar to the one presented in section 6 above.

7.2 Again we do not use the Interop Marshaler to perform the marshaling and memory deallocation. Additionally, we will also not release the memory of the returned string.

7.3 I shall use a new API which simply returns a NULL-terminated Unicode character array which has been allocated in a global unmanaged memory :

wchar_t gwszSampleString[] = L"Global Hello World";

extern "C" __declspec(dllexport) wchar_t*  __stdcall PtrReturnAPI02()
{
  return gwszSampleString;
}

This API returns a pointer to the pre-allocated global Unicode string “gwszSampleString”. Because it is allocated in global memory and may be shared by various functions in the DLL, it is crucial that it is not deleted.

7.4 The C# declaration for PtrReturnAPI02() is listed below :

[DllImport("<path to DLL>", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr PtrReturnAPI02();

Again, there is no declaration for interop marshaling (no use of the [return : …] declaration). The returned IntPtr is returned as is.

7.5 And a sample C# code to manage the returned IntPtr :

static void CallUsingLowLevelStringManagement02()
{
  // Receive the pointer to Unicde character array
  // from API.
  IntPtr pStr = PtrReturnAPI02();
  // Construct a string from the pointer.
  string str = Marshal.PtrToStringUni(pStr);
  // Display the string.
  Console.WriteLine("Returned string : " + str);
}

Here, the returned IntPtr is used to construct a managed string from an unmanaged NULL-terminated Unicode string. The memory of the unmanaged Unicode string is then left alone and is not deleted.

Note that because a mere IntPtr is returned, there is no way to know whether the returned string is ANSI or Unicode. In fact, there is no way to know whether the IntPtr actually points to a NULL-terminated string at all. This knowledge has to be known in advance.

7.6 Furthermore, the returned IntPtr must not point to some temporary string location (e.g. one allocated on the stack). If this was so, the temporary string may be deleted once the API returns. The following is an example :

extern "C" __declspec(dllexport) char* __stdcall PtrReturnAPI03()
{
  char szSampleString[] = "Hello World";
  return szSampleString;
}

By the time this API returns, the string contained in “szSampleString” may be completely wiped out or be filled with random data. The random data may not contain any NULL character until many bytes later. A crash may ensue a C# call like the following :

IntPtr pStr = PtrReturnAPI03();
// Construct a string from the pointer.
string str = Marshal.PtrToStringAnsi(pStr);

Link:http://limbioliong.wordpress.com/2011/06/16/returning-strings-from-a-c-api/

 

How can I return a string from a C dll to C#?

// c code:
#include <ole2.h> /* needed for CoTaskMemAlloc */
EXPORT char* getUrlFromBaseURLInList(char *ListFilePath, char *SrchBaseURL, char* strBuffer)
{
    static const char[] sTest =  "Test My String";
    /* you must use CoTaskMemAlloc to allocate the memory, not malloc, new, or anything else */
    char* returnedString = CoTaskMemAlloc(sizeof(sTest));
    strcpy(returnedString, sTest);
    return returnedString;
}

// c# code:
[DllImport("SLSSeoUrlFunc.dll", CharSet=CharSet.Ansi, CallingConvention=CallingConvention.Cdecl)]
public static extern string getUrlFromBaseURLInList(byte[] ListFilePath, byte[] sSrchUrl);

string myTestString = SLSSeoUrlFuncDllWrap.getUrlFromBaseURLInList(null, null);
Console.WriteLine("Eureka - {0}", myTestString);

C#调用C/C++动态库必须注意的几个问题

经常需要封装一些C/C++函数放入动态库中给C#程序调用,通常情况下直接写成如下形式即可:

C#封装调用:

[DllImport("depressor.dll")]
private static extern void DataDepress(ushort[] data);

但是经常出现找不到函数、出现PInvoke错误、传入的数据是一堆莫名其妙的数值之类的错误。

实际上,DLLImport的几个参数非常非常重要,这些值的设置直接影响到了是否能够找到函数、函数调用时的压栈方式、参数传入的是否正确有直接的关系。

以下几个问题必须注意:

1、dll中的函数前必须加入extern “C”,否则会按照C++方式编译,函数会是ZxA+函数名之类的函数名称,导致找不到函数的错误;

2、在C#中做函数封装时,函数必须加入CallingConvention = CallingConvention.Cdecl标签,按照C格式调用,否则会报PInvoke错误;

3、在C#中做函数封装时,函数必须加入CharSet = CharSet.Ansi标签,保证参数以Ansi方式传入,否则传入地址混乱!!!

举例:

C++ Dll函数:

 extern "C" DEPRESSOR_API void DataDepress(ushort* cprs);

C#封装调用:

[DllImport("depressor.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private static extern void DataDepress(ushort[] data);

4、未处理DllNotFoundException:无法加载 DLL“EKFLib.dll”: 内存位置访问无效。 (异常来自 HRESULT:0x800703E6)

这通常是因为dll中直接给空指针赋值等C++ dll中的bug导致的问题。

因为我刚刚解决了一个bug:
C++dll中的一个对象构造函数时调用了一个回调函数,此时回调函数还是个未赋值的指针,所以出现内存位置访问无效的错误,但C#中提示的是:

未处理DllNotFoundException
无法加载 DLL“EKFLib.dll”: 内存位置访问无效。 (异常来自 HRESULT:0x800703E6)。

不要被DllNotFoundException误导了。

C#中利用联合类型(union)做数值类型转换(byte[] to float)

在C/C++中,联合类型经常用来做数据类型转换,比如下面的联合类型可以直接将char数组转换成float类型:

#include <iostream.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

typedef union _UnFloat
{
float value;
char buff[4];
}UnFloat;
int main()
{
// convertor
UnFloat uf;
uf.value = 0;

float f = 3.1415626;

// byte array
char cf[4];
memset(cf, '\0', sizeof(cf));
memcpy(cf, &f, sizeof(cf));

// convert
memcpy(uf.buff, cf, sizeof(uf.buff));

// output
printf("The union converted float value is %f", uf.value);

char input[255];
gets(input);

return 0;
}

输出为:

The union converted float value is 3.141563

C#中没有联合类型(union),但是利用FieldOffset属性,就可以通过结构体定义联合类型:

using System.Runtime.InteropServices;

namespace FloatConvert
{
[StructLayout(LayoutKind.Explicit)]
public struct UnionFloat
{
[FieldOffset(0)]
public float value;
[FieldOffset(0)]
public byte buff0;
[FieldOffset(1)]
public byte buff1;
[FieldOffset(2)]
public byte buff2;
[FieldOffset(3)]
public byte buff3;
}

}

WPF中的Timer与DispatcherTimer的区别与应用

WPF / Silverlight中的 Timer 与 DispatcherTimer 有什么区别呢?

这里我给大家简单介绍一下他们在使用和实现上的区别。

在一个应用程序中,Timer会重复生成time事件,而DispatcherTimer是一个集成到了Dispatcher队列中的时钟,这可以使它被按照指定的时间间隔以指定的priority定期执行。

对于一个Timer时钟事件,系统并不能保证在时间间隔到达后被立即执行,但是能够确保在时间间隔到达之前不被执行。这是因为DispatcherTimer像其他操作一样被放置在了Dispatcher队列中。何时执行DispatcherTimer事件依赖于队列中的其他任务以及他们的优先级.

如果一个WPF应用程序使用了Timer时钟,那么它的事件必须在一个单独的时钟线程中运行,而不是在UI线程中,这对于WPF应用程序毫无用处——你没法在UI线程之外直接访问UI元素,而只能通过Invoke或者BeginInvoke将操作发送给Dispatcher 对象,委托Dispatcher去执行UI操作。

看到这里,你大概知道了为什么我们在WPF中应该用DispatcherTimer而不是Timer了:DispatcherTimer与Dispatcher运行于同一个线程中——UI线程,而且具有相同的DispatcherPriority优先级。

所以,在WPF/Silverlight应用中,正确的做法如下所示:

[code lang=”c#”]

DispatcherTimer timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromSeconds(100);
timer.Tick += new EventHandler(timer_Tick);
timer.Start();

[/code]

C# Tostring() 格式大全 [转]

C

 

货币

 

2.5.ToString(“C”)

 

¥2.50

 

D

 

十进制数

 

25.ToString(“D5”)

 

00025

 

E

 

科学型

 

25000.ToString(“E”)

 

2.500000E+005

 

F

 

固定点

 

25.ToString(“F2”)

 

25.00

 

G

 

常规

 

2.5.ToString(“G”)

 

2.5

 

N

 

数字

 

2500000.ToString(“N”)

 

2,500,000.00

 

X

 

十六进制

 

255.ToString(“X”)

 

FF

 

formatCode 是可选的格式化代码字符串。(详细内容请搜索“格式化字符串”查看)

必须用“{”和“}”将格式与其他字符分开。如果恰好在格式中也要使用大括号,可以用连续的两个大括号表示一个大括号,即: “{{”或者“}}”。

常用格式举例:

(1) int i=12345;

this.textBox1.Text=i.ToString();

//结果 12345(this指当前对象,或叫当前类的实例)

this.textBox2.Text=i.ToString(“d8”);

//结果 00012345

(2) int i=123;

double j=123.45;

string s1=string.Format(“the value is {0,7:d}”,i);

string s2=string.Format(“the value is {0,7:f3}”,j);

this.textBox1.Text=s1 ;

//结果 the value is 123

this.textBox2.Text=s2;

//结果 the value is 123.450

(3)double i=12345.6789;

this.textBox1.Text=i.ToString(“f2”); //结果 12345.68

this.textBox2.Text=i.ToString(“f6”);

//结果 12345.678900

(4)double i=12345.6789;

this.textBox1.Text=i.ToString(“n”); //结果 12,345.68

this.textBox2.Text=i.ToString(“n4”); //结果 12,345.6789

(5)double i=0.126;

string s=string.Format(“the value is {0:p}”,i);

this.textBox1.Text=i.ToString(“p”); //结果 12.6%

this.textBox2.Text=s; //结果 the value is 12.6%

(6) DateTime dt =new DateTime(2003,5,25);

this.textBox1.Text=dt.ToString(“yy.M.d”);

//结果 03.5.25

this.textBox2.Text=dt.ToString(“yyyy年M月”);

//结果 2003年5月

Convert.ToDateTime(“2005/12/22 22:22:22”).ToString(“yyyy/MM/dd HH:mm:ss”)
“2005/12/22 22:22:22”

(7) int i=123;

double j=123.45;

string s=string.Format(“i:{0,-7},j:{1,7}”,i,j);

//-7表示左对齐,占7位

this.textBox1.Text=s ;

//结果i:123 ,j: 123.45

 DateTime.ToString()用法详解

我们经常会遇到对时间进行转换,达到不同的显示效果,默认格式为:2006-6-6 14:33:34
如果要换成成200606,06-2006,2006-6-6或更多的格式该怎么办呢?
这里将要用到:DateTime.ToString的方法(String, IFormatProvider)
示例:
using System;
using System.Globalization;
String format=”D”;
DateTime date=DataTime.Now;
Response.Write(date.ToString(format, DateTimeFormatInfo.InvariantInfo));
结果输出
Thursday, June 16, 2006

在这里列出了参数format格式详细用法
=======================
格式字符 关联属性/说明
d ShortDatePattern
D LongDatePattern
f 完整日期和时间(长日期和短时间)
F FullDateTimePattern(长日期和长时间)
g 常规(短日期和短时间)
G 常规(短日期和长时间)
m、M MonthDayPattern
r、R RFC1123Pattern
s 使用当地时间的 SortableDateTimePattern(基于 ISO 8601)
t ShortTimePattern
T LongTimePattern
u UniversalSortableDateTimePattern 用于显示通用时间的格式
U 使用通用时间的完整日期和时间(长日期和长时间)
y、Y YearMonthPattern

下表列出了可被合并以构造自定义模式的模式
========================================
这些模式是区分大小写的;例如,识别“MM”,但不识别“mm”。如果自定义模式包含空白字符或用单引号括起来的字符,则输出字符串页也将包含这些字符。未定义为格式模式的一部分或未定义为格式字符的字符按其原义复制。

格式模式 说明 :
d 月中的某一天。一位数的日期没有前导零。
dd 月中的某一天。一位数的日期有一个前导零。
ddd 周中某天的缩写名称,在 AbbreviatedDayNames 中定义。
dddd 周中某天的完整名称,在 DayNames 中定义。
M 月份数字。一位数的月份没有前导零。
MM 月份数字。一位数的月份有一个前导零。
MMM 月份的缩写名称,在 AbbreviatedMonthNames 中定义。
MMMM 月份的完整名称,在 MonthNames 中定义。
y 不包含纪元的年份。如果不包含纪元的年份小于 10,则显示不具有前导零的年份。
yy 不包含纪元的年份。如果不包含纪元的年份小于 10,则显示具有前导零的年份。
yyyy 包括纪元的四位数的年份。
gg 时期或纪元。如果要设置格式的日期不具有关联的时期或纪元字符串,则忽略该模式。
h 12 小时制的小时。一位数的小时数没有前导零。
hh 12 小时制的小时。一位数的小时数有前导零。
H 24 小时制的小时。一位数的小时数没有前导零。
HH 24 小时制的小时。一位数的小时数有前导零。
m 分钟。一位数的分钟数没有前导零。
mm 分钟。一位数的分钟数有一个前导零。
s 秒。一位数的秒数没有前导零。
ss 秒。一位数的秒数有一个前导零。
f 秒的小数精度为一位。其余数字被截断。
ff 秒的小数精度为两位。其余数字被截断。
fff 秒的小数精度为三位。其余数字被截断。
ffff 秒的小数精度为四位。其余数字被截断。
fffff 秒的小数精度为五位。其余数字被截断。
ffffff 秒的小数精度为六位。其余数字被截断。
fffffff 秒的小数精度为七位。其余数字被截断。
t 在 AMDesignator 或 PMDesignator 中定义的 AM/PM 指示项的第一个字符(如果存在)。
tt 在 AMDesignator 或 PMDesignator 中定义的 AM/PM 指示项(如果存在)。
z 时区偏移量(“+”或“-”后面仅跟小时)。一位数的小时数没有前导零。例如,太平洋标准时间是“-8”。
zz 时区偏移量(“+”或“-”后面仅跟小时)。一位数的小时数有前导零。例如,太平洋标准时间是“-08”。
zzz 完整时区偏移量(“+”或“-”后面跟有小时和分钟)。一位数的小时数和分钟数有前导零。例如,太平洋标准时间是“-08:00”。
: 在 TimeSeparator 中定义的默认时间分隔符。
/ 在 DateSeparator 中定义的默认日期分隔符。
% c 其中 c 是格式模式(如果单独使用)。如果格式模式与原义字符或其他格式模式合并,则可以省略“%”字符。
\ c 其中 c 是任意字符。照原义显示字符。若要显示反斜杠字符,请使用“\\”。

只有上面第二个表中列出的格式模式才能用于创建自定义模式;在第一个表中列出的标准格式字符不能用于创建自定义模式。自定义模式的长度至少为两个字符;例如,

DateTime.ToString( “d”) 返回 DateTime 值;“d”是标准短日期模式。
DateTime.ToString( “%d”) 返回月中的某天;“%d”是自定义模式。
DateTime.ToString( “d “) 返回后面跟有一个空白字符的月中的某天;“d”是自定义模式。

比较方便的是,上面的参数可以随意组合,并且不会出错,多试试,肯定会找到你要的时间格式
如要得到2005年06月 这样格式的时间
可以这样写:
date.ToString(“yyyy年MM月”, DateTimeFormatInfo.InvariantInfo)
如此类推.

下面列出一些Asp.net中具体的日期格式化用法:
============================================
1.绑定时格式化日期方法:

2.数据控件如DataGrid/DataList等的件格式化日期方法:
e.Item.Cell[0].Text = Convert.ToDateTime(e.Item.Cell[0].Text).ToShortDateString();

3.用String类转换日期显示格式:
String.Format( “yyyy-MM-dd “,yourDateTime);

4.用Convert方法转换日期显示格式:
Convert.ToDateTime(“2005-8-23”).ToString

(“yyMMdd”,System.Globalization.DateTimeFormatInfo.InvariantInfo); //支持繁体数据库

5.直接用ToString方法转换日期显示格式:
DateTime.Now.ToString(“yyyyMMddhhmmss”);
DateTime.Now.ToString(“yyyy/MM/dd hh:mm:ss”)

6.只显示年月
DataBinder.Eval(Container.DataItem,”starttime”,”{0:yyyy-M}”)

7.显示时间所有部分,包括:年月日时分秒
DataFormatString='{0:yyyy-MM-dd HH24:mm:ss}’>

用DateTime.ToString(string format)输出不同格式的日期

DateTime.ToString()函数有四个重载。一般用得多的就是不带参数的那个了。殊不知,DateTime.ToString(string format)功能更强大,能输出不同格式的日期。以下把一些情况罗列出来,供大家参考。有些在MSDN上有的就没有列出来了。

1.         y代表年份,注意是小写的y,大写的Y并不代表年份。

2.         M表示月份。

3.         d表示日期,注意D并不代表什么。

4.         h或H表示小时,h用的是12小时制,H用的是24小时制。

5.         m表示分钟。

6.         s表示秒。注意S并不代表什么。

格式

输出

示例

y 7 string yy = DateTime.Now.ToString(“y-MM”)

yy=”7-05″

yy 07 string yy = DateTime.Now.ToString(“yy-MM”)

yy=”07-05″

yyy或更多的y 1984 string yy = DateTime.Now.ToString(“yyyy”);

yy=”2007″

M 5. string mon = DateTime.Parse(“1984-05-09”)ToString(“yyyy-M”)

mon = “1984-5”

MM 05. string mon = DateTime.Parse(“1984-05-09”)ToString(“MM”)

mon = “05”

MMM 如果是中文版的操作系统,则会输出:五月.

如果是英文操作系统,则输入月份前三个字母的简写:May

string mon = DateTime.Parse(“2006-07-01”).ToString(“MMM”)

英文版操作系统:Jul

中文版操作系统:七月

MMMM或更多的M 如果是中文版的操作系统,则会输出:五月.

如果是英文操作系统,则输入月份的全写

string mon = DateTime.Parse(“2006-07-01”).ToString(“MMM”)

英文版操作系统:July

中文版操作系统:七月

日期或星期
d 9 string dd= DateTime.Parse(“1984-05-09”)ToString(“d”)

dd= “9”

 

dd 09 string dd= DateTime.Parse(“1984-05-09”)ToString(“dd”)

dd= “09”

ddd 如果是中文版的操作系统,则会输出星期,如星期三。.

如果是英文操作系统,则输出星期的简写:如

Wed

string dd = DateTime.Parse(“2006-07-01”).ToString(“ddd”)

英文版操作系统:Wed

中文版操作系统:星期三

dddd或更多的d 如果是中文版的操作系统,则会输出星期,如星期三。.

如果是英文操作系统,则输出星期:如

Wednesday

string dd = DateTime.Parse(“2006-07-01”).ToString(“dddd”)

英文版操作系统:Wednesday

中文版操作系统:星期三

小时
h 小时范围:1-12 string hh = DateTime.Now.ToString(“h”);

hh = 8

hh或更多的h 小时范围:1-12 string hh = DateTime.Now.ToString(“hh”);

hh = 08

H 小时范围:0-23 string hh = DateTime.Now.ToString(“yyyy-H”);

hh = 2006-8

HH或更多的H 小时范围:0-23 string hh = DateTime.Now.ToString(“yyyy-HH”);

hh = 2006-08

string hh = DateTime.Pare(“2006-7-4 18:00:00”).ToString(“yyyy-HH”);

hh = 2006-18

分钟
m 6 string mm = DateTime.Now.ToString(“yyyy-MM-dd-m”);

mm = “2006-07-01-6”;

mm或更多的m 06 string mm = DateTime.Now.ToString(“yyyy-MM-dd-mm”);

mm = “2006-07-01-06”;

s 6 string mm = DateTime.Now.ToString(“yyyy-MM-dd-s”);

mm = “2006-07-01-6”;

ss或更多的s 06 string mm = DateTime.Now.ToString(“yyyy-MM-dd-ss”);

mm = “2006-07-01-06”;

【原】实用的C#串口类CommPort,用于串口通信的源代码类

此CommPort串口类基于网上的mycomm修改,修复了一些bug、完善了事件处理机制,算是相当稳定实用了,不敢独享,贡献给大家想用。

使用注意事项:

1、几个属性的设置,用0、1、2、3等表示相应的选项;

2、port是数字,Open的时候自动串上“\\\\.\\COM”字符串;

3、加入了数据接收事件的触发,由于是从后台调用DataReceived回调函数,在接收和处理数据时可能不能访问UI元素(没试过,如果哪位碰到这个问题,自己加一下Invokee调用吧);

4、几个字段没有加正确性校验,别传错了;

5、读取数据时尽量用Read一次全部读出,然后做分解,而不是用ReadByte一次读一个字节,你想想,如果一块数据好几k,那么你用Read一次就读出来了,而ReadByte要读好几千次!性能马上就掉下来了;

6、低调的实现了startThread函数,你也会看到,如果要做的更好,线程启动时应该判断一下先前的线程是否退出;

7、你也应该看到,访问串口时没有加锁,安全的做法是,在读写串口时应该保证同时只有个读或者写,搞多线程的童鞋这里要注意一下;

8、跑的爽的时候想想我,出错的时候别骂我 :-)

—— HYH || khler

 

[code lang=”C”]

/*
* 作者:何元会
* E-mail:khler@163.com
* QQ:23381103
* 授权:GPL,同时必须保证本说明的完整性!
*
* 此串口类基于网上的一个类 mycomm 修改而来,网址:http://hi.baidu.com/p1220/blog/item/bf014e01d1d6da047aec2cb9.html
*
* 此处提到的mycomm类实际上是有bug的,就是DCB结构体的定义,对几个bit flag的位字段包装有问题,
* DCB中的这些flag每个只占一个bit,而包装中占了一个int,显然传递进去后标志位会错位,肯定会有问题。
* 我碰到的就是每次接收到的数据,大于127的数值都被折半了。修复DCB结构体对几个flag的定义就解决了。
* C#里面没有位字段定义,需要与枚举类型配合使用,此bug已修复;
*
* 另一个问题是,对于’USB转串口’的串口设备,在CreateFile时会打开失败的,报告找不到此文件。
* 通用做法是在前面加’\\\\.\\’,如COM1应该是"\\\\.\\COM1"
*
* 再一个改进:添加了数据接收事件探测,当有数据到达时调用事件处理函数。用法与自带的SerialPort类一样,
* 在定义对象时记得给他的DataReceived串接一个事件处理函数就行了。
*
* 加入功能:
* 1、包装了ClearCommError()函数,用于读取当前串口可读数据,BytesToRead,只读;
* 2、加入CommDataReceivedEventHandler委托,当有数据时向使用者发送消息;
* 3、加入数据监测线程,用于后台监视数据可读;
* 4、加入ReadByte函数,用于读取单个字节;
* 5、基本模拟了C#自带的SerialPort类,使用极其方便;
* 6、在读取数据后手动从BytesToRead中减去刚刚读取掉的字节数,避免多线程异步更新问题;
* 7、属性做了包装和说明;
* 8、修复DCB结构体定义bug;
* 9、修复无法打开’USB转串口’的串口设备的问题;
* 10、添加GetLastError()函数,打开设备失败时提取错误号;
*
*
*
*
* 接口:
* CommPort() ,构造函数
* DataReceived ,事件处理回调
* void Open() ,打开串口
* byte[] Read(int NumBytes) ,读取数据
* byte ReadByte() ,读取字节
* int Write(byte[] WriteBytes) ,写入数据
* void Close() ,关闭串口
*
*
* 使用范例:
*
private void OpenPort(int port)
{
try
{
comm = new CommPort();
comm.PortNum = port;
comm.BaudRate = 115200;
comm.ByteSize = 8;
comm.Parity = 0;
comm.StopBits = 0;
comm.ReadTimeout = 10;
comm.DataReceived += new CommDataReceivedEventHandler(comm_DataReceived);
comm.Open();
Console.WriteLine("COM" + comm.PortNum + " 打开成功");
}
catch (Exception ex)
{
Console.WriteLine("COM" + comm.PortNum + " 打开失败:" + ex.Message);
}
}

void comm_DataReceived(object sender)
{
if (comm.BytesToRead > 0)
{
byte[] buffer = comm.Read(comm.BytesToRead);
// 分解数据

}
}

*
*
**/

using System;
using System.Threading;
using System.Runtime.InteropServices;
using System.IO.Ports;

namespace HYHComm
{
public delegate void CommDataReceivedEventHandler(object sender);

class CommPort
{
#region 构造函数
public CommPort()
{
}
#endregion// 构造函数

#region 属性
/// <summary>
/// 数据接收事件
/// </summary>
public event CommDataReceivedEventHandler DataReceived;

//comm port win32 file handle
private int hComm = -1;

/// <summary>
/// 打开标示
/// </summary>
public bool Opened;
/// <summary>
/// 串口号:1,2,3,4…
/// </summary>
public int PortNum;
/// <summary>
/// 波特率:1200,2400,4800,9600,115200
/// </summary>
public int BaudRate;
/// <summary>
/// 数据位 8 bits
/// </summary>
public byte ByteSize;
/// <summary>
/// 数据校验 0-4=no,odd,even,mark,space
/// </summary>
public byte Parity;
/// <summary>
/// 停止位 0,1,2 = 1, 1.5, 2
/// </summary>
public byte StopBits;
/// <summary>
/// 超时时间 //10
/// </summary>
public int ReadTimeout;
/// <summary>
/// 缓存中可读字节数
/// </summary>
public int BytesToRead
{
get { return _BytesToRead; }
}
private int _BytesToRead;

private enum ComStatFlags
{
fCtsHold = 0x1,
fDsrHold = 0x2,
fRlsdHold = 0x4,
fXoffHold = 0x8,
fXoffSent = 0x10,
fEof = 0x20,
fTxim = 0x40
}
[StructLayout(LayoutKind.Sequential)]
private struct COMSTAT
{
public ComStatFlags flags;
public uint cbInQue;
public uint cbOutQue;
};

private const uint GENERIC_READ = 0x80000000;
private const uint GENERIC_WRITE = 0x40000000;
private const int OPEN_EXISTING = 3;
private const int INVALID_HANDLE_VALUE = -1;

private enum DCBFlags
{
fBinary = 0x01, // binary mode, no EOF check
fParity = 0x02, // enable parity checking
fOutxCtsFlow = 0x04, // CTS output flow control
fOutxDsrFlow = 0x08, // DSR output flow control
fDtrControl = 0x30, // DTR flow control type , 2bits
fDsrSensitivity = 0x40, // DSR sensitivity
fTXContinueOnXoff = 0x80, // XOFF continues Tx
fOutX = 0x100, // XON/XOFF out flow control
fInX = 0x200, // XON/XOFF in flow control
fErrorChar = 0x400, // enable error replacement
fNull = 0x800, // enable null stripping
fRtsControl = 0x3000, // RTS flow control , 2bits
fAbortOnError = 0x4000, // abort on error
fDummy2 = unchecked((int)0xffff8000), // reserved , 17bits
};
[StructLayout(LayoutKind.Sequential)]
private struct DCB
{
//taken from c struct in platform sdk
public int DCBlength; // sizeof(DCB)
public int BaudRate; // current baud rate
public DCBFlags fFlags; // DCBFlags
public ushort wReserved; // not currently used
public ushort XonLim; // transmit XON threshold
public ushort XoffLim; // transmit XOFF threshold
public byte ByteSize; // number of bits/byte, 4-8
public byte Parity; // 0-4=no,odd,even,mark,space
public byte StopBits; // 0,1,2 = 1, 1.5, 2
public char XonChar; // Tx and Rx XON character
public char XoffChar; // Tx and Rx XOFF character
public char ErrorChar; // error replacement character
public char EofChar; // end of input character
public char EvtChar; // received event character
public ushort wReserved1; // reserved; do not use
}
[StructLayout(LayoutKind.Sequential)]
private struct COMMTIMEOUTS
{
public int ReadIntervalTimeout;
public int ReadTotalTimeoutMultiplier;
public int ReadTotalTimeoutConstant;
public int WriteTotalTimeoutMultiplier;
public int WriteTotalTimeoutConstant;
}
[StructLayout(LayoutKind.Sequential)]
private struct OVERLAPPED
{
public int Internal;
public int InternalHigh;
public int Offset;
public int OffsetHigh;
public int hEvent;
}
#endregion// 属性

#region 方法
[DllImport("kernel32.dll")]
private static extern bool ClearCommError(int hFile, ref int lpdwErrorFlag, ref COMSTAT lpCommStat);
[DllImport("kernel32.dll")]
private static extern int CreateFile(string lpFileName, uint dwDesiredAccess, int dwShareMode, int lpSecurityAttributes, int dwCreationDisposition, int dwFlagsAndAttributes, int hTemplateFile);
[DllImport("kernel32.dll")]
private static extern bool GetCommState(int hFile, ref DCB lpDCB);
[DllImport("kernel32.dll")]
private static extern bool BuildCommDCB(string lpDef, ref DCB lpDCB);
[DllImport("kernel32.dll")]
private static extern bool SetCommState(int hFile, ref DCB lpDCB);
[DllImport("kernel32.dll")]
private static extern bool GetCommTimeouts(int hFile, ref COMMTIMEOUTS lpCommTimeouts);
[DllImport("kernel32.dll")]
private static extern bool SetCommTimeouts(int hFile, ref COMMTIMEOUTS lpCommTimeouts);
[DllImport("kernel32.dll")]
private static extern bool ReadFile(int hFile, byte[] lpBuffer, int nNumberOfBytesToRead, ref int lpNumberOfBytesRead, ref OVERLAPPED lpOverlapped);
[DllImport("kernel32.dll")]
private static extern bool WriteFile(int hFile, byte[] lpBuffer, int nNumberOfBytesToWrite, ref int lpNumberOfBytesWritten, ref OVERLAPPED lpOverlapped);
[DllImport("kernel32.dll")]
private static extern UInt32 GetLastError();
[DllImport("kernel32.dll")]
private static extern bool CloseHandle(int hObject);
private void startThread()
{
// 匿名方法
ParameterizedThreadStart threadRun = delegate(object self)
{
//COMM comm = (COMM)self;
int dwErrorFlags = 0;
COMSTAT ComStat = new COMSTAT();

while (Opened)
{
// 打破无限循环,降低CPU使用率
Thread.Sleep(0);
if (!ClearCommError(hComm, ref dwErrorFlags, ref ComStat))
{
continue;
}

if (dwErrorFlags != 0)
{
//getMsg("上一次对串口的操作存在错误");
continue;
}

_BytesToRead = (int)ComStat.cbInQue;

if (_BytesToRead > 0)
{
if (DataReceived != null)
{
DataReceived(this);
}
}
}
};
Thread thrHasData = new Thread(threadRun);
thrHasData.Start(this);
}

public void Open()
{
DCB dcbCommPort = new DCB();
COMMTIMEOUTS ctoCommPort = new COMMTIMEOUTS();

// OPEN THE COMM PORT.
hComm = CreateFile("\\\\.\\COM" + PortNum, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);

// IF THE PORT CANNOT BE OPENED, BAIL OUT.
if (hComm == INVALID_HANDLE_VALUE)
{
UInt32 dwErr = GetLastError();
throw (new ApplicationException("Comm Port Can Not Be Opened, Error Code:" + dwErr));
}

// SET THE COMM TIMEOUTS.
GetCommTimeouts(hComm, ref ctoCommPort);
ctoCommPort.ReadTotalTimeoutConstant = ReadTimeout;
ctoCommPort.ReadTotalTimeoutMultiplier = 0;
ctoCommPort.WriteTotalTimeoutMultiplier = 0;
ctoCommPort.WriteTotalTimeoutConstant = 0;
SetCommTimeouts(hComm, ref ctoCommPort);

// SET BAUD RATE, PARITY, WORD SIZE, AND STOP BITS.
// THERE ARE OTHER WAYS OF DOING SETTING THESE BUT THIS IS THE EASIEST.
// IF YOU WANT TO LATER ADD CODE FOR OTHER BAUD RATES, REMEMBER
// THAT THE ARGUMENT FOR BuildCommDCB MUST BE A POINTER TO A STRING.
// ALSO NOTE THAT BuildCommDCB() DEFAULTS TO NO HANDSHAKING.

dcbCommPort.DCBlength = Marshal.SizeOf(dcbCommPort);
GetCommState(hComm, ref dcbCommPort);
dcbCommPort.BaudRate = BaudRate;
dcbCommPort.Parity = Parity;
dcbCommPort.ByteSize = ByteSize;
dcbCommPort.StopBits = StopBits;
SetCommState(hComm, ref dcbCommPort);

Opened = true;

startThread();
}

public byte[] Read(int NumBytes)
{
byte[] BufBytes = null;
byte[] OutBytes = null;
BufBytes = new byte[NumBytes];
if (hComm != INVALID_HANDLE_VALUE)
{
OVERLAPPED ovlCommPort = new OVERLAPPED();
int BytesRead = 0;
if (ReadFile(hComm, BufBytes, NumBytes, ref BytesRead, ref ovlCommPort))
{
_BytesToRead -= BytesRead;
OutBytes = new byte[BytesRead];
Array.Copy(BufBytes, OutBytes, BytesRead);
}
}
else
{
throw (new ApplicationException("Comm Port Not Open"));
}
return OutBytes;
}

public byte ReadByte()
{
byte[] BufBytes = new byte[1];
if (hComm != INVALID_HANDLE_VALUE)
{
OVERLAPPED ovlCommPort = new OVERLAPPED();
int BytesRead = 0;
if (ReadFile(hComm, BufBytes, 1, ref BytesRead, ref ovlCommPort))
{
_BytesToRead -= 1;
}
}
else
{
throw (new ApplicationException("Comm Port Not Open"));
}
return BufBytes[0];
}

public int Write(byte[] WriteBytes)
{
int BytesWritten = 0;
if (hComm != INVALID_HANDLE_VALUE)
{
OVERLAPPED ovlCommPort = new OVERLAPPED();
WriteFile(hComm, WriteBytes, WriteBytes.Length, ref BytesWritten, ref ovlCommPort);
}
else
{
throw (new ApplicationException("Comm Port Not Open"));
}
return BytesWritten;
}

public void Close()
{
// 置为false,监测线程将会退出
Opened = false;
if (hComm != INVALID_HANDLE_VALUE)
{
CloseHandle(hComm);
}
}
#endregion// 方法
}
}

[/code]

Thrift简介

Thrift是一个跨语言服务部署框架,最初由Facebook于2007年开发,后于2008年进入Apache孵化器(Apache Incubator)。

类似于SOAP,COM 和CORBA,Thrift通过定义一个中间定义语言和Thrift代码生成工具,生成指定语言的代码。目前,Thrift支持C++,Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, Smalltalk和OCaml的代码生成。

简单分析其机理,Thrift就是实现C/S模式,通过代码生成工具将接口定义文件生成服务器端和客户端代码(可以为不同语言),从而实现服务端和客户端跨语言的支持。

Thrift可以分为传输层和协议层:

1、传输层定义了数据的传输方式,可以为TCP/IP传输,内存共享或者文件共享等形式;
2、协议层定义了数据的传输格式,可以为二进制流或者XML等形式。

简单例子:

中间语言定义:

1 struct UserProfile {
2 1: i32 uid,
3 2: string name,
4 3: string blurb
5 }
6 service UserStorage {
7 void store(1: UserProfile user),
8 UserProfile retrieve(1: i32 uid)
9 }

Python客户端代码:

01 # Make an object
02 up = UserProfile(uid=1,
03                  name="Mark Slee",
04                  blurb="I'll find something to put here.")
05   
06 # Talk to a server via TCP sockets, using a binary protocol
07 transport = TSocket.TSocket("localhost", 9090)
08 transport.open()
09 protocol = TBinaryProtocol.TBinaryProtocol(transport)
10   
11 # Use the service we already defined
12 service = UserStorage.Client(protocol)
13 service.store(up)
14   
15 # Retrieve something as well
16 up2 = service.retrieve(2)

服务器端代码:

01 class UserStorageHandler : virtual public UserStorageIf {
02  public:
03   UserStorageHandler() {
04     // Your initialization goes here
05   }
06   
07   void store(const UserProfile& user) {
08     // Your implementation goes here
09     printf("store\n");
10   }
11   
12   void retrieve(UserProfile& _return, const int32_t uid) {
13     // Your implementation goes here
14     printf("retrieve\n");
15   }
16 };
17   
18 int main(int argc, char **argv) {
19   int port = 9090;
20   shared_ptr<UserStorageHandler> handler(new UserStorageHandler());
21   shared_ptr<TProcessor> processor(new UserStorageProcessor(handler));
22   shared_ptr<TServerTransport> serverTransport(new TServerSocket(port));
23   shared_ptr<TTransportFactory> transportFactory(new TBufferedTransportFactory());
24   shared_ptr<TProtocolFactory> protocolFactory(new TBinaryProtocolFactory());
25   TSimpleServer server(processor, serverTransport, transportFactory, protocolFactory);
26   server.serve();
27   return 0;
28 }

参考资料

1. http://incubator.apache.org/thrift

本文来自 http://www.imneio.com/2009/10/thrift-intro/

转载请保留原作者链接,谢谢!!!

RPC 数据交换格式

RPC简称远程调用,说白了就是不同机器之间的交流通信,不同平台之间的交流通信,牵涉到机器间的交流通信,就要做到跟平台无关,不论是linux,windows也好都可以进行相互间的交流,跟语言无关,不论是C,C++,java等,都能够近可能多的表达出够准确的意思,也就是说,数据交换的格式定义在分布式数据通讯当中很重要,传统的xml,json都能满足上述的功能需求,而对于一个大型的分布式应用,定义一种高效,更少的传输数据量,更快的解释和生成速度满足系统间的调用需求,对资源的节省十分可观,由此各个公司纷纷推出各自内部进行数据通讯的rpc数据格式。
目前使用最多的莫过于xml和json,而且它们相应的支持的语言和平台更多是一种通用的标准,像soap,rest等都采用了xml进行数据通讯,而json常用作WEB程序客户端跟服务器端进行数据通讯的一种交换格式。目前很火热的莫过于Google Protocol Buffer( 简称 Protobuf) ,Protobuf是 Google 公司内部的混合语言数据标准,目前已经正在使用的有超过 48,162 种报文格式定义和超过 12,183 个 .proto 文件。他们用于 RPC 系统和持续数据存储系统。Protocol Buffers 是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。它很适合做数据存储或 RPC 数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。目前提供了 C++、Java、Python 三种语言的 API(摘抄自wikipedia)。facebook相应的也推出自己的一个叫thrift的rpc数据交换格式,thrift属于facebook.com技术核心框架之一,使用不同开发语言开发的系统可以通过该框架实现彼此间的通讯,开发者只需编辑一份 thrift脚本,即可自动获得与其它开发语言的代码进行数据通讯,它的数据文件定义格式跟Protobuf很像。Avro是著名的分布式计算框架hadoop采用的一种分布式的数据通讯格式,相应的可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。BSON是一种类似于json的一种数据格式,目前著名的nosql软件mongodb采用该格式进行数据通讯。最近看到日本的nosql大会上介绍了一个叫kumofs的分布式存储软件,它当中采用一个叫MessagePack的数据格式进行数据交换,从作者的介绍来看,它支持的功能要比上述的格式要多,支持异步rpc,Parallel Pipelining,Thrift IDL,动态的数据类型,连接池,延迟返回,事件驱动I/O类型等,而且作者号称速度要比其它的数据格式小,解释和生成速度快。具体可参照文档

rpc数据交换格式多,相应的的选择的余力也就更大,到底那一种选择那一种,传统的xml,json也够用,新奇的也很有诱惑力,关键在于那一种适合你当前的需求。

本文转自:http://blog.netzhou.net/?p=101

转载请注明原作者及出处!

使用Team Foundation Server(TFS)进行项目Bug管理

一、TFS的简单介绍

TFS中的BUG管理为开发人员提供了BUG查询,BUG回复,及BUG review的功能。
1. BUG查询
在团队资源管理器中,BUG是作为工作项来管理,和项目中任务,场景等统一编号。在工作项中有两种工作项的查询方式。团队查询是在TFS Server端定义的工作项查询方式,我的查询是用户自定义的工作项查询。
开发人员可以定义一个查询所有指定给自己的BUG的查询项。

2.BUG回复
开发人员在指定给自己的Bug中可以按照测试人员提供的BUG描述重现,修改BUG。修改后可以将BUG的状态置为Resolved,反馈给测试人员,以便测试人员对BUG 的跟踪测试。
3. BUG Review
在BUG查询给出的列表中,可以自定义显示列,分组排列BUG,以便跟踪。或者新建查询。例如:建立一个查询所有制定给A成员的所有状态是激活的BUG查询项。A在Review自己的BUG时就可通过这个查询很方便的查出自己未解决的BUG。

二、Bug的优先级设置

在TFS中将将Bug的优先级分为3个等级。在项目中按照以下分级说明进行分类。开发人员应该按照优先级解决BUG。

1    Priority 1 致命的(Fatal):系统任何一个主要功能完全丧失,用户数据受到破坏,系统崩溃,悬挂,死机,或者危机人身安全。(当天必须完成)
2    Priority 2严重的(Critical):系统的主要功能部分丧失,数据不能保存,系统地次要功能完全丧失,系统所提供的功能或服务受到明显的影响。(在回归测试前必须完成)
3   Priority 3 一般的(Major):系统的次要功能没有完全实现,但不影响用户的正常使用。例如:提示信息不太准确或用户界面差,文字排列不整齐,操作不方便等。(在回归测试前修改)

三、 Bug数据库管理

本系统的测试BUG跟踪采用微软TeamFoundationServer进行管理,由测试环境部署人员在SQL Server 2005中建立BUG管理数据库。
1    TeamFoundationServer 

2    Bug(Title) 标题

 Bug的标题格式可遵照以下格式:功能名:提出简短描述。
其中功能名指发现Bug的产品功能。提出的简短描述可尽可能解释发现的问题。这里举例为:“软件:中断执行IRM文件引起应用崩溃。”
3    Bug(Classification)分级
Area:指BUG产生的区域,例如:CMS
Iteration:指BUG产生的测试迭代,一般项目采用三次迭代。
4    Bug(Status) 状态

Assigned To :指定负责解决该BUG的人员

Status:指bug现在的状态
        Active:激活状态
        Closed:关闭状态
        Resolved:解决状态
Triage
        Approved:
        Investigate:

Rank:bug的等级

Reason:bug产生的原因
        Build Failure:编译失败
        New:新BUG
Bug解决的原因
       As Designed:系统设计如此
       Deferred:BUG延期解决
       Duplicate:已有相同bug
       Fixed:以解决的bug
       Obsolete:旧的BUG
       Unable to Reproduce:bug无法重现
Bug关闭的原因
      As Designed:系统设计如此
      Deferred:BUG延期解决
      Duplicate:已有相同bug
      Fixed:以解决的bug
      Obsolete:旧的BUG
      Unable to Reproduce:bug无法重现

Priority : 解决Bug的优先级。用于决定各开发小组任务与工作计划。
1:Bug必须立即修改,否则下一步工作无法展开。
2:Bug可在发布前任意时间修改。
3:Bug是微不足道的,可在发布后解决。

5    Bug(Description)描述
Bug 的描述应描述发现的问题,以及在何种情况下能重现该问题,举例说明:“1:打开Edit菜单项,2:按下Edit按钮,3:按下 Delete按钮。希望:得到能进行编辑或删除操作。实际:应用中断崩溃”
6    Bug(History)历史
Bug历史栏是由TFS自动生成的,供bug跟踪人员查询。不能手动修改。
7    Bug(Link)连接
Bug链接栏可以添加链接,为描述不清楚地bug提供链接。
8    Bug(File Attachments)附件
Bug附件栏可以添加bug描述的附件。
9    Bug(Details)其他
Bug 其他可以添加Bug 的其他内容,主要由测试人员填写。包括BUG是否发布,发现的生成迭代,解决的编译迭代,等等。

四、防止 Bug 重复提交

为避免bug的重复提交可先查询VSTF数据库看该bug是否已存在。如果按照一定的格式录入bug,就能很容易发现是否重复。测试人员相互沟通是减少重复提交bug数的重要方法。 

五、Bug 状态和转换

Bug 是表明系统中可能存在或已经存在问题的工作项。打开 Bug 的目的是以一种可使读者理解问题的全部影响的方式准确报告 Bug。Bug 报表中的说明应便于跟踪在遇到 Bug 时所使用的步骤,从而使 Bug 易于重现。测试结果应该明确显示问题。此说明的明确性和可理解性通常会影响到修复 Bug 的可能性。 

新建

在软件产品中检测到 Bug 时,必须尽快记录这些 Bug,以便开发人员能够解决这些 Bug。在打开 Bug 报表之前,应该对现有的 Bug 进行查询,以确保您发现的 Bug 未经报告。

新建 活动

新建 当首次创建 Bug 时,该 Bug 作为新的 Bug 被激活。除非 Bug 是由于生成失败创建的,否则作为新 Bug 创建所有 Bug。
生成失败 当由于生成失败而直接创建 Bug 时,Bug 因生成失败被激活。

活动

当您发现新的 Bug 并使用团队资源管理器进入该 Bug 时,该 Bug 工作项将自动设置为活动状态。活动 Bug 指示存在必须解决的问题。

活动 已解决

已修复 当签入更改的代码时,Bug 作为“已修复”解决。当签入该修复时,将该 Bug 链接到变更集。
保留原样 如果某一 Bug 描述预期的系统情况或行为,则该 Bug 作为“保留原样”解决。
已推迟 如果当前迭代中将不会修复某个 Bug,则该 Bug 将因“已推迟”而被解决。它将被延迟,直到可在产品将来的迭代或版本中重新评估该 Bug。
重复 如果一个 Bug 与另一个 Bug 描述的是同一个问题,则该 Bug 将因“重复”而被解决。请包含一个指向相应的重复 Bug 的链接,以便于该 Bug 的作者能在关闭该 Bug 之前轻松地确认此重复情况。
已过时 如果某个 Bug 不再适用于产品,则该 Bug 作为“已过时”解决。例如,如果 Bug 描述的问题处在产品中已不再存在的功能区域内,则该 Bug 已过时。
无法重现 如果开发人员无法在其计算机上重现某个 Bug,则该 Bug 作为“无法重现”解决。

已解决

当某个 Bug 已由开发人员解决,或者正在进行会审处理时,该 Bug 处于已解决状态。Bug 可作为“已修复”或“保留原样”解决。

已解决 已关闭

已修复 当 Bug 的作者验证已在某个版本中修复了该 Bug 时,该 Bug 作为“已修复”关闭。
保留原样 如果 Bug 的作者同意该 Bug 所描述的某件事物是故意为之,则该 Bug 作为“保留原样”关闭。
已推迟 如果 Bug 的作者同意该 Bug 应该推迟解决,则该 Bug 作为“已推迟”关闭。
重复 如果 Bug 的作者确认该 Bug 与另一个 Bug 描述的是同一问题,则该 Bug 作为“重复”关闭。
已过时 如果 Bug 的作者的同意所描述的问题不再适用于该产品,则该 Bug 作为“已过时”关闭。
无法重现 如果 Bug 的作者无法生成该 Bug 的工作示例或提供更具体的说明以重现该 Bug,则该 Bug 作为“无法重现”关闭。

已解决 活动

解决方案被拒绝 如果解决方法不可接受,则该 Bug 返回到“活动”状态。提供有关解决方法被拒绝的原因的具体信息,以便帮助后面接手该 Bug 的人员能够适当地解决它。
错误修复 如果未正确修复,则该 Bug 返回到“活动”状态。提供有关修复 Bug 的方式和未正确修复 Bug 的原因的详细信息。
测试未通过 如果测试表明 Bug 仍然存在,则 Bug 恢复为“活动”状态。请提供有关哪个测试未通过以及在哪个版本中测试未通过的详细信息。

已关闭

已关闭的 Bug 表示对于当前产品版本不需要再做进一步的工作。Bug 在解决方法得到验证后关闭。

已关闭 活动

回归测试 如果回归测试指示 Bug 再次出现,则激活该 Bug 并对该 Bug 进行会审。请将“原因”字段设置为“回归测试”。
转载声明:
原载:IT智库网——让开发者尽情的分享知识、迅速的成长.
本文链接:http://www.it118.org/specials/c670f670-c001-4aa9-a92d-371d40ad1dc0/cf96e81a-052d-4d0c-b3b8-74ddf9becbf7.htm
如需转载必须以链接形式注明原载或原文地址,谢谢合作

教程:VS2010 之TFS入门指南

[原文发表地址] Tutorial: Getting Started with TFS in VS2010

[原文发表时间] Wednesday, October 21, 2009 1:00 PM

[本文转载地址] http://www.cnblogs.com/ewyb/archive/2010/09/27/1836458.html

本月初,我们发布了TFS新基础配置。该配置为建立支持源码管理,工作项和生成(builds)的TFS版本提供了便利。 这是一个好机会将你在VSS(Visual Source Safe)上的资源迁移到TFS,并且还可以选用一些新的特性。现在VS2010 Beta2的正式版已经发布了,下面是该系统的入门指南。

这篇文章对那些还没有安装或使用过TFS的人将最有帮助。TFS有对复杂环境的良好支持。比如,报表,SharePoint的整合,支持跨多域,分布式数据库等等。不过我不打算在这里讲述其中的任何一个部分,我的目的是帮助你们了解为什么我们要选择TFS,以及如何使用它。如果你是VSS的用户, 在今后的文章中,我会讲述如何将VSS数据库迁移到TFS上。

在上图中,每个系统都有独立的存储空间,资源标识集,命令和工具集。要让整个系统工作起来,就像把一组自定义立体组件联接在一起:可以实现,但工作量巨大,而且可能在一些地方出现纰漏。

我更想要的就是这样一个系统,它可以将这些工作整合到一起并实现我默认的工作流程。

这个整合实现了一些非常常见的场景。例如每天我会编辑源代码,生成产品并测试它,报Bug并修复它,周而复始。当有一个整合的系统可以全部支持这些工作流程时,那么所有的工作就可以被关联起来。例如,当我签入Bug的修复时,我很想看到那些缺陷被解决时这个变更集能被纪录下来。(详见下面的例子)

TFS的基础配置可以让你精确地做到这些。这跟简单的源码管理相比是一个巨大的进步。TFS的完整版将会加入一些新的特性,包括自动化测试,虚拟实验室的部署和架构验证。下面是扩展后的工作流程:

当你使用Visual Studio 加强版和旗舰版的时候,你可以根据需要选择安装这些新组件。

有许多方法可以访问TFS。开发人员经常会通过Visual Studio来访问它。测试人员可以通过新的Test and Lab Manager来访问TFS(没有必要安装VS)。如果你是项目经理,你也可以通过web接口,Excel,Microsoft Project,或者dashboards的MOSS支持(VS2010的新功能)来访问TFS。更多相关内容以后介绍。

在这篇文章的其余部分,我会向你们逐步介绍如何使用基础配置来开始我们的第一个TFS工程。

入门指南

现在, 有了概念层次的了解,是时候把它们连接起来了。以Brian Harry的 TFS文章所列出的步骤为开端。所有必要的软件会以默认集合(创造性的称为DefaultCollection)的形式安装到你的机器上。

在这里我们能通过Visual Studio连接到TFS里。做到这一点最简单的方法是使用菜单“团队”(你也可以使用起始页上的链接):

这里需要输入TFS的服务器名称。例如,我的Windows 7机器:JLZB2REL。通过添加按钮把服务器加入到列表里, 然后点击关闭:

在这里,你可以从组合框里选择服务器,接着选择DefaultCollection,然后点击连接:

现在团队资源管理器选项卡有了服务器连接和DefaultCollection,但我们还没有一个可以存储东西的TFS项目:

我为本教程创建了一个新的Windows窗体项目作为我们的解决方案样本(文件,新建项目,Windows窗体)。如果你试图添加新的代码项目到源代码管理,会出现错误。例如:

你选择“将解决方案添加到源代码管理”菜单项后,你会得到“没有可用的团队项目源代码管理文件夹”的错误信息:

该错误不是很直观(特别是所提供的词项目是用于TFS和你们代码解决方案里面,而它们是不同的概念)。此错误的意思是你必须创建一个真实的TFS项目去包含你工作中有用的资源。在团队资源管理器中,右键点击你的集合,选择新建团队项目:

在这里我将为应付帐款系统创建一个TFS项目。该项目将包含整个系统所有需要的解决方案,数据等。填写完资料,点击下一步:

默认的是Agile模板,但你也可以选择CMMI模板。关于模板类型你可以在MSDN上获取更详细的说明。如果你正在使用agile方法(比如TDD),这是个不错的选择。选择后,点击完成。

项目创建过程中,会有各种状态更新。

成功后,点击关闭按钮:

团队资源管理器显示了该项目,将包含工作项,生成和源代码管理:

此时可以更新项目集合。再增加一个解决方案到TFS中:右击解决方案资源管理器中的项目,选择“将解决方案添加到源代码管理”:

此时可以在TFS中为解决方案新建一个文件夹或者只是采用默认值。如果准备好了,请点击确定。

到此就可以在解决方案资源管理器中看到所有文件已经在源码管理下了。(查看文件前面的“+”号)

此时可以看到列出的源代码管理器可采取的公开解决方案的动作。添加注释然后点击签入:

点击是确认签入。

此时新的解决方案就在TFS中了,并且可以开始工作项了。

工作项

可以直接在Visual Studio里用团队资源管理器或者通过网页前端和Test and Lab Management工具 来创建工程项目。打开团队资源管理器,并展开工作项下的Team Queries项来浏览你的工程项目。也可以通过双击任意查询选项(例如Active Bugs)来浏览任意你所能看到的项目。

因为我们的TFS工程是空的,所以在列表中没有active Bug。

创建一个新的bug,选择菜单:团队,新建工程项。这里可以创建多种工作项来跟踪功能点,缺陷等等。选择Bug继续:

为这个新的Bug填入相关资料,然后点击保存工程项来提交到数据库中。

如果现在刷新Active Bug查询列表,你会看到这个新的Bug:

现在添加一个真正的Bug来修复我们的工程。在我的例子中,只是创建了一个默认的Windows Forms应用程序。如要更新标题:

现在我们需要修复这个Bug。重新回到解决方案资源管理器,选择Form1.cs,然后选择“签出以进行编辑”:

点击“签出”按钮来确定:

现在在文件的旁边会有一个打勾的标记,这样你就知道它已经可以编辑了:

当你更新主窗口的Text属性时,VS会自动签出任何依赖的文件:

这个例子虽然是一个Windows Forms应用程序,但它也支持其他所有的solution/project类型。现在我们对代码改动满意了,在VS的底部选择“挂起的更改”标签。

在这个例子中,我们修复了一个Bug,所以点击“工作项”图标按钮:

选择用来跟踪我们标题错误的Bug#6。我们想要通过这个签入来解决它:

添加注释并点击签入,然后点击“是”确认。

如果刷新Bug#6,你会看到现在状态已经变为Resolved,并且历史纪录已经更新了。

请注意“变更集”(源码管理改变的集合)已经被自动添加到历史纪录中。

这时候可以按你工程需要继续创建和修复Bug。

其他访问TFS的方法

我前面提到过没必要必须使用VS来访问TFS。我们已经将TFS与其它客户端作了许多深层次的整合,例如网页和Office。举个例子,我可以通过网页浏览器,很简单地用服务器的名字连接到我的服务器(8080是默认端口):http://jlzb2rel:8080/tfs/

现在我可以浏览我的集合和工程了。如果你选择我们刚刚新建的AccountsPayable项目,然后点击“继续”按钮,会看到更多的信息。在这个例子中,通过导航到Work Items标签,可以找到这个系统中所有的Bug。

这确实是一个浏览你的工程的简单方法。可以在任何一台电脑上,并且不需要安装额外的东西。这些操作在Excel,Microsoft Project等其他软件中也有相关的支持。这种访问方式使该项目中的所有成员一起工作变得更加简单。

此时,你拥有了一套非常有用的工具来轻松完成工作。如果你在使用VSS,仅仅是这些内容就已经令人兴奋了。现在可以放下这个教程,休息会再回来,如果你想要尝试一些高级属性的话,例如测试场景。我会使用beta 1在这个教程中来演示。

生成支持

工作流程的下一个典型的环节就是自动生成产品。如果遵循Brian的安装说明,那么现在你的机器上就有了TFS基本的本地生成支持。第一步是要导航到团队资源管理器中,右键点击“所有生成定义”,选择“新建生成定义”:

有一系列的定义需要填,就像一个代码项目的属性页:

触发页面使我们能够决定生成何时开始。你可以从下列项中选择:

·默认情况下使用手动项。我们必须以这一项开始我们自己的生成。

·每一次签入后,当你想拥有一个新的生成的时候,持续集成是非常有用的。它允许你立刻验证新的签入而不用等很多的签入混合在一起后再验证。

·滚动生成提供了一种批处理改变的方法,当开始生成要花一点时间并且你无法去做每一项时,这种方法很便利。

·封闭签入让你确保所有的签入传入TFS前被生成。并确保你不会对你项目组的其它成员造成生成破坏。

·计划生成为整个团队做每日生成进行了有益的尝试

可以创建和使用多个不同的生成定义,允许您根据不同的目的来使用不同类型的生成。

你可以在空闲的时候查看所有标签(每一项在产品上都完整的文档说明). 但是我们需要提供给生成一个存储新生成的位置来解决默认生成中的黄色警告标志,在这里,我在我的机器上创建了一个公共的UNC:

现在可以保存生成定义到TFS。如果回到团队资源管理器,我们可以“使新的生成入队”:

在跳出的确认对话框中,选择排队:

这是我机器上状态页显示的已排队的一个生成:

如果双击队列中的生成,可以得到这个生成的详细状态:

从这里你可以看到警告和错误,日志文件,导航到Drop的目录等。例如,如果你选择“查看日志文件”,你能看到执行生成的脚本(子集):

如果你选择打开Drop文件夹链接,你会被带到我们drop的位置:

现在任何人都可以选择生成来做他们日常测试,或发布给客户等。

此时您已经知道利用了TFS的基本配置所有东西。

将来我会做一个如何安装使用虚拟实验室系统的教程(Visual Studio 旗舰版的一部分),使您能够部署复杂的应用程序到Hyper – V的环境,做自动化测试。

创建一个新的TFS集合

 [注意:这部分是完全可选的] 如果你喜欢你可以在一个TFS中存储你所有的工作。如果你是一个Visual Source Safe用户,你可以跳过这一整部分。但是如果你想创建一个新的顶级集合,相当的简单。第一步是启动,然后是团队基础管理控制台:

控制台启动后,选择“团队项目集合项”, 点击“创建团队项目集合”链接:

为项目的收集填写一个你想要描述的名称后,点击“下一步”:

接受数据层的默认值,然后点击“下一步”:

TFS基本配置不支持实验室管理,因此直接下一步:

在这里所有需要的数据都被配置了,你可以选择“核实”:

验证这些信息主要是为了集合可以顺利的创建:

当核实结束后,点击创建:

这一步为TFS的每一个配置提供所有需要的东西。点击下一步就完成了:

你将会看到一个默认版本的新项目集:

【转】典型开源3D引擎分类比较

 

典型开源3D引擎分类比较

 

常见的3D引擎有:Unreal,Quake,Lithtech,OGRE,Nebula,Irrlicht,Truevision3D…等,其中开源免费的有:orge,irrlicht,fly3d, NeoEngine, revolution3d, Nebula2Genesis3d等。OGRE 在免费、开源的引擎中评价最高,一个原因是作为一个图形渲染引擎,它支持的图形特性最多,所以渲染质量也不错;另一个原因是设计模式的清晰;另外,速度也不错。OGRE 将专注于向一个纯粹的、然而富于协作和扩展性的图形引擎发展,这得益于它的庞大的社群支持,使得很多事情可以通过外挂一些更专业的引擎来实现,物理引擎使用 ODE  Tokamak  NovodeX ,网络引擎使用 openTNL  RakNet  eNet ,声音引擎使用 FMod  OpenAL ,以及界面引擎使用 CEGUI 

 

 

Standard

OGRE

IrrLicht

Truevision3D

General

Type

Only rendering engine

 

Game engine

Include TV3DEngine,TV3DMedia,TV3DNet……

Languages

Written in pure C++, totally object orientated.

Written in pure C++, totally object orientated.

Written in C++ and VB6 with DirectX8.1

License

LGPL

zlib/libpng License(totally free)

·   Free TV3DSDK

·   Registered Developer License

·   group or site licenses

Develop platforms

·   C++

·   .NET

·   C++

·   .NET language binding

VB/VC++/DELPHI/

C#/vb.net

Platforms

Windows

ü         Support

ü         Support

ü         Support

Linux

 

ü         Support

ü         Support

 

MacOS

ü         Support

ü         Support

 

3D API

Direct3D

ü         Support

ü         Support

ü         Support

OpenGL

ü         Support

ü         Support

 

Other

 

·   The Irrlicht Engine software renderer.

·   The Apfelbaum Software Renderer

 

Architecture

Extensibility

·   Extensible example framework

·   Flexible plugin architecture allows engine to be extended without recompilation

·   Use some other professional engines to achieve other features

 

 

Scripting

 

 

 

Materials & Textures

Materials

·   Powerful material declaration language

·   Multiple material

·   Material LOD

·   Multi-texture and multi-pass blending

·   Projective texturing

·   Textures can be provided and updated in real-time by plugins

·   Provide common built in materials based on fixed function pipeline or programmable pipeline

·   add new materials to Irrlicht at runtime, without the need of modifying/recompiling the engine

 

Textures and Mapping

自动产生MipMap

 

自动调整纹理大小以满足硬件需求。

 

支持可程序控制的纹理坐标生成和转换。

·   Bump mapping

·   Parallax mapping

·   Light maps

·   Sphere mapping

DOT3 Bump mapping

 

没有提供多层纹理和混合模式

Shaders

·   Supports vertex and fragment programs, both written in assembler, and in Cg, DirectX9 HLSL, or GLSL

·   Support many commonly bound constant parameters like worldview matrices, light state information, object space eye position etc

·   Pixel and Vertex Shaders 1.1 to 3.0

·   ARB Fragment and Vertex Programs

·   HLSL

·   GLSL

·   Hardware Vertex Shaders

·   HLSL

Scene Management

General

·   Highly customizable, flexible scene management, not tied to any single scene type.

·   Hierarchical scene graph; nodes allow objects to be attached to each other and follow each others movements, articulated structures etc

·   Scene querying

·   Using a hierarchical scene graph, can mix indoor and outdoor scene seamless together

·   Extensible hierarchical scene management

 

 

BSP Support

BSP 场景的天空穹、粒子特效、高级材质属性,乃至 BSP 的空间分割规则都会被正确识别、自动实现

·    BSP 的特性支持比较贫乏

 BSP 的读取存在 BUG

Other

 

 

 

Special effects

Particle System

 

·   easily extensible emitters, affectors and renderers

·   can be defined in text scripts for adjusting in real-time

·   particle pooling

·   Customizeable Particle systems for snow, smoke, fire, …

Atmospheric effects like fog, cloud, sky (box and sphere), stars.

Light & Shadow

 

当前支持三种阴影:调整纹理阴影,调整模版阴影,附加模板阴影。不支持软阴影

Multiple shadow rendering techniques,

·   Dynamic lights

·   Dynamic shadows using the stencil buffer

·   Optimized dynamic stencil shadows

·   DirectX Hardware Lighting

Other

·   Support for skyboxes, skyplanes and skydomes

·   Billboarding for sprite graphics

·   Transparent objects automatically managed

 

·   Realistic water surfaces

·   Billboards

·   Transparent objects

·   Skyboxes

·   Fog

·   Texture animation

Billboard

Character Animation

 

·   Skeletal animation

·   blending of multiple animations

·   variable bone weight skinning

·   hardware-accelerated skinning

 

·   Morph target animation:

·   Skeletal animation

·   Skeleton based, Key frame based, morph based animations

·   Animations that can be enhanced by bone attaching or custom bone rotations.

Supported Formats

textures file formats

·   PNG, JPEG, TGA, BMP or DDS

·   1D textures, volumetric textures, cubemaps and compressed textures (DXT/S3TC)

·   PSD,JPG,PNG,TGA,BMP,PCX 

 

 

mesh file formats

Milkshape3D, 3D Studio Max, Maya, Blender and Wings3D

·   3DS,OBJ,CSM,DAE,DMF,OCT,X,MS3D,MY3D,LMTS,BSP,MD2

3DS, X, MDL, MD2, MD3

Document

 

Great number of users and well documented.

well documented API with lots of examples and tutorials.

 

Other

GUI

 

 

 

2D Drawing

 

 

 

XML

ü         Support

XML Converter: convert efficient runtime binary formats to/from XML for interchange or editing

ü        Support

快速的XML分析器

 

Web Site

 

http://www.ogre3d.org

http://irrlicht.sourceforge.net

http://www.truevision3d.com

 


 本文来自:http://www.cnblogs.com/cyberchen-net/archive/2007/03/04/663597.html

 

VB.NET and C# 语法比较手册

 

VB.NET and C# Comparison
This is a quick reference guide to highlight some key syntactical differences between VB.NETand C#. Hope you find this useful!
Thank you to Tom Shelton, Fergus Cooney, and others for your input.

VB.NET

C#

Comments
‘ Single line only
Rem Single line only

// Single line
/* Multiple
    line  */
/// XML comments on single line
/** XML comments on multiple lines */

Data Types

Value Types
Boolean
Byte
Char   (example: “A”c)
Short, Integer, Long
Single, Double
Decimal
Date

Reference Types
Object
String

Dim x As Integer
Console.WriteLine(x.GetType())     ‘ Prints System.Int32
Console.WriteLine(TypeName(x))  ‘ Prints Integer

‘ Type conversion
Dim numDecimal As Single = 3.5
Dim numInt As Integer
numInt = CType(numDecimal, Integer)   ‘ set to 4 (Banker’s rounding)
numInt = CInt(numDecimal)  ‘ same result as CType
numInt = Int(numDecimal)    ‘ set to 3 (Int function truncates the decimal)

Value Types
bool
byte, sbyte
char   (example: ‘A’)
short, ushort, int, uint, long, ulong
float, double
decimal
DateTime   (not a built-in C# type)

Reference Types
object
string

int x;
Console.WriteLine(x.GetType());    // Prints System.Int32
Console.WriteLine(typeof(int));      // Prints System.Int32


// Type conversion

double numDecimal = 3.5;
int numInt = (int) numDecimal;   // set to 3  (truncates decimal)

Constants
Const MAX_STUDENTS As Integer = 25 const int MAX_STUDENTS = 25;
Enumerations
Enum Action
  Start 
  [Stop]   ‘ Stop is a reserved word
  Rewind
  Forward
End Enum

Enum Status
  Flunk = 50
  Pass = 70
  Excel = 90
End Enum

Dim a As Action = Action.Stop
If a <> Action.Start Then Console.WriteLine(a)     ‘ Prints 1

Console.WriteLine(Status.Pass)     ‘ Prints 70

Dim s As Type = GetType(Status)
Console.WriteLine([Enum].GetName(s, Status.Pass))    ‘ Prints Pass

enum Action {Start, Stop, Rewind, Forward};
enum Status {Flunk = 50, Pass = 70, Excel = 90};

Action a = Action.Stop;
if (a != Action.Start)
  Console.WriteLine(a + ” is ” + (int) a);    // Prints “Stop is 1”

Console.WriteLine(Status.Pass);    // Prints Pass

Operators

Comparison
=  <  >  <=  >=  <>

Arithmetic
+  –  *  /
Mod
(integer division)
(raise to a power)

Assignment
=  +=  -=  *=  /=  \=  ^=  <<=  >>=  &=

Bitwise
And  AndAlso  Or  OrElse  Not  <<  >>

Logical
And  AndAlso  Or  OrElse  Not

Note: AndAlso and OrElse are for short-circuiting logical evaluations

String Concatenation
&

Comparison
==  <  >  <=  >=  !=

Arithmetic
+  –  *  /
(mod)
(integer division if both operands are ints)
Math.Pow(x, y)

Assignment
=  +=  -=  *=  /=   %=  &=  |=  ^=  <<=  >>=  ++  —

Bitwise
&  |  ^   ~  <<  >>

Logical
&&  ||   !

Note: && and || perform short-circuit logical evaluations

String Concatenation
+

Choices

greeting = IIf(age < 20, “What’s up?”, “Hello”)

‘ One line doesn’t require “End If”, no “Else”
If language = “VB.NET” Then langType = “verbose”

‘ Use : to put two commands on same line
If x <> 100 Then x *= 5 : y *= 2  

‘ or to break up any long single command use _
If whenYouHaveAReally < longLine And itNeedsToBeBrokenInto2 > Lines Then _
  UseTheUnderscore(charToBreakItUp)

If x > 5 Then
  x *= y
ElseIf x = 5 Then
  x += y
ElseIf x < 10 Then
  x -= y
Else
  x /= y
End If

Select Case color   ‘ Must be a primitive data type
  Case “pink”, “red”
    r += 1
  Case “blue”
    b += 1
  Case “green”
    g += 1
  Case Else
    other += 1
End Select

greeting = age < 20 ? “What’s up?” : “Hello”;

if (x != 100) {    // Multiple statements must be enclosed in {}
  x *= 5;
  y *= 2;
}

No need for _ or : since ; is used to terminate each statement.


if
(x > 5)
  x *= y;
else if (x == 5)
  x += y;
else if (x < 10)
  x -= y;
else
  x /= y;

switch (color) {                          // Must be integer or string
  case “pink”:
  case “red”:    r++;    break;        // break is mandatory; no fall-through
  case “blue”:   b++;   break;
  case “green”: g++;   break;
  default:    other++;   break;       // break necessary on default
}

Loops
Pre-test Loops:
While c < 10
  c += 1
End While

Do Until c = 10 
  c += 1
Loop

Do While c < 10
  c += 1
Loop

For c = 2 To 10 Step 2
  Console.WriteLine(c)
Next


Post-test Loops:
Do 
  c += 1
Loop While c < 10
Do 
  c += 1
Loop Until c = 10

‘  Array or collection looping
Dim names As String() = {“Fred”, “Sue”, “Barney”}
For Each s As String In names
  Console.WriteLine(s)
Next

Pre-test Loops:  

// no “until” keyword
while (i < 10)
  i++;

for (i = 2; i < = 10; i += 2)
  Console.WriteLine(i);

Post-test Loop:

do
  i++;
while (i < 10);

// Array or collection looping

string[] names = {“Fred”, “Sue”, “Barney”};
foreach (string s in names)
  Console.WriteLine(s);

Arrays

Dim nums() As Integer = {1, 2, 3} 
For i As Integer = 0 To nums.Length – 1
  Console.WriteLine(nums(i))
Next

‘ 4 is the index of the last element, so it holds 5 elements
Dim names(4) As String
names(0) = “David”
names(5) = “Bobby”  ‘ Throws System.IndexOutOfRangeException

‘ Resize the array, keeping the existing values (Preserve is optional)
ReDim Preserve names(6)

Dim twoD(rows-1, cols-1) As Single
twoD(2, 0) = 4.5

Dim jagged()() As Integer = { _
  New Integer(4) {}, New Integer(1) {}, New Integer(2) {} }
jagged(0)(4) = 5

int[] nums = {1, 2, 3};
for (int i = 0; i < nums.Length; i++)
  Console.WriteLine(nums[i]);

// 5 is the size of the array
string[] names = new string[5];
names[0] = “David”;
names[5] = “Bobby”;   // Throws System.IndexOutOfRangeException

// C# doesn’t can’t dynamically resize an array.  Just copy into new array.
string[] names2 = new string[7];
Array.Copy(names, names2, names.Length);   // or names.CopyTo(names2, 0); 

float[,] twoD = new float[rows, cols];
twoD[2,0] = 4.5f; 

int[][] jagged = new int[3][] {
  new int[5], new int[2], new int[3] };
jagged[0][4] = 5;

Functions

‘ Pass by value (in, default), reference (in/out), and reference (out) 
Sub TestFunc(ByVal x As Integer, ByRef y As Integer, ByRef z As Integer)
  x += 1
  y += 1
  z = 5
End Sub

Dim a = 1, b = 1, c As Integer   ‘ c set to zero by default 
TestFunc(a, b, c)
Console.WriteLine(“{0} {1} {2}”, a, b, c)   ‘ 1 2 5

‘ Accept variable number of arguments
Function Sum(ByVal ParamArray nums As Integer()) As Integer
  Sum = 0 
  For Each i As Integer In nums
    Sum += i
  Next
End Function   ‘ Or use Return statement like C#

Dim total As Integer = Sum(4, 3, 2, 1)   ‘ returns 10

‘ Optional parameters must be listed last and must have a default value
Sub SayHello(ByVal name As String, Optional ByVal prefix As String = “”)
  Console.WriteLine(“Greetings, ” & prefix & ” ” & name)
End Sub

SayHello(“Strangelove”, “Dr.”)
SayHello(“Madonna”)

// Pass by value (in, default), reference (in/out), and reference (out)
void TestFunc(int x, ref int y, out int z) {
  x++;  
  y++;
  z = 5;
}

int a = 1, b = 1, c;  // c doesn’t need initializing
TestFunc(a, ref b, out c);
Console.WriteLine(“{0} {1} {2}”, a, b, c);  // 1 2 5

// Accept variable number of arguments
int Sum(params int[] nums) {
  int sum = 0;
  foreach (int i in nums)
    sum += i;
  return sum;
}

int total = Sum(4, 3, 2, 1);   // returns 10

/* C# doesn’t support optional arguments/parameters.  Just create two different versions of the same function. */ 
void SayHello(string name, string prefix) {
  Console.WriteLine(“Greetings, ” + prefix + ” ” + name);

void SayHello(string name) {
  SayHello(name, “”);
}

Exception Handling

‘ Deprecated unstructured error handling
On Error GoTo MyErrorHandler

MyErrorHandler: Console.WriteLine(Err.Description)

Dim ex As New Exception(“Something is really wrong.”)
Throw  ex 

Try 
  y = 0
  x = 10 / y
Catch ex As Exception When y = 0 ‘ Argument and When is optional
  Console.WriteLine(ex.Message)
Finally
  Beep()
End Try

Exception up = new Exception(“Something is really wrong.”);
throw up;  // ha ha

try
  y = 0;
  x = 10 / y;
}
catch (Exception ex) {   // Argument is optional, no “When” keyword 
  Console.WriteLine(ex.Message);
}
finally {
  // Must use unmanaged MessageBeep API function to beep
}

Namespaces

Namespace Harding.Compsci.Graphics 
  …
End Namespace

‘ or

Namespace Harding
  Namespace Compsci
    Namespace Graphics 
      …
    End Namespace
  End Namespace
End Namespace

Import Harding.Compsci.Graphics

namespace Harding.Compsci.Graphics {
  …
}

// or

namespace Harding {
  namespace Compsci {
    namespace Graphics {
      …
    }
  }
}

using Harding.Compsci.Graphics;

Classes / Interfaces

Accessibility keywords
Public
Private
Friend                   
Protected
Protected Friend
Shared

‘ Inheritance
Class FootballGame
  Inherits Competition
  …
End Class 

‘ Interface definition
Interface IAlarmClock 
  …
End Interface

// Extending an interface 
Interface IAlarmClock
  Inherits IClock
  …
End Interface

// Interface implementation
Class WristWatch 
  Implements IAlarmClock, ITimer 
   …
End Class 

Accessibility keywords
public
private
internal
protected
protected internal
static

// Inheritance
class FootballGame : Competition {
  …
}


// Interface definition

interface IAlarmClock {
  …
}

// Extending an interface 
interface IAlarmClock : IClock {
  …
}


// Interface implementation

class WristWatch : IAlarmClock, ITimer {
   …
}

Constructors / Destructors
Class SuperHero
  Private _powerLevel As Integer

  Public Sub New ()
    _powerLevel = 0
  End Sub

  Public Sub New (ByVal powerLevel As Integer)
    Me._powerLevel = powerLevel
  End Sub

  Protected Overrides Sub Finalize () 
   ‘ Desctructor code to free unmanaged resources
    MyBase.Finalize()
  End Sub
End Class

class SuperHero {
  private int _powerLevel;

  public SuperHero() {
     _powerLevel = 0;
  }

  public SuperHero(int powerLevel) {
    this._powerLevel= powerLevel; 
  }

  ~SuperHero() {
    // Destructor code to free unmanaged resources.
    // Implicitly creates a Finalize method

  }
}

Objects

Dim hero As SuperHero = New SuperHero
With hero
  .Name = “SpamMan”
  .PowerLevel = 3
End With

hero.Defend(“Laura Jones”)
hero.Rest()     ‘ Calling Shared method
‘ or
SuperHero.Rest()

Dim hero2 As SuperHero = hero  ‘ Both refer to same object
hero2.Name = “WormWoman”
Console.WriteLine(hero.Name)   ‘ Prints WormWoman

hero = Nothing    ‘ Free the object

If hero Is Nothing Then _
  hero = New SuperHero

Dim obj As Object = New SuperHero
If TypeOf obj Is SuperHero Then _
  Console.WriteLine(“Is a SuperHero object.”)

SuperHero hero = new SuperHero();

// No “With” construct
hero.Name = “SpamMan”;
hero.PowerLevel = 3;

hero.Defend(“Laura Jones”);
SuperHero.Rest();   // Calling static method

SuperHero hero2 = hero;   // Both refer to same object
hero2.Name = “WormWoman”;
Console.WriteLine(hero.Name);   // Prints WormWoman

hero = null ;   // Free the object

if (hero == null)
  hero = new SuperHero();

Object obj = new SuperHero(); 
if (obj is SuperHero)
  Console.WriteLine(“Is a SuperHero object.”);

Structs

Structure StudentRecord
  Public name As String
  Public gpa As Single

  Public Sub New(ByVal name As String, ByVal gpa As Single)
    Me.name = name
    Me.gpa = gpa
  End Sub
End Structure

Dim stu As StudentRecord = New StudentRecord(“Bob”, 3.5)
Dim stu2 As StudentRecord = stu  

stu2.name = “Sue”
Console.WriteLine(stu.name)    ‘ Prints Bob
Console.WriteLine(stu2.name)  ‘ Prints Sue

struct StudentRecord {
  public string name;
  public float gpa;

  public StudentRecord(string name, float gpa) {
    this.name = name;
    this.gpa = gpa;
  }
}

StudentRecord stu = new StudentRecord(“Bob”, 3.5f);
StudentRecord stu2 = stu;  

stu2.name = “Sue”;
Console.WriteLine(stu.name);    // Prints Bob
Console.WriteLine(stu2.name);   // Prints Sue

Properties

Private _size As Integer

Public Property Size() As Integer
  Get
    Return _size
  End Get
  Set (ByVal Value As Integer)
    If Value < 0 Then
      _size = 0
    Else
      _size = Value
    End If
  End Set
End Property

foo.Size += 1

private int _size;

public int Size {
  get {
    return _size;
  }
  set {
    if (value < 0)
      _size = 0;
    else
      _size = value;
  }
}

foo.Size++;

Delegates / Events

Delegate Sub MsgArrivedEventHandler(ByVal message As String)

Event MsgArrivedEvent As MsgArrivedEventHandler

‘ or to define an event which declares a delegate implicitly
Event MsgArrivedEvent(ByVal message As String)

AddHandler MsgArrivedEvent, AddressOf My_MsgArrivedCallback
‘ Won’t throw an exception if obj is Nothing
RaiseEvent MsgArrivedEvent(“Test message”)
RemoveHandler MsgArrivedEvent, AddressOf My_MsgArrivedCallback

Imports System.Windows.Forms

Dim WithEvents MyButton As Button   ‘ WithEvents can’t be used on local variable
MyButton = New Button

Private Sub MyButton_Click(ByVal sender As System.Object, _
  ByVal e As System.EventArgs) Handles MyButton.Click
  MessageBox.Show(Me, “Button was clicked”, “Info”, _
    MessageBoxButtons.OK, MessageBoxIcon.Information)
End Sub

delegate void MsgArrivedEventHandler(string message);

event MsgArrivedEventHandler MsgArrivedEvent;

// Delegates must be used with events in C#

MsgArrivedEvent += new MsgArrivedEventHandler(My_MsgArrivedEventCallback);
MsgArrivedEvent(“Test message”);    // Throws exception if obj is null
MsgArrivedEvent -= new MsgArrivedEventHandler(My_MsgArrivedEventCallback);

using System.Windows.Forms;

Button MyButton = new Button(); 
MyButton.Click += new System.EventHandler(MyButton_Click);

private void MyButton_Click(object sender, System.EventArgs e) {
  MessageBox.Show(this, “Button was clicked”, “Info”,
    MessageBoxButtons.OK, MessageBoxIcon.Information);
}

Console I/O

Special character constants
vbCrLf, vbCr, vbLf, vbNewLine
vbNullString
vbTab
vbBack
vbFormFeed
vbVerticalTab
“”
Chr(65)  ‘ Returns ‘A’

Console.Write(“What’s your name? “)
Dim name As String = Console.ReadLine()
Console.Write(“How old are you? “)
Dim age As Integer = Val(Console.ReadLine())
Console.WriteLine(“{0} is {1} years old.”, name, age) 
‘ or
Console.WriteLine(name & ” is ” & age & ” years old.”)

Dim c As Integer
c = Console.Read()    ‘ Read single char
Console.WriteLine(c)   ‘ Prints 65 if user enters “A”

Escape sequences
\n, \r
\t
\\
\”

Convert.ToChar(65)  // Returns ‘A’ – equivalent to Chr(num) in VB
// or

(char) 65

Console.Write(“What’s your name? “);
string name = Console.ReadLine();
Console.Write(“How old are you? “);
int age = Convert.ToInt32(Console.ReadLine());
Console.WriteLine(“{0} is {1} years old.”, name, age);
// or
Console.WriteLine(name + ” is ” + age + ” years old.”);

int c = Console.Read();  // Read single char
Console.WriteLine(c);    // Prints 65 if user enters “A”

File I/O

Imports System.IO

Dim writer As StreamWriter = File.CreateText(“c:\myfile.txt”)
writer.WriteLine(“Out to file.”)
writer.Close()

Dim reader As StreamReader = File.OpenText(“c:\myfile.txt”)
Dim line As String = reader.ReadLine()
While Not line Is Nothing
  Console.WriteLine(“line=” & line)
  line = reader.ReadLine()
End While
reader.Close()

Dim str As String = “Text data”
Dim num As Integer = 123
Dim binWriter As New BinaryWriter (File.OpenWrite(“c:\myfile.dat”)) 
binWriter.Write(str) 
binWriter.Write(num) 
binWriter.Close()

Dim binReader As New BinaryReader (File.OpenRead(“c:\myfile.dat”))
str = binReader.ReadString()
num = binReader.ReadInt32()
binReader.Close()

using System.IO;

StreamWriter writer = File.CreateText(“c:\\myfile.txt”);
writer.WriteLine(“Out to file.”);
writer.Close();

StreamReader reader = File.OpenText(“c:\\myfile.txt”);
string line = reader.ReadLine();
while (line != null) {
  Console.WriteLine(line);
  line = reader.ReadLine();
}
reader.Close();

string str = “Text data”;
int num = 123;
BinaryWriter binWriter = new BinaryWriter(File.OpenWrite(“c:\\myfile.dat”));
binWriter.Write(str);
binWriter.Write(num);
binWriter.Close();

BinaryReader binReader = new BinaryReader(File.OpenRead(“c:\\myfile.dat”));
str = binReader.ReadString();
num = binReader.ReadInt32();
binReader.Close();

Page last modified: 04/20/2006 16:39:28 01/14/2004 05:34:59
Please send any corrections or comments to fmccown@harding.edu.
Home

C#中使用多線程訪問Winform問題

 

我們在做winform應用的時候,大部分情況下都會碰到使用多線程控制界面上控件信息的問題。然而我們并不能用傳統方法來做這個問題,下面我將詳細的介紹。

首先來看傳統方法:

 

public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Thread thread = new Thread(ThreadFuntion);
thread.IsBackground = true;
thread.Start();
}
private void ThreadFuntion()
{
while (true)
{
this.textBox1.Text = DateTime.Now.ToString();
Thread.Sleep(1000);
}
}
}

 

運行這段代碼,我們會看到系統拋出一個異常:Cross-thread operation not valid:Control ‘textBox1’ accessed from a thread other than the thread it was created on . 這是因為.net 2.0以后加強了安全機制,不允許在winform中直接跨線程訪問控件的屬性。那么怎么解決這個問題呢,下面提供幾種方案。

第一種方案,我們在Form1_Load()方法中加一句代碼:

 

private void Form1_Load(object sender, EventArgs e)
{
Control.CheckForIllegalCrossThreadCalls = false;
Thread thread = new Thread(ThreadFuntion);
thread.IsBackground = true;
thread.Start();
}

 

加入這句代碼以后發現程序可以正常運行了。這句代碼就是說在這個類中我們不檢查跨線程的調用是否合法(如果沒有加這句話運行也沒有異常,那么說明系統以及默認的采用了不檢查的方式)。然而,這種方法不可取。我們查看CheckForIllegalCrossThreadCalls 這個屬性的定義,就會發現它是一個static的,也就是說無論我們在項目的什么地方修改了這個值,他就會在全局起作用。而且像這種跨線程訪問是否存在異常,我們通常都會去檢查。如果項目中其他人修改了這個屬性,那么我們的方案就失敗了,我們要采取另外的方案。

下面來看第二種方案,就是使用delegate和invoke來從其他線程中控制控件信息。網上有很多人寫了這種控制方式,然而我看了很多這種帖子,表明上看來是沒有什么問題的,但是實際上并沒有解決這個問題,首先來看網絡上的那種不完善的方式:

 

public partial class Form1 : Form
{
private delegate void FlushClient();//代理
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Thread thread = new Thread(CrossThreadFlush);

thread.IsBackground=true;
thread.Start();
}

private void CrossThreadFlush()
{
//將代理綁定到方法
FlushClient fc = new FlushClient(ThreadFuntion);
this.BeginInvoke(fc);//調用代理
}
private void ThreadFuntion()
{
while (true)
{
this.textBox1.Text = DateTime.Now.ToString();
Thread.Sleep(1000);
}
}
}

 

使用這種方式我們可以看到跨線程訪問的異常沒有了。但是新問題出現了,界面沒有響應了。為什么會出現這個問題,我們只是讓新開的線程無限循環刷新,理論上應該不會對主線程產生影響的。其實不然,這種方式其實相當于把這個新開的線程“注入”到了主控制線程中,它取得了主線程的控制。只要這個線程不返回,那么主線程將永遠都無法響應。就算新開的線程中不使用無限循環,使可以返回了。這種方式的使用多線程也失去了它本來的意義。

現在來讓我們看看推薦的解決方案:

 

public partial class Form1 : Form
{
private delegate void FlushClient();//代理
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Thread thread = new Thread(CrossThreadFlush);
thread.IsBackground = true;
thread.Start();
}

private void CrossThreadFlush()
{
while (true)
{
//將sleep和無限循環放在等待異步的外面
Thread.Sleep(1000);
ThreadFunction();
}
}
private void ThreadFunction()
{
if (this.textBox1.InvokeRequired)//等待異步
{
FlushClient fc = new FlushClient(ThreadFunction);
this.Invoke(fc);//通過代理調用刷新方法
}
else
{
this.textBox1.Text = DateTime.Now.ToString();
}
}
}

 

運行上述代碼,我們可以看到問題已經被解決了,通過等待異步,我們就不會總是持有主線程的控制,這樣就可以在不發生跨線程調用異常的情況下完成多線程對winform多線程控件的控制了。

 

 發佈時間:3/18/2009

 http://www.jlorocks.com/content.aspx?conId=2395

 

【转】.NET中强名及其技巧

 

如果你使用.NET作为开发工具很长时间了,你肯定回会碰到“强名”(strong name)这个概念。这个概念并不意味你的组件命名方式必须类似于MyCompany.Gorilla.Biceps的方式。强名的力量体现在对组件的保护方面,.NET Framework使用强名来标识组件和保护组件使其免遭破坏。在这篇文章中我将说明如何建立强名,以及在.NET中使用强名的技巧。

1. 散列和签名 为了了解强名的工作原理,你必须首先理解密码学方面的两个概念:散列(hashing)和数字签名(digital signatures)。散列是用来为明文(plaintext)信息创建一个唯一的压缩的值。这里的“信息”是一个很广泛的概念,对于组件来说,信息就是它本身。如图1所示,信息作为散列作为散列算法的输入而被处理,对于强名情况,采用SHA1算法。

散列是不可逆的,一旦计算出来,是不可能解密的。但是,散列在比较值方面是非常有用的。如果两个组件生成同样的散列值,你可以认为这两个组件是相同的。相反,通过散列计算出来的值和以前计算的值不一样,说明组件中某个地方被修改了。 因此通过散列值你就可以知道,组件是否被修改或破坏。然而你怎样通过散列值来防止别人对你的组件的修改呢?下面就是我要讲的“数字签名”。 虽然数字签名的具体数学原理很复杂,但是概念很简单。一个数字签名依赖于两个相关的数字:公共秘匙(public key)和私有密匙(private key)。如图2所示,当数据通过公共秘匙加密后,它只能通过私有密匙来解密,反之亦然。

2. 组件的强名 在.NET中共同使用散列和数字签名就能够保护你的组件免遭破坏。其原理如图3所示,首先从组件产生一个散列值,然后这个散列值通过你的私有密匙被加密,然后这个散列值和公共密匙被一起防止在组件中。 
在运行的时候,公共语言运行时(CLR)通过比较散列值验证组件。首先公共的密匙用来解密散列,从当前的组件中计算出一个新的散列。如果它和原来的值吻合,那就通过了验证
那么如果一个组件在签名之后被破坏将会发生什么样的情况呢?显然,在运行时从组件中计算出来的散列值将不会与存储在组件中通过私有密匙加密的散列值吻合,这样公共语言运行时将拒绝加载该组件。 

注意强名只是保证了组件的完整性,而并非它的安全性,没有什么办法来防止某个人创建一个恶意的组件,并通过强名来给它签名。你可以通过强名来验证某个组件在签名后是否被破坏。基于你对于信息的选择。这一切将由你来决定是否相信从某个地方来的组件和代码信息。 

3.强名的其它信息 

除了从组件内容求出来的散列,强名还包含一下三个方面的信息。 

l 组件简单文本名称 

l 组件版本号 

l 组件的语言文化代码 

所有的这些信息,将为每个组件提供唯一的标识。有了这些信息,公共语言运行时可以决定一个组件是否被另一个组件通过引用的方式调用。当你从一个组件引用另一个组件,该组件将存储被调用组件的公共密匙。在运行的时候,公共语言运行时将通过这些来判断该组件是否来自正确的供应商。另外强名中的其它信息用来判断组件是否符合引用的绑定策略(binding policy)的要求。 

4.使用强名的技巧 

.NET Framwork SDK和 Visual Studio.NET为指定强名提供了很多的工具,这是很有意义的,因为通过Visual Studio.NET来创建组件具有更多的选择。在你签名之前你必须创建一对密匙(公共密匙和私有密匙),通常密匙对放置在以.snk结尾的文件中。可以通过强名工具sn.exe来实现。 

sn -k MyKeyFile.snk 
如果你使用命令行编译器(vbc.exe或者csc.exe),你就可以为组件连接器al.exe,指定密匙对。如下面的命令行所示,你可以将存储在MyKeyFile.snk中的密匙对作为MyFile.dll的签名。 

al /out:MyFile.dll MyFile.netmodule /keyfile:MyKeyFile.snk 
如果你使用Visual Studio .NET,你仍然可以通过命令行的方式产生你的密匙对。有了密匙对之后你就可以通过设定在组件信息文件(AssemblyInfo.vb 或者semblyInfo.cs)的【AssemblyKeyFile】属性来把它包含到组件中。例如C#工程,你可以通过该属性包含密匙文件而生成一个签名组件。 

[assembly: AssemblyKeyFile(“..\\..\\MyKeyFile.snk”)] 

从被编译的组件到密匙文件,【AssemblyKeyFile】属性中的文件名必须包含完整的相对路径。 

5.通过延迟签名加密 

保护你的私有密匙是非常重要的,如果某个恶意的人取得了你的密匙,他生成的组件将和你签过名的组件一样。因此,应当把你的密匙设于高度的保密状态,公司里面可能就只有几个人知道。这样以来,又怎么给你的组件签名呢?如果就只有你一个人知道密匙,这样会很繁琐,因为你必须为公司中每个开发者开发出来的每个组件签名。 

很幸运的是.NET为这个问题提供了一个很好的方法:延迟签名。通过延迟签名,你可以在只知道公共密匙的情况下创建和测试组件。只有在组件实际分发给用户时才将私有密钥匙包含到组件。下面是使用延迟签名过程的一些技巧总结: 

1. 从公共/私有密钥匙对中提取公共密匙。为了提取公共密匙,也就是存储公共/私有密匙对,你可以使用强名工具,命令行如下: 

sn.exe -p MyKeyFile.snk MyPublicKeyFile.snk 

2. 将包含公共密匙的文件分发给公司所有的开发者,同时将包含公共/私有密钥匙对的文件安全存放。 

3. 在组件信息文件中包含公共密匙的文件,并注明延迟签名。 

[assembly: AssemblyDelaySign(true)] 
[assembly: AssemblyKeyFile(“..\\..\\MyPublicKeyFile.snk”)] 
如果你使用组件连接工具,而非Visual Studio .NET,你可以使用/delaysign命令行来表明延迟签名。 

4. 如果你把组件保存在全局程序集缓存(GAC)中,应关闭对组件的验证,因为GAC默认验证每个组件的强名。如果组件没有采用私有密钥匙来签名,这个验证将会失败。因此,出于开发和测试的目的你可以输入如下的命名行: 

sn.exe -Vr MyFile.dll 

5. 这样,你就可以在开发和测试的时候很自由的使用组件 

6. 当部署一个延迟签名的组件的时候,你必须通过私有密匙来给它签名。 

sn.exe -R MyFile.dll MyKeyFile.snk 

7. 最后,你必须通知GAC重新开启对组件的验证。命名行如下: 

sn.exe -Vu MyFile.dll 
6.可信任计算(TrsutWorthy Computing) 

你肯定听说过微软的“可信任计算”措施,它包含了有关微软产品的很多安全方面的措施。微软正慢慢的而且必然的朝着一个方向发展,那就是他们将使他们所有的软件产品是默认安全的。如果你知道你在做什么,你就可以降低软件(如Windows Server 2003)的安全级别。虽然降低了安全级别,但太可能使你受到突发事件的攻击。 

当你给你的组件赋予强名,你在做就是“可信任计算”。通过强名来分发你的组件,会使得组件不太可能成为木马和其他攻击计算机黑客程序的载体。.NET Framework和Visual Studio .NET提供的工具保证了你分发组件的完整性。因此我强烈建议你使用这些工具,通过这些工具来使得计算世界变得更安全一点。 

来源:未明 

 

C# 程序如何添加对 COM 组件的引用

1、注册控件

     用 regsvr32 注册控件。注意,在Win7下必须以管理员身份启动cmd,然后才能正确注册控件。

     例:C:\Windows\system32>regsvr32 F:\Workspace\SmartBridgeCulvert\Licence\Libs\NetRy6S.dll

 

2、添加引用 

      项目 右键—>添加引用…(Add Reference …),在出现的对话框中选择COM选项卡,就能看到刚才注册的组建了,选定,OK

 

 

使用WPF提供的数据验证机制

在用户通过WPF对话框窗口提供所需的数据时,对话框负责确保提供的数据有效,原因如下:

  • 从安全角度讲,应验证所有输入。

  • 从特定于域的角度讲,数据验证可防止该代码处理错误的数据,因为这样可能会引发异常。

  • 从用户体验的角度讲,对话框可以通过向用户显示哪些输入数据无效来为用户提供帮助。

  • 从性能角度讲,多层应用程序中的数据验证可以减少客户端和应用程序层之间的往返次数,尤其是在该应用程序由 Web 服务或基于服务器的数据库组成时。

若要验证 WPF 中的绑定控件,您需要定义验证规则,然后将其与该绑定关联。验证规则是派生自 ValidationRule 的自定义类。下面的示例演示验证规则 MarginValidationRule,该规则检查绑定值是否是 Double 以及是否位于指定的范围内。

C#

using System.Globalization;
using System.Windows.Controls;

namespace SDKSample
{
    public class MarginValidationRule : ValidationRule
    {
        double minMargin;
        double maxMargin;

        public double MinMargin
        {
            get { return this.minMargin; }
            set { this.minMargin = value; }
        }

        public double MaxMargin
        {
            get { return this.maxMargin; }
            set { this.maxMargin = value; }
        }

        public override ValidationResult Validate(object value, CultureInfo cultureInfo)
        {
            double margin;

            // Is a number?
            if (!double.TryParse((string)value, out margin))
            {
                return new ValidationResult(false, “Not a number.”);
            }

            // Is in range?
            if ((margin < this.minMargin) || (margin > this.maxMargin))
            {
                string msg = string.Format(“Margin must be between {0} and {1}.”, this.minMargin, this.maxMargin);
                return new ValidationResult(false, msg);
            }

            // Number is valid
            return new ValidationResult(true, null);
        }
    }
}

在此代码中,通过重写 Validate 方法来实现验证规则的验证逻辑,该方法对数据进行验证并返回相应的 ValidationResult。

若要将验证规则与绑定控件关联,您需要使用以下标记。

XAML

<Window 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="SDKSample.MarginsDialogBox"
    xmlns:local="clr-namespace:SDKSample"
    Title="Margins"
    Height="190"
    Width="300"
    MinHeight="10"
    MinWidth="300"
    ResizeMode="CanResizeWithGrip"
    ShowInTaskbar="False"
    WindowStartupLocation="CenterOwner" 
    FocusManager.FocusedElement="{Binding ElementName=leftMarginTextBox}">

  <Grid>



...


<Label Grid.Column="0" Grid.Row="0">Left Margin:</Label>
<TextBox Name="leftMarginTextBox" Grid.Column="1" Grid.Row="0">
  <TextBox.Text>
    <Binding Path="Left" UpdateSourceTrigger="PropertyChanged">
      <Binding.ValidationRules>
        <local:MarginValidationRule MinMargin="0" MaxMargin="10" />
      </Binding.ValidationRules>
    </Binding>
  </TextBox.Text>
</TextBox>


...


</Window>

对验证规则进行关联之后,WPF 将在数据输入绑定控件时自动应用该规则。如果控件包含无效的数据,则 WPF 将在无效控件周围显示一个红色边框,如下图所示。

untitled

在用户输入有效数据之前,WPF 不会将用户限制于无效的控件。这对于对话框来说是很有利的;无论数据是否有效,用户都应当能够在对话框中自由导航控件。但是,这意味着用户可以输入无效的数据,然后按“确定”按钮。因此,在按“确定”按钮时,您的代码还需要通过处理 Click 事件来验证对话框中的所有控件。

C#

using System.Windows; // Window, RoutedEventArgs, IInputElement, DependencyObject
using System.Windows.Controls; // Validation
using System.Windows.Input; // Keyboard

namespace SDKSample
{
    public partial class MarginsDialogBox : Window
    {

void okButton_Click(object sender, RoutedEventArgs e)
{
    // Don’t accept the dialog box if there is invalid data
    if (!IsValid(this)) return;

        }

        // Validate all dependency objects in a window
        bool IsValid(DependencyObject node)
        {
            // Check if dependency object was passed
            if (node != null)
            {
                // Check if dependency object is valid.
                // NOTE: Validation.GetHasError works for controls that have validation rules attached
                bool isValid = !Validation.GetHasError(node);
                if (!isValid)
                {
                    // If the dependency object is invalid, and it can receive the focus,
                    // set the focus
                    if (node is IInputElement) Keyboard.Focus((IInputElement)node);
                    return false;
                }
            }

            // If this dependency object is valid, check all child dependency objects
            foreach (object subnode in LogicalTreeHelper.GetChildren(node))
            {
                if (subnode is DependencyObject)
                {  
                    // If a child dependency object is invalid, return false immediately,
                    // otherwise keep checking
                    if (IsValid((DependencyObject)subnode) == false) return false;
                }
            }

            // All dependency objects are valid
            return true;
        }
    }
}

此代码将在窗口上枚举所有依赖项对象。如果有任何一个对象无效(由 GetHasError 返回),则此无效的控件将获得焦点,IsValid 方法将返回 false 并将该窗口视为无效。

一旦对话框有效,则可以安全地关闭并返回。在返回过程中,需要向调用函数返回一个结果。

 

本文来自msdn,一切归msdn所有。

GetPixel截屏

作     者:周强 2009.05.02

 

/**************************************************************************
* 函数名称:SaveToBmpFile
* 函数功能:把客户区内由两点所确定的矩形内图像保存为bmp图像
***************************************************************************
* 入口参数:CPoint point1     矩形左上角点
*           CPoint point2     矩形右下角点
*           char *filename    保存后文件名
*           CDC* pDC          设备内容对象指针
* 出口参数:std::vector<CtlKeyPoint> &vec_point       提取的极值点的坐标
* 返 回 值:成功,返回true ; 失败返回false。
***************************************************************************
* 备    注:使用方法举例:
*                         CPoint p1(0,0);
*                           CPoint p2(1200,800);
*                         if(SaveToBmpFile(p1,p2,”图像.bmp”,pDC)==true)
*                           {
*                            AfxMessageBox(“保存成功!”);
*                           }
*作     者:周强 2009.05.02
***************************************************************************/
BOOL CMyView::SaveToBmpFile(CPoint point1, CPoint point2, char *filename,CDC* pDC)// 当宽度不是4的倍数时自动添加成4的倍数
{
 int width, height;//保存数据的宽高
 width = point2.x-point1.x;
 height = point2.y-point1.y;
 
 BYTE *pImg = new BYTE[3*width*height];//从屏幕提取要保存的图像数据
 for (int index1=0;index1<height;index1++)
 {
  for (int index2=0;index2<3*width;index2+=3)
  {
   COLORREF coler = pDC->GetPixel(index2/3,index1);
   pImg[index1*width*3+index2+0]=GetBValue(coler);
   pImg[index1*width*3+index2+1]=GetGValue(coler);
   pImg[index1*width*3+index2+2]=GetRValue(coler);
  }
 }
 
 FILE *BinFile;
    BITMAPFILEHEADER FileHeader;
    BITMAPINFOHEADER BmpHeader;
    bool Suc=true;
    int i,extend;
 BYTE *pCur;
 
    // Open File
    if((BinFile=fopen(filename,”w+b”))==NULL) {  return false; }
 // Fill the FileHeader
 FileHeader.bfType= ((WORD) (‘M’ << 8) | ‘B’);
 FileHeader.bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
    FileHeader.bfSize=FileHeader.bfOffBits+width*height*3L ;
    FileHeader.bfReserved1=0;
    FileHeader.bfReserved2=0;
 if (fwrite((void *)&FileHeader,1,sizeof(BITMAPFILEHEADER),BinFile)!=sizeof(BITMAPFILEHEADER)) Suc=false;
 // Fill the ImgHeader
 BmpHeader.biSize = 40;
    BmpHeader.biWidth = width;
 BmpHeader.biHeight = height;
 BmpHeader.biPlanes = 1 ;
 BmpHeader.biBitCount = 24 ;
 BmpHeader.biCompression = 0 ;
 BmpHeader.biSizeImage = 0 ;
 BmpHeader.biXPelsPerMeter = 0;
 BmpHeader.biYPelsPerMeter = 0;
 BmpHeader.biClrUsed = 0;
 BmpHeader.biClrImportant = 0;
 if (fwrite((void *)&BmpHeader,1,sizeof(BITMAPINFOHEADER),BinFile)!=sizeof(BITMAPINFOHEADER)) Suc=false;
 // write image data
 extend=(width+3)/4*4-width;
 if (extend==0)
 {  
  for(pCur=pImg+(height-1)*3*width;pCur>=pImg;pCur-=3*width)
  {  
   if (fwrite((void *)pCur,1,width*3,BinFile)!=(unsigned int)(3*width)) Suc=false; // 真实的数据
  }
 }
 else
 {  
  for(pCur=pImg+(height-1)*3*width;pCur>=pImg;pCur-=3*width)
  {  
   if (fwrite((void *)pCur,1,width*3,BinFile)!=(unsigned int)(3*width)) Suc=false; // 真实的数据
   for(i=0;i<extend;i++) // 扩充的数据
   {
    if (fwrite((void *)(pCur+3*(width-1)+0),1,1,BinFile)!=1) Suc=false;
    if (fwrite((void *)(pCur+3*(width-1)+1),1,1,BinFile)!=1) Suc=false;
    if (fwrite((void *)(pCur+3*(width-1)+2),1,1,BinFile)!=1) Suc=false;
   }
  }
 }
 // return;
 fclose(BinFile);
 delete []pImg;
 return Suc;
}

C#中三种截屏方式总结

[转载自:http://www.yqdown.com/chengxukaifa/CC/4012.htm]

昨天写自动化测试的CASE的时候,碰到一个疑难杂症,调用截图的函数去截取一个Popup窗口,但是总是把背景程序给截下来,Popup窗口就跟看不到一样。本来以为是同步的问题,也就是以为先截图再点击弹出Popup窗口了。后来加了N个Thread.Sleep来测试,发觉根本不是因为这个原由,而是截图的函数截不下来这个窗口。

这个为啥呢,只好把截图的函数代码翻出来看,以前是用这种方式的:
BitBlt(dcImage, 0, 0, (int)(rect.Width), (int)(rect.Height), dcScreen, (int)(rect.Left), (int)(rect.Top), TernaryRasterOperations.SRCCOPY);

凭直觉感觉应该是因为这种通过DC的方式对WPF程序支持有问题,但是又觉得奇怪就是截取其它的WPF组件和窗口都没有问题,偏偏Popup窗口不可以。

前些天听说另外一种截屏的要领,这种要领连被遮挡的窗口都可以截,于是就Google一大把,找打了PrintWindow函数,于是就有了第二种处理方案,代码如下:

IntPtr hdc = Native.GetWindowDC(this.Handle);
if (hdc != IntPtr.Zero)
{
    IntPtr hdcMem = Native.CreateCompatibleDC(hdc);
    if (hdcMem != IntPtr.Zero)
    {
        IntPtr hbitmap = Native.CreateCompatibleBitmap(hdc, (int)(Rect.Width), (int)(Rect.Height));
        if (hbitmap != IntPtr.Zero)
        {
            Native.SelectObject(hdcMem, hbitmap);
            Native.PrintWindow(this.Handle, hdcMem, 0);

            Native.DeleteObject(hbitmap);
            Bitmap bmp = Bitmap.FromHbitmap(hbitmap);
            bmp.Save(sPath);
       }
        Native.DeleteObject(hdcMem);
    }
    Native.ReleaseDC(this.Handle, hdc);
}

 

就是拿到窗口的句柄,通过PrintWindow API来截取窗口。

但是更让人气愤的事情出现了,截出来的窗口中,只要是用到WPF组件的地点,全部是黑块儿,只有MFC的窗口框架和按钮可以正常被截取。

于是乎,就无奈的继续分析这个问题,我记得WPF是没有走GDI,而是通过Directx渲染的,那就是说DC的方式和PrintWindow的方式都不靠谱,但是截Directx的貌似还比较复杂。

突然想起来,平常报bug的时候都是按PrintScreen,然后再处理一下的,那应该PrintScreen按键是管用的,看来只能曲线救国了。但是那样就得走剪切板了,貌似会破坏剪切板的数据,不过如果我在截取前保存一下数据,在截取后再恢复一下剪切板数据,那就没有问题了。

于是就有了第三种处理方案(暂时还没有加恢复剪切板数据的代码):

const uint KEYEVENTF_EXTENDEDKEY = 0x1;
const uint KEYEVENTF_KEYUP = 0x2;
const byte VK_SNAPSHOT = 0x2C;
Native.keybd_event(VK_SNAPSHOT, 0x45, KEYEVENTF_EXTENDEDKEY, UIntPtr.Zero);
Native.keybd_event(VK_SNAPSHOT, 0x45, KEYEVENTF_EXTENDEDKEY KEYEVENTF_KEYUP, UIntPtr.Zero);

IDataObject iObj = Clipboard.GetDataObject();
if (iObj.GetDataPresent(DataFormats.Bitmap, true))
{
    Bitmap bmpScreen = iObj.GetData(DataFormats.Bitmap, true) as Bitmap;
    Bitmap bmpOutput = new Bitmap((int)this.Rect.Width, (int)this.Rect.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
    Graphics g = Graphics.FromImage(bmpOutput);
    Rectangle destRectangle = new Rectangle(0, 0, (int)this.Rect.Width, (int)this.Rect.Height);
    g.DrawImage(bmpScreen,destRectangle,  (int)this.Rect.X, (int)this.Rect.Y, (int)this.Rect.Width, (int)this.Rect.Height, GraphicsUnit.Pixel);

 

    bmpOutput.Save(sPath, System.Drawing.Imaging.ImageFormat.Bmp);
}

 

测试可用,只好先用着了

不过还有多个问题,先写下来,留待以后处理:

1. 针对第三种方案,既然可以按PrintScreen键截图,那对应的API是什么,总觉得发键盘消息没有直接调API稳定

2. 针对WPF截图有没有更好的处理方案