What part of Windows is written in .Net/WPF/Silverlight?

user 曹一聪 on zhihu is curious how widely .Net is used within Windows components.

Obviously an operating system component cannot depend on something that isn’t in the operating system, so nothing uses Silverlight in the desktop version Windows. And because XP editions mostly did not ship with any .Net framework version, practically nothing from the XP days can use .Net. Only components newer than XP or rewrote after XP can use .Net, except those Windows Media Center and Tablet PC components that have .Net dependencies.

The first time .Net is available in almost all editions is Windows Vista/2008 (the server core editions make .Net optional). Because a Vista Capable sticker does not guarantee a machine with user-acceptable speed, not much in Windows Vista is using .Net. OR Windows 7 on that matter if you skip Windows Powershell. The usage of .Net is more in the consumer software (e.g. Windows Live Essentials), developer tools (e.g. Visual Studio) and business tools (e.g. SQL Server). In short, .Net is for writing apps, not really for the OS itself.

After Windows 8, Microsoft’s focus moved to Windows RT, now UWP, not much effort is done to improve the desktop, so practically nothing in the desktop got rewritten in .Net Framework.  Windows Help 2.0 is a notable exception however. Because more and more desktop components are moving to UWP (control panel, calculator, games etc), Windows 8 might be the peak of .Net Framework usage in Windows.

If you want a completed list of managed assemblies, you can just scan all dll/exe files on your system and see which file is a managed. And if a file is a managed assembly, search its references to find if WPF is used. AssemblyName.GetAssemblyName can detect a managed assembly file but it throws too many exceptions as most dll/exe files are not managed, so I use Meta Data APIs (IMetaDataDispenser/IMetaDataImport) to detect managed assemblies. The full code can be found at github.com/jiangsheng/Samples/blob/master/FindDotnetInWindows.

The initial list of managed files I found is pretty long so the scanner skip known locations that have known .Net assemblies, like the GAC, the .Net Framework folder, Windows Powershell, Visual Studio, SQL Server and  IIS. The usage of .Net in the rest of system is surprisingly sparse.  Windows itself is still a C/C++ stronghold.

Posted in enmsdn, Microsoft | Tagged , | Leave a comment

Flash Player: Loading from memory

user cucao wants to know how to play a flash file from application resource. The user wants to use a dongle to protect unauthorized viewing of the flash file so can’t just put the swf file in plain sight.

Adobe’s Flash Player, being an ActiveX with supports for many OLE containers such as Internet Explorer and Microsoft Word, implements many standard OLE interfaces (for details open the ActiveX in oleview). One of them is IPersistStreamInit, a standard interface for ActiveX controls who want to distinguish new page load vs refresh, but can be used for the user to load the swf file from memory.

The data source passed into IPersistStreamInit::Load could be a home-brew COM server that implements IStream, but since Windows already has such an implementation (CreateStreamOnHGlobal), it saves a lot of work to just load the resource into global memory (GlobalAlloc/GlobalLock/memcpy/GlobalUnlock), and creates a stream off there for loading.

//MFC and ATL instead of Windows API for readability

void LoadFlash(LPUNKNOWN player, LPCVOID data, UINT size) {
COleStreamFile streamFile; if(!streamFile.CreateMemoryStream() return;
CArchive ar (&streamFile, CArchive::store );
ar.Write(data,size); ar.Close();
streamFile.SeekToBegin();
CComQIPtr<IPersistStreamInit> playerStreamLoader(player);
if(playerStreamLoader==NULL) return;
playerStreamLoader->InitNew();
playerStreamLoader->Load(streamFile.GetStream());
}

This only works for self-contained swf files, however. To set a base url for relative url search in action scripts, a home-brew COM server that implements both IStream and IMoniker is needed.

Posted in ATL, enmsdn, MFC, Microsoft Foundation Class Library | Tagged , , , | Leave a comment

What’s new in Visual Studio Tools for Windows 10 Preview

Looks like Windows 10 SDK gets renamed.

This post focuses on the Win32 wide of changes. Changes in the WinRT Universal App Platform (UAP) side can be found at https://dev.windows.com/en-us/whats-new-windows-10-dev-preview. Some UAP APIs are available to all desktop apps, for exam DirectX 12 and notification center (should not come as surprise, older version of these APIs are already callable from Win32 apps ). Project Centennial also supports enabling UAP APIs to desktop apps, provided they meet certain requirements and deploy using the new appx model.

In

DirectX12 (of course)

The Spartan webbrowser (not sure, some Spartan-specific commands like IDM_SHAREPICTURE exists but no word on how to host a Spartan webbrowser)

new API partition WINAPI_FAMILY_ONECORE_APP

Support for ARM 64

Universal CRT 

Windows Core Audio improvements: Prioritize buffer selection in Windows Core Audio APIs to reduce audio latency (e.g. alerts, game effects, game chats, user speech, movie etc) . Low power consumption audio is there for WinRT but not for Win32😦. Well DJs are gonna be happy about this change.

Unsurprisingly some low power API for video codec too. Guess playing movies do kill battery life.

Losts of changes in the Certificate Enrollment API…. and what’s up with the desktop family guard macros? someone gonna try include the headers in a Windows Store app?

APIs for not domain/AD joined clusters. Wow the documentation is up in MSDN already. The Windows Server team is faster this time. Guess the consumer team fall behind because of the Phone edition.

Wow Microsoft is adding stuff to COM again. Did not expect this to happen. The REGCLS enumeration gets a new member.. after 15 years.

You are joking right? Why this empty ifdef in the tree control styles and extended styles? Looks like something got cut.

#if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
#endif

API for Jet – Extensible Storage Engine version version 10, like the new 8-byte unsigned long long column type.

IShellUIHelper7 adds some more methods to the window.external object… like SelectUAString and getter/setter methods for experimental flag. Can I change between Trident and EdgeHtml this way?

Secured D3D in Media Foundation. Guess WebGL forced that.

Now you can customize the context menu in a PDF document displayed in a hosted webbrowser control.

Some new SQLite-specific error codes in winerr.h. It seems SQLite is part of OS.

New system folders like camera roll and saved pictures. FOLDERID_SkyDrive is getting renamed (of course) but will remain for some time for backward compatible reasons.

TODO: neerajsi-2013/12/08 – this should be moved to official documentation.huh?

Graphics streaming in Remote Desktop APIs.

Out:

D3D 8 (it is still there in Windows 8? Did not notice)

IA64

Moved (desktop APIs that were not available to Windows Store apps to be supported in universal apps)

Threading (CreateThread etc)

Winsock

Posted in enmsdn, Microsoft | Tagged , | Leave a comment

Book review: Mastering Windows 8 C++ App Development

Disclaimer: I am a Microsoft MVP in Visual C++, so is Pavel Yosifovich, the author of the book.

Mastering Windows 8 C++ App Development by Pavel Yosifovich is a new addition to the Windows Store development books. It took me a while to read (304 pages) but is well worth the time from the perspective of a C++ developer.

The book obviously went through great lengths to be comprehensive, not only introduced the typical Windows Runtime (WinRT) APIs in a crisp manner but also added a refreshment of C++ 11, the Model View ViewModel (MVVM) pattern, and the Windows Runtime C++ Template Library (WRL). In addition, being a book for C++ developers, the author’s emphasis on performance can be found in several tips and discussions, which is a nice focus for C++ developers.

There are also several weaknesses in the book. Introducing WRL before C++/CX may be a poor choice as it is difficult to inspire the reader with laborious code. Also the book could use a few more details on DirectX and some WinRT APIs such as GeoPosition.

Nevertheless, the book is an excellent text for the subject and I recommend the book to C++ programmers who want to start developing Windows Store apps and can do research outside of the book.

Posted in enmsdn, Microsoft, Visual C++, Visual Studio | Tagged , , , , , , | Leave a comment

Another breaking change related to 64 bit compatibility, this time in TAPISRV w/Windows 8

The ADO team had to make a breaking change the ADO APIs due to compatibility problem with Microsoft Office. Now it seems to be Tapi’s turn. In the 64 bit editions of Windows 8 and Windows Server 2012, some TAPI providers would stop getting incoming calls due to implementation changes in the TAPI service (TAPISRV) that had to be done for 64 bit compatibility.

The problem is pretty obvious from the documentation, for example LINE_NEWCALL has this:

htLine = (HTAPILINE) hLineDevice;
htCall = (HTAPICALL) 0;
dwMsg = (DWORD) LINE_NEWCALL;
dwParam1 = (DWORD)(HDRVCALL) hdCall;
dwParam2 = (DWORD)(LPHTAPICALL) &htCall;
dwParam3 = (DWORD) 0;

The parameters can’t really be passed as DWORDs – on a 64 bit build, pointers are 64 bit in size, and a DWORD is 32 bit.

Fortunately this can be corrected fairly easily, just ignore what the documentation said and treat the DWORD parameters as DWORD_PTR, however for the end users they would need updates from TAPI providers to get TAPI working on their Windows 8 /Server 2012 machine.

According to TAPI MVP Andreas Marschall, the following TAPI providers are affected (If you got a provider released before 2012,  it is likely also affected)

  • Avaya TAPI
  • Acatel-Lucent TSP
  • NEC TSP (for PBX NEC SL1100)
  • Siemens HiPath TAPI 120/170 (V2 R1.66.0)
Posted in enmsdn, Microsoft | 1 Comment

Howto: Ignoring web browser certificate errors in a webbrowser host

The webbrowser queries host services via IServiceProvider implemented on the ActiveX host. One of the services is IHttpSecurity, which can be used to override the certificate problem dialog.

Security warning: ignoring security problems can compromise your application.

IHttpSecurity is derived from IWindowForBindingUI, so the host needs to implement it too.

In Windows forms, customizing certificate error handling involves the following:

  • derive a class from WebBrowser
  • create a nested class derived from WebBrowser.WebBrowserSite (the only way you can derive from the nested class)
  • overwrite CreateWebBrowserSiteBase and return a new instance of your webbrowser site.
  • implement IServiceProvider on the webbrowser site
  • implement IServiceProvider.QueryService so it returns an IHttpSecurity imepleemntation when the IHttpSecurity service is requested
  • handle IHttpSecurity.OnSecurityProblem and return S_OK (warning: undocumented code, won’t work in IE6)
  • use the new webbrowser in the form

Sample code:

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        private void webBrowser1_DocumentCompleted(object sender,
            WebBrowserDocumentCompletedEventArgs e)
        {
            if (e.Url.ToString() == “about:blank”)
            {
                //create a certificate mismatch
                webBrowser1.Navigate(“https://74.125.225.229&#8221;);
            }
        }
    }
    [Guid(“6D5140C1-7436-11CE-8034-00AA006009FA”)]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    [ComImport]
    public interface UCOMIServiceProvider
    {
        [return: MarshalAs(UnmanagedType.I4)]
        [PreserveSig]
        int QueryService(
            [In] ref Guid guidService,
            [In] ref Guid riid,
            [Out] out IntPtr ppvObject);
    }
    [ComImport()]
    [ComVisible(true)]
    [Guid(“79eac9d5-bafa-11ce-8c82-00aa004ba90b”)]
    [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IWindowForBindingUI
    {
        [return: MarshalAs(UnmanagedType.I4)]
        [PreserveSig]
        int GetWindow(
            [In] ref Guid rguidReason,
            [In, Out] ref IntPtr phwnd);
    }
    [ComImport()]
    [ComVisible(true)]
    [Guid(“79eac9d7-bafa-11ce-8c82-00aa004ba90b”)]
    [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IHttpSecurity
    {
        //derived from IWindowForBindingUI
        [return: MarshalAs(UnmanagedType.I4)]
        [PreserveSig]
        int GetWindow(
            [In] ref Guid rguidReason,
            [In, Out] ref IntPtr phwnd);
        [PreserveSig]
        int OnSecurityProblem(
            [In, MarshalAs(UnmanagedType.U4)] uint dwProblem);
    }
    public class MyWebBrowser : WebBrowser
    {
        public static Guid IID_IHttpSecurity
            = new Guid(“79eac9d7-bafa-11ce-8c82-00aa004ba90b”);
        public static Guid IID_IWindowForBindingUI
            = new Guid(“79eac9d5-bafa-11ce-8c82-00aa004ba90b”);
        public const int S_OK = 0;
        public const int S_FALSE = 1;
        public const int E_NOINTERFACE = unchecked((int)0x80004002);
        public const int RPC_E_RETRY = unchecked((int)0x80010109);
        protected override WebBrowserSiteBase CreateWebBrowserSiteBase()
        {
            return new MyWebBrowserSite(this);
        }
        class MyWebBrowserSite : WebBrowserSite,
            UCOMIServiceProvider,
            IHttpSecurity,
            IWindowForBindingUI
        {
            private MyWebBrowser myWebBrowser;
            public MyWebBrowserSite(MyWebBrowser myWebBrowser)
                :base(myWebBrowser)
            {
                this.myWebBrowser = myWebBrowser;
            }
            public int QueryService(ref Guid guidService
                , ref Guid riid
                , out IntPtr ppvObject)
            {
                if (riid ==IID_IHttpSecurity)
                {
                    ppvObject= Marshal.GetComInterfaceForObject(this
                        , typeof(IHttpSecurity));
                    return S_OK;
                }
                if (riid == IID_IWindowForBindingUI)
                {
                    ppvObject = Marshal.GetComInterfaceForObject(this
                        , typeof(IWindowForBindingUI));
                    return S_OK;
                }
                ppvObject = IntPtr.Zero;
                return E_NOINTERFACE;
            }
            public int GetWindow(ref Guid rguidReason
                , ref IntPtr phwnd)
            {
                if (rguidReason == IID_IHttpSecurity
                    || rguidReason == IID_IWindowForBindingUI)
                {
                    phwnd = myWebBrowser.Handle;
                    return S_OK;
                }
                else
                {
                    phwnd = IntPtr.Zero;
                    return S_FALSE;
                }
            }
            public int OnSecurityProblem(uint dwProblem)
            {
                //ignore errors
                //undocumented return code, does not work on IE6
                return S_OK;
            }
        }
    }
For sample code in providing the service using MFC, check Handle NewWindow3 and ShowModalDialog in CHtmlView. The way to implements IHttpSecurity is similar to how the article exposes the INewWindowManager service to the webbrowser control.
Posted in C#, MFC, Microsoft, Visual Studio, Webbrowser control | Tagged , , , , , | 8 Comments

When you get a System.BadImageFormatException. maybe you indeed have a bad system image.

I am getting a StackOverflowException with two functions repeating on the call stack, one is the constructor of System.BadImageFormatException, another is System.Environment.GetResourceStringLocal.  Since the call that throws the stack overflow is to a web service proxy defined in the same project as the application, there isn’t a 32bit/64bit mismatch here (32bit machine with every project targeting x86), unlike almost all other discussions on the internet about this exception.

After inspecting Fusion and event logs and finding nothing suspicious, I decided to treat it as a real bad image problem and reinstalled .Net. Ура! The exception went away after reinstalling.

Sometimes, the exception name isn’t misleading.

PS a similar question was closed as too vague on stackoverflow.

Posted in Microsoft, Visual Studio | Tagged , , | 1 Comment

How to restart Windows Explorer programmatically using Restart manager

For shell extension programmers, restart Windows Explorer is one of steps in their setup programs. A programmer may also want to force some shell setting changes that would only be read by Explorer on start up. For example, this posts is inspired by a programmer who want to toggle task bar layout automatically depending on the screen resolution, and is used to demonstrate the new Windows Vista restart manager API like RmStartSession, RmRegisterResources, RmGetList, RmShutdown, RmRestart and RmEndSession.

Traditionally restarting explorer is a hackish process. Most of people kill the process by brutal force like taskkill or TerminateProcess, however this approach produces a pretty mess, and if HKLM\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\AutoRestartShell is on, Windows would restart explorer. This is not want the user want – not only because changes in the previous explorer session are only saved during a clean shutdown, but the restarting also needs to be delayed to allow some time to change the task bar layout. A grace way to terminate would be to send WM_QUIT, or use Vista’s new restart manager API.

Restart manager is introduced in Vista to minimize system restarts when installing patches. For example, you can now upgrade IE without a restart. Here it is merely used to restart a particular program, but it has more interesting usage, such as restarting an application after crash.

The first step of restarting explorer would be registering the main explorer process for restarting. RmRegisterResources can be used to restart a process in a session started by RmStartSession, however the machine could have extra explorer processes if the “launch folder in a separate process” setting is on, or if other users are logged in. Here the code loop through all explorer processes and try to find the oldest one for restarting purpose. This is assuming only one user is going to use the computer, an IsProcessInSession check is needed if multiple users would be logged on concurrently.

//returns the process id and create time for the oldest explorer.exe 
RM_UNIQUE_PROCESS GetExplorerApplication()
{
    RM_UNIQUE_PROCESS  result={0};
    DWORD bytesReturned=0;
    DWORD processIdSize=4096;
    std::vector<DWORD> processIds;
    processIds.resize(1024);
    EnumProcesses(processIds.data(),processIdSize,&bytesReturned);
    while(bytesReturned==processIdSize)
    {
        processIdSize+=processIdSize;
        processIds.resize(processIdSize/4);
        EnumProcesses(processIds.data(),processIdSize,&bytesReturned);
    }    std::for_each(processIds.begin(), processIds.end(), [&result] (DWORD processId) {
         HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION|PROCESS_VM_READ,
                                   FALSE, processId);
         if (hProcess) {
            std::wstring imageName;
            imageName.resize(4096);
            if(GetProcessImageFileName (hProcess,(LPWSTR)imageName.data(),4096)>0)
            {
                if(wcscmp(L"explorer.exe",PathFindFileName(imageName.data()))==0)
                {
                    //this is assmuing the user is not running elevated and won't see explorer processes in other sessions
                    FILETIME ftCreate, ftExit, ftKernel, ftUser;
                    if (GetProcessTimes(hProcess, &ftCreate, &ftExit,&ftKernel, &ftUser))
                    {
                        if(result.dwProcessId==0)
                        {
                            result.dwProcessId=processId;
                            result.ProcessStartTime=ftCreate;
                        }
                        else if(CompareFileTime(&result.ProcessStartTime,&ftCreate)>0)
                        {
                            result.dwProcessId=processId;
                            result.ProcessStartTime=ftCreate;
                        }
                    }
                }
            }
            CloseHandle(hProcess);
         }
    });
    return result;
}

Next step would be telling the restart manager to request a shutdown via RmShutdown and restart via RmRestart after some registry tweak:

//taskbar position calculating code omitted
    DWORD dwSession=0;
    WCHAR szSessionKey[CCH_RM_SESSION_KEY+1] = { 0 };
    DWORD dwError = RmStartSession(&dwSession, 0, szSessionKey);
    if (dwError == ERROR_SUCCESS) {
        RM_UNIQUE_PROCESS rgApplications[1]={GetExplorerApplication()};
        dwError=RmRegisterResources(
            dwSession,0,NULL,1,rgApplications,0,NULL);
        DWORD dwReason;
        UINT nProcInfoNeeded;
        UINT nProcInfo = 10;
        RM_PROCESS_INFO rgpi[10];
        dwError = RmGetList(dwSession, &nProcInfoNeeded,
                       &nProcInfo, rgpi, &dwReason);
        if(dwReason==RmRebootReasonNone)//now free to restart explorer
        {
            RmShutdown(dwSession,RmForceShutdown,NULL);//important, if we change the registry before shutting down explorer will override our change
            //using undocumented setting structure, could break any time
            //edge setting is stored at HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\StuckRects2!Settings
            HKEY hKey={0};
            DWORD result=0;
            result=::RegOpenKeyEx(HKEY_CURRENT_USER, _T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\StuckRects2"),
                    0, KEY_READ|KEY_WRITE, &hKey) ;
            if (result== ERROR_SUCCESS)
            {
                std::vector<BYTE> data;
                data.resize(256);
                TCHAR settingValue[]= _T("Settings");
                DWORD dwKeyDataType=0;
                DWORD dwDataBufSize=data.size();
                result=::RegQueryValueEx(hKey,settingValue, NULL, &dwKeyDataType,
                    (LPBYTE) data.data(), &dwDataBufSize);
                while(ERROR_MORE_DATA==result)
                {
                    data.resize(256+data.size());
                    dwDataBufSize=data.size();
                    result=::RegQueryValueEx(hKey,settingValue, NULL, &dwKeyDataType, 
                        (LPBYTE) data.data(), &dwDataBufSize);
                }
                data.resize(dwDataBufSize);
                if(result==ERROR_SUCCESS)
                {
                    switch ( dwKeyDataType )
                    {
                        case REG_BINARY:
                            if(data.size()==40)
                            {
                                BYTE taskbarPosition=data[12];
                                taskbarPosition=edge;
                                data[12]=taskbarPosition;
                                RECT* taskbarRect=(RECT*)&data[24];
                                CopyRect (taskbarRect,&abd.rc);
                                result=::RegSetValueEx(hKey,settingValue,0,REG_BINARY,(LPBYTE) data.data(), dwDataBufSize);
                            }
                            break;
                    }
                }
                ::RegCloseKey( hKey );
            }
            RmRestart (dwSession,0,NULL);
        }
    }
    RmEndSession(dwSession);

Finally the RmEndSession function is called to free up resources. For other lockable resource like files the steps to restart affected processes are similar. This API can also be used to write restart manager custom actions if the installer authoring software does not support the restart manager.

Posted in enmsdn, Microsoft, Visual C++, Visual Studio | Tagged , , , | 6 Comments

How to: Migrating a CLR console Visual C++ project to Windows Forms

Note: Windows Forms programming in new development is discouraged by Microsoft at this point as Microsoft wants to make full use of hardware accelerated drawing instead of using the CPU-intensive GDI. But maintaining old Windows Forms code in Visual C++ 2012 is still supported, GDI isn’t going anywhere anytime soon.

Visual C++ 2012 removed the Windows Forms project template and I see people scramble to find ways to create a Windows Forms project.  There is a walkaround that’s been around for years, that is, to convert a CLR console application to a Windows one, then add forms related code to the project.

The basic steps are listed at http://support.microsoft.com/kb/317433, conveniently named “How to suppress the console window for a managed extensions to Visual C++ Windows Forms application“. Yes, the need to convert a C++ console application to windows forms is that old, back all the way back in the managed extensions for C++  days.

So what I need to do to add Windows Forms support to a C++/CLI console application? Of course the System.Windows.Forms.dll reference is missing in a new console application, so I need to reference it:

  • In code:

#using <System.dll>
#using <System.Drawing.dll>
#using <System.Windows.Forms.dll>

using namespace System::Windows::Forms;

  • Or In IDE:
    • Select the project in Solution Explorer
    • On the Project menu or the context menu of the project node, select Properties
    • In the Property Pages dialog box, expand the Common Properties node, select Framework and References, and then click Add New Reference.
    • Find and add System.Windows.Forms in the list of available references.

In reality the Windows Forms Designer generates a lot of layout code that uses System.Drawing types like Point and Size, so I need to repeat the steps for System.Drawing.

Next the console window needs to be suppressed. The C++/CLI console project template in fact does not specify an entry point, so compiler guesses because a main function exists, the project is a console application. A Windows Forma application’s entry point has the same signature with the main function, therefore the /subsystem setting of the project needs to be changed to Windows:

  • Go back to the project’s Property Pages dialog box.
  • Click the Linker folder on the type.
  • Click the System property page.
  • Change the SubSystem property to Windows.

Ready? Not yet. The compiler is now trying to find a WinMain function, and throws LNK2028 and  LNK2019 when it can’t find the entry point, which of course isn’t there. I am not interested in writing one as I prefer to keep the nice args command line parameter of an array<String^>^ type , so I need to tell the compiler my entry point function is still main:

  • Go back to the the Linker folder.
  • Click the Advanced property page.
  • Change the Entry Point property to main.

We are almost here. Windows Forms needs an STA thread, so I have to add  [STAThreadAttribute] to the main function:

[STAThreadAttribute]
int main(array<System::String ^> ^args)
{

….. // wait did I add anything here? I guess not yet

Here you go,  a Windows Forms application that does nothing interesting.  Mmm, to make it a little more visual, I create a new Windows Form class named Form1 and run it in the main function:

//before main function
#include “Form1.h”
using namespace ProjectName;

//in the main function
Application::EnableVisualStyles();
Application::SetCompatibleTextRenderingDefault(false);

// Create the main window and run it
Application::Run(gcnew Form1());

Yada! I have a Windows Forms application running now.

Posted in C++/CLI, enmsdn, Microsoft, Visual C++, Visual Studio | Tagged , , , | 1 Comment

Add the correct interface first in Adding an MFC Class from an ActiveX Control wizard.

In good old times aka VC6, if you want to use an ActiveX in MFC, the Components and Controls Gallery would generate all the properties and methods of an ActiveX and pull dependent types and create wrapper classes for them as well. And it was good. It magically knows the main class of the Windows Media Player ActiveX is CWMPPlayer4 and that GetCurrentMedia method returns CWMPMedia.

Welcome to the modern Visual C++. Now comes a new wizard that gives you options to choose interfaces to import from. At the same time you got to decide if you really need that many types from an ActiveX.  If you went the unfortunate path of Adding a Member Variable wizard in the dialog editor and somehow got a COCX1 class back, you don’t have the luxury to choose, and you end up with an CWnd wrapper class that does not really wrap up the properties and methods of the ActiveX. It is still good for early binding (like querying IWMPPlayer4 from the result of CWnd::GetControlUnknown) but if you plan to use late binding and MFC’s OLE support (hello COleException!),  you probably want to delete the COCX1 files and start over with the Adding a Class from an ActiveX Control wizard.

Good thing about the new Adding a Class from an ActiveX Control wizard is that most of time we don’t care about the majority of the types in the ActiveX’s type library. We are not going to call most of the ActiveX’s functionality anyways, so selecting what we would use makes sense. Bad thing is that now we don’t know the return type of a method from the generated classes if the return type is LPDISPATCH, lead to guesswork or fire up OLEView to look up the types in the ActiveX’s type library).

Now comes the worst part of the new wizard. The wizard somehow lost its ability to find the correct ActiveX control class and opt to use the first selected interface to generate a CWnd wrapper.  If you naturally click on the >> button to select all interfaces in the Windows Media Player ActiveX at once, you get a CWMPCdromCollection derived from CWnd. That  won’t work as the ActiveX does not implement the IWMPCdromCollection interfaces (undefined behavior can happen if you call the generated methods), and certainly the WMPCdromCollection class isn’t an insertable ActiveX.

Fortunately it is easy to avoid this issue if you add the ActiveX’s main interface first (e.g. IWMPPlayer4 for the Windows Media Player control) before adding the rest of interfaces, or correct the generated code for the first interface. That is, swap the CWnd and COleDispatchDriver code between the wrong class and the correct class for the main interface. It takes some time to find the correct class for the ActiveX control, however.

Side note: For code cleanness, you probably want to move the InvokeHelper code in the headers to cpp files like VC6 did. A good C++ refactoring tool can help that tremendously.

Posted in enmsdn, MFC, Microsoft, Microsoft Foundation Class Library, Visual C++, Visual Studio | Tagged , , , | Leave a comment