自动化Microsoft Access [英] automate Microsoft Access
问题描述
我想在Access Runtime中的不同主题中创建2个不同的.accde文件的2个实例,
但收到错误消息:
"调用线程无法访问此对象,因为不同的线程拥有它。"
Access.Application oAccessOne = null;
Access .Application oAccessTwo = null;
private void createInstances()
      {
          Parallel.Invoke(()=>
{
Console.WriteLine(" Begin first task ...");
    oAccessOne = ShellGetApp(@"""" + @" C:\Users\user\Downloads\One.accde" + @"""" + QUOT; /运行/嵌入和QUOT ;, ProcessWindowStyle.Minimized);
        },  //靠近第一操作
()=>
     {
           Console.WriteLine("开始第二个任务..." ;);
oAccessTwo = She llGetApp(@"""" + @" C:\ Users \ user \Downloads \Two.accde" + @"""" +" / runtime / EMBEDDING",ProcessWindowStyle.Minimized);
              } //, //关闭第二个动作
< /跨度>); // close parallel.invoke
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; //设置可见
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; if(!oAccessEPM.Visible)
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; oAccessEPM.Visible = true;
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; if(!oAccessMPM.Visible)
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; oAccessMPM.Visible = true;
&NBSP; &NBSP; &NBSP; }
私有Access.Application ShellGetApp(字符串sCmdLine,ProcessWindowStyle enuWindowStyle)
&NBSP; &NBSP; &NBSP; {
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; Access.Application oAccess = null;
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; string sAccPath = null; // msaccess.exe的路径
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP;处理p = null;
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; int iMaxTries = 20; //最大GetActiveObject在失败之前尝试
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; int iTries = 0; // GetActiveObject尝试制作
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; //启用异常处理程序:
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP;试试
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; {
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; //获取msaccess.exe的路径:
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; // tuka e patot
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; sAccPath = GetOfficeAppPath("Access.Application","msaccess.exe");
$
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; if(sAccPath == null)
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; {
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; MessageBox.Show("无法确定msaccess.exe的路径");
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP;返回null;
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; }
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; //开始传递sCmdLine的新Access实例:
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; ProcessStartInfo startInfo = new ProcessStartInfo();
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; startInfo.FileName = sAccPath;
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; startInfo.Arguments = sCmdLine;
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; startInfo.WindowStyle = enuWindowStyle;
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; p = Process.Start(startInfo);
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; p.WaitForInputIdle(60000); //最多1分钟等待空闲输入状态
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; //将焦点移回此表单。这确保了访问权限
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; //在ROT中注册自己:
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; this.Activate();
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; tryGetActiveObject:
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; //启用异常处理程序:
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP;试试
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; {
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; //尝试使用GetActiveObject引用正在运行的$
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; //访问实例:
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; oAccess =(Access.Application)Marshal.GetActiveObject(" Access.Application");
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; }¥b $ b &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP;抓住
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; {
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; // GetActiveObject可能已失败,因为有足够的时间没有b $ b &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; //已过去查找正在运行的Office应用程序。等一下1/2
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; //秒并重试GetActiveObject。如果您尝试iMaxTries
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; //次和GetActiveObject仍然失败,假设其他一些为
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; // GetActiveObject失败的原因并退出程序。
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; iTries ++;
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; if(iTries< iMaxTries)
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; {
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; System.Threading.Thread.Sleep(500); //等待1/2秒
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; this.Activate();
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP;转到tryGetActiveObject;
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; }¥b $ b &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; MessageBox.Show(QUOT;无法GetActiveObject后" +
&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP; iTries + QUOT;尝试。");
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP;返回null;
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; }
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; //返回Access Application对象:
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP;返回oAccess;
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; }¥b $ b &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; catch(例外e)
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; {
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; MessageBox.Show(e.Message);
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; //由于意外错误,请尝试退出访问权限:
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP;尝试//使用try..catch以防oAccess未设置
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; {
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; oAccess.Quit(Access.AcQuitOption.acQuitSaveNone);
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; }¥b $ b &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; catch {}
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; NAR(oAccess);
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; oAccess = null;
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP;返回null;
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; }¥b $ b &NBSP; &NBSP; &NBSP; }
私有字符串GetOfficeAppPath(字符串sProgId,字符串sEXE)
&NBSP; &NBSP; &NBSP; {
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; //返回Office应用程序的路径。例如
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; //GetOfficeAppPath("Access.Application"," msaccess.exe")返回
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; // Microsoft Access的完整路径。基于Q240794的方法。
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; //如果在注册表中找不到路径,则返回null。
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; //启用异常处理程序:
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP;试试
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; {
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; Microsoft.Win32.RegistryKey oReg =
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; Microsoft.Win32.Registry.LocalMachine;
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; Microsoft.Win32.RegistryKey oKey = null;
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; string sCLSID = null;
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; string sPath = null;
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; int iPos = 0;
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; //首先,从注册表项中获取progid的clsid
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; // HKEY_LOCAL_MACHINE \Software \Classes \< PROGID> \ CLSID:
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; oKey = oReg.OpenSubKey(@" Software \Classes \" + sProgId + @" \ CLSID");
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; sCLSID = oKey.GetValue("")。ToString();
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; oKey.Close();
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; //现在我们有了CLSID,找到服务器路径为
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; // HKEY_LOCAL_MACHINE \Software \Classes \CLSID \
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx} \ LocalServer32:
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP;奥基= oReg.OpenSubKey(@" Software\Classes\CLSID\" + sCLSID +
&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP ; @" \ LocalServer32");
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; sPath = oKey.GetValue("")。ToString();
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; oKey.Close();
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; //删除exe名称以外的任何字符:
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; iPos = sPath.ToUpper()。IndexOf(sEXE.ToUpper()); //基于0的位置
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; sPath = sPath.Substring(0,iPos + sEXE.Length);
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; return sPath.Trim();
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; }¥b $ b &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; catch(例外e)
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; {
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; string sta = e.ToString();
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP;返回null;
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; }¥b $ b &NBSP; &NBSP; &NBSP; }
&NBSP; &NBSP; &NBSP;私人空虚NAR(对象o)
&NBSP; &NBSP; &NBSP; {
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; //释放Automation对象。
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP;尝试//使用try..catch,以防o未设置为
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; {
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; Marshal.ReleaseComObject(o);
&NBSP; &NBSP; &NBSP; &NBSP; &NBSP; }¥b $ b &NBSP; &NBSP; &NBSP; &NBSP; &NBSP; catch {}
&NBSP; &NBSP; &NBSP; }
如果我理解你的代码, Marshal.GetActiveObject将返回当前正在运行的实例。如果你想在一个单独的线程上创建一个新实例,我想你可能想要Activator.CreateInstance。
另外,请记住Office应用程序不支持多个线程进入一次运行实例
Hi,
I want to create 2 instances of 2 different .accde files in different threads in Access Runtime,
but I get error message:
"The calling thread cannot access this object because different thread owns it."
Access.Application oAccessOne = null;
Access.Application oAccessTwo = null;
private void createInstances()
{
Parallel.Invoke(() =>
{
Console.WriteLine("Begin first task...");
oAccessOne = ShellGetApp(@"""" + @"C:\Users\user\Downloads\One.accde" + @"""" + " /runtime /EMBEDDING", ProcessWindowStyle.Minimized);
}, // close first Action
() =>
{
Console.WriteLine("Begin second task...");
oAccessTwo = ShellGetApp(@"""" + @"C:\Users\user\Downloads\Two.accde" + @"""" + " /runtime /EMBEDDING", ProcessWindowStyle.Minimized);
} //, //close second Action
); //close parallel.invoke
// set visible
if (!oAccessEPM.Visible)
oAccessEPM.Visible = true;
if (!oAccessMPM.Visible)
oAccessMPM.Visible = true;
}
private Access.Application ShellGetApp(string sCmdLine, ProcessWindowStyle enuWindowStyle)
{
Access.Application oAccess = null;
string sAccPath = null; //path to msaccess.exe
Process p = null;
int iMaxTries = 20; //max GetActiveObject attempts before failing
int iTries = 0; //GetActiveObject attempts made
// Enable exception handler:
try
{
// Obtain the path to msaccess.exe:
// tuka e patot
sAccPath = GetOfficeAppPath("Access.Application", "msaccess.exe");
if (sAccPath == null)
{
MessageBox.Show("Can't determine path to msaccess.exe");
return null;
}
// Start a new instance of Access passing sCmdLine:
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = sAccPath;
startInfo.Arguments = sCmdLine;
startInfo.WindowStyle = enuWindowStyle;
p = Process.Start(startInfo);
p.WaitForInputIdle(60000); //max 1 minute wait for idle input state
// Move focus back to this form. This ensures that Access
// registers itself in the ROT:
this.Activate();
tryGetActiveObject:
// Enable exception handler:
try
{
// Attempt to use GetActiveObject to reference a running
// instance of Access:
oAccess = (Access.Application)Marshal.GetActiveObject("Access.Application");
}
catch
{
//GetActiveObject may have failed because enough time has not
//elapsed to find the running Office application. Wait 1/2
//second and retry the GetActiveObject. If you try iMaxTries
//times and GetActiveObject still fails, assume some other
//reason for GetActiveObject failing and exit the procedure.
iTries++;
if (iTries < iMaxTries)
{
System.Threading.Thread.Sleep(500); //wait 1/2 second
this.Activate();
goto tryGetActiveObject;
}
MessageBox.Show("Unable to GetActiveObject after " +
iTries + " tries.");
return null;
}
// Return the Access Application object:
return oAccess;
}
catch (Exception e)
{
MessageBox.Show(e.Message);
// Try to quit Access due to an unexpected error:
try // use try..catch in case oAccess is not set
{
oAccess.Quit(Access.AcQuitOption.acQuitSaveNone);
}
catch { }
NAR(oAccess);
oAccess = null;
return null;
}
}
private string GetOfficeAppPath(string sProgId, string sEXE)
{
//Returns path of the Office application. e.g.
//GetOfficeAppPath("Access.Application", "msaccess.exe") returns
//full path to Microsoft Access. Approach based on Q240794.
//Returns null if path not found in registry.
// Enable exception handler:
try
{
Microsoft.Win32.RegistryKey oReg =
Microsoft.Win32.Registry.LocalMachine;
Microsoft.Win32.RegistryKey oKey = null;
string sCLSID = null;
string sPath = null;
int iPos = 0;
// First, get the clsid from the progid from the registry key
// HKEY_LOCAL_MACHINE\Software\Classes\<PROGID>\CLSID:
oKey = oReg.OpenSubKey(@"Software\Classes\" + sProgId + @"\CLSID");
sCLSID = oKey.GetValue("").ToString();
oKey.Close();
// Now that we have the CLSID, locate the server path at
// HKEY_LOCAL_MACHINE\Software\Classes\CLSID\
// {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx}\LocalServer32:
oKey = oReg.OpenSubKey(@"Software\Classes\CLSID\" + sCLSID +
@"\LocalServer32");
sPath = oKey.GetValue("").ToString();
oKey.Close();
// Remove any characters beyond the exe name:
iPos = sPath.ToUpper().IndexOf(sEXE.ToUpper()); // 0-based position
sPath = sPath.Substring(0, iPos + sEXE.Length);
return sPath.Trim();
}
catch (Exception e)
{
string sta = e.ToString();
return null;
}
}
private void NAR(object o)
{
// Releases the Automation object.
try // use try..catch in case o is not set
{
Marshal.ReleaseComObject(o);
}
catch { }
}
If I understand your code, Marshal.GetActiveObject will simply return a currently running instance. I think you probably want Activator.CreateInstance if you want to create a new instance on a separate thread.
Also, keep in mind the the Office apps do not support multiple threads into a single running instance.
这篇关于自动化Microsoft Access的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!