default.natstepfilter not working any more with VS 2022

Fabian 0 Reputation points
2025-01-28T20:24:52.9266667+00:00

The C++ debugger steps into all functions, regardless if they are excluded like this

<Function><Name>T*operator-></Name><Action>NoStepInto</Action></Function>

in default.natstepfilter

Visual Studio Debugging
Visual Studio Debugging
Visual Studio: A family of Microsoft suites of integrated development tools for building applications for Windows, the web and mobile devices.Debugging: The act or process of detecting, locating, and correcting logical or syntactical errors in a program or malfunctions in hardware. In hardware contexts, the term troubleshoot is the term more frequently used, especially if the problem is major.
1,022 questions
0 comments No comments
{count} votes

3 answers

Sort by: Most helpful
  1. Dou Xu-MSFT 170 Reputation points Microsoft Vendor
    2025-01-29T06:31:57.0666667+00:00

    Hello fabian,

    Welcome to Microsoft Q&A forum.

    I created a c++ console app to do a quick test but found default.natstepfilter is working well.

    Here is my test result:

    After adding the function printGreeting in default.natstepfilter file, VS will skip the function when stepping into or pressing F11.debug

    For this issue, i have noticed a ReSharper Limiting

    Note that at the moment Visual Studio’s support for .natstepfilter files gets disabled if ReSharper C++ is installed.

    Please check if you have installed ReSharper extension, if so, try to temporarily disable it.

    What's more you can also use natjmc file instead. During C++ debugging, non-user code is skipped by default.

    Debug > Step Into (or F11) on non-user code steps over the code or runs to the next line of user code, if Step Into is called from non-user code.

    For example:

    <?xml version="1.0" encoding="utf-8"?>
    <NonUserCode xmlns="http://schemas.microsoft.com/vstudio/debugger/jmc/2015">
      <!-- Functions -->
      <Function Name="your function name" />
    </NonUserCode>
    
    

    Best Regards,

    Dou


    If the answer is helpful, please click "Accept Answer" and upvote it.

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.


  2. Fabian 0 Reputation points
    2025-01-30T17:00:11.4233333+00:00

    Hello Dou,

    I tried to upload an attachement with a project to reproduce the issue, but zip is not allowed, and the maximum file size is 1.

    So I tried to upload a .cpp file. Also not allowed. Strange that even text files like .cpp are not allowed.

    Anyway, here is the content of the .cpp:

    //#include <vsg/all.h>
    //
    //#include <vsgXchange/all.h>
    //
    //#include <algorithm>
    //#include <chrono>
    #include <iostream>
    #include <thread>
    namespace vsg
    {
        /// smart pointer that works with objects that have intrusive reference counting, such as all vsg::Object
        /// broadly similar in role to std::shared_ptr<> but half size and faster thanks to lower memory footprint and better cache coherency
        template<class T>
        class ref_ptr
        {
        public:
            using element_type = T;
            ref_ptr() noexcept :
                _ptr(nullptr) {}
            ref_ptr(const ref_ptr& rhs) noexcept :
                _ptr(rhs._ptr)
            {
                if (_ptr) _ptr->ref();
            }
            /// move constructor
            template<class R>
            ref_ptr(ref_ptr<R>&& rhs) noexcept :
                _ptr(rhs._ptr)
            {
                rhs._ptr = nullptr;
            }
            template<class R>
            ref_ptr(const ref_ptr<R>& ptr) noexcept :
                _ptr(ptr._ptr)
            {
                if (_ptr) _ptr->ref();
            }
            explicit ref_ptr(T* ptr) noexcept :
                _ptr(ptr)
            {
                if (_ptr) _ptr->ref();
            }
            template<class R>
            explicit ref_ptr(R* ptr) noexcept :
                _ptr(ptr)
            {
                if (_ptr) _ptr->ref();
            }
            // std::nullptr_t requires extra header
            ref_ptr(decltype(nullptr)) noexcept :
                ref_ptr() {}
            ~ref_ptr()
            {
                if (_ptr) _ptr->unref();
            }
            void reset()
            {
                if (_ptr) _ptr->unref();
                _ptr = nullptr;
            }
            ref_ptr& operator=(T* ptr)
            {
                if (ptr == _ptr) return *this;
                T* temp_ptr = _ptr;
                _ptr = ptr;
                if (_ptr) _ptr->ref();
                // unref the original pointer after ref in case the old pointer object is a parent of the new pointer's object
                if (temp_ptr) temp_ptr->unref();
                return *this;
            }
            ref_ptr& operator=(const ref_ptr& rhs)
            {
                if (rhs._ptr == _ptr) return *this;
                T* temp_ptr = _ptr;
                _ptr = rhs._ptr;
                if (_ptr) _ptr->ref();
                // unref the original pointer after ref in case the old pointer object is a parent of the new pointer's object
                if (temp_ptr) temp_ptr->unref();
                return *this;
            }
            template<class R>
            ref_ptr& operator=(const ref_ptr<R>& rhs)
            {
                if (rhs._ptr == _ptr) return *this;
                T* temp_ptr = _ptr;
                _ptr = rhs._ptr;
                if (_ptr) _ptr->ref();
                // unref the original pointer after ref in case the old pointer object is a parent of the new pointer's object
                if (temp_ptr) temp_ptr->unref();
                return *this;
            }
            /// move assignment
            template<class R>
            ref_ptr& operator=(ref_ptr<R>&& rhs)
            {
                if (rhs._ptr == _ptr) return *this;
                if (_ptr) _ptr->unref();
                _ptr = rhs._ptr;
                rhs._ptr = nullptr;
                return *this;
            }
            template<class R>
            bool operator<(const ref_ptr<R>& rhs) const { return (_ptr < rhs._ptr); }
            template<class R>
            bool operator==(const ref_ptr<R>& rhs) const { return (rhs._ptr == _ptr); }
            template<class R>
            bool operator!=(const ref_ptr<R>& rhs) const { return (rhs._ptr != _ptr); }
            template<class R>
            bool operator<(const R* rhs) const { return (_ptr < rhs); }
            template<class R>
            bool operator==(const R* rhs) const { return (rhs == _ptr); }
            template<class R>
            bool operator!=(const R* rhs) const { return (rhs != _ptr); }
            bool valid() const noexcept { return _ptr != nullptr; }
            explicit operator bool() const noexcept { return valid(); }
            // potentially dangerous automatic type conversion, could cause dangling pointer if ref_ptr<> assigned to C pointer and ref_ptr<> destruction causes an object delete.
            operator T*() const noexcept { return _ptr; }
            void operator[](int) const = delete;
            T& operator*() const noexcept { return *_ptr; }
            T* operator->() const noexcept { return _ptr; }
            T* get() const noexcept { return _ptr; }
            T* release_nodelete() noexcept
            {
                T* temp_ptr = _ptr;
                if (_ptr) _ptr->unref_nodelete();
                _ptr = nullptr;
                return temp_ptr;
            }
            void swap(ref_ptr& rhs) noexcept
            {
                T* temp_ptr = _ptr;
                _ptr = rhs._ptr;
                rhs._ptr = temp_ptr;
            }
            template<class R>
            ref_ptr<R> cast() const { return ref_ptr<R>(_ptr ? _ptr->template cast<R>() : nullptr); }
        protected:
            template<class R>
            friend class ref_ptr;
            T* _ptr;
        };
    	class  Object
        {
        public:
            Object() {}
            Object(const Object& object /*, const CopyOp& copyop = {}*/) {}
            Object& operator=(const Object&);
            static ref_ptr<Object> create() { return ref_ptr<Object>(new Object); }
            static ref_ptr<Object> create_if(bool flag)
            {
                if (flag)
                    return ref_ptr<Object>(new Object);
                else
                    return {};
            }
            /// provide new and delete to enable custom memory management via the vsg::Allocator singleton, using the MEMORY_AFFINTY_OBJECTS
            //static void* operator new(std::size_t count);
            //static void operator delete(void* ptr);
            virtual std::size_t sizeofObject() const noexcept { return sizeof(Object); }
            //virtual const char* className() const noexcept { return type_name<Object>(); }
            /// return the std::type_info of this Object
            virtual const std::type_info& type_info() const noexcept { return typeid(Object); }
            virtual bool is_compatible(const std::type_info& type) const noexcept { return typeid(Object) == type; }
            template<class T>
            T* cast() { return is_compatible(typeid(T)) ? static_cast<T*>(this) : nullptr; }
            template<class T>
            const T* cast() const { return is_compatible(typeid(T)) ? static_cast<const T*>(this) : nullptr; }
            /// clone this object using CopyOp's duplicates map to decide whether to clone or to return the original object.
            /// The default clone(CopyOp&) implementation simply returns ref_ptr<> to this object rather attempt to clone.
            //virtual ref_ptr<Object> clone(const CopyOp& copyop = {}) const;
            /// compare two objects, return -1 if this object is less than rhs, return 0 if it's equal, return 1 if rhs is greater,
            virtual int compare(const Object& rhs) const { return 0; }
            //virtual void accept(Visitor& visitor);
            //virtual void traverse(Visitor&) {}
            //virtual void accept(ConstVisitor& visitor) const;
            //virtual void traverse(ConstVisitor&) const {}
            //virtual void accept(RecordTraversal& visitor) const;
            //virtual void traverse(RecordTraversal&) const {}
            //virtual void read(Input& input);
            //virtual void write(Output& output) const;
            // ref counting methods
            inline void ref() const noexcept { _referenceCount.fetch_add(1, std::memory_order_relaxed); }
            inline void unref() const noexcept
            {
                if (_referenceCount.fetch_sub(1, std::memory_order_seq_cst) <= 1) _attemptDelete();
            }
            inline void unref_nodelete() const noexcept { _referenceCount.fetch_sub(1, std::memory_order_seq_cst); }
            inline unsigned int referenceCount() const noexcept { return _referenceCount.load(); }
            /// meta data access methods
            /// wraps the value with a vsg::Value<T> object and then assigns via setObject(key, vsg::Value<T>)
            template<typename T>
            void setValue(const std::string& key, const T& value);
            /// specialization of setValue to handle passing C strings
            void setValue(const std::string& key, const char* value) { setValue(key, value ? std::string(value) : std::string()); }
            /// get specified value type, return false if value associated with key is not assigned or is not the correct type
            template<typename T>
            bool getValue(const std::string& key, T& value) const;
            /// assign an Object associated with key
            void setObject(const std::string& key, ref_ptr<Object> object) {}
            /// get Object pointer associated with key, return nullptr if no object associated with key has been assigned
            Object* getObject(const std::string& key);
            /// get const Object pointer associated with key, return nullptr if no object associated with key has been assigned
            const Object* getObject(const std::string& key) const;
            /// get object pointer of specified type associated with key, return nullptr if no object associated with key has been assigned
            template<class T>
            T* getObject(const std::string& key) { return dynamic_cast<T*>(getObject(key)); }
            /// get const object pointer of specified type associated with key, return nullptr if no object associated with key has been assigned
            template<class T>
            const T* getObject(const std::string& key) const { return dynamic_cast<const T*>(getObject(key)); }
            /// get ref_ptr<Object> associated with key, return nullptr if no object associated with key has been assigned
            ref_ptr<Object> getRefObject(const std::string& key);
            /// get ref_ptr<const Object> pointer associated with key, return nullptr if no object associated with key has been assigned
            ref_ptr<const Object> getRefObject(const std::string& key) const;
            /// get ref_ptr<T> of specified type associated with key, return nullptr if no object associated with key has been assigned
            template<class T>
            ref_ptr<T> getRefObject(const std::string& key) { return getRefObject(key).cast<T>(); }
            /// get ref_ptr<const T> of specified type associated with key, return nullptr if no object associated with key has been assigned
            template<class T>
            const ref_ptr<const T> getRefObject(const std::string& key) const { return getRefObject(key).cast<const T>(); }
            /// remove meta object or value associated with key
            void removeObject(const std::string& key) {}
            // Auxiliary object access methods, the optional Auxiliary is used to store meta data
            //Auxiliary* getOrCreateAuxiliary();
            //Auxiliary* getAuxiliary() { return _auxiliary; }
            //const Auxiliary* getAuxiliary() const { return _auxiliary; }
        protected:
            virtual ~Object() {}
            virtual void _attemptDelete() const {}
           // void setAuxiliary(Auxiliary* auxiliary);
        private:
            friend class Auxiliary;
            mutable std::atomic_uint _referenceCount;
            //Auxiliary* _auxiliary;
        };
    	template<class ParentClass, class Subclass>
        class Inherit : public ParentClass
        {
        public:
            template<typename... Args>
            Inherit(Args&&... args) :
                ParentClass(std::forward<Args>(args)...) {}
            template<typename... Args>
            static ref_ptr<Subclass> create(Args&&... args)
            {
                return ref_ptr<Subclass>(new Subclass(std::forward<Args>(args)...));
            }
            template<typename... Args>
            static ref_ptr<Subclass> create_if(bool flag, Args&&... args)
            {
                if (flag) return ref_ptr<Subclass>(new Subclass(std::forward<Args>(args)...));
                return {};
            }
            std::size_t sizeofObject() const noexcept override { return sizeof(Subclass); }
            //const char* className() const noexcept override { return type_name<Subclass>(); }
            const std::type_info& type_info() const noexcept override { return typeid(Subclass); }
            bool is_compatible(const std::type_info& type) const noexcept override { return typeid(Subclass) == type || ParentClass::is_compatible(type); }
            int compare(const Object& rhs) const override
            {
                int result = ParentClass::compare(rhs);
                if (result != 0) return result;
                size_t startOfSubclass = sizeof(ParentClass);
                size_t size = sizeof(Subclass) - startOfSubclass;
                // Subclass adds no extra data to compare
                if (size == 0) return 0;
                const char* lhs_ptr = reinterpret_cast<const char*>(this);
                const char* rhs_ptr = reinterpret_cast<const char*>(&rhs);
                // compare the data that Subclass adds over ParentClass
                return std::memcmp(lhs_ptr + startOfSubclass, rhs_ptr + startOfSubclass, size);
            }
            //void accept(Visitor& visitor) override { visitor.apply(static_cast<Subclass&>(*this)); }
            //void accept(ConstVisitor& visitor) const override { visitor.apply(static_cast<const Subclass&>(*this)); }
            //void accept(RecordTraversal& visitor) const override { visitor.apply(static_cast<const Subclass&>(*this)); }
        };
        class  Instrumentation : public Inherit<Object, Instrumentation>
        {
        public:
            Instrumentation() {}
            virtual ref_ptr<Instrumentation> shareOrDuplicateForThreadSafety() { return ref_ptr<Instrumentation>(this); }
            virtual void setThreadName(const std::string& /*name*/) const {
                std::cout << "setThreadName";
    		};
    		void testFuncRef(ref_ptr<Instrumentation>& reference) {
    			reference->setThreadName("test");
    		}
    		void testFunc(ref_ptr<Instrumentation> reference)
                    {
                        reference->setThreadName("test");
                    }
            //virtual void enterFrame(const SourceLocation* /*sl*/, uint64_t& /*reference*/, FrameStamp& /*frameStamp*/) const {};
            //virtual void leaveFrame(const SourceLocation* /*sl*/, uint64_t& /*reference*/, FrameStamp& /*frameStamp*/) const {};
            //virtual void enter(const SourceLocation* /*sl*/, uint64_t& /*reference*/, const Object* /*object*/ = nullptr) const {};
            //virtual void leave(const SourceLocation* /*sl*/, uint64_t& /*reference*/, const Object* /*object*/ = nullptr) const {};
            //virtual void enterCommandBuffer(const SourceLocation* /*sl*/, uint64_t& /*reference*/, CommandBuffer& /*commandBuffer*/) const {};
            //virtual void leaveCommandBuffer(const SourceLocation* /*sl*/, uint64_t& /*reference*/, CommandBuffer& /*commandBuffer*/) const {};
            //virtual void enter(const SourceLocation* /*sl*/, uint64_t& /*reference*/, CommandBuffer& /*commandBuffer*/, const Object* /*object*/ = nullptr) const {};
            //virtual void leave(const SourceLocation* /*sl*/, uint64_t& /*reference*/, CommandBuffer& /*commandBuffer*/, const Object* /*object*/ = nullptr) const {};
            virtual void finish() const {};
        protected:
            virtual ~Instrumentation() {}
        };
    } // namespace vsg
    int main(int argc, char** argv)
    {
        vsg::ref_ptr<vsg::Instrumentation> resourceHints(new vsg::Instrumentation());
    	resourceHints->setThreadName("main");   // debugger steps into operator ->
        resourceHints->finish();              // debugger steps into operator ->
    	vsg::ref_ptr<vsg::Instrumentation> resourceHints2(new vsg::Instrumentation());
        resourceHints2->testFuncRef(resourceHints);   // debugger steps into operator ->
        resourceHints2->testFunc(resourceHints);   // debugger steps into into operator ->    AND   ref_ptr
    	
        return 0;
    	/*   debugger steps into these operators and functions, despite this in default.natstepfilter:
    	
    	<Function><Name>Platform::EventSource::Invoke.*</Name><Action>NoStepInto</Action></Function>
    <Function><Name>IID_PPV_ARGS_Helper&lt;.*</Name><Action>NoStepInto</Action></Function>
    <Function><Name>Microsoft::WRL::ComPtr&lt;.*&gt;::operator&amp;</Name><Action>NoStepInto</Action></Function>
    <Function><Name>Microsoft::WRL::ComPtr&lt;.*&gt;::operator-&gt;</Name><Action>NoStepInto</Action></Function>
    <Function><Name>Microsoft::WRL::Details::ComPtrRef.*</Name><Action>NoStepInto</Action></Function>
    <Function><Name>operator new</Name><Action>NoStepInto</Action></Function>
    <Function><Name>std::forward&lt;.*</Name><Action>NoStepInto</Action></Function>
    <Function><Name>std::move&lt;.*</Name><Action>NoStepInto</Action></Function>
    <Function><Name>operator new</Name><Action>NoStepInto</Action></Function>
    <Function><Name>operator-&gt;</Name><Action>NoStepInto</Action></Function>
    <Function><Name>T* operator-&gt;</Name><Action>NoStepInto</Action></Function>
    <Function><Name>T*operator-&gt;</Name><Action>NoStepInto</Action></Function>
    <Function><Name>*operator-&gt;()</Name><Action>NoStepInto</Action></Function>
    <Function><Name>*operator-&gt;() const</Name><Action>NoStepInto</Action></Function>
    <Function><Name>std::shared_ptr.+::operator-&gt;</Name><Action>NoStepInto</Action></Function>
    <Function><Name>*shared_ptr*</Name><Action>NoStepInto</Action></Function>
    <Function><Name>*shared_ptr.*</Name><Action>NoStepInto</Action></Function>
    <Function><Name>std::.*</Name><Action>NoStepInto</Action></Function>
    <Function><Name>osg::ref_ptr.+::operator-&gt;</Name><Action>NoStepInto</Action></Function>
    <Function><Name>osg::ref_ptr.*</Name><Action>NoStepInto</Action></Function>
    <Function><Name>osg::ref_ptr*</Name><Action>NoStepInto</Action></Function>
    <Function><Name>boost::.*</Name><Action>NoStepInto</Action></Function>
    <Function><Name>SoPtr*</Name><Action>NoStepInto</Action></Function>
    <Function><Name>SoPtr.*</Name><Action>NoStepInto</Action></Function>
    <Function><Name>*c_str()</Name><Action>NoStepInto</Action></Function>
    <Function><Name>*get()</Name><Action>NoStepInto</Action></Function>
    <Function><Name>*::vec</Name><Action>NoStepInto</Action></Function>
    <Function><Name>handle</Name><Action>NoStepInto</Action></Function>
    <Function><Name>operator-&gt; ()</Name><Action>NoStepInto</Action></Function>
    <Function><Name>operator-&gt;</Name><Action>NoStepInto</Action></Function>
    <Function><Name>operator+</Name><Action>NoStepInto</Action></Function>
    <Function><Name>T*operator-&gt;()</Name><Action>NoStepInto</Action></Function>
    <Function><Name>explicit operator bool () const</Name><Action>NoStepInto</Action></Function>
    <Function><Name>ref_ptr()</Name><Action>NoStepInto</Action></Function>
    <Function><Name>ref_ptr</Name><Action>NoStepInto</Action></Function>
    <Function><Name>*ref_ptr<*></Name><Action>NoStepInto</Action></Function>
    <Function><Name>vsg::ref_ptr.*</Name><Action>NoStepInto</Action></Function>
    <Function><Name>vsg::ref_ptr*</Name><Action>NoStepInto</Action></Function>
    <Function><Name>t_vec3</Name><Action>NoStepInto</Action></Function>
    <Function><Name>vsg::t_vec3</Name><Action>NoStepInto</Action></Function>
    <Function><Name>vsg::t_vec3*</Name><Action>NoStepInto</Action></Function>
    <Function><Name>vsg::t_vec3.*</Name><Action>NoStepInto</Action></Function>
    <Function><Name>vsg::t_vec3::*</Name><Action>NoStepInto</Action></Function>
    <Function><Name>vsg::t_vec3::t_vec3</Name><Action>NoStepInto</Action></Function>
    <Function><Name>vsg::t_vec3.t_vec3</Name><Action>NoStepInto</Action></Function>
    <Function><Name>vsg::t_vec3<*>::*</Name><Action>NoStepInto</Action></Function>
    <Function><Name>create</Name><Action>NoStepInto</Action></Function>
    */
    	
        // clean up done automatically thanks to ref_ptr<>
        return 0;
    }
    

  3. Fabian 0 Reputation points
    2025-01-30T17:02:26.67+00:00

    Hello Dou,

    The debugger is still stepping into

    namespace vsg { struct t_vec3{ ... constexpr t_vec3<T> operator+(const t_vec3<T>& lhs, const t_vec3<T>& rhs)

    same struct: operator*

    and also

    namespace vsg { class ref_ptr { T* operator->()

    Screenshot 2025-01-31 094326

    and also
    namespace vsg { struct t_vec3 { constexpr t_vec3(value_type in_x, value_type in_y, value_type in_z)

    Screenshot 2025-01-31 094513

    Regards,

    Fabian

    P.S. there seem to be bugs in the QnA platform software. Screenshots sometimes don't work, and SQL is automatically detected and some code highlight block inserted where it doesn't belong

    
    
    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.