jiangsheng

http://www.csdn.net/develop/author/netauthor/jiangsheng/
随笔 - 142, 评论 - 629, 引用 - 27

导航

关于

 
这下要维护3个BLOG了,faint 其他的地址:

所有的文章版权归原文作者所有,任何人需要转载文章,必须征得原文作者授权。
我的MVP配置

标签

每月存档

最新留言

广告

【第1页/共2页,20条】
首页
前页
1

415 Unsupported Media Type when WSE is NOT configured

I have a web service that runs fine on my Windows XP. However, when I deploy to the production server, the web service returns 415 Unsupported Media Type when calling.

I have seen this error when WSE is not enabled on the client. The problem is, the web service is NOT using WSE. so I did the usual, uninstall ASP.Net, reinstalling, adding asmx extension to IIS, same error.

Now I need to fire a debugger to see what’s going on. Surprisingly, Microsoft.Web.Services3.dll is loaded even when there is no trace of it in my projects. Now I probably know what’s going on. There is another web service in a different virtual directory that uses WSE.

OK, I will isolate my web service to a new application pool. Well, that does not help. In the end I have to add WSE configuration to both my web service and my Windows client.

posted on 2009-02-14 23:30:53 by jiangsheng  评论(1) 阅读(3994)

自定义浏览器控件

同自动化浏览器(http://blog.joycode.com/jiangsheng/archive/2005/10/20/65489.aspx)相比,自动化浏览器控件(WebBrowser Control) 在应用程序中更加常用。从Outlook的预览窗格到Maxthon这样的基于IE引擎的浏览器,从无界面的HTML分析器到Norton Antivirusd的主界面,浏览器控件在众多领域被用作各种各样的用途。这也使得有必要根据具体的用户需求自定义浏览器控件的行为。

在应用程序中加入浏览器控件

集成浏览器控件的最简单的方法是找一个支持ActiveX的集成开发环境,在工具箱中加入Microsoft Web Browser这个控件,往表单上拖一个这个控件就可以完成工作。你甚至可以用集成开发环境添加ActiveX的事件处理函数。如果要直接导入ActiveX的话,建议使用mehrcpp的vbMHWB控件(http://www.codeproject.com/atl/vbmhwb.asp)。这个控件在浏览器控件的基础上进行了扩展,暴露了很多底层接口。

通常导入ActiveX就可以满足大部分需求  ,但是有些类库中也集成了浏览器控件,并且提供了更多的功能,例如MFC的CHTMLView和CDHtmlDialog,ATL的HTML Control,以及.Net 2.0中的Windows.Forms.WebBrowser。如果使用Visual C++来进行非托管编程,那么建议使用MFC或者ATL的封装类,或者使用vbMHWB控件。托管编程中当然首选Windows.Forms.WebBrowser。除非这些类的BUG影响到了应用程序的开发,否则建议使用这些功能更加强大的封装类。

在使用浏览器控件及其封装类的时候要注意一些已知问题

常见任务

在集成浏览器控件之后,可以完成基本的网页浏览,但是对于不同的任务,也需要进一步的处理,例如设置控件的属性、为控件添加事件处理、操作HTML文档等等。

修改浏览器控件的属性

这在集成开发环境中可以很容易地设置,也可以自己实现容器来设置,但是CHTMLView这样的封装类没有这个选项(http://support.microsoft.com/kb/197921)。

  • 链接目标解析。对于用浏览器控件来做浏览器的场合来说,需要将浏览器的RegisterAsBrowser属性设置为true。这使得Internet Explorer在解析HTML链接的target属性指定的目标窗口时可以找到这个窗口。
  • 禁用拖放。对于使用浏览器控件来做预览窗格的场合来说,需要将浏览器的RegisterAsDropTarget属性设置为false。这使得窗口不接受拖进来的文件和链接。
  • 禁用消息框。对于用浏览器控件来做HTML分析器的场合来说,有时需要屏蔽脚本产生的消息框以避免阻塞程序运行。这可以通过设置浏览器的Silent属性来实现,或者实现IDocHostShowUI::ShowMessage。

捕获浏览器控件的事件

集成开发环境中可以也很容易地添加浏览器的事件处理函数。比较常用的事件包括

  • NewWindow2或者NewWindow3事件。默认情况下,浏览器控件中创建的新窗口会是一个Internet Explorer的窗口。这通常不是预期的行为,对于浏览器程序来说更是这样。需要处理浏览器的NewWindow2或者NewWindow3(在Windows XP SP2或者Windows 2003 SP1之后可用)事件来让新的浏览器窗口在应用程序提供的窗口中运行。
  • WindowClosing事件。浏览器控件需要处理WindowClosing事件来在浏览器控件被脚本关闭时关闭浏览器控件的宿主窗口(http://support.microsoft.com/kb/253219)。
  • BeforeNavigate2事件。可以在自己的网页中加入自定义的协议,之后在BeforeNavigate2事件中扫描URL来进行网页和应用程序之间的交互(http://www.microsoft.com/msj/0100/c/c0100.aspx)。当然,自定义的网络协议也可以用Asynchronous Pluggable Protocol来处理(参见http://support.microsoft.com/kb/303740),vbMHWB控件就实现了这个功能。但是更加常用的是在弹出广告过滤器程序中用BeforeNavigate2来判断在NewWindow2事件中创建的窗口是否需要关闭。

操作MSHTML文档

通常HTML分析和浏览器自动化程序都需要分析网页的结构,找到需要操作的元素。这需要对网页的结构进行分析,找到目标元素的标识方法。 一些常用的操作包括:

 在页面包含框架的时候,可能需要跨框架访问HTML文档。可以通过查询框架元素所支持的IWebBrowser2接口或者IHTMLWindow2接口来访问框架中的文档(http://support.microsoft.com/kb/196340),但是也有可能因为安全设置而无法访问(http://support.microsoft.com/kb/167796)。

在浏览器控件中显示其它类型的文档时,可以用IWebBrowser2的document属性来访问ActiveX文档,例如在显示Microsoft Word时,IWebBrowser2的document属性就是Word的文档对象,在显示文件夹的时候,IWebBrowser2的document属性就是文件夹对象等等。

扩展浏览器的宿主

浏览器控件在创建时会查询ActiveX容器的IOleClientSite的实现的如下接口:IDocHostUIHandler, IDocHostUIHandler2 and IDocHostShowUI

虽然在无法自定义ActiveX容器的情况下可以用ICustomDoc::SetUIHandler来挂接IDocHostUIHandler到浏览器控件,但是这样也会造成内存泄漏(http://support.microsoft.com/kb/893629)。一些类库,例如MFC、ATL和.Net类库都实现了IDocHostUIHandler接口。

除了专门用于浏览器用途的程序之外,通常都需要自定义浏览器控件的上下文菜单。这需要实现IDocHostUIHandler::ShowContextMenu。通常的实现包括完全禁用上下文菜单、完全替换上下文菜单、以及修改部分上下文菜单。经常被从上下文菜单中移除的菜单项包含查看源代码、刷新和属性。一种替代的方案是在容器中过滤右键消息(http://support.microsoft.com/kb/231578)。

与浏览器相比,一些Internet Explorer的宿主功能在浏览器控件中并不是默认启用。在某些场合,默认启用的宿主功能可能并非预期。这时需要实现IDocHostUIHandler::GetHostInfo。可以通过实现IDocHostUIHandler::GetHostInfo来自定义的功能包括:

  • 自动完成功能。对于用浏览器控件来做浏览器的场合来说,这个功能是有必要启用的。启用的方法是设置DOCHOSTUIFLAG_ENABLE_FORMS_AUTOCOMPLETE位
  • 如果浏览器中的链接网址包含非ASCII的字符,那么需要实现IDocHostUIHandler::GetHostInfo,并且在返回的DOCHOSTUIINFO结构中设置dwFlags成员的DOCHOSTUIFLAG_URL_ENCODING_ENABLE_UTF8位。这使得网址会在发送之前用UTF-8编码。
  • 3D边框、滚动条,禁用文字选择功能和禁用页面上的脚本。
  • 对于使用浏览器控件来做HTML编辑器的场合来说,有时需要修改默认的页面样式。这都需要实现IDocHostUIHandler::GetHostInfo(http://support.microsoft.com/kb/328803)。注意在有些版本的IE中IDocHostUIHandler::GetHostInfo只在MSHTML被初始化的时候被调用,所以如果你需要在MSHTML被初始化之后使你的修改生效,你需要浏览到一个Word之类的非HTML Active document文档,之后再浏览回来。

在使用浏览器控件来做数据录入界面的场合,需要更改浏览器控件默认的Tab键处理使得用户可以使用Tab键切换到容器中的其他控件。这需要实现IDocHostUIHandler::TranslateAccelerator来自定义浏览器控件的快捷键处理。对于MFC这样用消息钩子来做消息预处理的可自定义容器来说,也可以用PreTranslateMessage来过滤F5键盘消息,而不是实现IDocHostUIHandler::TranslateAccelerator。

在脚本中调用应用程序对浏览器控件的扩展,这需要实现IDocHostUIHandler::GetExternal。使用.Net的WebBrowser控件的话设置ObjectForScripting属性就可以了。

对于用浏览器控件来做HTML分析器的场合来说,有时需要屏蔽脚本产生的消息框。这需要实现IDocHostShowUI::ShowMessage,或者设置浏览器的Silent属性。

另外,浏览器也会查询IOleClientSite来获得其它的服务信息,例如

其他控制

对于用浏览器控件来做HTML分析器的场合来说,有时需要禁用浏览器的脚本、ActiveX或者图片下载。这可以通过在容器中实现IDispatch,处理DISPID_AMBIENT_DLCONTROL来做到(http://msdn.microsoft.com/library/default.asp?url=/workshop/browser/overview/Overview.asp)。

看来离线浏览的控制并不能用这种方法来控制(http://support.microsoft.com/kb/247336)。不过你可以自己编写一个HTTP层传递 BINDF_OFFLINEOPERATION标志 (http://groups-beta.google.com/group/microsoft.public.inetsdk.programming.mshtml_hosting/msg/76bf4910a289d4b3

在浏览器控件中java小程序可能不能正常运行,如果使用Sun JVM1.4之后的版本,可以用SetEnvironmentVariable 来设置JAVA_PLUGIN_WEBCONTROL_ENABLE为1来启用Sun JVM。

默认情况下在页面载入时会有点击声。屏蔽点击声的一个方法是在程序运行时修改注册表键(http://support.microsoft.com/kb/201901),另一个方法是将浏览器控件隐藏,在调用Navigate2之后再显示,但是这也需要锁定控件的更新区域(LockWindowUpdate)以避免闪烁。在IE7中,也可以调用 CoInternetSetFeatureEnabled函数,传递FEATURE_DISABLE_NAVIGATION_SOUNDS来禁用浏览时的声音。

在需要使用代理服务器时,有可能需要在应用程序中使用非默认的代理服务器设置。这可以通过调用UrlMkSetSessionOption来实现。

posted on 2007-01-01 22:22:00 by jiangsheng  评论(2) 阅读(12597)

.Net 2.0中的工作线程和MFC8.0的托管支持

计算亲和数的时候,由于涉及到密集运算,有必要把计算工作转移到背景线程,以避免界面会失去响应。在.Net 1.0中,可以用ManualResetEvent线程Delegate的异步调用来实现,但是在.Net 2.0中,可以使用BackgroundWorker对象来简化这个工作。这个对象自动化了进度报告和终止线程的功能。

要使用这个对象来创建工作线程,首先需要加入一个BackgroundWorker对象到表单(Form)或者用户控件(UserControl),然后调用其RunWorkerAsync方法:

private: System::Void AmicableNumberView_Load(System::Object^ sender, System::EventArgs^ e) { propertyGrid->SelectedObject = Range; listViewPairs->VirtualListSize=0; timer->Start(); m_rAmicableNumberPairs->Clear(); backgroundWorker->RunWorkerAsync (Range); }

在线程创建之后会自动触发DoWork事件。这个事件中的处理类似于1.1中的线程函数体,可以通过访问DoWorkEventArgs参数的argument属性来访问在用RunWorkerAsync启动线程时传递的参数,以及调用ReportProgress定时报告进度。

private: System::Void backgroundWorker_DoWork(System::Object^ sender, System::ComponentModel::DoWorkEventArgs^ e) { CAmicableNumberRange range=(CAmicableNumberRange)e->Argument; int nNumbers=range.Max-range.Min; int nPercentComplete=0; m_rGenerator->Range=range; m_rGenerator->StartWork(); while(!backgroundWorker->CancellationPending && m_rGenerator->DoWork()){ nPercentComplete = (int)( (float)(m_rGenerator->GetNumberInQuestion()-range.Min) /(float)nNumbers * 100); backgroundWorker->ReportProgress(nPercentComplete,m_rGenerator->GetProgress()); } m_rGenerator->EndWork(); if(backgroundWorker->CancellationPending) e->Cancel=true; }

在主线程中可以处理ProgressChanged事件来更新界面。

private: System::Void backgroundWorker_ProgressChanged(System::Object^ sender, System::ComponentModel::ProgressChangedEventArgs^ e) { progressBar->Value=e->ProgressPercentage; if(e->UserState!=nullptr)//a new pair is found { CAmicableNumberPair^ anp=(CAmicableNumberPair^)e->UserState; m_rAmicableNumberPairs->Add(anp); } }

 

在计算完成之后,可以处理RunWorkCompleted事件来更新界面

private: System::Void backgroundWorker_RunWorkerCompleted(System::Object^ sender, System::ComponentModel::RunWorkerCompletedEventArgs^ e) { timer->Stop(); if(e->Error!=nullptr) MessageBox::Show(e->Error->Message); progressBar->Value=100; }

当然,为了避免过于频繁地更新界面,仍然可以使用传统的定时更新方法

private: System::Void timer_Tick(System::Object^ sender, System::EventArgs^ e) { if(IsHandleCreated) { int nOldListSize=listViewPairs->VirtualListSize; int nNewListSize=m_rAmicableNumberPairs->Count; if(nNewListSize>nOldListSize) { listViewPairs->VirtualListSize=nNewListSize; listViewPairs->RedrawItems(nOldListSize, nNewListSize - 1, true); } } }

用户有时会在计算中途取消计算,这时候可以调用CancelAsync方法,然后等待至IsBusy属性成为false为止。在DoWork中检查到CancellationPending属性为true时,应该终止并从DoWork中返回。

void Stop() { backgroundWorker->CancelAsync(); while(backgroundWorker->IsBusy) { Application::DoEvents(); } }

对于Windows Forms,应该在表单的FormClosing事件处理中自动停止计算,但是对于UserControl,由于没有办法判断其容器的类型,所以只能在容器中手动调用一个方法来终止计算。下面是在MFC8.0的容器类CWinFormsView中调用的方法:

void CAmicableNumbersView::OnDestroy() { m_ViewControl->Stop(); CWinFormsView::OnDestroy(); }

在使用MFC的.Net容器类和用户控件来计算亲和数时,需要在.Net控件的Load事件之前给控件传递一个计算范围参数。在MFC中,.Net控件是以ActiveX控件的形式被创建的,但是在传统的窗口初始化函数CWinFormsView::OnInitialUpdate被执行之前,控件的Load事件已经被调用了,这使得参数的传递更加困难。.Net控件是在窗口的Create方法中被创建的:

BOOL CWinFormsView::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext) { m_nFlags |= WF_ISWINFORMSVIEWWND; BOOL ok=__super::Create(lpszClassName, lpszWindowName, dwStyle, rect,pParentWnd, nID, pContext); ASSERT(ok); ok=ok && m_control.CreateManagedControl(m_pManagedViewType,WS_VISIBLE, rect, this,nID); ASSERT(ok); ..... return ok; }

但是在CWinFormsControl::CreateManagedControl返回之前,Load事件就已经被触发了。在Load事件中设置一个断点,在执行时查看调用堆栈发现,这个事件是COleControlSite::CreateControlCommon中的下面语句调用的

// control is visible: just activate it
hr = DoVerb(OLEIVERB_INPLACEACTIVATE);

也就是说,为了在这之前调用自定义代码,需要自定义一个COleControlSite类。在MFC6.0中,COleControlSite是一个未文档化的类,但是在微软知识库文章Q236312Q329802中描述了自定义全局ActiveX控件站点的方法。从MFC7.0开始,COleControlSite被文档化了,而且可以通过重载CWnd::CreateControlSite来使用自定义控件站点类。这个方法在MFC7.0中被用来扩展CHTMLView和CDHTMLDialog,而且在MFC8.0中被用来将默认的COleControlSite替换成CWinFormsControlSite以提供托管控件支持。同样的方法可以用来将CWinFormsControlSite替换成其派生类来自定义控件创建过程。

virtual BOOL CreateControlSite(COleControlContainer* pContainer, COleControlSite** ppSite, UINT nID, REFCLSID clsid) { COleControlSite* pSite=NULL; if (InlineIsEqualGUID(clsid , CLSID_WinFormsControl)) { pSite=new CCAmicablesControlSite(this, pContainer); if(pSite) { *ppSite=pSite;return TRUE; } } return FALSE; }

自定义控件创建过程中需要重载的函数是CWinFormsControlSite::CreateOrLoad。CWinFormsControlSite的成员函数并不多,只有这个函数是在控件的创建之前被调用,并且在控件的Load事件被触发之前结束执行。重载的函数只是简单的调用视图的一个成员函数,而由视图来负责进行具体的处理

class CCAmicablesControlSite:public CWinFormsControlSite { public: CCAmicablesControlSite(CAmicableNumbersView* pView, COleControlContainer* pCtrlCont) : CWinFormsControlSite(pCtrlCont) ,m_pView(pView) {} virtual HRESULT CreateOrLoad(const CControlCreationInfo& creationInfo){ HRESULT hr =CWinFormsControlSite::CreateOrLoad(creationInfo); if (SUCCEEDED(hr)) { m_pView->OnControlCreated(); } return hr; } protected: CAmicableNumbersView* m_pView; }; void CAmicableNumbersView::OnControlCreated() { m_ViewControl = safe_cast<Amicable::AmicableNumberView ^>(this->GetControl()); m_ViewControl->Range = m_range; }

和传统的视图不同,使用CWinFormsView时,不能在重载的PreCreateWindow函数中返回FALSE来终止视图的创建。这是因为CWinFormsView::Create没有对视图创建失败的情况进行正确处理,而是在创建.Net控件失败之后仍试图访问这个对象而造成的。如果确实要终止视图的创建,那么可以重载CWinFormsView::Create,在这之前终止视图的创建。

BOOL CAmicableNumbersView::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext) { // TODO: Add your specialized code here and/or call the base class //prompt for search range FormRange^ fr= gcnew FormRange(); fr->Range=m_range; if (fr->ShowDialog() == DialogResult::OK) { Amicable::CAmicableNumberRange range=fr->Range; m_range=range; } else return FALSE; return CWinFormsView::Create(lpszClassName, lpszWindowName, dwStyle, rect, pParentWnd, nID, pContext); }
尽管可以使用MFC来给应用程序加上托管扩展,但是托管代码和非托管代码互操作造成的性能影响还是很明显。没有绝对必要的话,还是建议像上面调用FormRange表单那样直接调用托管的表单而不是使用MFC的托管支持。

posted on 2006-06-04 19:57:00 by jiangsheng  评论(6) 阅读(7594)

一个算法的优化的相关代码

一个算法的优化的相关代码

// Amicable.h #pragma once #include "WinFormThread.h" using namespace System; using namespace System::Collections::Generic; /// <summary> /// Amicable Number Generator /// Search a range for amicable pairs. /// </summary> namespace Amicable { /// <summary> /// An amicable pair consists of two distinct integers /// for which the sum of proper divisors (the divisors excluding the number itself) /// of one number equals the other. /// for example, (220, 284) is amicable because /// the restricted divisor function of 220 /// 1 + 2 + 4 + 5 + 10 + 11 + 20 + 22 + 44 + 55 + 110 /// equals to 284 /// and the restricted divisor function of 284 /// 1 + 2 + 4 + 71 + 142 /// equals to 220 /// </summary> public ref class CAmicableNumberPair:public ICloneable { public: /// <summary>The smaller number in the pair</summary> property Int32 m ; /// <summary>The larger number in the pair</summary> property Int32 n ; /// <summary> /// If both numbers are happy numbers. /// <remarks> /// A number is happy when repeating the process of /// taking the sum of the squares of the digits of a given number /// eventually yields 1. /// </remarks> /// </summary> property Boolean isHappy ; /// <summary> /// If both numbers are Harshad numbers. /// <remarks> /// A Harshad number, or Niven number, is an integer that // is divisible by the sum of its digits. /// </remarks> /// </summary> property Boolean isHarshad; virtual Object^ Clone(); }; /// <summary> /// /// </summary> public ref class CAmicableNumberPairGeneratorProgressEventArgs : public EventArgs { public: property int nNumberInQuestion; /// <summary>the generated amicable pair, nullptr if the number is not amicable </summary> property CAmicableNumberPair^ Pair; }; ///<summary>The range to look for amicable pairs</summary> public value class CAmicableNumberRange { public: property Int32 Min; property Int32 Max; }; ///<summary>The amicable pair generator, look a range for amicable pairs</summary> public ref class CAmicableNumberPairGenerator:public IWinFormThreadWork { public: CAmicableNumberPairGenerator(){} ////////////////////////////////////////// //Properties /// <summary>The range of amicable pairs </summary> property CAmicableNumberRange Range; /////////////////////////////////////////////// ///Methods public: virtual System::Boolean StartWork() ; virtual System::Boolean DoWork(); virtual void EndWork(); virtual System::EventArgs^ GetProgressEventArgs(); /////////////////////////////////////////////// ///Implementation protected: int m_nRangeMax,m_nRangeMin;//cached range for faster access //loop vars int m_nNumberInQuestion; CAmicableNumberPair^ m_anp; //a cache of the amicable pair //the generate amicable pairs List<CAmicableNumberPair^>^m_rAmicableNumberPairs; //generate all primes below range.Max+1 for number decomposition List<int>^ m_rListPrimes; /// <summary> /// generate the list of primes below a cenain number /// use the Sieve of Eratosthenes algorithm /// </summary> void GeneratePrimes(int nMax) ; /// <summary> /// Given a positive number n and m returns m to the power n /// Formulae: exponentiation by squaring /// pow(m, 0)=1; /// pow(m, 2*k) = pow(square(m), k) /// pow(m, 2*k+1)= m * pow(m,2*k) /// Algorithm /// when n is >=0 /// Repeatedly reduce the power until it becomes zero /// keeping track of the effect in p and m. /// </summary> static Int64 pow(Int64 m, int n); /// <summary> /// The sum of the aliquot divisors of nNumber /// can be calculated by minusing the nNumber form the divisor function /// </summary> Int64 RestrictedDivisorFunction(Int64 nNumber); /// <summary> /// detect If a numbers is a happy number. /// <remarks> /// A number is happy when repeating the process of /// taking the sum of the squares of the digits of a given number /// eventually yields 1. /// </remarks> /// </summary> static bool IsHappy(int nNumber); /// <summary> /// detect If a numbers is a Harshad number. /// <remarks> /// A Harshad number, or Niven number, is an integer that // is divisible by the sum of its digits. /// </remarks> static bool IsHarshad(int nNumber); }; } // This is the source file. #include "stdafx.h" #include <stack> using namespace std; #include "Amicable.h" namespace Amicable { ///<summary>The element of the prime factorization </summary> struct CFactor { int prime;//the prime factor int exponent;//the exponent of the factor }; ///<summary>The Primality of a natural number</summary> public enum class Primality { Unknown,Prime,Composite }; Object^ CAmicableNumberPair::Clone() { CAmicableNumberPair^ p=gcnew CAmicableNumberPair(); p->m=m; p->n=n; p->isHappy=isHappy; p->isHarshad=isHarshad; return p; } System::Boolean CAmicableNumberPairGenerator::StartWork() { m_nRangeMax=Range.Max; m_nNumberInQuestion=m_nRangeMin=Range.Min; m_rAmicableNumberPairs=gcnew List<CAmicableNumberPair^>(); m_rListPrimes=gcnew List<int>();; GeneratePrimes(m_nRangeMax+1); return true; } System::Boolean CAmicableNumberPairGenerator::DoWork() { if(m_nNumberInQuestion>m_nRangeMax) return false;//stop wording int nNumberInQuestion=m_nNumberInQuestion;//cached version of the member var m_nNumberInQuestion+=1; //Primality check. Primes are not amicable for each (int i in m_rListPrimes) { if(i==nNumberInQuestion) return true;//next number if(i>nNumberInQuestion) break; } //amicable pair check. skip if it is in an existing amicable pair for each (CAmicableNumberPair^ anp in m_rAmicableNumberPairs) { if(anp->n==nNumberInQuestion) return true;//next number } Int64 nRestrictedDivisorFunction =RestrictedDivisorFunction(nNumberInQuestion); // if nNumberInQuestion > RestrictedDivisorFunction (nNumberInQuestion) and nNumberInQuestion is amicable // then (nRestrictedDivisorFunction,nNumberInQuestion) can not pass the amicable pair check if(nRestrictedDivisorFunction< nNumberInQuestion) return true;//next number //the first amicable partner is 284, and perfect numbers are not our target if( nRestrictedDivisorFunction < 284 || nRestrictedDivisorFunction ==nNumberInQuestion) return true;//next number //if nNumberInQuestion mod 6==0 and nRestrictedDivisorFunction is even //then they can not be amicable pairs //Lee, E. J. "On Divisibility of the Sums of Even Amicable Pairs." Math. Comput. 23, 545-548, 1969. if(nNumberInQuestion % 6 == 0 && nRestrictedDivisorFunction % 2 == 1) return true;//next number //amicable if((Int64)nNumberInQuestion==RestrictedDivisorFunction(nRestrictedDivisorFunction)) { int nAmicablePartner=(int)nRestrictedDivisorFunction; m_anp = gcnew CAmicableNumberPair(); m_anp->m = nNumberInQuestion; m_anp->n = nAmicablePartner; m_anp->isHappy = IsHappy(nNumberInQuestion) && IsHappy(nAmicablePartner); m_anp->isHarshad = IsHarshad(nNumberInQuestion) && IsHarshad(nAmicablePartner); m_rAmicableNumberPairs->Add(m_anp); } return true;//next number } void CAmicableNumberPairGenerator::EndWork() { if(m_anp!=nullptr) { delete m_anp; m_anp=nullptr; } if(m_rListPrimes!=nullptr) { delete m_rListPrimes; m_rListPrimes=nullptr; } if(m_rAmicableNumberPairs!=nullptr) { delete m_rAmicableNumberPairs; m_rAmicableNumberPairs=nullptr; } } System::EventArgs^ CAmicableNumberPairGenerator::GetProgressEventArgs() { CAmicableNumberPairGeneratorProgressEventArgs^ args=gcnew CAmicableNumberPairGeneratorProgressEventArgs(); if(m_anp) { args->Pair=(CAmicableNumberPair^)m_anp->Clone(); m_anp=nullptr; } args->nNumberInQuestion=m_nNumberInQuestion; return args; } void CAmicableNumberPairGenerator::GeneratePrimes(int nMax) { //should be able to use vector<int>, but since this is not the bottleneck, use manageed array array<Primality>^ arPrimality=gcnew array<Primality>(nMax); for each (Primality p in arPrimality) { p=Primality ::Unknown; } //not really, but it helps arPrimality[0]=Primality::Prime; arPrimality[1]=Primality::Prime; int i,j; //Loop through each unknown value for (i= 2; i<nMax; i++) { if(arPrimality[i]!=Primality::Unknown) continue; //The next FirstUnknown value must be prime arPrimality[i] = Primality::Prime; m_rListPrimes->Add(i); //mark out all multiples of this prime as being Composite for (j = i * 2; j < nMax; j += i) { arPrimality[j] = Primality::Composite; } } } Int64 CAmicableNumberPairGenerator::pow(Int64 m, int n) { //uses //Local Data Int64 p = 1; //working storage for the power. if( n<0 ){ p=0; } else // n>=0 { while(n>0) // here pow(m0,n0) is p * pow(m, n) // where m0 and n0 were the initial values of m,n. { //n>0 if( n % 2 == 0 ) // so that n is 2*k or equivalently // k is n/2. { m*= m; n = n/2; } else // n is odd and 2*k+1 { p = p * m; n--; } } // n==0 // So pow(m0,n0) is p*pow(m,0) is p. } return p; } Int64 CAmicableNumberPairGenerator::RestrictedDivisorFunction(Int64 nNumber) { if(nNumber<4) return 1; //4 is the first composite number //decomposition results stack<CFactor> stackFactors; //temp vars int nPrime; CFactor f; Int64 nNumberToFactor=nNumber; for each (nPrime in m_rListPrimes) { if(nNumberToFactor==1) break;//done //if nPrime> sqrt(nNumberToFactor), then nNumberToFactor is a prime if(nPrime > nNumberToFactor/nPrime) { //nNumberToFactor is a prime f.exponent=1;f.prime=(int)nNumberToFactor; stackFactors.push(f); break;//done } //composite while(nNumberToFactor % nPrime==0) { //increase the exponent if it is already a factor if(stackFactors.size()>0 && nPrime==stackFactors.top().prime) { f = stackFactors.top(); stackFactors.pop(); f.exponent = f.exponent + 1; stackFactors.push(f); } else { //a new factor f.exponent=1;f.prime=nPrime; stackFactors.push(f); } nNumberToFactor /= nPrime; } } //calculate the sum of the aliquot divisors Int64 nSumDivisor=1; Int64 nPrime64;//cache for 64 bit operations //for each factor f, calculate the sum of the divisors of pow( f.nPrime, f.exponent) //and multiply with existing result, nSumDivisor while(stackFactors.size()>0) { f = stackFactors.top(); stackFactors.pop(); nPrime64=f.prime; //the sum of the divisors of pow( nPrime, exponent) //is 1 + nPrime + nPrime ^ 2 + nPrime ^ 3 +...+ nPrime ^ exponent //and can be calculated by the fomula // ( pow ( nPrime, exponent + 1) - 1 ) / ( nPrime - 1 ) nSumDivisor*= ( pow ( nPrime64, f.exponent + 1) - 1 ); nSumDivisor/=(nPrime64- 1); } //calculate the restricted divisor function return nSumDivisor-nNumber; } bool CAmicableNumberPairGenerator::IsHappy(int nNumber) { int sum = 0; //Declaring variable for sum of squre digits int nDigits = 0; int nDigit = 0; do { sum = 0; nDigits = nNumber; while (nDigits > 0) { nDigit = nDigits % 10; sum += nDigit * nDigit; nDigits /= 10; } nNumber = sum; } while (nNumber > 9); return nNumber == 1 || nNumber == 7; } bool CAmicableNumberPairGenerator::IsHarshad(int nNumber) { int sum = 0; //Declaring variable for sum of digits int nDigits = nNumber; while (nDigits > 0) { sum += nDigits % 10; nDigits /= 10; } if (sum > 0) { return nNumber % sum == 0; } return false; } }

posted on 2006-06-02 00:14:00 by jiangsheng  评论(1) 阅读(2175)

一个算法的优化

 点此查看本文代码

 相亲数(Amicable Pair),又称亲和数友爱数,指两个正整数中,彼此的全部约数之和(本身除外)与另一方相等。

例如220与284:

  • 220的全部约数(除掉本身)相加是:1+2+4+5+10+11+20+22+44+55+110=284
  • 284的全部约数(除掉284本身)相加的和是:1+2+4+71+142=220

寻找指定范围内的相亲数的一个方法如下:对于范围内的每个数,用试除法获得约数,之后求和:

int GetAmicableNumber(int nNumber) { int nSumDivisors = SumDivisors(nNumber); if (nSumDivisors == nNumber) return 0;//perfect number if (nNumber == SumDivisors(nSumDivisors)) return nSumDivisors; return 0; } int SumDivisors(int n) { int sum = 0; //Declaring variable for sum of divisors for (int i = 1; i <= n / 2; i++) if (n % i == 0) sum += i; return sum; //Return value of sum of divisors }

但是这个算法效率很低。一个优化的算法是在计算约数之前进行质因数分解,然后用质因数分解的结果来计算约数之和。

    n   ki
M = pi  
    i=1    

这里pi为M的质因子,而ki为质因子的幂,那么M的约数之和可以表示为

    n   2   ki         ki
N = (1+pi+pi   +……+pi )   -
n
i=1
pi  
    i=1                  

而上式中的求和可以用一个简单的除法来替代

ki   i     ki +1  
pi   = (pi     -1) / (pi-1)
i=0              

这样做的好处是

  1. 减少了运算的次数。
  2. 质数表可以在分解不同的数时重用。由于质数表只生成一次,这里没做什么优化,就用了最古老的筛法。
    System::Boolean CAmicableNumberPairGenerator::StartWork() { m_nRangeMax=Range.Max; m_nNumberInQuestion=m_nRangeMin=Range.Min; m_rAmicableNumberPairs=gcnew List<CAmicableNumberPair^>(); m_rListPrimes=gcnew List<int>();; GeneratePrimes(m_nRangeMax+1); return true; } void CAmicableNumberPairGenerator::GeneratePrimes(int nMax) { //should be able to use vector, but since this is not the bottleneck, use manageed array array<Primality>^ arPrimality=gcnew array<Primality>(nMax); for each (Primality p in arPrimality) { p=Primality ::Unknown; } //not really, but it helps arPrimality[0]=Primality::Prime; arPrimality[1]=Primality::Prime; int i,j; //Loop through each unknown value for (i= 2; i<nMax; i++) { if(arPrimality[i]!=Primality::Unknown) continue; //The next FirstUnknown value must be prime arPrimality[i] = Primality::Prime; m_rListPrimes->Add(i); //mark out all multiples of this prime as being Composite for (j = i * 2; j < nMax; j += i) { arPrimality[j] = Primality::Composite; } } }
  3. 质数表可以用来判断一个数是否是质数。质数不是相亲数。在这里我用的是遍历,但是可以用二叉树来进一步优化。
    //Primality check. Primes are not amicable for each (int i in m_rListPrimes) { if(i==nNumberInQuestion) return true;//next number if(i>nNumberInQuestion) break; }
    另外一些优化包括
  4. 仅在计算约数和时使用64位整数
  5. 跳过已经找出的相亲数对中的数。由于相亲数对并不多,这里不必用二叉树。
    //amicable pair check. skip if it is in an existing amicable pair for each (CAmicableNumberPair^ anp in m_rAmicableNumberPairs) { if(anp->n==nNumberInQuestion) return true;//next number } Int64 nRestrictedDivisorFunction =RestrictedDivisorFunction(nNumberInQuestion); // if nNumberInQuestion > RestrictedDivisorFunction (nNumberInQuestion) and nNumberInQuestion is amicable // then (nRestrictedDivisorFunction,nNumberInQuestion) can not pass the amicable pair check if(nRestrictedDivisorFunction< nNumberInQuestion) return true;//next number

跳过约数和小于284的数。284以内只有220一个是相亲数,其约数之和是284。

//the first amicable partner is 284, and perfect numbers are not our target
if( nRestrictedDivisorFunction < 284 || nRestrictedDivisorFunction ==nNumberInQuestion) return true;//next number

如果一个数可以被6整除,且其约数之和是奇数,那么这个数不是相亲数,可以跳过一步求约数和运算

//if nNumberInQuestion mod 6==0 and nRestrictedDivisorFunction is even
//then they can not be amicable pairs
//Lee, E. J. "On Divisibility of the Sums of Even Amicable Pairs." Math. Comput. 23, 545-548, 1969.
if(nNumberInQuestion % 6 == 0 && nRestrictedDivisorFunction % 2 == 1) return true;//next number

质因数分解中,不用遍历整个质数表,在商小于当前质数的平方时就可以终止分解

Int64 CAmicableNumberPairGenerator::RestrictedDivisorFunction(Int64 nNumber)
{
    if(nNumber<4) return 1; //4 is the first composite number

    //decomposition results
    stack<CFactor> stackFactors;

    //temp vars
    int nPrime;
    CFactor f; 
    Int64 nNumberToFactor=nNumber;
    for each (nPrime in m_rListPrimes)
    {
        if(nNumberToFactor==1) break;//done

        //if nPrime> sqrt(nNumberToFactor), then nNumberToFactor is a prime
        if(nPrime > nNumberToFactor/nPrime) 
        {
            //nNumberToFactor is a prime
            f.exponent=1;f.prime=(int)nNumberToFactor;
            stackFactors.push(f);
            break;//done
        }
        //composite
        while(nNumberToFactor % nPrime==0)
        {
            //increase the exponent if it is already a factor
            if(stackFactors.size()>0 && nPrime==stackFactors.top().prime)
            {
                f = stackFactors.top();
                stackFactors.pop();
                f.exponent = f.exponent + 1;
                stackFactors.push(f);
            }
            else
            {
                //a new factor
                f.exponent=1;f.prime=nPrime;
                stackFactors.push(f);
            }
            nNumberToFactor /= nPrime;
        }
    }
    //calculate the sum of the aliquot divisors
    Int64 nSumDivisor=1;
    Int64 nPrime64;//cache for 64 bit operations
    //for each factor f, calculate the sum of the divisors of pow( f.nPrime, f.exponent)
    //and multiply with existing result, nSumDivisor
    while(stackFactors.size()>0)
    {
        f = stackFactors.top();
        stackFactors.pop();
        nPrime64=f.prime;
        //the sum of the divisors of pow( nPrime, exponent)
        //is 1 + nPrime + nPrime ^ 2 + nPrime ^ 3 +...+ nPrime ^ exponent
        //and can be calculated by the fomula 
        // ( pow ( nPrime, exponent + 1) - 1 ) / ( nPrime - 1 )
        nSumDivisor*= ( pow ( nPrime64, f.exponent + 1) - 1 );
        nSumDivisor/=(nPrime64- 1);
    }
    //calculate the restricted divisor function 
    return nSumDivisor-nNumber;
}

计算乘幂时,使用平方乘幂法

Int64 CAmicableNumberPairGenerator::pow(Int64 m, int n)
{
    //uses
    //Local Data
    Int64 p = 1; //working storage for the power.
    if( n<0 ){
        p=0;
    }
    else // n>=0
    {
        while(n>0) // here pow(m0,n0) is p * pow(m, n)
        // where m0 and n0 were the initial values of m,n.
        { //n>0
            if( n % 2 == 0 ) // so that n is 2*k or equivalently
            // k is n/2.
            {
                m*= m;
                n = n/2;
            }
            else // n is odd and 2*k+1
            {
                p = p * m;
                n--;
            }
        } // n==0
        // So pow(m0,n0) is p*pow(m,0) is p.
    } 
    return p;
}

尽可能使用STL和基本数据类型来替换范型和托管数据类型。

尽管编译器可以对代码进行优化,但是真正高效的算法还是要手工编写的。

 

posted on 2006-06-02 00:10:00 by jiangsheng  评论(9) 阅读(9148)

Managed Spy++

尽管4月份还没到,一篇MSDN杂志4月号的文章已经出来了(http://msdn.microsoft.com/msdnmag/issues/06/04/ManagedSpy)。这篇文章描述了如何使用ManagedSpyLib库来访问其他进程内的托管控件及其属性和事件。尽管这个程序的主要目的是监视,但是也可以用ManagedSpyLib库来控制其他进程内的托管控件。

posted on 2006-03-15 18:41:00 by jiangsheng  评论(2) 阅读(7279)

托管C++中函数调用的双重转换(Double Thunking)

 在VC.Net中使用默认设置/clr编译时,一个托管函数会产生两个入口点,一个是托管的,供托管代码调用,另外一个是非托管的,供非托管代码调用。但是函数地址,特别是虚函数指针只能有一个值,所以需要有一个默认的入口。

非托管入口点可能是所有调用的默认入口(在 Visual Studio .NET2003 中,编译器总是会选择非托管入口,但是在Visual Studio 2005中,如果参数或者返回值中包含托管类型,那么编译器会选择托管入口),而另外一个只是使用托管C++中的互操作功能对默认入口的调用。在一个托管函数被另一个托管函数调用的时候,这可能会造成不必要的托管/非托管上下文切换和参数/返回值的复制。如果函数不会被非托管代码使用指针调用,那么可以在声明函数时用VC2005新增的__clrcall修饰符阻止编译器生成两个入口。
现在用简单的冒泡排序算法来比较一下使用__clrcall之后的性能改善程度。

using namespace System; #define ARRAY_SIZE 1000 struct bubbleBase { int value; }; class bubble1:public bubbleBase { public: virtual int getvalue(){return value;} virtual void setvalue(int newvalue){value=newvalue;} }; class bubble2:public bubbleBase { public: virtual int __clrcall getvalue(){return value;} virtual void __clrcall setvalue(int newvalue){value=newvalue;} }; template<class T> void bubbleSort(int length) { TimeSpan ts; T* array1=new T[ARRAY_SIZE]; for (int i=0;i<ARRAY_SIZE ;i++) { array1[i].setvalue(ARRAY_SIZE-i-1); } Int64 ticks=DateTime::Now.Ticks; int i, j,temp, test; for(i = length - 1; i > 0; i--) { test=0; for(j = 0; j < i; j++) { if(array1[j].getvalue() > array1[j+1].getvalue()) { temp = array1[j].getvalue(); array1[j].setvalue(array1[j+1].getvalue()); array1[j+1] .setvalue(temp); test=1; } } if(test==0) break; } ts=TimeSpan::FromTicks(DateTime::Now.Ticks-ticks); Console::WriteLine("BubbleSort {0} Items: {1} Ticks", ARRAY_SIZE, ts.Ticks ); delete array1; } int main(array<System::String ^> ^args) { bubbleSort<bubble1>(ARRAY_SIZE); bubbleSort<bubble2>(ARRAY_SIZE); return 0; }

运行结果是
BubbleSort 1000 Items: 3281250 Ticks
BubbleSort 1000 Items: 312500 Ticks
可以看到,__clrcall会大大加快在托管代码中调用托管函数的速度。

顺便说一下,在随VC8.0发布的STL中增加了很多安全特性,但是这也会造成程序的运行速度减慢。如果你确认程序不会有缓冲区溢出或者内存越界访问的问题,那么可以把_SECURE_SCL定义成0来关掉这个特性。
参考

posted on 2005-10-07 00:12:00 by jiangsheng  评论(1) 阅读(4535)

西雅图MVP峰会见闻

个人觉得这次MVP峰会最大的进步就是技术相关的Session数量大大增加,按照MVP专长来分类;而不像上次那样按主题分类。我只需要在VC产品组的日程里面选择就可以了,而不是像上回那样不得不去听移动开发。当然这回也有MVP不去参加VC的Session,跑去听IE和移动开发。内容方面也比上次活泼很多,Don Box还是那么幽默,比尔·盖茨也有搞笑的演出,不过他看起来比去年七月份在北京的时候老多了。

一些可能有人会感兴趣的技术信息

一些建议

  • 停止开发新的面向Win9x的程序和静态链接MFC的程序。使用新的MFC版本编译旧的程序来增加应用程序的安全性。
  • 在新的程序中使用Unicode编码,同时尽可能将现有程序移植到Unicode。
  • 移植到Visual C++ 2005来使用强大的编译器和调试器。

尽管限于保密协议我不能说得更多,但是微软在11月7号就会正式发布Visual Studio 2005、SQL Server 2005和BizTalk Server 2006了。新的Visual Studio版本(代号Orca和Hawaii)也正在规划中。

posted on 2005-10-03 14:42:00 by jiangsheng  评论(8) 阅读(4684)

C++/CLI中的默认属性访问

目前版本的VC2005测试版中,default关键字不仅用于指定类级别的索引器,而且也用于访问默认属性。但是奇怪的是,默认属性的原名不能访问了,也就是说,如果要把下面的代码段从托管C++移植到VC2005附带的C++/CLI,不仅需要更改指针的类型,而且要把属性的名称更改为default:

//[System::Reflection::DefaultMemberAttribute("Fields")] interface _Recordset

//托管C++语法
//extern _Recordset* results;
Fields* ResultFields=results->Fields;

//C++/CLI语法
//extern _Recordset^ results;
Fields^ ResultFields=results->default;

如果继续使用原来名字来访问属性的话,会报告编译错误:

Fields^ ResultFields=results->Fields;//C3293: 'Fields': use 'default' to access the default property (indexer)

这是一个Breaking Change。在语言规范中,默认索引属性只使用一个名字“default”,而且只有这一个实现。更进一步,默认索引属性只能用如下方式访问:

  • obj[index]
  • obj->default[index]
  • obj->default::get(index)
  • obj->default::set(index, value)

顺便说一下,在C++/CLI中也可以使用类似C#里面的for each语句了( http://msdn2.microsoft.com/library/ms177202(en-us,vs.80).aspx),而且对于非托管的STL容器也有效,不过看起来真不习惯。

参考

posted on 2005-08-30 14:57:00 by jiangsheng  评论(0) 阅读(4788)

MFC,欢乐与痛苦

?MFC提供了许多十分有用的类和对象,在很多时候在Office插件、BHO、常规DLL这样的工程中加入MFC支持是一个不错的选择。但是,MFC中的很多功能,例如资源查找,消息预处理等等都依赖于在进程或者线程创建时被初始化的MFC内部数据;而对于需要添加MFC支持的工程,这些数据并不会被自动地初始化。这时候使用一些MFC的功能,例如使用CString从字符串表加载一个字符串,或者使用CDialog::DoModal()创建一个模态对话框,都会有断言错误,用ATL向导创建的支持MFC的程序也没有多少改善,在CWinApp的DLL版本中没有初始化线程数据,所以调用AfxGetThread会返回空指针。解决这个问题的一个办法是使用AfxBeginThread来启动一个MFC线程,这样MFC会初始化线程相关的数据。在下面的示例中,我在线程初始化时建立了一个模态对话框,以避免直接创建模态对话框会触发的断言失败信息。为了模拟模态对话框的效果,在CDialogThread::WaitForDoModal()这个函数中创建了一个消息循环来等待线程结束,同时用MsgWaitForMultipleObjects来避免死锁。因为MFC中和进程相关的数据并不总是被正确初始化,在调用模态对话框之前也需要手动设置一下。

 

posted on 2005-08-20 13:04:00 by jiangsheng  评论(1) 阅读(5372)

【第1页/共2页,20条】
首页
前页
1

Powered by: Joycode.MVC引擎 0.5.2.0