CLR寄宿——C++调用C# dll
在看到CLR寄宿的内容的时候,研究了一下非托管C++启用CLR的过程。在MSDN sample gallary中看到了一个很好的例子。直接把代码贴到下面(略作了一些修改)。生怕以后找不到了。另外,从C++传送到C#方法的参数只试验过int, double, wchar_t*(字符串)。其他的没有做过实验。
C++代码
//
// FUNCTION: RuntimeHostV4Demo1(PCWSTR, PCWSTR)
//
// Invoking way: RuntimeHostV4Demo1(L"v4.0.30319", L"CSNET2ClassLibrary", L"CSNET2ClassLibrary.CSSimpleObject");
//
// PURPOSE: The function demonstrates using .NET Framework 4.0 Hosting
// Interfaces to host a .NET runtime, and use the ICorRuntimeHost interface
// that was provided in .NET v1.x to load a .NET assembly and invoke its
// type.
//
// If the .NET runtime specified by the pszVersion parameter cannot be
// loaded into the current process, the function prints ".NET runtime <the
// runtime version> cannot be loaded", and return.
//
// If the .NET runtime is successfully loaded, the function loads the
// assembly identified by the pszAssemblyName parameter. Next, the function
// instantiates the class (pszClassName) in the assembly, calls its
// ToString() member method, and print the result. Last, the demo invokes
// the public static function 'int GetStringLength(string str)' of the class
// and print the result too.
//
// PARAMETERS:
// * pszVersion - The desired DOTNETFX version, in the format “vX.X.XXXXX”.
// The parameter must not be NULL. It’s important to note that this
// parameter should match exactly the directory names for each version of
// the framework, under C:\Windows\Microsoft.NET\Framework[64]. The
// current possible values are "v1.0.3705", "v1.1.4322", "v2.0.50727" and
// "v4.0.30319". Also, note that the “v” prefix is mandatory.
// * pszAssemblyName - The display name of the assembly to be loaded, such
// as "CSClassLibrary". The ".DLL" file extension is not appended.
// * pszClassName - The name of the Type that defines the method to invoke.
//
// RETURN VALUE: HRESULT of the demo.
//
HRESULT RuntimeHostV4Demo1(PCWSTR pszVersion, PCWSTR pszAssemblyName,
PCWSTR pszClassName)
{
HRESULT hr;
ICLRMetaHost *pMetaHost = NULL;
ICLRRuntimeInfo *pRuntimeInfo = NULL;
// ICorRuntimeHost and ICLRRuntimeHost are the two CLR hosting interfaces
// supported by CLR 4.0. Here we demo the ICorRuntimeHost interface that
// was provided in .NET v1.x, and is compatible with all .NET Frameworks.
ICorRuntimeHost *pCorRuntimeHost = NULL;
IUnknownPtr spAppDomainThunk = NULL;
_AppDomainPtr spDefaultAppDomain = NULL;
// The .NET assembly to load.
bstr_t bstrAssemblyName(pszAssemblyName);
_AssemblyPtr spAssembly = NULL;
// The .NET class to instantiate.
bstr_t bstrClassName(pszClassName);
_TypePtr spType = NULL;
variant_t vtObject;
variant_t vtEmpty;
// The static method in the .NET class to invoke.
bstr_t bstrStaticMethodName(L"GetStringLength");
SAFEARRAY *psaStaticMethodArgs = NULL;
variant_t vtStringArg1(L"HelloWorld");
variant_t vtStringArg2(18);
variant_t vtStringArg3(123.321);
variant_t vtLengthRet;
// The instance method in the .NET class to invoke.
bstr_t bstrMethodName(L"ToString");
SAFEARRAY *psaMethodArgs = NULL;
variant_t vtStringRet;
//
// Load and start the .NET runtime.
//
wprintf(L"Load and start the .NET runtime %s \n", pszVersion);
hr = CLRCreateInstance(CLSID_CLRMetaHost, IID_PPV_ARGS(&pMetaHost));
if (FAILED(hr))
{
wprintf(L"CLRCreateInstance failed w/hr 0x%08lx\n", hr);
goto Cleanup;
}
// Get the ICLRRuntimeInfo corresponding to a particular CLR version. It
// supersedes CorBindToRuntimeEx with STARTUP_LOADER_SAFEMODE.
hr = pMetaHost->GetRuntime(pszVersion, IID_PPV_ARGS(&pRuntimeInfo));
if (FAILED(hr))
{
wprintf(L"ICLRMetaHost::GetRuntime failed w/hr 0x%08lx\n", hr);
goto Cleanup;
}
// Check if the specified runtime can be loaded into the process. This
// method will take into account other runtimes that may already be
// loaded into the process and set pbLoadable to TRUE if this runtime can
// be loaded in an in-process side-by-side fashion.
BOOL fLoadable;
hr = pRuntimeInfo->IsLoadable(&fLoadable);
if (FAILED(hr))
{
wprintf(L"ICLRRuntimeInfo::IsLoadable failed w/hr 0x%08lx\n", hr);
goto Cleanup;
}
if (!fLoadable)
{
wprintf(L".NET runtime %s cannot be loaded\n", pszVersion);
goto Cleanup;
}
// Load the CLR into the current process and return a runtime interface
// pointer. ICorRuntimeHost and ICLRRuntimeHost are the two CLR hosting
// interfaces supported by CLR 4.0. Here we demo the ICorRuntimeHost
// interface that was provided in .NET v1.x, and is compatible with all
// .NET Frameworks.
hr = pRuntimeInfo->GetInterface(CLSID_CorRuntimeHost,
IID_PPV_ARGS(&pCorRuntimeHost));
if (FAILED(hr))
{
wprintf(L"ICLRRuntimeInfo::GetInterface failed w/hr 0x%08lx\n", hr);
goto Cleanup;
}
// Start the CLR.
hr = pCorRuntimeHost->Start();
if (FAILED(hr))
{
wprintf(L"CLR failed to start w/hr 0x%08lx\n", hr);
goto Cleanup;
}
//
// Load the NET assembly. Call the static method GetStringLength of the
// class CSSimpleObject. Instantiate the class CSSimpleObject and call
// its instance method ToString.
//
// The following C++ code does the same thing as this C# code:
//
// Assembly assembly = AppDomain.CurrentDomain.Load(pszAssemblyName);
// object length = type.InvokeMember("GetStringLength",
// BindingFlags.InvokeMethod | BindingFlags.Static |
// BindingFlags.Public, null, null, new object[] { "HelloWorld", 18, 123.321 });
// object obj = assembly.CreateInstance("CSClassLibrary.CSSimpleObject");
// object str = type.InvokeMember("ToString",
// BindingFlags.InvokeMethod | BindingFlags.Instance |
// BindingFlags.Public, null, obj, new object[] { });
// Get a pointer to the default AppDomain in the CLR.
hr = pCorRuntimeHost->GetDefaultDomain(&spAppDomainThunk);
if (FAILED(hr))
{
wprintf(L"ICorRuntimeHost::GetDefaultDomain failed w/hr 0x%08lx\n", hr);
goto Cleanup;
}
hr = spAppDomainThunk->QueryInterface(IID_PPV_ARGS(&spDefaultAppDomain));
if (FAILED(hr))
{
wprintf(L"Failed to get default AppDomain w/hr 0x%08lx\n", hr);
goto Cleanup;
}
// Load the .NET assembly.
wprintf(L"Load the assembly %s\n", pszAssemblyName);
hr = spDefaultAppDomain->Load_2(bstrAssemblyName, &spAssembly);
if (FAILED(hr))
{
wprintf(L"Failed to load the assembly w/hr 0x%08lx\n", hr);
goto Cleanup;
}
// Get the Type of CSSimpleObject.
hr = spAssembly->GetType_2(bstrClassName, &spType);
if (FAILED(hr))
{
wprintf(L"Failed to get the Type interface w/hr 0x%08lx\n", hr);
goto Cleanup;
}
// Call the static method of the class:
// public static int GetStringLength(string str);
// Create a safe array to contain the arguments of the method. The safe
// array must be created with vt = VT_VARIANT because .NET reflection
// expects an array of Object - VT_VARIANT. There is only one argument,
// so cElements = 1.
psaStaticMethodArgs = SafeArrayCreateVector(VT_VARIANT, 0, 3);
LONG index = 0;
hr = SafeArrayPutElement(psaStaticMethodArgs, &index, &vtStringArg1);
index = 1;
hr = SafeArrayPutElement(psaStaticMethodArgs, &index, &vtStringArg2);
index = 2;
hr = SafeArrayPutElement(psaStaticMethodArgs, &index, &vtStringArg3);
if (FAILED(hr))
{
wprintf(L"SafeArrayPutElement failed w/hr 0x%08lx\n", hr);
goto Cleanup;
}
// Invoke the "GetStringLength" method from the Type interface.
hr = spType->InvokeMember_3(bstrStaticMethodName, static_cast<BindingFlags>(
BindingFlags_InvokeMethod | BindingFlags_Static | BindingFlags_Public),
NULL, vtEmpty, psaStaticMethodArgs, &vtLengthRet);
if (FAILED(hr))
{
wprintf(L"Failed to invoke GetStringLength w/hr 0x%08lx\n", hr);
goto Cleanup;
}
// Print the call result of the static method.
wprintf(L"Call %s.%s(\"%s\") => %d\n",
static_cast<PCWSTR>(bstrClassName),
static_cast<PCWSTR>(bstrStaticMethodName),
static_cast<PCWSTR>(vtStringArg1.bstrVal),
vtLengthRet.lVal);
// Instantiate the class.
hr = spAssembly->CreateInstance(bstrClassName, &vtObject);
if (FAILED(hr))
{
wprintf(L"Assembly::CreateInstance failed w/hr 0x%08lx\n", hr);
goto Cleanup;
}
// Call the instance method of the class.
// public string ToString();
// Create a safe array to contain the arguments of the method.
psaMethodArgs = SafeArrayCreateVector(VT_VARIANT, 0, 0);
// Invoke the "ToString" method from the Type interface.
hr = spType->InvokeMember_3(bstrMethodName, static_cast<BindingFlags>(
BindingFlags_InvokeMethod | BindingFlags_Instance | BindingFlags_Public),
NULL, vtObject, psaMethodArgs, &vtStringRet);
if (FAILED(hr))
{
wprintf(L"Failed to invoke ToString w/hr 0x%08lx\n", hr);
goto Cleanup;
}
// Print the call result of the method.
wprintf(L"Call %s.%s() => %s\n",
static_cast<PCWSTR>(bstrClassName),
static_cast<PCWSTR>(bstrMethodName),
static_cast<PCWSTR>(vtStringRet.bstrVal));
Cleanup:
if (pMetaHost)
{
pMetaHost->Release();
pMetaHost = NULL;
}
if (pRuntimeInfo)
{
pRuntimeInfo->Release();
pRuntimeInfo = NULL;
}
if (pCorRuntimeHost)
{
// Please note that after a call to Stop, the CLR cannot be
// reinitialized into the same process. This step is usually not
// necessary. You can leave the .NET runtime loaded in your process.
//wprintf(L"Stop the .NET runtime\n");
//pCorRuntimeHost->Stop();
pCorRuntimeHost->Release();
pCorRuntimeHost = NULL;
}
if (psaStaticMethodArgs)
{
SafeArrayDestroy(psaStaticMethodArgs);
psaStaticMethodArgs = NULL;
}
if (psaMethodArgs)
{
SafeArrayDestroy(psaMethodArgs);
psaMethodArgs = NULL;
}
return hr; }
// FUNCTION: RuntimeHostV4Demo1(PCWSTR, PCWSTR)
//
// Invoking way: RuntimeHostV4Demo1(L"v4.0.30319", L"CSNET2ClassLibrary", L"CSNET2ClassLibrary.CSSimpleObject");
//
// PURPOSE: The function demonstrates using .NET Framework 4.0 Hosting
// Interfaces to host a .NET runtime, and use the ICorRuntimeHost interface
// that was provided in .NET v1.x to load a .NET assembly and invoke its
// type.
//
// If the .NET runtime specified by the pszVersion parameter cannot be
// loaded into the current process, the function prints ".NET runtime <the
// runtime version> cannot be loaded", and return.
//
// If the .NET runtime is successfully loaded, the function loads the
// assembly identified by the pszAssemblyName parameter. Next, the function
// instantiates the class (pszClassName) in the assembly, calls its
// ToString() member method, and print the result. Last, the demo invokes
// the public static function 'int GetStringLength(string str)' of the class
// and print the result too.
//
// PARAMETERS:
// * pszVersion - The desired DOTNETFX version, in the format “vX.X.XXXXX”.
// The parameter must not be NULL. It’s important to note that this
// parameter should match exactly the directory names for each version of
// the framework, under C:\Windows\Microsoft.NET\Framework[64]. The
// current possible values are "v1.0.3705", "v1.1.4322", "v2.0.50727" and
// "v4.0.30319". Also, note that the “v” prefix is mandatory.
// * pszAssemblyName - The display name of the assembly to be loaded, such
// as "CSClassLibrary". The ".DLL" file extension is not appended.
// * pszClassName - The name of the Type that defines the method to invoke.
//
// RETURN VALUE: HRESULT of the demo.
//
HRESULT RuntimeHostV4Demo1(PCWSTR pszVersion, PCWSTR pszAssemblyName,
PCWSTR pszClassName)
{
HRESULT hr;
ICLRMetaHost *pMetaHost = NULL;
ICLRRuntimeInfo *pRuntimeInfo = NULL;
// ICorRuntimeHost and ICLRRuntimeHost are the two CLR hosting interfaces
// supported by CLR 4.0. Here we demo the ICorRuntimeHost interface that
// was provided in .NET v1.x, and is compatible with all .NET Frameworks.
ICorRuntimeHost *pCorRuntimeHost = NULL;
IUnknownPtr spAppDomainThunk = NULL;
_AppDomainPtr spDefaultAppDomain = NULL;
// The .NET assembly to load.
bstr_t bstrAssemblyName(pszAssemblyName);
_AssemblyPtr spAssembly = NULL;
// The .NET class to instantiate.
bstr_t bstrClassName(pszClassName);
_TypePtr spType = NULL;
variant_t vtObject;
variant_t vtEmpty;
// The static method in the .NET class to invoke.
bstr_t bstrStaticMethodName(L"GetStringLength");
SAFEARRAY *psaStaticMethodArgs = NULL;
variant_t vtStringArg1(L"HelloWorld");
variant_t vtStringArg2(18);
variant_t vtStringArg3(123.321);
variant_t vtLengthRet;
// The instance method in the .NET class to invoke.
bstr_t bstrMethodName(L"ToString");
SAFEARRAY *psaMethodArgs = NULL;
variant_t vtStringRet;
//
// Load and start the .NET runtime.
//
wprintf(L"Load and start the .NET runtime %s \n", pszVersion);
hr = CLRCreateInstance(CLSID_CLRMetaHost, IID_PPV_ARGS(&pMetaHost));
if (FAILED(hr))
{
wprintf(L"CLRCreateInstance failed w/hr 0x%08lx\n", hr);
goto Cleanup;
}
// Get the ICLRRuntimeInfo corresponding to a particular CLR version. It
// supersedes CorBindToRuntimeEx with STARTUP_LOADER_SAFEMODE.
hr = pMetaHost->GetRuntime(pszVersion, IID_PPV_ARGS(&pRuntimeInfo));
if (FAILED(hr))
{
wprintf(L"ICLRMetaHost::GetRuntime failed w/hr 0x%08lx\n", hr);
goto Cleanup;
}
// Check if the specified runtime can be loaded into the process. This
// method will take into account other runtimes that may already be
// loaded into the process and set pbLoadable to TRUE if this runtime can
// be loaded in an in-process side-by-side fashion.
BOOL fLoadable;
hr = pRuntimeInfo->IsLoadable(&fLoadable);
if (FAILED(hr))
{
wprintf(L"ICLRRuntimeInfo::IsLoadable failed w/hr 0x%08lx\n", hr);
goto Cleanup;
}
if (!fLoadable)
{
wprintf(L".NET runtime %s cannot be loaded\n", pszVersion);
goto Cleanup;
}
// Load the CLR into the current process and return a runtime interface
// pointer. ICorRuntimeHost and ICLRRuntimeHost are the two CLR hosting
// interfaces supported by CLR 4.0. Here we demo the ICorRuntimeHost
// interface that was provided in .NET v1.x, and is compatible with all
// .NET Frameworks.
hr = pRuntimeInfo->GetInterface(CLSID_CorRuntimeHost,
IID_PPV_ARGS(&pCorRuntimeHost));
if (FAILED(hr))
{
wprintf(L"ICLRRuntimeInfo::GetInterface failed w/hr 0x%08lx\n", hr);
goto Cleanup;
}
// Start the CLR.
hr = pCorRuntimeHost->Start();
if (FAILED(hr))
{
wprintf(L"CLR failed to start w/hr 0x%08lx\n", hr);
goto Cleanup;
}
//
// Load the NET assembly. Call the static method GetStringLength of the
// class CSSimpleObject. Instantiate the class CSSimpleObject and call
// its instance method ToString.
//
// The following C++ code does the same thing as this C# code:
//
// Assembly assembly = AppDomain.CurrentDomain.Load(pszAssemblyName);
// object length = type.InvokeMember("GetStringLength",
// BindingFlags.InvokeMethod | BindingFlags.Static |
// BindingFlags.Public, null, null, new object[] { "HelloWorld", 18, 123.321 });
// object obj = assembly.CreateInstance("CSClassLibrary.CSSimpleObject");
// object str = type.InvokeMember("ToString",
// BindingFlags.InvokeMethod | BindingFlags.Instance |
// BindingFlags.Public, null, obj, new object[] { });
// Get a pointer to the default AppDomain in the CLR.
hr = pCorRuntimeHost->GetDefaultDomain(&spAppDomainThunk);
if (FAILED(hr))
{
wprintf(L"ICorRuntimeHost::GetDefaultDomain failed w/hr 0x%08lx\n", hr);
goto Cleanup;
}
hr = spAppDomainThunk->QueryInterface(IID_PPV_ARGS(&spDefaultAppDomain));
if (FAILED(hr))
{
wprintf(L"Failed to get default AppDomain w/hr 0x%08lx\n", hr);
goto Cleanup;
}
// Load the .NET assembly.
wprintf(L"Load the assembly %s\n", pszAssemblyName);
hr = spDefaultAppDomain->Load_2(bstrAssemblyName, &spAssembly);
if (FAILED(hr))
{
wprintf(L"Failed to load the assembly w/hr 0x%08lx\n", hr);
goto Cleanup;
}
// Get the Type of CSSimpleObject.
hr = spAssembly->GetType_2(bstrClassName, &spType);
if (FAILED(hr))
{
wprintf(L"Failed to get the Type interface w/hr 0x%08lx\n", hr);
goto Cleanup;
}
// Call the static method of the class:
// public static int GetStringLength(string str);
// Create a safe array to contain the arguments of the method. The safe
// array must be created with vt = VT_VARIANT because .NET reflection
// expects an array of Object - VT_VARIANT. There is only one argument,
// so cElements = 1.
psaStaticMethodArgs = SafeArrayCreateVector(VT_VARIANT, 0, 3);
LONG index = 0;
hr = SafeArrayPutElement(psaStaticMethodArgs, &index, &vtStringArg1);
index = 1;
hr = SafeArrayPutElement(psaStaticMethodArgs, &index, &vtStringArg2);
index = 2;
hr = SafeArrayPutElement(psaStaticMethodArgs, &index, &vtStringArg3);
if (FAILED(hr))
{
wprintf(L"SafeArrayPutElement failed w/hr 0x%08lx\n", hr);
goto Cleanup;
}
// Invoke the "GetStringLength" method from the Type interface.
hr = spType->InvokeMember_3(bstrStaticMethodName, static_cast<BindingFlags>(
BindingFlags_InvokeMethod | BindingFlags_Static | BindingFlags_Public),
NULL, vtEmpty, psaStaticMethodArgs, &vtLengthRet);
if (FAILED(hr))
{
wprintf(L"Failed to invoke GetStringLength w/hr 0x%08lx\n", hr);
goto Cleanup;
}
// Print the call result of the static method.
wprintf(L"Call %s.%s(\"%s\") => %d\n",
static_cast<PCWSTR>(bstrClassName),
static_cast<PCWSTR>(bstrStaticMethodName),
static_cast<PCWSTR>(vtStringArg1.bstrVal),
vtLengthRet.lVal);
// Instantiate the class.
hr = spAssembly->CreateInstance(bstrClassName, &vtObject);
if (FAILED(hr))
{
wprintf(L"Assembly::CreateInstance failed w/hr 0x%08lx\n", hr);
goto Cleanup;
}
// Call the instance method of the class.
// public string ToString();
// Create a safe array to contain the arguments of the method.
psaMethodArgs = SafeArrayCreateVector(VT_VARIANT, 0, 0);
// Invoke the "ToString" method from the Type interface.
hr = spType->InvokeMember_3(bstrMethodName, static_cast<BindingFlags>(
BindingFlags_InvokeMethod | BindingFlags_Instance | BindingFlags_Public),
NULL, vtObject, psaMethodArgs, &vtStringRet);
if (FAILED(hr))
{
wprintf(L"Failed to invoke ToString w/hr 0x%08lx\n", hr);
goto Cleanup;
}
// Print the call result of the method.
wprintf(L"Call %s.%s() => %s\n",
static_cast<PCWSTR>(bstrClassName),
static_cast<PCWSTR>(bstrMethodName),
static_cast<PCWSTR>(vtStringRet.bstrVal));
Cleanup:
if (pMetaHost)
{
pMetaHost->Release();
pMetaHost = NULL;
}
if (pRuntimeInfo)
{
pRuntimeInfo->Release();
pRuntimeInfo = NULL;
}
if (pCorRuntimeHost)
{
// Please note that after a call to Stop, the CLR cannot be
// reinitialized into the same process. This step is usually not
// necessary. You can leave the .NET runtime loaded in your process.
//wprintf(L"Stop the .NET runtime\n");
//pCorRuntimeHost->Stop();
pCorRuntimeHost->Release();
pCorRuntimeHost = NULL;
}
if (psaStaticMethodArgs)
{
SafeArrayDestroy(psaStaticMethodArgs);
psaStaticMethodArgs = NULL;
}
if (psaMethodArgs)
{
SafeArrayDestroy(psaMethodArgs);
psaMethodArgs = NULL;
}
return hr; }
C#代码
namespace CSNET2ClassLibrary
{
public class CSSimpleObject
{
/// <summary>
/// Constructor
/// </summary>
public CSSimpleObject()
{
}
private float fField = 0F;
/// <summary>
/// This is a public Property. It allows you to get and set the value
/// of a float field.
/// </summary>
public float FloatProperty
{
get { return fField; }
set
{
// Fire the event FloatPropertyChanging
bool cancel = false;
if (FloatPropertyChanging != null)
{
FloatPropertyChanging(value, out cancel);
}
// If the change is not canceled, make the change.
if (!cancel)
{
fField = value;
}
}
}
/// <summary>
/// Returns a String that represents the current Object. Here, we
/// return the string form of the float field fField.
/// </summary>
/// <returns>the string form of the float field fField.</returns>
public override string ToString()
{
return this.fField.ToString("F2");
}
/// <summary>
/// This is a public static method. It returns the number of
/// characters in a string.
/// </summary>
/// <param name="str">a string</param>
/// <returns>the number of characters in the string</returns>
public static int GetStringLength(string str, string length)
{
return (str.Length + Int32.Parse(length));
}
public static int GetStringLength(string str, int length, double somevalue)
{
Console.WriteLine(str + " " + length.ToString() + " " + somevalue.ToString() );
return (str.Length + length + (int)somevalue);
}
/// <summary>
/// This is an event. The event is fired when the float property is
/// set.
/// </summary>
public event PropertyChangingEventHandler FloatPropertyChanging;
}
/// <summary>
/// Property value changing event handler
/// </summary>
/// <param name="NewValue">the new value of the property</param>
/// <param name="Cancel">
/// Output whether the change should be cancelled or not.
/// </param>
public delegate void PropertyChangingEventHandler(object NewValue, out bool Cancel); }
{
public class CSSimpleObject
{
/// <summary>
/// Constructor
/// </summary>
public CSSimpleObject()
{
}
private float fField = 0F;
/// <summary>
/// This is a public Property. It allows you to get and set the value
/// of a float field.
/// </summary>
public float FloatProperty
{
get { return fField; }
set
{
// Fire the event FloatPropertyChanging
bool cancel = false;
if (FloatPropertyChanging != null)
{
FloatPropertyChanging(value, out cancel);
}
// If the change is not canceled, make the change.
if (!cancel)
{
fField = value;
}
}
}
/// <summary>
/// Returns a String that represents the current Object. Here, we
/// return the string form of the float field fField.
/// </summary>
/// <returns>the string form of the float field fField.</returns>
public override string ToString()
{
return this.fField.ToString("F2");
}
/// <summary>
/// This is a public static method. It returns the number of
/// characters in a string.
/// </summary>
/// <param name="str">a string</param>
/// <returns>the number of characters in the string</returns>
public static int GetStringLength(string str, string length)
{
return (str.Length + Int32.Parse(length));
}
public static int GetStringLength(string str, int length, double somevalue)
{
Console.WriteLine(str + " " + length.ToString() + " " + somevalue.ToString() );
return (str.Length + length + (int)somevalue);
}
/// <summary>
/// This is an event. The event is fired when the float property is
/// set.
/// </summary>
public event PropertyChangingEventHandler FloatPropertyChanging;
}
/// <summary>
/// Property value changing event handler
/// </summary>
/// <param name="NewValue">the new value of the property</param>
/// <param name="Cancel">
/// Output whether the change should be cancelled or not.
/// </param>
public delegate void PropertyChangingEventHandler(object NewValue, out bool Cancel); }
------------------------------------------------------------------------
email : aicrosoft1104@126.com
吃遍天下。