ActiveDocument.ActiveWindow.Hwnd crash - vb.net

I'm using ActiveDocument.ActiveWindow.Hwnd to get the handle of the active document Window in MS Word, but it does crash in Word 2007. I tried using long instead of int, tried to catch the exception but no exception is thrown.
Public WithEvents wa As Microsoft.Office.Interop.Word.Application
wa = HostApplication
Public Function getWindowHWND() As Integer
Try
getWindowHWND = wa.ActiveDocument.ActiveWindow.Hwnd
Catch ex As Exception
MsgBox(1)
getWindowHWND = -1
Finally
End Try
End Function
Am I doing something wrong? Is this a bug? How can I get the handle in an equivalent way if it's not fixable? Thank you in advance.

No such property exist in Word 2007.
Since Word 2013 you could use the Hwnd property of Window that is exposed from the Application object:
var windowHandle = wordApplication.ActiveWindow.Hwnd;
There is no need to use the ActiveDocument property in the middle. This fails when no document is open.
In earlier Word versions you could use the Process class from the .NET BCL to retrieve the main window handle, for example:
var word = new Microsoft.Office.Interop.Word.Application();
word.Visible = true;
word.Activate();
word.Application.Caption = "My Word";
foreach( Process p in Process.GetProcessesByName( "winword" ) )
{
if( p.MainWindowTitle == "My Word" )
{
Debug.WriteLine( p.Handle.ToString() );
}
}

Related

Using mailitem.PrintOut() to print a single page?

I'm working on a simple Outlook 2016/2019 VSTO plugin.
When an email is selected and a ribbon button is pressed, it needs to print just the first page of the email to the default printer. mailitem.PrintOut(); works, but will print the whole email. Is there a way to specify the first page only?
var m = e.Control.Context as Inspector;
var mailitem = m.CurrentItem as MailItem;
if (mailitem != null)
{
mailitem.PrintOut();
}
Update: See my answer for the code I used to get this working.
The Outlook object model doesn't provide any property or method for that. You need to parse the message body on your own and use .net mechanisms for printing this piece on your own.
Note, you may try using the Word object model for printing the message bodies (a specific range of pages). The Document.PrintOut method prints all or part of the specified document. Optional parameters allow specifying the page range.
The Outlook object model provides three main ways for working with item bodies:
Body - a string representing the clear-text body of the Outlook item.
HTMLBody - a string representing the HTML body of the specified item.
Word editor - the Microsoft Word Document Object Model of the message being displayed. The WordEditor property of the Inspector class returns an instance of the Document class from the Word object model which you can use to deal with the message body.
You can read more about all these ways in the Chapter 17: Working with Item Bodies.
As #Eugene said, there's no way to specify a single page using mailItem.PrintOut.
I've finally managed to find a way to do this. I save the document as a .doc file in the temp directory, then using Microsoft.Office.Interop.Word to setup the page margins / size and then send the current page to the printer. Hopefully this helps someone as I couldn't find any working examples for c#!
private void btnPrintOnePage_Click(object sender, RibbonControlEventArgs e)
{
string randFile = Path.GetTempPath() + "POP_" + RandomString(35) + ".doc";
var m = e.Control.Context as Inspector;
var mailitem = m.CurrentItem as MailItem;
if (mailitem != null)
{
mailitem.SaveAs(randFile, OlSaveAsType.olDoc);
Word.Application ap = new Word.Application();
Word.Document document = ap.Documents.Open(randFile);
document.PageSetup.PaperSize = Word.WdPaperSize.wdPaperA4;
document.PageSetup.TopMargin = 25;
document.PageSetup.RightMargin = 25;
document.PageSetup.BottomMargin = 25;
document.PageSetup.LeftMargin = 25;
Word.WdPrintOutRange printRange = Word.WdPrintOutRange.wdPrintCurrentPage;
document.PrintOut(false,null,printRange);
document.Close(false, false, false);
File.Delete(randFile);
}
}
public static string RandomString(int length)
{
Random random = new Random();
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
return new string(Enumerable.Repeat(chars, length)
.Select(s => s[random.Next(s.Length)]).ToArray());
}

Call instance method inline after New statement

How can i convert this code to VB.net
public void SetBooks(IEnumerable<Book> books)
{
if (books == null)
throw new ArgumentNullException("books");
new System.Xml.Linq.XDocument(books).Save(_filename);
}
in http://converter.telerik.com/ it says:
Public Sub SetBooks(books As IEnumerable(Of Book))
If books Is Nothing Then
Throw New ArgumentNullException("books")
End If
New System.Xml.Linq.XDocument(books).Save(_filename)
End Sub
But visual studio says "Syntax error." because of "New"
What is the keyword for this situation, i searched on Google but no result.
Actually, you can do it in one line with the Call keyword
Call (New System.Xml.Linq.XDocument(books)).Save(_filename)
You cannot initialize an object and use it in one statement in VB.NET (as opposed to C#). You need two:
Dim doc = New System.Xml.Linq.XDocument(books)
doc.Save(_filename)
In C# the constructor returns the instance of the created object, in VB.NET not.

Cannot create ActiveX component.Error while using GetObject in autocad 2011

I am using below code to use autocad object.
Dim acadapp As AcadApplication
acadapp = GetObject(, "AutoCAD.Application")
'''and using below code to create object -------------
acadapp = CreateObject("AutoCAD.Application")
Getting error "Cannot create ActiveX component".
I tried using 18,19 and various combination as below :
acadapp = GetObject(, "AutoCAD.Application.18")
But nothing work.
Please help.
#Locke : Thanks for reply.I tried your soltion as below :
Dim acadType As Type
Try
acadapp =
System.Runtime.InteropServices.Marshal.GetActiveObject("AutoCAD.Application.18.1")
''Above code din't worked so tried below code also
' acadapp = DirectCast(Marshal.GetActiveObject("AutoCAD.Application.18.1"),
'AcadApplication)
Catch ex As Exception
acadType = Type.GetTypeFromProgID("AutoCAD.Application")
acadapp = DirectCast(Activator.CreateInstance(acadType, True), AcadApplication)
End Try
Showing Exception :
Unable to cast COM object of type 'System.__ComObject' to interface type 'AutoCAD.AcadApplication'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{8E75D910-3D21-11D2-85C4-080009A0C626}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE)).
Here's what I typically use when dealing with AutoCAD interop. It checks for a running instance, and creates a new one if necessary:
private static AcadApplication GetAcadApp(string progId)
{
// Create the return application
AcadApplication returnApp = null;
try
{
// Try getting a running instance
returnApp = (AcadApplication)Marshal.GetActiveObject(progId);
}
catch (COMException)
{
try
{
// Try creating a new instance
Type acadType = Type.GetTypeFromProgID(progId);
returnApp = (AcadApplication)Activator.CreateInstance(acadType, true);
}
catch (COMException)
{
// Report failure
MessageBox.Show(string.Format("Cannot create object of type \"{0}\"", progId));
}
}
// Return the application
return returnApp;
}
An AcadApplication COM object can be set like this:
// Get/create an AutoCAD instance
var acadApp = getAcadApp("AutoCAD.Application.18");
Regardless of C# or VB.NET, using Marshal.GetActiveObject and Activator.CreateInstance are probably the better ways to approach this.
According to the exception, the problem is not the GetActiveObject() call, but that the returned object doesn't support the interface you're looking for. Most likely reason is that your code references a different version of AcadApplication than the one you're getting back from GetActiveObject(). Change your project to reference the COM library version for the returned AutoCAD instance, and it should work fine.

How to Accept and add Categories to RequiredAttendees Appointments using Exchange Web Services

I’m using ExchangeService(ExchangeVersion.Exchange2010_SP1)
I want to Accept and add Categories to RequiredAttendees Appointments. To do this i need to find these Appointments.
My understanding in EWS is that on Save of an Appointment that has RequiredAttendees a new Meeting Request is created for each of the ‘required attendees’.
How can I access the Appointments that were created automatically for the ‘required attendees’? These are shown in the required attendees Calendars as Appointments, along with a Meeting Request.
I have managed to do a crude find on the Subject (steps below)
Connect to server as Organiser
Create Appointment
Set Subject
Add Required Attendee
Save Appointment
Connect to server as Required Attendee from step 4
Find Appointment that has Subject at step 3
Add Categories to Appointment at step 7
Update Appointment at step 7
Accept Appointment at step 7
And this does work, but concerned users will change the Subject.
I have tried adding an Extended Property and value to the Appointment created by the Organiser and then FindItems for the Extended Property value in the Appointments connected as the Required Attendee. This does not work.
Is there a preferred method for what I’m trying to accomplish?
Thank you
Private Shared ReadOnly m_organiserEmailAddress As String = "Organiser#test.com"
Private Shared ReadOnly m_eventIdExtendedPropertyDefinition As New ExtendedPropertyDefinition(DefaultExtendedPropertySet.Meeting, "EventId", MapiPropertyType.Long)
'--> start here
Public Shared Function SaveToOutlookCalendar(eventCalendarItem As EventCalendarItem) As String
If eventCalendarItem.Id Is Nothing Then
'new
Dim newAppointment = EventCalendarItemMapper.SaveNewAppointment(eventCalendarItem)
'set the Id
eventCalendarItem.Id = newAppointment.Id.UniqueId.ToString()
'accept the calendar item on behalf of the Attendee
EventCalendarItemMapper.AcceptAppointmentAsAttendees(newAppointment)
Return eventCalendarItem.Id
Else
'update existing appointment
Return EventCalendarItemMapper.UpdateAppointment(eventCalendarItem)
End If
End Function
Private Shared Sub ConnectToServer(Optional autoUser As String = "")
If autoUser = "" Then
_service.Url = New Uri(ExchangeWebServicesUrl)
Else
_service.AutodiscoverUrl(autoUser)
End If
End Sub
Private Shared Sub ImpersonateUser(userEmail As String)
_service.Credentials = New NetworkCredential(ImpersonatorUsername, ImpersonatorPassword, Domain)
_service.ImpersonatedUserId = New ImpersonatedUserId(ConnectingIdType.SmtpAddress, userEmail)
End Sub
Private Shared Function SaveNewAppointment(eventCalendarItem As EventCalendarItem) As Appointment
Try
ConnectToServer(m_organiserEmailAddress)
ImpersonateUser(m_organiserEmailAddress)
Dim appointment As New Appointment(_service) With {
.Subject = eventCalendarItem.Subject}
'add attendees
For Each attendee In eventCalendarItem.Attendees
appointment.RequiredAttendees.Add(attendee.Email)
Next
'add categories
For Each category In eventCalendarItem.Categories
appointment.Categories.Add(Globals.GetEnumDescription(category))
Next
'add EventId = 5059 as an extended property of the appointment
appointment.SetExtendedProperty(m_eventIdExtendedPropertyDefinition, 5059)
appointment.Save(SendInvitationsMode.SendOnlyToAll)
Return appointment
Catch
Throw New Exception("Can't save appointment")
End Try
End Function
Private Shared Sub AcceptAppointmentAsAttendees(appointment As Appointment)
For Each attendee In appointment.RequiredAttendees
Try
ConnectToServer(attendee.Address.ToString())
ImpersonateUser(attendee.Address.ToString())
For Each a In FindRelatedAppiontments(appointment)
a.Categories.Add(Globals.GetEnumDescription(CalendarItemCategory.Workshop))
a.Update(ConflictResolutionMode.AlwaysOverwrite, SendInvitationsOrCancellationsMode.SendToNone)
a.Accept(True)
Next
Catch
Throw
End Try
Next
End Sub
Private Shared Function FindRelatedAppiontments(appointment As Appointment) As List(Of Appointment)
Dim view As New ItemView(1000)
Dim foundAppointments As New List(Of Appointment)
view.PropertySet =
New PropertySet(New PropertyDefinitionBase() {m_eventIdExtendedPropertyDefinition})
'Extended Property value = 5059
Dim searchFilter = New SearchFilter.IsEqualTo(m_eventIdExtendedPropertyDefinition, 5059)
For Each a In _service.FindItems(WellKnownFolderName.Calendar, searchFilter, view)
If a.ExtendedProperties.Count > 0 Then
foundAppointments.Add(appointment.Bind(_service, CType(a.Id, ItemId)))
End If
Next
Return foundAppointments
End Function
Well one thing is for sure that there is nothing straight forward in the EWS.
I will be honest with that, that till the moment, I did not work with integrating from the interior calendar to the Exchange calendar, my experience is the opposite , which is get what in the exchange to the internal one.
Anyway after reading your code, I think you are almost there. However I suggest that you catch the appointments that reach to the attendee by using the Streaming Notifications it is not that hard!
So I would say the steps should be like this
Create the appointment
Apply The extended property thing ( I suggest to use the GUID instead of hard coded number) as follows
Create an extended property, and put guid for the appointment, and it wont change unless you made a copy from another appointment (after all it is just a property)
private static readonly PropertyDefinitionBase AppointementIdPropertyDefinition = new ExtendedPropertyDefinition(DefaultExtendedPropertySet.PublicStrings, "AppointmentID", MapiPropertyType.String);
public static PropertySet PropertySet = new PropertySet(BasePropertySet.FirstClassProperties, AppointementIdPropertyDefinition);
//Setting the property for the appointment
public static void SetGuidForAppointement(Appointment appointment)
{
try
{
appointment.SetExtendedProperty((ExtendedPropertyDefinition)AppointementIdPropertyDefinition, Guid.NewGuid().ToString());
appointment.Update(ConflictResolutionMode.AlwaysOverwrite, SendInvitationsOrCancellationsMode.SendToNone);
}
catch (Exception ex)
{
// logging the exception
}
}
//Getting the property for the appointment
public static string GetGuidForAppointement(Appointment appointment)
{
var result = "";
try
{
appointment.Load(PropertySet);
foreach (var extendedProperty in appointment.ExtendedProperties)
{
if (extendedProperty.PropertyDefinition.Name == "AppointmentID")
{
result = extendedProperty.Value.ToString();
}
}
}
catch (Exception ex)
{
// logging the exception
}
return result;
}
Catch the appointment with the StreamingNotificationEvent. In my opinion a good way to do that is to run both Organizer and Attendee in the same time and catch the appointments between them.
To see an example, I have posted an answer to a previous question Multiple impersonation-threads in Exchange Web Service (EWS) . Please vote for the answers of both posts (here and there) if you found my answers are useful.
I do not want to make you scared, but once you solve your current problem; if you want to continue with it will get more complicated. I could write down how I solved the meetings problem, but I do not see it straight forward at all so it might be better if you write your own.
Cheers

Error : Exception has been thrown by the target of an invocation Options

I have written an external application to drive autocad with a dll that was registered for COM. I have followed this codes to write my application however i have replaced the following code with AddNumbers() method:
public string OpenDWGFile(string MyDWGFilePath)
{
DocumentCollection dm = Application.DocumentManager;
Document doc = null;
if(File.Exists(MyDWGFilePath))
{
doc = dm.Open(MyDWGFilePath, false);
Application.DocumentManager.MdiActiveDocument = doc;
return "This file is exists";
}
else
return "This file is not exist";
}
but when i run my application the autocad software open and then close immediatly and this error message is shown : Exception has been thrown by the target of an invocation.
but if i comment the following lines of my code the application works without any errors:
doc = dm.Open(MyDWGFilePath, false);
Application.DocumentManager.MdiActiveDocument = doc;
You are creating a second instance of the DocumentManager and giving it a reference to an object retrieved from the first one. I think you want to use
dm.MdiActiveDocument = doc;