Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / Visual Studio.NET / Extensibility / July 2006

Tip: Looking for answers? Try searching our database.

Accessing sln properties in Common Properties->Debug Source Files

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
Don B. - 19 Jun 2006 21:13 GMT
I have a native implementation of a debug engine for native code.  I want to
add support to use the contents of the Common Properties->Debug Source Files
page from the solution properties.  I don't know how to access these
properties.

I had hoped that there would be a way to do it in IVSDebugger[2], but no
luck there.

IVsSolution[2] supports GetProperty(), and one of the properties returns a
string containing a semicolon-delimited list of the CLSIDs of the solution
property pages, but that doesn't really seem like a productive path to follow.

How can I get at this data?
thanks
--Don

How can I get th
"Gary Chang[MSFT]" - 20 Jun 2006 04:31 GMT
Hi Don,

Thank you posting!

This is a quick note to let you know that I am performing research on this
issue and will get back to you as soon as possible. I appreciate your
patience.

Thanks for your understanding.

Best regards,

Gary Chang
Microsoft Online Community Support
==================================================
When responding to posts, please "Reply to Group" via your newsreader so
that others may learn and benefit from your issue.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
"Gary Chang[MSFT]" - 22 Jun 2006 03:20 GMT
Hi Don,

Currently I have already forwarded this issue to our corresponding product
team, we will update you as we get any results.

Thanks!

Best regards,

Gary Chang
Microsoft Online Community Support
==================================================
When responding to posts, please "Reply to Group" via your newsreader so
that others may learn and benefit from your issue.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
"Ed Dore" - 27 Jun 2006 01:03 GMT
Hi Don,

I've spent the better part of today digging through the VS sources (to no
avail). From the looks of it, there is no programatic access to these
properties short of reading them out of the user's .SUO. The default
directories (the ones we pre-populate that property page with) are
specified as a ';' deliniated list in the registry under the
HKLM\SOFTWARE\Microsoft\VisualStudio\8.0\Source directories value.

But the actual list of directories used, is maintained by the debugger, and
persisted to the .SUO file. I have some email out to the development team
asking to confirm my suspicions regarding the having to read the values out
of the .SUO. As soon as I hear back, I'll post back on this thread.

Thanks,
Ed Dore [MSFT]

This post is "AS IS" with no warranties, and confers no rights.
Don B. - 28 Jun 2006 22:06 GMT
Thanks Ed.  Still interested on this end.

> Hi Don,
>
[quoted text clipped - 14 lines]
>
> This post is "AS IS" with no warranties, and confers no rights.
"Ed Dore" - 12 Jul 2006 04:37 GMT
Hi Don,

My apologies for the delayed response. I've been swamped with a large
number of support cases the last couple weeks. I was also tripped up by
several different problems in trying to solve this, but I was finally able
to get this working this evening.

The debugger implements an interface called IVsPersistSolutionOpts that we
can query for, off the IVsDebugger service. We then need to redefine a
couple of interfaces because the Microsoft.VisualStudio.OLE.Interop
assembly has a pretty heinous bug in it's definition of IStream.Write,
which prevents us from implementing an object that derives from this
interface. At least in this particular instance.

The last parameter to IStream::Write (in the native COM world) is a ULONG*
pbWritten. The interop assembly declares this as an "out ulong". The
problem is, it's perfectly legal to pass a NULL for this parameter (which
unfortunately is exactly what the debugger does when we pass said IStream
to it's IVsPersistSolutionOpts::WriteUserOptions.

We can't compile the code unless we make an assignment to the out
parameter. But because it's NULL, any attempt to assign a value to it, will
cause an exception to be thrown. Consequently, you'll have to redeclare
both the IStream and IVsPersistSolutionOpts interfaces, as follows:

   [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
   [Guid("0000000c-0000-0000-C000-000000000046")]
   internal interface IStream
   {
       // ISequentialStream portion
       void Read([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1),
Out] Byte[] pv, int cb, ulong[] pcbRead);
       void Write([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)]
Byte[] pv, int cb, ulong[] pcbWritten);

       // IStream portion
       void Seek(Int64 dlibMove, int dwOrigin, IntPtr plibNewPosition);
       void SetSize(Int64 libNewSize);
       void CopyTo(IStream pstm, Int64 cb, IntPtr pcbRead, IntPtr
pcbWritten);
       void Commit(int grfCommitFlags);
       void Revert();
       void LockRegion(Int64 libOffset, Int64 cb, int dwLockType);
       void UnlockRegion(Int64 libOffset, Int64 cb, int dwLockType);
       void Stat(out Microsoft.VisualStudio.OLE.Interop.STATSTG pstatstg,
int grfStatFlag);
       void Clone(out IStream ppstm);
   }

   // Customized IVsPersistSolutionOpts, so that we can pass our
customized IStream to the debugger's
   // IVsPersistSolutionOpts::WriteUserOptions. The IVsPersistSolutionOpts
   [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
   [Guid("53BA0F89-24DD-46E1-A7D6-ED24C039FBC4")]
   internal interface IVsPersistSolutionOpts
   {
       void SaveUserOptions([In, MarshalAs(UnmanagedType.Interface)]
IVsSolutionPersistence pPersistence);
       void LoadUserOptions([In, MarshalAs(UnmanagedType.Interface)]
IVsSolutionPersistence pPersistence, [In,
ComAliasName("Microsoft.VisualStudio.Shell.Interop.VSLOADUSEROPTS")] uint
grfLoadOpts);
       void WriteUserOptions([In, MarshalAs(UnmanagedType.Interface)]
IStream pOptionsStream, [In,
ComAliasName("Microsoft.VisualStudio.OLE.Interop.LPCOLESTR"),
MarshalAs(UnmanagedType.LPWStr)] string pszKey);
       void ReadUserOptions([In, MarshalAs(UnmanagedType.Interface)]
IStream pOptionsStream, [In,
ComAliasName("Microsoft.VisualStudio.OLE.Interop.LPCOLESTR"),
MarshalAs(UnmanagedType.LPWStr)] string pszKey);
   }

Note that I've changed last param to both Read and Write to a ulong[],
which allows us to check a NULL and readily make the assignment. I could
have used an IntPtr, but they we'd have to muck with the Marshal class to
return the pcbWritten and pcbRead values. Also, the IVsPersistSolutionOpts
methods will now use our custom IStream, instead of the one defined in the
interop assembly.

Next, we have to implement an object that supports our new IStream
interface, and can store the information when IStream.Write is invoked. I
created an object that derived from both the .Net MemoryStream object and
our new IStream. The IStream.Write simply calls the MemoryStream.Write
which stores the bits in a memory buffer. The ParseBuffer method then pulls
both the source directory strings, and the file to ignore strings out of
the buffer, and drops them into the member string arrays
(sourceDirectories, and ignoredFiles).

   // A dangerouly minimal IStream implementation that only supports
IStream.Write, in order
   // to get the "DebuggerFindSource" stream in the .SUO.
   internal class OptStream : MemoryStream, Microsoft.ReadSUO.IStream
   {
       public string[] sourceDirectories;
       public string[] ignoredFiles;

       // Parses the "DebuggerFindSourceStream and pulls out the
Solution's Source File Directories,
       // and Ignored source files, from the .SUO.
       public void ParseBuffer()
       {
           base.Seek(0, SeekOrigin.Begin);

           BinaryReader reader = new BinaryReader((Stream)this);
           System.Text.Encoding encoding = new
System.Text.UnicodeEncoding();
           
           // first two dwords are version numbers
           ulong verDirCache = reader.ReadUInt32();
           ulong verStringList = reader.ReadUInt32();
           
           // read Source Directories
           ulong numStrings = reader.ReadUInt32();
           
           if (numStrings > 0)
           {
               sourceDirectories = new string[numStrings];
               for (uint i = 0; i < numStrings; i++)
               {
                   // persisted as unicode strings
                   int numBytes = reader.ReadInt32();
                   char[] chars =
encoding.GetChars(reader.ReadBytes(numBytes));
                   sourceDirectories[i] = new string(chars).TrimEnd('\0');
               }
           }

           // read Ignored Files
           verStringList = reader.ReadUInt32();
           numStrings = reader.ReadUInt32();

           if (numStrings > 0)
           {
               ignoredFiles = new string[numStrings];
               for (uint j = 0; j < numStrings; j++)
               {
                   // persisted as unicode strings
                   int numBytes = reader.ReadInt32();
                   char[] chars =
encoding.GetChars(reader.ReadBytes(numBytes));
                   ignoredFiles[j] = new string(chars).TrimEnd('\0');
               }
           }
       }

       #region IStream Members

       public void Read(byte[] pv, int cb, ulong[] pcbRead)
       {
           throw new System.NotImplementedException("IStream::Read is not
implemented.");
       }

       public void Write(byte[] pv, int cb, ulong[] pcbWritten)
       {
           base.Write(pv, 0, cb);
           if (pcbWritten != null)
               pcbWritten[0] = (ulong)cb;
       }

       public void Seek(long dlibMove, int dwOrigin, IntPtr
plibNewPosition)
       {
           throw new System.NotImplementedException("IStream::Seek is not
implemented.");
       }

       public void SetSize(long libNewSize)
       {
           throw new System.NotImplementedException("IStream::SetSize is
not implemented.");
       }

       public void CopyTo(IStream pstm, long cb, IntPtr pcbRead, IntPtr
pcbWritten)
       {
           throw new System.NotImplementedException("IStream::CopyTo is
not implemented.");
       }

       public void Commit(int grfCommitFlags)
       {
           throw new System.NotImplementedException("IStream::Commit is
not implemented.");
       }

       public void Revert()
       {
           throw new System.NotImplementedException("IStream::Revert is
not implemented.");
       }

       public void LockRegion(long libOffset, long cb, int dwLockType)
       {
           throw new System.NotImplementedException("IStream::LockRegion
is not implemented.");
       }

       public void UnlockRegion(long libOffset, long cb, int dwLockType)
       {
           throw new System.NotImplementedException("IStream::UnlockRegion
is not implemented.");
       }

       public void Stat(out Microsoft.VisualStudio.OLE.Interop.STATSTG
pstatstg, int grfStatFlag)
       {
           throw new System.NotImplementedException("IStream::Stat is not
implemented.");
       }

       public void Clone(out IStream ppstm)
       {
           throw new System.NotImplementedException("IStream::Clone is not
implemented.");
       }

Finally, we just need to force the debugger to save it's settings to our
IStream. For example:

       private void OnReadSUO(object sender, EventArgs e)
       {
           // retrieve IVsPersistSolutionOpts interface from debugger
object
           IVsPersistSolutionOpts dbgPersistSlnOpts =
(IVsPersistSolutionOpts)GetService(typeof(IVsDebugger));

           // create a memory based IStream (using System.IO.MemoryStream)
           OptStream optStream = new OptStream();
           dbgPersistSlnOpts.WriteUserOptions((IStream)optStream,
"DebuggerFindSource");
           optStream.ParseBuffer();

           if (optStream.sourceDirectories != null)
           {
               Debug.WriteLine("Source Directories:");
               foreach (string dir in optStream.sourceDirectories)
                   Debug.WriteLine(dir);
           }

           if (optStream.ignoredFiles != null)
           {
               Debug.WriteLine("Ignored Source Files");
               foreach (string file in optStream.ignoredFiles)
                   Debug.WriteLine(file);
           }
       }
   }

The above code snippets are from my prototype package, and should probably
be sufficient for you to implement a workable solution. However, if you'd
like a copy of the prototype package I worked up, just fire me off an email
(removing the ".online" from my email address), and I'd be more than happy
to provide the entire project.

Sincerely,
Ed Dore [MSFT]

This post is 'AS IS' with no warranties and confers no rights.
Don B. - 12 Jul 2006 20:14 GMT
Thanks for following through on this, Ed.  A couple of questions:

(1) we have a native implementation, so our assumption is that we don't need
to worry about the workaround you describe. Correct?

(2) our debug engine is not currently a vs package, and so isn't currently
able to do QueryService.  Turning it into a vs package is probably not that
difficult, but we want to make certain that we don't go down that path if
it's breaking some architectural rules, or if it might cause the debug engine
launch (or other functions) to break.

thanks
--Don B

> Hi Don,
>
[quoted text clipped - 255 lines]
>
> This post is 'AS IS' with no warranties and confers no rights.
Ed Dore - 13 Jul 2006 02:35 GMT
Hi Don,

Shoot, I wish I'd have known that ahead of time :-)

You're correct, you don't need to worry about the interop issue if you're
addin/package is native C++ code.

How to you interact with the IDE if you cannot call QueryService? If you're
an addin, you can QI the DTE object for IServiceProvider, and call QS for
that IVsDebugger interface. Our debugger's
IVsPersistSolutionOpts::WriteUserOptions is the only way I could figure out
how to get at the "DebuggerFindSource" stream in the .SUO. I don't think
there's another way to do that unless you can reverse engineer the entire
.SUO, which is next to impossible as many different packages dump stuff in
there.

It's been a couple years since I've messed with DE's. Does your engine hold
any interfaces on the IDE? I'll have to muck with the TI sample a bit and
see if there's any way to pull an IServiceProvider from the IDE.

Sincerely,
Ed Dore [MSFT]

This post is 'AS IS' with no warranties, and confers no rights.
Don B. - 13 Jul 2006 17:18 GMT
Thanks Ed.

Barring any definitive message telling us "Don't make your DE into a
vspackage", we will probably go ahead and make that change to see if it
works.  We have another service or two we want/need to get at from the DE as
well.

thanks again
--Don B

> Hi Don,
>
[quoted text clipped - 20 lines]
>
> This post is 'AS IS' with no warranties, and confers no rights.
"Ed Dore" - 13 Jul 2006 23:01 GMT
Hi Don,

If your DE is inproc, you can use ::GetCurrentProcessId() and then find the
appropriate instance of the DTE object in the Running Object Table.  That
might be a viable alternative to implementing a VSIP package. But the
package scenario could also be used as well.

The moniker in the ROT will have a format similar to
"!VisualStudio.DTE.8.0:xxxx"

where xxxx is the Process ID for the instance(s) of DevEnv.exe running.

Sincerely,
Ed Dore [MSFT]

This post is 'AS IS' with no warranties and confers no rights.

Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.