When calling a native function that takes IntPtr I understand how to do it with a Sync native function.
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
public static void Operation(SafeHandle handle)
{
var mustReleaseSafeHandle = false;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
handle.DangerousAddRef(ref mustReleaseSafeHandle);
if (mustReleaseSafeHandle) WinAPI.Operation(handle.DangerousGetHandle());
}
finally
{
if (mustReleaseSafeHandle) handle.DangerousRelease();
}
}
How do I ensure the DangerousRelease happens when the native function is async?
Here is a pseudo example of my async attempt to add more context as Hans Passant suggests.
public static unsafe void Operation(SafeHandle fileHandle, SafeHandle nativeMemoryHandle, int offset)
{
var mustReleaseSafeHandle = false;
var ioCompletionCallback = new IOCompletionCallback((code, bytes, overlap) =>
{
if (mustReleaseSafeHandle) nativeMemoryHandle.DangerousRelease();
Overlapped.Free(overlap);
});
var overlapped = new Overlapped();
var nativeOverlapped = overlapped.Pack(ioCompletionCallback, null);
nativeMemoryHandle.DangerousAddRef(ref mustReleaseSafeHandle);
if (mustReleaseSafeHandle)
{
var memoryStartLocation = IntPtr.Add(nativeMemoryHandle.DangerousGetHandle(), offset);
if (!WinAPI.Operation(fileHandle, memoryStartLocation, offset, nativeOverlapped))
{
nativeMemoryHandle.DangerousRelease();
Overlapped.Free(nativeOverlapped);
}
}
else
{
Overlapped.Free(nativeOverlapped);
}
}
Related
I get error
System.IO.IOException: 'The process cannot access the file 'xxx' because it is being used by another process.'
when I try to delete a temp file in a background worker service in aspnet core.
I am eventually allowed to delete the file after about a minute (52s, 73s).
If I change garbage collection to workstation mode, I may instead delete after ~1s (but still, a delay).
I have tried a combination of FileOptions to no avail, including FileOptions.WriteThrough.
When the controller writes the file, I use
FlushAsync(), Close(), Dispose() and 'using' (I know it's overkill.)
I also tried using just File.WriteAllBytesAsync, with same result.
In the background reader, I as well use Close() and Dispose().
(hint: background reader will not allow me to use DeleteOnClose,
which would have been ideal.)
As I search stackoverflow for similar 'used by another process' issues,
all those I have found eventually resolve to
'argh it turns out I/he still had an extra open instance/reference
he forgot about',
but I have not been able to figure out that I am doing that.
Another hint:
In the writing controller, I am able to delete the file immediately
after writing it, I presume because I am still on the same thread?
Is there some secret knowledge I should read somewhere,
about being able to delete recently open files, across threads?
UPDATE: Here relevant(?) code snippets:
// (AspNet Controller)
[RequestSizeLimit(9999999999)]
[DisableFormValueModelBinding]
[RequestFormLimits(MultipartBodyLengthLimit = MaxFileSize)]
[HttpPost("{sessionId}")]
public async Task<IActionResult> UploadRevisionChunk(Guid sessionId) {
log.LogWarning($"UploadRevisionChunk: {sessionId}");
string uploadFolder = UploadFolder.sessionFolderPath(sessionId);
if (!Directory.Exists(uploadFolder)) { throw new Exception($"chunk-upload failed"); }
var cr = parseContentRange(Request);
if (cr == null) { return this.BadRequest("no content range header specified"); }
string chunkName = $"{cr.From}-{cr.To}";
string saveChunkPath = Path.Combine(uploadFolder,chunkName);
await streamToChunkFile_WAB(saveChunkPath); // write-all-bytes.
//await streamToChunkFile_MAN(saveChunkPath); // Manual.
long crTo = cr.To ?? 0;
long crFrom = cr.From ?? 0;
long expected = (crTo - crFrom) + 1;
var fi = new FileInfo(saveChunkPath);
var dto = new ChunkResponse { wrote = fi.Length, expected = expected, where = "?" };
string msg = $"at {crFrom}, wrote {dto.wrote} bytes (expected {dto.expected}) to {dto.where}";
log.LogWarning(msg);
return Ok(dto);
}
private async Task streamToChunkFile_WAB(string saveChunkPath) {
using (MemoryStream ms = new MemoryStream()) {
Request.Body.CopyTo(ms);
byte[] allBytes = ms.ToArray();
await System.IO.File.WriteAllBytesAsync(saveChunkPath, allBytes);
}
}
// stream reader in the backgroundService:
public class MyMultiStream : Stream {
string[] filePaths;
FileStream curStream = null;
IEnumerator<string> i;
ILogger log;
QueueItem qItem;
public MyMultiStream(string[] filePaths_, Stream[] streams_, ILogger log_, QueueItem qItem_) {
qItem = qItem_;
log = log_;
filePaths = filePaths_;
log.LogWarning($"filepaths has #items: {filePaths.Length}");
IEnumerable<string> enumerable = filePaths;
i = enumerable.GetEnumerator();
i.MoveNext();// necessary to prime the iterator.
}
public override bool CanRead { get { return true; } }
public override bool CanWrite { get { return false; } }
public override bool CanSeek { get { return false; } }
public override long Length { get { throw new Exception("dont get length"); } }
public override long Position {
get { throw new Exception("dont get Position"); }
set { throw new Exception("dont set Position"); }
}
public override void SetLength(long value) { throw new Exception("dont set length"); }
public override long Seek(long offset, SeekOrigin origin) { throw new Exception("dont seek"); }
public override void Write(byte[] buffer, int offset, int count) { throw new Exception("dont write"); }
public override void Flush() { throw new Exception("dont flush"); }
public static int openStreamCounter = 0;
public static int closedStreamCounter = 0;
string curFileName = "?";
private FileStream getNextStream() {
string nextFileName = i.Current;
if (nextFileName == null) { throw new Exception("getNextStream should not be called past file list"); }
//tryDelete(nextFileName,log);
FileStream nextStream = new FileStream(
path:nextFileName,
mode: FileMode.Open,
access: FileAccess.Read,
share: FileShare.ReadWrite| FileShare.Delete,
bufferSize:4096, // apparently default.
options: 0
| FileOptions.Asynchronous
| FileOptions.SequentialScan
// | FileOptions.DeleteOnClose // (1) this ought to be possible, (2) we should fix this approach (3) if we can fix this, our issue is solved, and our code much simpler.
); // None); // ReadWrite); // None); // ReadWrite); //| FileShare.Read);
log.LogWarning($"TELLUS making new stream [{nextFileName}] opened:[{++openStreamCounter}] closed:[{closedStreamCounter}]");
curFileName = nextFileName;
++qItem.chunkCount;
return nextStream;
}
public override int Read(byte[] buffer, int offset, int count) {
int bytesRead = 0;
while (true) {
bytesRead = 0;
if (curStream == null) { curStream = getNextStream(); }
try {
bytesRead = curStream.Read(buffer, offset, count);
log.LogWarning($"..bytesRead:{bytesRead} [{Path.GetFileName(curFileName)}]"); // (only show a short name.)
} catch (Exception e) {
log.LogError($"failed reading [{curFileName}] [{e.Message}]",e);
}
if (bytesRead > 0) { break; }
curStream.Close();
curStream.Dispose();
curStream = null;
log.LogWarning($"TELLUS closing stream [{curFileName}] opened:[{openStreamCounter}] closed:[{++closedStreamCounter}]");
//tryDelete(curFileName); Presumably we can't delete so soon.
bool moreFileNames = i.MoveNext();
log.LogWarning($"moreFileNames?{moreFileNames}");
if (!moreFileNames) {
break;
}
}
return bytesRead;
}
..
// Background worker operating multistream:
public class BackgroundChunkWorker: BackgroundService {
ILogger L;
ChunkUploadQueue q;
public readonly IServiceScopeFactory scopeFactory;
public BackgroundChunkWorker(ILogger<int> log_, ChunkUploadQueue q_, IServiceScopeFactory scopeFactory_) {
q = q_; L = log_;
scopeFactory = scopeFactory_;
}
override protected async Task ExecuteAsync(CancellationToken cancel) { await BackgroundProcessing(cancel); }
private async Task BackgroundProcessing(CancellationToken cancel) {
while (!cancel.IsCancellationRequested) {
try {
await Task.Delay(1000,cancel);
bool ok = q.q.TryDequeue(out var item);
if (!ok) { continue; }
L.LogInformation($"item found! {item}");
await treatItemScope(item);
} catch (Exception ex) {
L.LogCritical("An error occurred when processing. Exception: {#Exception}", ex);
}
}
}
private async Task<bool> treatItemScope(QueueItem Qitem) {
using (var scope = scopeFactory.CreateScope()) {
var ris = scope.ServiceProvider.GetRequiredService<IRevisionIntegrationService>();
return await treatItem(Qitem, ris);
}
}
private async Task<bool> treatItem(QueueItem Qitem, IRevisionIntegrationService ris) {
await Task.Delay(0);
L.LogWarning($"TryAddValue from P {Qitem.sessionId}");
bool addOK = q.p.TryAdd(Qitem.sessionId, Qitem);
if (!addOK) {
L.LogError($"why couldnt we add session {Qitem.sessionId} to processing-queue?");
return false;
}
var startTime = DateTime.UtcNow;
Guid revisionId = Qitem.revisionId;
string[] filePaths = getFilePaths(Qitem.sessionId);
Stream[] streams = filePaths.Select(fileName => new FileStream(fileName, FileMode.Open)).ToArray();
MyMultiStream multiStream = new MyMultiStream(filePaths, streams, this.L, Qitem);
BimRevisionStatus brs = await ris.UploadRevision(revisionId, multiStream, startTime);
// (launchDeletes is my current hack/workaround,
// it is not part of the problem)
// await multiStream.launchDeletes();
Qitem.status = brs;
return true;
}
..
So I've read the documentation and countless examples online how to marshal array of structures. I've marshalled array of int's, I've marshalled structures, but now I'm completely stuck and can't get it to work no matter what I've try. Been stuck on it for over a day now.
Structure/class, tried as both
[StructLayout(LayoutKind.Sequential,CharSet = CharSet.Unicode)]
public class SaveDetails
{
[MarshalAs(UnmanagedType.LPWStr)]
public string Log;
public FILETIME FileTime;
[MarshalAs(UnmanagedType.Bool)]
public bool Saved;
}
Pinvoke and call delegate
public class LogSaveFiles : IDisposable
{
[UnmanagedFunctionPointer(CallingConvention.Winapi,CharSet = CharSet.Unicode)]
private delegate Status DLogSaveFiles([ In, Out] SaveDetails[] logsToSave, string destinationPath);
private static DLogSaveFiles _dLogSaveFiles;
private IntPtr PLogSaveFiles { get; set; }
public bool LogSaveFilesAvailable => PLogSaveFiles != IntPtr.Zero;
public LogSaveFiles(Importer importer)
{
if (importer.dllLibraryPtr!= IntPtr.Zero)
{
PLogSaveFiles = Importer.GetProcAddress(importer.dllLibrary, "LogSaveFiles");
}
}
public Status SaveFiles(SaveDetails[] logsToSave,string destinationPath)
{
Status result = Status.FunctionNotAvailable;
if (LogSaveFilesAvailable)
{
_dLogSaveFiles = (DLogSaveFiles)Marshal.GetDelegateForFunctionPointer(PLogSaveFiles, typeof(DLogSaveFiles));
result = _dLogSaveFiles(logsToSave, destinationPath);
}
return result;
}
public void Dispose()
{
}
}
Call
private void SaveLogs()
{
var logsToSave = new[]{
new SaveDetails{
FileTime = new FILETIME {dwHighDateTime = 3,dwLowDateTime = 5},
Log = LogTypes.logDeviceLog,
Saved = true},
new SaveDetails{
FileTime = new FILETIME {dwHighDateTime = 1,dwLowDateTime = 2},
Log = LogTypes.logDeviceLog,
Saved = false}
};
var pathToSave = "C:\\Logs";
_logSaveFiles.SaveFiles(logsToSave, pathToSave);
}
c++ exposed call
typedef struct _LOG_SAVE_DETAILS
{
LPTSTR szLog;
FILETIME fromFileTime;
BOOL bSaved;
} LOG_SAVE_DETAILS, *PLOG_SAVE_DETAILS;
/* Function definitions */
ULY_STATUS _API LogSaveFiles (PLOG_SAVE_DETAILS ppLogs [],
LPCTSTR szDestinationPath);
Path to destination gets passed properly, but array of structures never goes through resulting in access violation when trying to access it. At first I thought it was issue with LPTSTR not going through properly but I've implemented other calls with it on its own and succeeded marshalling it through.
I've read everything on https://learn.microsoft.com/en-us/dotnet/framework/interop/marshaling-data-with-platform-invoke , it all indicates that my approach is correct, but it doesn't work.
Any help is appreciated.
Simple solution: C side change PLOG_SAVE_DETAILS ppLogs [] to LOG_SAVE_DETAILS ppLogs [], then C#-side change public class SaveDetails to public struct SaveDetails.
Marshaling array of objects seems to be difficult (I wasn't able to do it). Marshaling array of structs works. An alternative is to do the marshaling manually, but it is a pain.
The "pain" of manual marshaling (only modified lines of code):
[UnmanagedFunctionPointer(CallingConvention.Winapi, CharSet = CharSet.Unicode)]
private delegate Status DLogSaveFiles(IntPtr[] logsToSave, string destinationPath);
and then
public Status SaveFiles(SaveDetails[] logsToSave, string destinationPath)
{
Status result = Status.FunctionNotAvailable;
if (LogSaveFilesAvailable)
{
if (_dLogSaveFiles == null)
{
_dLogSaveFiles = (DLogSaveFiles)Marshal.GetDelegateForFunctionPointer(PLogSaveFiles, typeof(DLogSaveFiles));
}
int size = Marshal.SizeOf(typeof(SaveDetails));
IntPtr basePtr = IntPtr.Zero;
IntPtr[] ptrs = new IntPtr[logsToSave.Length + 1];
try
{
basePtr = Marshal.AllocHGlobal(size * logsToSave.Length);
for (int i = 0; i < logsToSave.Length; i++)
{
ptrs[i] = IntPtr.Add(basePtr, (i * size));
Marshal.StructureToPtr(logsToSave[i], ptrs[i], false);
}
result = _dLogSaveFiles(ptrs, destinationPath);
}
finally
{
if (basePtr != IntPtr.Zero)
{
for (int i = 0; i < logsToSave.Length; i++)
{
if (ptrs[i] != IntPtr.Zero)
{
Marshal.DestroyStructure(ptrs[i], typeof(SaveDetails));
}
}
Marshal.FreeHGlobal(basePtr);
}
}
}
return result;
}
Important: this is a marshaler C#->C++. The C++ mustn't modify the received array in any way or there will be a memory leak.
I'm trying to find Unicast, Dns and Gateway Address in windows IOT.
Normally I can access these values with NetworkInterface.GetAllNetworkInterfaces() method.
But in UWP, that method is missing.
Is there any alternative for getting these values?
You could try to PInvoke methods from Iphlpapi.dll. There are several methods that may contain the Unicast, Dns and Gateway info you're looking for, like GetInterfaceInfo(), GetAdaptersInfo(), GetAdaptersAdresses(), etc. Please see a complete list of available methods here: IP Helper Functions - MSDN. Eventually more than one method might be necessary.
As an example on how to do it, here's some sample code from PInvoke.Net retrieving interface names in my local computer, implemented as a standard UWP app:
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
private void Page_Loaded(object sender, RoutedEventArgs e)
{
List<string> list = new List<string>();
IP_INTERFACE_INFO ips = Iphlpapi.GetInterfaceInfo();
list.Add(string.Format("Adapter count = {0}", ips.NumAdapters));
foreach (IP_ADAPTER_INDEX_MAP ip in ips.Adapter)
list.Add(string.Format("Index = {0}, Name = {1}", ip.Index, ip.Name));
listView1.ItemsSource = list;
}
}
// PInvoke
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct IP_ADAPTER_INDEX_MAP
{
public int Index;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = PInvokes.MAX_ADAPTER_NAME)]
public String Name;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct IP_INTERFACE_INFO
{
public int NumAdapters;
public IP_ADAPTER_INDEX_MAP[] Adapter;
public static IP_INTERFACE_INFO FromByteArray(Byte[] buffer)
{
unsafe
{
IP_INTERFACE_INFO rv = new IP_INTERFACE_INFO();
int iNumAdapters = 0;
Marshal.Copy(buffer, 0, new IntPtr(&iNumAdapters), 4);
IP_ADAPTER_INDEX_MAP[] adapters = new IP_ADAPTER_INDEX_MAP[iNumAdapters];
rv.NumAdapters = iNumAdapters;
rv.Adapter = new IP_ADAPTER_INDEX_MAP[iNumAdapters];
int offset = sizeof(int);
for (int i = 0; i < iNumAdapters; i++)
{
IP_ADAPTER_INDEX_MAP map = new IP_ADAPTER_INDEX_MAP();
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(map));
Marshal.StructureToPtr(map, ptr, false);
Marshal.Copy(buffer, offset, ptr, Marshal.SizeOf(map));
//map = (IP_ADAPTER_INDEX_MAP)Marshal.PtrToStructure(ptr, typeof(IP_ADAPTER_INDEX_MAP));
map = Marshal.PtrToStructure<IP_ADAPTER_INDEX_MAP>(ptr);
Marshal.FreeHGlobal(ptr);
rv.Adapter[i] = map;
offset += Marshal.SizeOf(map);
}
return rv;
}
}
}
internal class PInvokes
{
public const int MAX_ADAPTER_NAME = 128;
public const int ERROR_INSUFFICIENT_BUFFER = 122;
public const int ERROR_SUCCESS = 0;
[DllImport("Iphlpapi.dll", CharSet = CharSet.Unicode)]
public static extern int GetInterfaceInfo(Byte[] PIfTableBuffer, ref int size);
[DllImport("Iphlpapi.dll", CharSet = CharSet.Unicode)]
public static extern int IpReleaseAddress(ref IP_ADAPTER_INDEX_MAP AdapterInfo);
}
public class Iphlpapi
{
public static IP_INTERFACE_INFO GetInterfaceInfo()
{
int size = 0;
int r = PInvokes.GetInterfaceInfo(null, ref size);
Byte[] buffer = new Byte[size];
r = PInvokes.GetInterfaceInfo(buffer, ref size);
if (r != PInvokes.ERROR_SUCCESS)
throw new Exception("GetInterfaceInfo returned an error.");
IP_INTERFACE_INFO info = IP_INTERFACE_INFO.FromByteArray(buffer);
return info;
}
}
Try this code Snippet I found here:
https://social.msdn.microsoft.com/Forums/en-US/27a8b7a8-8071-4bc1-bbd4-e7c1fc2bd8d7/windows-10-iot-core-how-do-you-create-a-tcp-server-and-client?forum=WindowsIoT
public static string GetDirectConnectionName()
{
var icp = NetworkInformation.GetInternetConnectionProfile();
if (icp != null)
{
if(icp.NetworkAdapter.IanaInterfaceType == EthernetIanaType)
{
return icp.ProfileName;
}
}
return null;
}
public static string GetCurrentNetworkName()
{
var icp = NetworkInformation.GetInternetConnectionProfile();
if (icp != null)
{
return icp.ProfileName;
}
var resourceLoader = ResourceLoader.GetForCurrentView();
var msg = resourceLoader.GetString("NoInternetConnection");
return msg;
}
public static string GetCurrentIpv4Address()
{
var icp = NetworkInformation.GetInternetConnectionProfile();
if (icp != null && icp.NetworkAdapter != null && icp.NetworkAdapter.NetworkAdapterId != null)
{
var name = icp.ProfileName;
var hostnames = NetworkInformation.GetHostNames();
foreach (var hn in hostnames)
{
if (hn.IPInformation != null &&
hn.IPInformation.NetworkAdapter != null &&
hn.IPInformation.NetworkAdapter.NetworkAdapterId != null &&
hn.IPInformation.NetworkAdapter.NetworkAdapterId == icp.NetworkAdapter.NetworkAdapterId &&
hn.Type == HostNameType.Ipv4)
{
return hn.CanonicalName;
}
}
}
var resourceLoader = ResourceLoader.GetForCurrentView();
var msg = resourceLoader.GetString("NoInternetConnection");
return msg;
}
As jstreet commented, Invoke is the solution. I tried with a Lumia 950, and GetAdaptersInfo() works, not as IcmpSendEcho().
With the following link you should do it:
http://www.pinvoke.net/default.aspx/iphlpapi/GetAdaptersInfo.html
When we open Package Manager Console in any open solution, it shows all the projects of that solution. How it is loading all the projects of the same solution.
When I tried with below shown code it is fetching me projects of the first solution which I have opened.
private List<Project> GetProjects()
{
var dte = (DTE)Marshal.GetActiveObject(string.Format(CultureInfo.InvariantCulture, "VisualStudio.DTE.{0}.0", targetVsVersion));
var projects = dte.Solution.OfType<Project>().ToList();
return projects;
}
Here are a various set of functions that allow you to enumerate projects in a given solution. This is how you would use it with the current solution:
// get current solution
IVsSolution solution = (IVsSolution)Microsoft.VisualStudio.Shell.Package.GetGlobalService(typeof(IVsSolution));
foreach(Project project in GetProjects(solution))
{
....
}
....
public static IEnumerable<EnvDTE.Project> GetProjects(IVsSolution solution)
{
foreach (IVsHierarchy hier in GetProjectsInSolution(solution))
{
EnvDTE.Project project = GetDTEProject(hier);
if (project != null)
yield return project;
}
}
public static IEnumerable<IVsHierarchy> GetProjectsInSolution(IVsSolution solution)
{
return GetProjectsInSolution(solution, __VSENUMPROJFLAGS.EPF_LOADEDINSOLUTION);
}
public static IEnumerable<IVsHierarchy> GetProjectsInSolution(IVsSolution solution, __VSENUMPROJFLAGS flags)
{
if (solution == null)
yield break;
IEnumHierarchies enumHierarchies;
Guid guid = Guid.Empty;
solution.GetProjectEnum((uint)flags, ref guid, out enumHierarchies);
if (enumHierarchies == null)
yield break;
IVsHierarchy[] hierarchy = new IVsHierarchy[1];
uint fetched;
while (enumHierarchies.Next(1, hierarchy, out fetched) == VSConstants.S_OK && fetched == 1)
{
if (hierarchy.Length > 0 && hierarchy[0] != null)
yield return hierarchy[0];
}
}
public static EnvDTE.Project GetDTEProject(IVsHierarchy hierarchy)
{
if (hierarchy == null)
throw new ArgumentNullException("hierarchy");
object obj;
hierarchy.GetProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_ExtObject, out obj);
return obj as EnvDTE.Project;
}
There may be a nicer way but I had a quick go at this and found this to work (it assumes you have a way of knowing the solution name). According to this post, GetActiveObject does not guarantee the current instance of VS which is why you're getting results from another instance. Instead, you can use the GetDTE method shown there:
[DllImport("ole32.dll")]
private static extern int CreateBindCtx(uint reserved, out IBindCtx ppbc);
public static DTE GetDTE(int processId)
{
string progId = "!VisualStudio.DTE.10.0:" + processId.ToString();
object runningObject = null;
IBindCtx bindCtx = null;
IRunningObjectTable rot = null;
IEnumMoniker enumMonikers = null;
try
{
Marshal.ThrowExceptionForHR(CreateBindCtx(reserved: 0, ppbc: out bindCtx));
bindCtx.GetRunningObjectTable(out rot);
rot.EnumRunning(out enumMonikers);
IMoniker[] moniker = new IMoniker[1];
IntPtr numberFetched = IntPtr.Zero;
while (enumMonikers.Next(1, moniker, numberFetched) == 0)
{
IMoniker runningObjectMoniker = moniker[0];
string name = null;
try
{
if (runningObjectMoniker != null)
{
runningObjectMoniker.GetDisplayName(bindCtx, null, out name);
}
}
catch (UnauthorizedAccessException)
{
// Do nothing, there is something in the ROT that we do not have access to.
}
if (!string.IsNullOrEmpty(name) && string.Equals(name, progId, StringComparison.Ordinal))
{
Marshal.ThrowExceptionForHR(rot.GetObject(runningObjectMoniker, out runningObject));
break;
}
}
}
finally
{
if (enumMonikers != null)
{
Marshal.ReleaseComObject(enumMonikers);
}
if (rot != null)
{
Marshal.ReleaseComObject(rot);
}
if (bindCtx != null)
{
Marshal.ReleaseComObject(bindCtx);
}
}
return (DTE)runningObject;
}
If you know the solution name in advance, you can find it in the MainWindowTitle property of Process and pass the ProcessID to the method above.
var dte = GetDTE(System.Diagnostics.Process.GetProcesses().Where(x => x.MainWindowTitle.StartsWith("SolutionName") && x.ProcessName.Contains("devenv")).FirstOrDefault().Id);
Whilst the above code worked, I encountered a COM error which I fixed by using the MessageFilter class shown here.
From that post, this is what the MessageFilter class looks like
public class MessageFilter : IOleMessageFilter
{
// Class containing the IOleMessageFilter
// thread error-handling functions.
// Start the filter.
public static void Register()
{
IOleMessageFilter newFilter = new MessageFilter();
IOleMessageFilter oldFilter = null;
CoRegisterMessageFilter(newFilter, out oldFilter);
}
// Done with the filter, close it.
public static void Revoke()
{
IOleMessageFilter oldFilter = null;
CoRegisterMessageFilter(null, out oldFilter);
}
//
// IOleMessageFilter functions.
// Handle incoming thread requests.
int IOleMessageFilter.HandleInComingCall(int dwCallType,
System.IntPtr hTaskCaller, int dwTickCount, System.IntPtr lpInterfaceInfo)
{
//Return the flag SERVERCALL_ISHANDLED.
return 0;
}
// Thread call was rejected, so try again.
int IOleMessageFilter.RetryRejectedCall(System.IntPtr
hTaskCallee, int dwTickCount, int dwRejectType)
{
if (dwRejectType == 2)
// flag = SERVERCALL_RETRYLATER.
{
// Retry the thread call immediately if return >=0 &
// <100.
return 99;
}
// Too busy; cancel call.
return -1;
}
int IOleMessageFilter.MessagePending(System.IntPtr hTaskCallee,
int dwTickCount, int dwPendingType)
{
//Return the flag PENDINGMSG_WAITDEFPROCESS.
return 2;
}
// Implement the IOleMessageFilter interface.
[DllImport("Ole32.dll")]
private static extern int
CoRegisterMessageFilter(IOleMessageFilter newFilter, out
IOleMessageFilter oldFilter);
}
[ComImport(), Guid("00000016-0000-0000-C000-000000000046"),
InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
interface IOleMessageFilter
{
[PreserveSig]
int HandleInComingCall(
int dwCallType,
IntPtr hTaskCaller,
int dwTickCount,
IntPtr lpInterfaceInfo);
[PreserveSig]
int RetryRejectedCall(
IntPtr hTaskCallee,
int dwTickCount,
int dwRejectType);
[PreserveSig]
int MessagePending(
IntPtr hTaskCallee,
int dwTickCount,
int dwPendingType);
}
Then you can access the project names like this
var dte = GetDTE(System.Diagnostics.Process.GetProcesses().Where(x => x.MainWindowTitle.StartsWith("SolutionName") && x.ProcessName.Contains("devenv")).FirstOrDefault().Id);
MessageFilter.Register();
var projects = dte.Solution.OfType<Project>().ToList();
MessageFilter.Revoke();
foreach (var proj in projects)
{
Debug.WriteLine(proj.Name);
}
Marshal.ReleaseComObject(dte);
I believe you can use something like this:
var dte = (EnvDTE.DTE)GetService(typeof(EnvDTE.DTE));
if (dte != null)
{
var solution = dte.Solution;
if (solution != null)
{
// get your projects here
}
}
I am successfully using VirtualFileDataObject code from Delay's blog, but i want to avoid streaming the entire file into memory.
I found this previously answered question on Stack Overflow Drag and Drop large virtual files from c# to Windows Explorer The question was answered by matthieu, by changing the signature of the SetData method.
Here is my problem, after changing the signature of the SetData method, other places that call it are still looking for the old signature.
Here is the original SetData;
public void SetData(short dataFormat, int index, Action<Stream> streamData)
{
_dataObjects.Add(
new DataObject
{
FORMATETC = new FORMATETC
{
cfFormat = dataFormat,
ptd = IntPtr.Zero,
dwAspect = DVASPECT.DVASPECT_CONTENT,
lindex = index,
tymed = TYMED.TYMED_ISTREAM
},
GetData = () =>
{
// Create IStream for data
var ptr = IntPtr.Zero;
var iStream = NativeMethods.CreateStreamOnHGlobal(IntPtr.Zero, true);
if (streamData != null)
{
// Wrap in a .NET-friendly Stream and call provided code to fill it
using (var stream = new IStreamWrapper(iStream))
{
streamData(stream);
}
}
// Return an IntPtr for the IStream
ptr = Marshal.GetComInterfaceForObject(iStream, typeof(IStream));
Marshal.ReleaseComObject(iStream);
return new Tuple<IntPtr, int>(ptr, NativeMethods.S_OK);
},
});
}
matthieu suggested to change it to;
public void SetData(short dataFormat, int index, Stream stream)
{
...
var iStream = new StreamWrapper(stream);
...
// Ensure the following line is commented out:
//Marshal.ReleaseComObject(iStream);
return new Tuple<IntPtr, int>(ptr, NativeMethods.S_OK);
...
}
After I make these changes the following call will not work; ( and this is where i need help)
How do i fix this call;
foreach (var fileDescriptor in fileDescriptors)
{
**SetData(FILECONTENTS, index, fileDescriptor.StreamContents);**
index++;
}
Basically changing "Action streamData" To "Stream stream" is causing my problems. I am not sure on how to call it after the changes are made.
All this code comes from Delays VirtualFileDataObject. I don't know if i should post it on here or not. But if you follow the link above it will take you to the blog so you can view it.
I am so close, just can't figure this last step out, thanks for taking a look
I've had exactly the same problem. Here is what I did to fix this issue (which as you say has not been fully addressed in the other answer)
1) Modify FileDescriptor's StreamContents property from this:
public Action<Stream> StreamContents { get; set; }
to this:
public Func<Stream> StreamContents { get; set; }
(instead of passing a Stream the client can write, we'll expect a Stream we can read from, which is exactly how Explorer works and what it expects)
2) Modify the SetData method overload from this:
public void SetData(short dataFormat, int index, Action<Stream> streamData)
to this:
public void SetData(short dataFormat, int index, Func<Stream> streamData)
3) change SetData code's GetData lambda to this:
GetData = () =>
{
ManagedIStream istream = null;
if (streamData != null)
{
Stream stream = streamData();
if (stream != null)
{
istream = new ManagedIStream(stream);
}
}
IntPtr ptr = istream != null ? Marshal.GetComInterfaceForObject(istream, typeof(IStream)) : IntPtr.Zero;
return new Tuple<IntPtr, int>(ptr, NativeMethods.S_OK);
},
4) add this ManagedIStream class to the code (you can also delete the IStreamWrapper class completely)
private class ManagedIStream : IStream
{
private Stream _stream;
public ManagedIStream(Stream stream)
{
_stream = stream;
}
public void Clone(out IStream ppstm)
{
throw new NotImplementedException();
}
public void Commit(int grfCommitFlags)
{
throw new NotImplementedException();
}
public void CopyTo(IStream pstm, long cb, IntPtr pcbRead, IntPtr pcbWritten)
{
throw new NotImplementedException();
}
public void LockRegion(long libOffset, long cb, int dwLockType)
{
throw new NotImplementedException();
}
public void Read(byte[] pv, int cb, IntPtr pcbRead)
{
int read = _stream.Read(pv, 0, cb);
if (pcbRead != IntPtr.Zero)
{
Marshal.WriteInt32(pcbRead, read);
}
}
public void Revert()
{
throw new NotImplementedException();
}
public void Seek(long dlibMove, int dwOrigin, IntPtr plibNewPosition)
{
long newPos = _stream.Seek(dlibMove, (SeekOrigin)dwOrigin);
if (plibNewPosition != IntPtr.Zero)
{
Marshal.WriteInt64(plibNewPosition, newPos);
}
}
public void SetSize(long libNewSize)
{
_stream.SetLength(libNewSize);
}
public void Stat(out System.Runtime.InteropServices.ComTypes.STATSTG pstatstg, int grfStatFlag)
{
const int STGTY_STREAM = 2;
pstatstg = new System.Runtime.InteropServices.ComTypes.STATSTG();
pstatstg.type = STGTY_STREAM;
pstatstg.cbSize = _stream.Length;
pstatstg.grfMode = 0;
if (_stream.CanRead && _stream.CanWrite)
{
const int STGM_READWRITE = 0x00000002;
pstatstg.grfMode |= STGM_READWRITE;
return;
}
if (_stream.CanRead)
{
const int STGM_READ = 0x00000000;
pstatstg.grfMode |= STGM_READ;
return;
}
if (_stream.CanWrite)
{
const int STGM_WRITE = 0x00000001;
pstatstg.grfMode |= STGM_WRITE;
return;
}
throw new IOException();
}
public void UnlockRegion(long libOffset, long cb, int dwLockType)
{
throw new NotImplementedException();
}
public void Write(byte[] pv, int cb, IntPtr pcbWritten)
{
_stream.Write(pv, 0, cb);
if (pcbWritten != IntPtr.Zero)
{
Marshal.WriteInt32(pcbWritten, cb);
}
}
}
That's it. Now you can use the code like this (using the same sample as in the original article available here: http://dlaa.me/blog/post/9913083):
new VirtualFileDataObject.FileDescriptor
{
Name = "Alphabet.txt",
Length = 26,
ChangeTimeUtc = DateTime.Now.AddDays(-1),
StreamContents = () =>
{
var contents = Enumerable.Range('a', 26).Select(i => (byte)i).ToArray();
MemoryStream ms = new MemoryStream(contents); // don't dispose/using here, it would be too early
return ms;
}
};