Monday, December 29, 2014

Calling Win32 DLL from 64-bit WPF through ATL 32-bit Surrogate COM

(1) How to tell if a DLL is 32-bit or 64-bit

 dumpbin /headers MyServer.dll to tell by file Header x86 or x64

(2) Run 32-Bit COM in surrogate Procese so can be called from 64-bit WPF.(one kind of IPC)
       http://www.gfi.com/blog/32bit-object-64bit-environment/ 


 
  • Locate your COM object GUID under the HKey_Classes_Root\Wow6432Node\CLSID\[GUID]
  • Once located add a new REG_SZ (string) Value. Name should be AppID and data should be the same COM object GUID you have just searched for
  • Add a new key under HKey_Classes_Root\Wow6432Node\AppID\ The new key should be called the same as the com object GUID
  • Under the new key you just added, add a new REG_SZ (string) Value, and call it DllSurrogate. Leave the value empty
  • Create a new Key under HKey_Local_Machine\Software\Classes\AppID\ Again the new key should be called the same as the COM object’s GUID. No values are necessary to be added under this key.
  • (3) Step-by-step ALT 32-bit COM Creation (check to make sure project properties are x86) http://msdn.microsoft.com/en-us/library/dssw0ch4%28d=printer,v=vs.90%29.aspx http://msdn.microsoft.com/en-us/library/9yb4317s.aspx Note that C# COM is not usable in (2), especially HKey_Classes_Root\Wow6432Node\CLSID\[GUID] is not there. Also each build of ATL COM project will override this location and need to add back AppID={Guid} (4) A good overall review of 64-bit calling 32-bit using IPC http://blog.mattmags.com/2007/06/30/accessing-32-bit-dlls-from-64-bit-code/ (5) Dynamic Loading Win32 DLL to access its functions: #pragma hdrstop #pragma package(smart_init) // CObject1 HINSTANCE hMWXUSB=0; typedef int (__cdecl *MYPROC)(LPWSTR); typedef unsigned char (ACCEPT_LED)(int data); ACCEPT_LED *lpacceptled; typedef unsigned char (OPEN_USB)(void); OPEN_USB *lpopenusb; STDMETHODIMP CObject1::get_GetANum(SHORT* pVal) { hMWXUSB=LoadLibrary(L"MWXUSB.DLL"); MYPROC Accept_LED; //Accept_LED = (MYPROC) GetProcAddress(hMWXUSB, "Accept_LED"); if( (hMWXUSB = LoadLibrary(L"MWXUSB.DLL")) == NULL ) { *pVal=-5; return S_OK; } if( (lpopenusb=(OPEN_USB*)GetProcAddress(hMWXUSB,"Open_USB" ))==NULL ) { *pVal=-6; return S_OK; } unsigned char ucOpen; ucOpen = (*lpopenusb)(); if( (lpacceptled=(ACCEPT_LED*)GetProcAddress(hMWXUSB,"Accept_LED" ))==NULL ) { *pVal=-1105; return S_OK; } unsigned char ch; ch = (*lpacceptled)(1); if(ch) { *pVal=-2105; return S_OK; } *pVal=105; return S_OK; }

    Saturday, December 27, 2014

    Call 3rd party DLL from ATL COM

    (1) use tool to generate def and .lib from DLL ( assuming no header file)
        http://purefractalsolutions.com/show.php?a=utils/expdef
    
    (2) VC++ project linker-->input additional dependency
    
    (3) declare and use in CPP
    
    extern "C"   unsigned char Open_USB();
    extern "C"   unsigned char Accept_LED(int value);
    extern "C"  void Set_Callback(void* pAny);
    
    void(__stdcall*p)(int);  // calling convention matching far pascal, for most c function
    
    //void __cdecl* p;  // ptrAny
    
    
    static void __stdcall CB1(int value)  {..} instance function would hast this->* so callback must use static to remove this.
    
     p = &CB1;
     ::Open_USB();
     ::Accept_LED(0);
     ::Set_Callback(p);
    
    again cannot use __cdecl must use __stdcall. It turns out Calling Conversion even affect [DllImport] when re-target .net 4.0 from .net 4.0, must specify CDecl in DllImports
    

    Thursday, December 25, 2014

    Manually add Connection Point support after ATL Simple object wizard already run

    After running ATL COM Simple object wizard without check "Connection Points", manual steps are need to add this support.
    
    (1) myserver.h---IConnectionPointContainer/Impl, COM_MAP, IEvents
    
    #include "ATLProject1_i.h"
    #include "_IMyServerEvents_CP.h"
    
    
    class ATL_NO_VTABLE CMyServer :
     public CComObjectRootEx<CComSingleThreadModel>,
     public CComCoClass<CMyServer, &CLSID_MyServer>,
     public IConnectionPointContainerImpl<CMyServer>,
     public CProxy_IMyServerEvents<CMyServer>,
     public IDispatchImpl<IMyServer, &IID_IMyServer, &LIBID_ATLProject1Lib, /*wMajor =*/ 1, /*wMinor =*/ 0>
    {
    
    
    BEGIN_COM_MAP(CMyServer)
     COM_INTERFACE_ENTRY(IMyServer)
     COM_INTERFACE_ENTRY(IDispatch)
     COM_INTERFACE_ENTRY(IConnectionPointContainer)
    END_COM_MAP()
    
    BEGIN_CONNECTION_POINT_MAP(CMyServer)
     CONNECTION_POINT_ENTRY(__uuidof(_IMyServerEvents))
    END_CONNECTION_POINT_MAP()
    
    (2)   .\atlproject1\atlproject1\atlproject1.vcxproj.filters -- need one include
    
        <ClInclude Include="ATLProject1_i.h">
          <Filter>Generated Files</Filter>
        </ClInclude>
        <ClInclude Include="_IMyServerEvents_CP.h">
          <Filter>Header Files</Filter>
        </ClInclude>
        <ClInclude Include="MyServer.h">
          <Filter>Header Files</Filter>
        </ClInclude>
      </ItemGroup>
    
    
    (3).\atlproject1\atlproject1\atlproject1.vcxproj --- include .h for compiler
     
    
      <ItemGroup>
        <ClInclude Include="ATLProject1_i.h" />
        <ClInclude Include="dllmain.h" />
        <ClInclude Include="MyServer.h" />
        <ClInclude Include="Resource.h" />
        <ClInclude Include="stdafx.h" />
        <ClInclude Include="targetver.h" />
        <ClInclude Include="xdlldata.h" />
        <ClInclude Include="_IMyServerEvents_CP.h" />
      </ItemGroup>
    
    
    
    (4)   .\atlproject1\atlproject1\atlproject1.idl  -- disinterface and coclass
    
    library ATLProject1Lib
    {
     importlib("stdole2.tlb");
     [
      uuid(1D071C81-6C67-458F-9B35-992DBAE162D8) // one of these are duplicate 
      uuid(118E250A-B298-4749-90D8-36C764237BA4)  
     ]
     dispinterface _IMyServerEvents
     {
      properties:
      methods:
     };
     [
      uuid(48A5B08F-ED8F-4F1F-8344-5F7C913FB895)  
     ]
     coclass MyServer
     {
      [default] interface IMyServer;
      [default, source] dispinterface _IMyServerEvents;
     };
    };
    
    
    Notes:
    (n1) to user Connection point, Add Method to _IMyServerEvents (Class View Context Menu) e.g. signature CB(int data). 
    Then Add->Connection point onto CMyServer Class (again Class View Ctx Menu) select _IMyS erverEvents in
     Source Interface List. Now there will be a function Fire_CB to fire COM Event into WPF client
    
    (n2) MyServer.rgs need update ForceRemove {dispinterface _IMyServerEvents GUI in IDL} = s 'MyServer Class'
    
    (n3) Content of  [project dir]\_IMyServerEvents_CP.h
    #pragma once
    
    using namespace ATL;
    
    template 
    class CProxy_IMyServerEvents : public IConnectionPointImpl
    {
     // WARNING: This class may be regenerated by the wizard
    public:
    };
    
    
    

    Saturday, November 29, 2014

    IDataErroInfo in MVVM +Presenter

    
    (1) MVVM INPC not compativle with Validation Event so OnPropertyChanged instead
    (2) Consider Grid Cell validation and Ok button CanExecute
    (3) IDXDataErrorInfo is for view,VM validation required custom code
    (4) IDXDataErrorInfo GetError Not implemented=> avoid clustered error on row level
    
    
          #region Validation for VM
    
            private bool _isValid;
    
            public bool IsValid
            {
                get {  return _isValid; }
                set
                {
                    if (_isValid != value)  // check to reduce chking CanExcecute
                    {
                        _isValid = value;
                        NotifyPropertyChanged(() => IsValid);
                    }
                }
            }
    
            public void Validate()
            {
                int invalidChildrenCount= Children.Count(IsChildInvalid);
                bool allDataFieldValid = Validate("Pro1") == null && Validate("Pro2") ;
                IsValid= allDataFieldValid && invalidChildrenCount= == 0;
            }
    
            private bool IsChirldInvalid(ChirldGridVMData Chirld)
            {
                
                switch (SomeMode)
                {
                    case "Mode1":
                        if (Chirld.Validdate("Prop1") != null) return true; 
                        break; 
                    ...
                }
    
                switch (Mode2)
                {
                    ...
                }
    
                return false; 
            }
    
    
    
            private string Validate(string dataFieldName)
            {
    
                switch (dataFieldName)
                {
                    case "VMProp1":
                        if (string.IsNullOrEmpty(Description))
                        {
                            return "Please enter ..."; // Non-empty description
                        }
                        break;
                  ...
                }
    
                return null;
            }
    
            public string EnsureReferenceChirldChecked()
            {
                if (Chirlds.Count(l => l.IsRefChirld.HasValue && l.IsRefChirld.Value) >= 2)
                {
                    return "Only one Chirld can be reference.";
                }
    
                if (Chirlds.Count(l => l.IsRefChirld.HasValue && l.IsRefChirld.Value) == 0)
                {
                    return "At least one Chirld must be reference.";
                }
                return null;
            }
    
            #endregion
    
            #region IDataErrorInfo for View
    
            public string this[string dataFieldName]
            {
                get
                {
                    //CommandManager.InvalidateRequerySuggested(); does not work
                    var result = Validate(dataFieldName);
                    return result;
                }
            }
    
            public string Error
            {
                get
                {
                    // return this[string.Empty or Null] does not work
                    return null;
                }
            }
    
            #endregion
    
        public class LegGridVMData : ViewModelItemBase, IDXDataErrorInfo
        {
            #region Fields
    
            private string _p1;
            public string VMP1
            {
                get { return _p1;}
                set
                {
                    _p1= value;
                    NotifyPropertyChanged(() => WMP1);
                }
            }
    
      
    
            #endregion
    
            #region IDXDataErrorInfo for View
    
    
            public void GetError(ErrorInfo info)
            {
              // chose not to cluster error message to row level
            }
    
            public void GetPropertyError(string propertyName, ErrorInfo info)
            {
                info.ErrorType = ErrorType.Information;
                info.ErrorText= Validdate(propertyName);
            }
    
            #endregion
    
            #region Validation for VM
    
            public string Validdate(string propertyName)
            {
                switch (propertyName)
                {
                    case "VMP1":
                        if (string.IsNullOrEmpty(VMP1))
                        {
                            return "Please select a VMp1";
                        }
                        break;
                    ...
               
    
                
                }
                return null;
            }
    
    
    
            #endregion
    
    
    
            private bool CanExecuteOk()
            {
                return ViewModel.IsValid;
            }
    
            private void TriggerViewModelChirldsValidation()
            {
                ViewModel.Chirlds = ViewModel.Chirlds;  // Hack: assign itself to trigger Validation, w/o makeing NotifyPropertyChanged public.
            }
    
    
           void ListenToPropertyChangedEvent()
            {
                if (ViewModel == null || ViewModel.Chirlds == null) return;
                foreach (var l in ViewModel.Chirlds)
                    l.PropertyChanged += OnPropertyChanged;
    
                ViewModel.PropertyChanged += OnPropertyChanged;
            }
    
    
            void OnPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
            {
                if (e.PropertyName == "IsValid")  // IsValid changes => reevaluate CanExecute.
                {
                    var cmd = ViewModel.OkCommand as DeChirldateCommand;
                    if (cmd != null) cmd.RaiseCanExecuteChanged();
                }
                else  // otherwise, just do re-evaludation of IsValid
                {
                    ViewModel.Validate();
                }
    
                if (e.PropertyName == "IsRef")
                {
                    TriggerViewModelChildrenValidation();  // Trigger one and only one Ref validation on Ref checkbox
                }
    
            }
    

    Friday, October 31, 2014

    Comparing code using WPF Perforate and Visual Profiler

    
    For INPC code vs non standard code, there are some steps we can follow:
            private double? _q;
            public double? Qty
            {
              ... NotifyPropertyChanged(this,"Qty");
            }
    
            private double? _q;
            public double? Qty
            {
                get { return GetQty(0); }
                set { _bidQtys[0] = value; }  // by adding NotifyPropertyChanged(this,"Qty") here speed will pick  up to standard INPC
    
    (1) generate stressfull/human data 1ms, 10ms 250ms and bind to WPF Xaml UI
                d = Observable.Interval(TimeSpan.FromMilliseconds(1)).Delay(TimeSpan.FromSeconds(20)).Subscribe((ms) =>
                {
                    Random r = new Random();
                    for (int i = 0; i < FakeData.Count; i++)
                    {
                        FakeData[i].Qty = -1000 * r.NextDouble();
    
    (2) Perforator:FRPS/DRAR higher=> data updates faster (Key Observation: INPC has 9x FRPS than non-standard that use intermediate storage
    (3) VisualProfiler CPU %
     DispatcherInvoke -- Dispatcher Operation,  Increasing to high=> buggy code, e.g too many timer pushing UI
    
     Rendering Thread --- Unmanaged render pass (Brushed, tessalation, call DirectX), find only the visual element and draws 
    
    the whole window at a 60 FPS as default, popularly called as Composition Thread , Graphics acceleration uses Channel 
    
    Protocol to communicate with the other thread. High and increasing % => need to Profile to feed XPerf, GPUViwer, WPA, 
    
    etc in Windows Performance ToolKit.
    
     Layout ---measure/arrange passes higher => variable/Compute control size, fast changing text, bad GPU/Box.
    
    (4) 1ms -- most stressful  10ms -- Physical limit 16ms per frame =60 FRPS, 250 Human eyeball, Win8 App Fast= 100-200ms
    

    Sunday, October 19, 2014

    Rx ObserveOn SubscribeOn

    In WPF Window update UI code look like the follwoing:
    
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
                Observable.Interval(TimeSpan.FromSeconds(1)).ObserveOn(Dispatcher).Subscribe((i) => this.Title = i.ToString());
            }
        }
    or
    
     Observable.Interval(TimeSpan.FromSeconds(1)).ObserveOn(this).Subscribe((i) => this.Title = i.ToString());
    
    ObserveOn = Notify Observer on a Dispatcher
    SubscribeOn = Subscribe/unsubscribe Observers on a scheduler, where background/task pool will run.
    

    Friday, October 10, 2014

    Useful Tools, scripts and Concept

    
    Power Shell set path
    ====================
    (Get-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name PATH).Path
    
    $oldPath=(Get-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name PATH).Path
    
    $newPath=$oldPath+’;C:\tools\snoop\’
    
    Set-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name PATH –Value $newPath
    
    Power Shell:
    ============
    get-childitem | select-string "double"  ( find string)
    get-ChildItem -Path c:\log |select-string "error"
    
    add user to admin  net localgroup Administrators /Add Domain\UserId
    
    Color Hex converter
    ===================
    http://www.colorschemer.com/online.html
    
    Performance Tools and Concept
    ==============================
    
    Vsync --- GPU sync up buffers and refresh rate so no Tearning (frame override previous one). If GPU does not finsh render before one VSync,
     then could CPU taking too long.
    XPerf/WPA --ETW (CLR GC/ThreadPool/JIT events, Context Switching, CPU, Page Fault, Disk IO, reg access) best OS logging. some managed code
    
    PerfView --- Managed code, Stacks (CPU,Disk IO, GC Head Alloc,Image Load, Managed Load), Stats(GC, JIT,Event)
    
    Some details on PerfView:g 
    =========================
    (1) Memory -> Task Snapshot of Heap -> filter to your process -> Force GC-> dump your GC heap => can compare be
    fore after closing of some part of UI to see if Memory get reclaimed.
    
    (2)CPU Stack high CPU % path drill down can identify hot code.
    
    (3) Run Command " your.exe" will still collect all ETW data so you have to drill down to "your.exe". But this will bracket the time you are interested in.  The other is "Collect"
    
    (4) PerfView is for  managed code. So before any analysis, use VMMap to check workingset break down: Heap vs. Managed Heap size. Also, Task manager private Memory column can tell if Memory stay high and increasing even after some view are closed or running for a long time.
    
    (5) Diff requires open two stack viewer of dump to diff. If Comparison to baseline show positive MB=> memory increase or baseline reclaimed GC Heap after GC dump and your app GC Dump.
    (6) Click on a row in diff stack => trace back to root where you can start to analyze source code for who is holding heap memory.
    (7) Wall Clock Analysis: Collect ->check Thread Time-> get 3 Thread time view stack in collected data.
    (8) CPU_TIME, BLOCK_TIME= waiting for eg disk access come back, PAGE_FAULT= Virtual Ram. Ctx Menu Include Item/Back button can focus/exit on CPU_TIME.
    (9) Zoom in = Select two cell -> set time range 
    (10) Thread Time (With Task)--- charge Child task related time to Parent Task so time will be for the child  real work not in the task. Next use Include item can zoom in to the code in the thread that use Wall Clock time.