Skip to content

Commit 7cfd525

Browse files
committed
LoaderNotification gets the right dll path
1 parent a13afea commit 7cfd525

File tree

4 files changed

+162
-1
lines changed

4 files changed

+162
-1
lines changed

Source/ExcelDna.IntelliSense/ExcelDna.IntelliSense.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
<Compile Include="UIMonitor.cs" />
7272
<Compile Include="Watchers.cs" />
7373
<Compile Include="Win32Helper.cs" />
74+
<Compile Include="LoaderNotification.cs" />
7475
<Compile Include="WindowResizer.cs" />
7576
<Compile Include="WinEvents.cs" />
7677
<Compile Include="XlCall.cs" />

Source/ExcelDna.IntelliSense/FormattedText.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
namespace ExcelDna.IntelliSense
77
{
88
// TODO: Needs cleaning up for efficiency
9-
9+
// An alternative representation is as a string with extra attributes attached
10+
// each indicating a range in the string to modify.
11+
// (like NSAttributedString)
1012
class FormattedText : IEnumerable<TextLine>
1113
{
1214
readonly List<TextLine> _lines;

Source/ExcelDna.IntelliSense/IntelliSenseProvider.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ interface IIntelliSenseProvider
3838
}
3939

4040
// Provides IntelliSense info for all Excel-DNA based .xll add-ins, using the built-in RegistrationInfo helper function.
41+
// TODO: Loader monitoring - LdrRegisterDllNotification / see http://stackoverflow.com/questions/4242469/detect-when-a-module-dll-is-unloaded
4142
class ExcelDnaIntelliSenseProvider : IIntelliSenseProvider
4243
{
4344
class XllRegistrationInfo
@@ -46,10 +47,12 @@ class XllRegistrationInfo
4647
bool _regInfoNotAvailable = false; // Set to true if we know for sure that reginfo is #N/A
4748
double _version = -1; // Version indicator to enumerate from scratch
4849
object[,] _regInfo = null; // Default value
50+
LoaderNotification _dllLoadNotification;
4951

5052
public XllRegistrationInfo(string xllPath)
5153
{
5254
_xllPath = xllPath;
55+
_dllLoadNotification = new LoaderNotification();
5356
}
5457

5558
// Called in a macro context
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
using System;
2+
using System.Diagnostics;
3+
using System.Runtime.InteropServices;
4+
5+
namespace ExcelDna.IntelliSense
6+
{
7+
// Also not LDR_MODULE infor here: http://stackoverflow.com/questions/4242469/detect-when-a-module-dll-is-unloaded
8+
class LoaderNotification : IDisposable
9+
{
10+
public enum Reason : uint
11+
{
12+
Loaded = 1,
13+
Unloaded = 2
14+
}
15+
16+
// Helper for UNICODE_STRING type - couldn't figure out how to do it simply with Marshaling
17+
static class UnicodeString
18+
{
19+
// Layout is:
20+
21+
// ushort Length; // Bytes
22+
// ushort MaximumLength; // Bytes
23+
// IntPtr buffer;
24+
25+
public static string ToString(IntPtr pUnicodeString)
26+
{
27+
short length = (short)Marshal.PtrToStructure(pUnicodeString, typeof(short));
28+
IntPtr buffer = Marshal.ReadIntPtr(pUnicodeString, 4);
29+
return Marshal.PtrToStringUni(buffer, length / 2);
30+
}
31+
}
32+
33+
// At the moment, _LDR_DLL_LOADED_NOTIFICATION_DATA and _LDR_DLL_UNLOADED_NOTIFICATION_DATA
34+
// are the same, so we don't have to bother with the union.
35+
// TODO: Check 64-bit packing
36+
[StructLayout(LayoutKind.Sequential)]
37+
struct Data
38+
{
39+
public uint Flags; // Reserved.
40+
public IntPtr FullDllName; // PCUNICODE_STRING // The full path name of the DLL module.
41+
public IntPtr BaseDllName; // PCUNICODE_STRING // The base file name of the DLL module.
42+
public IntPtr DllBase; // A pointer to the base address for the DLL in memory.
43+
public uint SizeOfImage; // The size of the DLL image, in bytes.
44+
}
45+
46+
47+
enum NtStatus : uint
48+
{
49+
// Success
50+
Success = 0x00000000,
51+
DllNotFound = 0xc0000135,
52+
// Many, many others...
53+
}
54+
55+
delegate void LdrNotification(Reason notificationReason, IntPtr pNotificationData, IntPtr context);
56+
57+
// Registers for notification when a DLL is first loaded. This notification occurs before dynamic linking takes place.
58+
[DllImport("ntdll.dll")]
59+
static extern uint /*NtStatus*/ LdrRegisterDllNotification(
60+
uint flags, // This parameter must be zero.
61+
LdrNotification notificationFunction,
62+
IntPtr context,
63+
out IntPtr cookie);
64+
65+
[DllImport("ntdll.dll")]
66+
static extern uint /*NtStatus*/ LdrUnregisterDllNotification(IntPtr cookie);
67+
68+
IntPtr _cookie;
69+
LdrNotification _notificationDelegate;
70+
71+
public LoaderNotification()
72+
{
73+
IntPtr context = new IntPtr(12345);
74+
_notificationDelegate = Notification;
75+
var status = LdrRegisterDllNotification(0, _notificationDelegate, context, out _cookie);
76+
}
77+
78+
// WARNING! LoaderLock danger here
79+
void Notification(Reason notificationReason, IntPtr pNotificationData, IntPtr context)
80+
{
81+
IntPtr pFullDllName = Marshal.ReadIntPtr(pNotificationData, 4);
82+
string fullDllName = UnicodeString.ToString(pFullDllName);
83+
Debug.Print($"@@@@ LdrNotification: {notificationReason} - {fullDllName}");
84+
// Raise Event
85+
}
86+
87+
#region IDisposable Support
88+
private bool disposedValue = false; // To detect redundant calls
89+
90+
protected virtual void Dispose(bool disposing)
91+
{
92+
if (!disposedValue)
93+
{
94+
if (disposing)
95+
{
96+
// TODO: dispose managed state (managed objects).
97+
}
98+
99+
var status = LdrUnregisterDllNotification(_cookie);
100+
disposedValue = true;
101+
}
102+
}
103+
104+
~LoaderNotification()
105+
{
106+
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
107+
Dispose(false);
108+
}
109+
110+
// This code added to correctly implement the disposable pattern.
111+
public void Dispose()
112+
{
113+
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
114+
Dispose(true);
115+
GC.SuppressFinalize(this);
116+
}
117+
#endregion
118+
119+
120+
/*
121+
[StructLayout(LayoutKind.Sequential)]
122+
struct UnicodeString
123+
{
124+
// Helper for UNICODE_STRING type, with layout
125+
ushort Length; // Bytes
126+
ushort MaximumLength; // Bytes
127+
IntPtr buffer;
128+
129+
public override string ToString()
130+
{
131+
return Marshal.PtrToStringUni(buffer, Length / 2);
132+
}
133+
}
134+
135+
// At the moment, _LDR_DLL_LOADED_NOTIFICATION_DATA and _LDR_DLL_UNLOADED_NOTIFICATION_DATA
136+
// are the same, so we don't have to bother with the union.
137+
// TODO: Check 64-bit packing
138+
[StructLayout(LayoutKind.Sequential)]
139+
struct Data
140+
{
141+
public uint Flags; // Reserved.
142+
???? [MarshalAs(UnmanagedType.LPStruct, MarshalTypeRef = typeof(UnicodeString))]
143+
public UnicodeString FullDllName;
144+
???? [MarshalAs(UnmanagedType.LPStruct, MarshalTypeRef = typeof(UnicodeString))]
145+
public UnicodeString BaseDllName;
146+
//public IntPtr FullDllName; // PCUNICODE_STRING // The full path name of the DLL module.
147+
//public IntPtr BaseDllName; // PCUNICODE_STRING // The base file name of the DLL module.
148+
public IntPtr DllBase; // A pointer to the base address for the DLL in memory.
149+
public uint SizeOfImage; // The size of the DLL image, in bytes.
150+
}
151+
152+
delegate void LdrNotification(Reason notificationReason, [In] ref Data notificationData, IntPtr context);
153+
*/
154+
}
155+
}

0 commit comments

Comments
 (0)